Initial Revision
authorTheodore Tso <tytso@mit.edu>
Mon, 6 May 1991 15:14:31 +0000 (15:14 +0000)
committerTheodore Tso <tytso@mit.edu>
Mon, 6 May 1991 15:14:31 +0000 (15:14 +0000)
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@2099 dc483132-0cff-0310-8789-dd5450dbe970

17 files changed:
src/lib/gssapi/ac_cred.c [new file with mode: 0644]
src/lib/gssapi/acc_sec.c [new file with mode: 0644]
src/lib/gssapi/check_tok.c [new file with mode: 0644]
src/lib/gssapi/cmp_name.c [new file with mode: 0644]
src/lib/gssapi/comp_oid.c [new file with mode: 0644]
src/lib/gssapi/dsp_name.c [new file with mode: 0644]
src/lib/gssapi/imp_name.c [new file with mode: 0644]
src/lib/gssapi/ind_mechs.c [new file with mode: 0644]
src/lib/gssapi/init_sec.c [new file with mode: 0644]
src/lib/gssapi/make_tok.c [new file with mode: 0644]
src/lib/gssapi/rel_buffer.c [new file with mode: 0644]
src/lib/gssapi/rel_cred.c [new file with mode: 0644]
src/lib/gssapi/rel_name.c [new file with mode: 0644]
src/lib/gssapi/rel_oidset.c [new file with mode: 0644]
src/lib/gssapi/rel_sec.c [new file with mode: 0644]
src/lib/gssapi/seal.c [new file with mode: 0644]
src/lib/gssapi/unseal.c [new file with mode: 0644]

diff --git a/src/lib/gssapi/ac_cred.c b/src/lib/gssapi/ac_cred.c
new file mode 100644 (file)
index 0000000..3810263
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * ac_cred.c --- gss_acquire_cred
+ * 
+ * $Source$
+ * $Author$
+ * $Header$
+ * 
+ * Copyright 1991 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * For copying and distribution information, please see the file
+ * <krb5/copyright.h>.
+ *
+ */
+
+/*
+ * Note: There are really two kinds of credentials in Kerberos V5...
+ * the first kind is for users, and we use a krb5_ccache to get at
+ * that.  The second kind is for servers, and we use a krb5_keytab to
+ * point at that.
+ *
+ * It is possible to convert from one to another, but we don't address
+ * that right now.
+ *
+ * XXX We need to do something with time_rec.
+ */
+
+#include <gssapi.h>
+
+char *gss_krb5_fetchfrom = NULL;
+
+OM_uint32 gss_acquire_cred(minor_status, desired_name, time_req,
+                          desired_mechs, cred_usage, output_cred_handle,
+                          actual_mechs, time_rec)
+       OM_uint32       *minor_status;
+       gss_name_t      desired_name;
+       OM_uint32       time_req;
+       gss_OID_set     desired_mechs;
+       int             cred_usage;
+       gss_cred_id_t   *output_cred_handle;
+       gss_OID_set     *actual_mechs;
+       OM_uint32       *time_rec;
+{
+       krb5_keytab_entry       entry;
+       krb5_keytab     keytabid;
+       int             do_kerberos = 0;
+       int             i;
+       krb5_error_code retval;
+       
+       *minor_status = 0;
+
+       /*
+        * Figure out which mechanism we should be using.
+        */
+       if (desired_mechs == GSS_C_NULL_OID_SET)
+               do_kerberos++;
+       else {
+               for (i = 0; i <= desired_mechs->count; i++) {
+                       if (gss_compare_OID(&desired_mechs->elements[i],
+                                          &gss_OID_krb5))
+                               do_kerberos++;
+               }
+       }
+
+       /*
+        * Should we return failure here?
+        */
+       if (!do_kerberos)
+               return(gss_make_re(GSS_RE_FAILURE));
+       output_cred_handle->cred_flags = 0;
+
+       /*
+        * This is Kerberos V5 specific stuff starting here.
+        * First, let's try to search the keytab file.
+        * Applications that know what they are doing can mess with
+        * the variable gss_krb_fetchfrom.  Otherwise, we use the
+        * system default keytab file.
+        */
+       if (*minor_status = krb5_copy_principal(desired_name,
+                                               &output_cred_handle->principal)) {
+               return(gss_make_re(GSS_RE_FAILURE));
+       }
+       if (gss_krb5_fetchfrom) {
+               /* use the named keytab */
+               retval = krb5_kt_resolve(gss_krb5_fetchfrom, &keytabid);
+       } else {
+               /* use default keytab */
+               retval = krb5_kt_default(&keytabid);
+       }
+       if (!retval) {
+               retval = krb5_kt_get_entry(keytabid, desired_name, 0, 
+                                                 &entry);
+               (void) krb5_kt_close(keytabid);
+               if (!retval) {
+                       output_cred_handle->cred_flags |= GSS_KRB_HAS_SRVTAB;
+                       output_cred_handle->kvno = entry.vno;
+                       output_cred_handle->srvtab = entry.key;
+                       krb5_free_principal(entry.principal);
+               }
+       }
+       /*
+        * Now let's try opening the default credentials file and see
+        * if it contains the desired name.  We could try searching
+        * some directory (like /tmp) if we really cared, but not for
+        * now.
+        *
+        * We're not even looking in the default credentials file
+        * right now.  XXX
+        */
+
+       /*
+        * We're done, clean up and get out.
+        */
+       if (actual_mechs) {
+               gss_OID_set     set;
+
+               if (!(set = (gss_OID_set)
+                     malloc (sizeof(struct gss_OID_set_desc)))) {
+                       *minor_status = ENOMEM;
+                       return(gss_make_re(GSS_RE_FAILURE));
+               }
+               set->count = 1;
+               set->elements = &gss_OID_krb5;
+               *actual_mechs = set;
+       }
+       return(GSS_S_COMPLETE);
+
+}
+
diff --git a/src/lib/gssapi/acc_sec.c b/src/lib/gssapi/acc_sec.c
new file mode 100644 (file)
index 0000000..0cf361d
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * acc_sec.c --- accept security context
+ * 
+ * $Source$
+ * $Author$
+ * $Header$
+ * 
+ * Copyright 1991 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * For copying and distribution information, please see the file
+ * <krb5/copyright.h>.
+ *
+ */
+
+#include <gssapi.h>
+
+extern krb5_flags    krb5_kdc_default_options;
+
+/*
+ * To do in the future:
+ *
+ *     * Support replay cache
+ *
+ *     * Support delegation of credentials
+ *
+ *     * Do something with time_rec
+ *
+ *     * Should handle Kerberos error packets being sent back and
+ *     forth.
+ */
+
+static krb5_error_code gss_krb5_keyproc(cred_handle, principal, vno, key)
+       krb5_pointer    cred_handle;
+       krb5_principal  principal;
+       krb5_kvno       vno;
+       krb5_keyblock   **key;
+{
+       gss_cred_id_t   *creds;
+       
+       creds = (gss_cred_id_t *) cred_handle;
+       
+       if (krb5_principal_compare(creds->principal, principal)) {
+               if (creds->cred_flags & GSS_KRB_HAS_SRVTAB) {
+                       *key = &creds->srvtab;
+                       return(0);
+               } else
+                       return(KRB5_KT_NOTFOUND);
+       } else
+               return(KRB5_KT_NOTFOUND);
+}
+
+
+OM_uint32 gss_accept_sec_context(minor_status, context_handle,
+                                verifier_cred_handle, input_token,
+                                channel, src_name,
+                                mech_type, output_token,
+                                ret_flags, time_rec,
+                                delegated_cred_handle)
+       OM_uint32       *minor_status;
+       gss_ctx_id_t    *context_handle;
+       gss_cred_id_t   verifier_cred_handle;
+       gss_buffer_t    input_token;
+       gss_channel_bindings    channel;
+       gss_name_t      *src_name;
+       gss_OID         *mech_type;
+       gss_buffer_t    output_token;
+       int             *ret_flags;
+       OM_uint32       *time_rec;
+       gss_cred_id_t   *delegated_cred_handle;
+{
+       krb5_rcache             rcache;
+       krb5_address            sender_addr;
+       krb5_data               inbuf, outbuf;
+       krb5_principal          server;
+       krb5_tkt_authent        *authdat;
+       OM_uint32               retval;
+       gss_ctx_id_t    context;
+       
+       *minor_status = 0;
+
+       if (!context_handle) {
+               /*
+                * This is first call to accept_sec_context
+                *
+                * Make sure the input token is sane.
+                */
+               if (retval = gss_check_token(minor_status, input_token,
+                                            GSS_API_KRB5_TYPE,
+                                            GSS_API_KRB5_REQ))
+                       return(retval);
+               inbuf.length = input_token->length-5;
+               inbuf.data = ( (char *) input_token->value)+5;
+               sender_addr.addrtype = channel.sender_addrtype;
+               sender_addr.length = channel.sender_address.length;
+               sender_addr.contents = channel.sender_address.value;
+               server = verifier_cred_handle.principal;
+               /*
+                * Setup the replay cache.
+                */
+               if (*minor_status = krb5_get_server_rcache(server[1]->data,
+                                                          &rcache))
+                       return(gss_make_re(GSS_RE_FAILURE));
+               /*
+                * Now let's rip apart the packet
+                */
+               if (*minor_status = krb5_rd_req(&inbuf, server, &sender_addr,
+                                               0, gss_krb5_keyproc,
+                                               &verifier_cred_handle,
+                                               rcache, &authdat))
+                       return(gss_make_re(GSS_RE_FAILURE));
+               if (*minor_status = krb5_rc_close(rcache))
+                       return(gss_make_re(GSS_RE_FAILURE));
+               
+               /*
+                * Allocate the context handle structure
+                */
+               if (!(context = malloc(sizeof(struct gss_ctx_id_desc)))) {
+                       *minor_status = ENOMEM;
+                       return(gss_make_re(GSS_RE_FAILURE));
+               }
+               context->mech_type = &gss_OID_krb5;
+               context->flags = 0;
+               context->state =  GSS_KRB_STATE_DOWN;
+               context->am_client = 0;
+               
+               context->my_address.addrtype = channel.sender_addrtype;
+               context->my_address.length = channel.sender_address.length;
+               if (!(context->my_address.contents =
+                     malloc(context->my_address.length))) {
+                       xfree(context);
+                       return(gss_make_re(GSS_RE_FAILURE));
+               }
+               memcpy((char *) context->my_address.contents,
+                      (char *) channel.sender_address.value,
+                      context->my_address.length);
+               context->his_address.addrtype = channel.receiver_addrtype;
+               context->his_address.length = channel.receiver_address.length;
+               if (!(context->his_address.contents =
+                     malloc(context->my_address.length))) {
+                       xfree(context->my_address.contents);
+                       xfree(context);
+                       return(gss_make_re(GSS_RE_FAILURE));
+               }
+               memcpy((char *) context->his_address.contents,
+                      (char *) channel.receiver_address.value,
+                      context->his_address.length);
+               
+               /*
+                * Do mutual authentication if requested.
+                */
+               output_token->length = 0;
+               if ((authdat->ap_options & AP_OPTS_MUTUAL_REQUIRED)) {
+                       krb5_ap_rep_enc_part    repl;
+                       /*
+                        * Generate a random sequence number
+                        */
+                       if (*minor_status =
+                           krb5_generate_seq_number(authdat->ticket->enc_part2->session,
+                                                    &context->my_seq_num)) {
+                               xfree(context->his_address.contents);
+                               xfree(context->my_address.contents);
+                               xfree(context);
+                               krb5_free_tkt_authent(authdat);
+                               return(gss_make_re(GSS_RE_FAILURE));
+                       }
+
+                       repl.ctime = authdat->authenticator->ctime;
+                       repl.cusec = authdat->authenticator->cusec;
+                       repl.subkey = authdat->authenticator->subkey;
+                       repl.seq_number = context->my_seq_num;
+
+                       if (*minor_status =
+                           krb5_mk_rep(&repl,
+                                       authdat->ticket->enc_part2->session,
+                                       &outbuf)) {
+                               xfree(context->his_address.contents);
+                               xfree(context->my_address.contents);
+                               xfree(context);
+                               krb5_free_tkt_authent(authdat);
+                               return(gss_make_re(GSS_RE_FAILURE));
+                       }
+                       if (*minor_status = gss_make_token(minor_status,
+                                                          GSS_API_KRB5_TYPE,
+                                                          GSS_API_KRB5_REQ,
+                                                          outbuf.length,
+                                                          outbuf.data,
+                                                          output_token)) {
+                               xfree(context->his_address.contents);
+                               xfree(context->my_address.contents);
+                               xfree(context);
+                               xfree(outbuf.data);
+                               krb5_free_tkt_authent(authdat);
+                               return(gss_make_re(GSS_RE_FAILURE));
+                       }
+               }
+                       
+               /*
+                * Fill in context handle structure
+                */
+               if (*minor_status =
+                   krb5_copy_principal(verifier_cred_handle.principal,
+                                       &context->me)) {
+                       xfree(context->his_address.contents);
+                       xfree(context->my_address.contents);
+                       xfree(context);
+                       return(gss_make_re(GSS_RE_FAILURE));
+               }
+               if (*minor_status =
+                   krb5_copy_principal(authdat->authenticator->client,
+                                       &context->him)) {
+                       krb5_free_principal(context->me);
+                       xfree(context->his_address.contents);
+                       xfree(context->my_address.contents);
+                       xfree(context);
+                       return(gss_make_re(GSS_RE_FAILURE));
+               }
+               if (*minor_status =
+                   krb5_copy_keyblock(authdat->ticket->enc_part2->session,
+                                      &context->session_key)) {
+                       krb5_free_principal(context->me);
+                       krb5_free_principal(context->him);
+                       xfree(context->his_address.contents);
+                       xfree(context->my_address.contents);
+                       xfree(context);
+                       return(gss_make_re(GSS_RE_FAILURE));
+               }
+               context->his_seq_num = authdat->authenticator->seq_number;
+               context->cusec = authdat->authenticator->cusec;
+               context->ctime = authdat->authenticator->ctime;
+               context->flags = ((char *) input_token->value)[4];
+               /*
+                * Strip out flags we don't support (yet) XXX
+                */
+               context->flags  &= ~(GSS_C_DELEG_FLAG | GSS_C_REPLAY_FLAG);
+               /*
+                * Deliver output parameters
+                */
+               if (src_name) {
+                       if (*minor_status = krb5_copy_principal(context->him,
+                                                               src_name)) {
+                               xfree(context->session_key->contents);
+                               krb5_free_principal(context->me);
+                               krb5_free_principal(context->him);
+                               xfree(context->his_address.contents);
+                               xfree(context->my_address.contents);
+                               xfree(context);
+                               return(gss_make_re(GSS_RE_FAILURE));
+                       }
+               }
+               if (mech_type)
+                       *mech_type = &gss_OID_krb5;
+               *ret_flags = context->flags;
+               if (time_rec)
+                       *time_rec = GSS_TIME_REC_INDEFINITE;
+               return(GSS_S_COMPLETE);
+       } else {
+               /*
+                * Context is non-null, this is the second time through....
+                */
+               return(gss_make_re(GSS_RE_FAILURE));
+       }
+}
+
diff --git a/src/lib/gssapi/check_tok.c b/src/lib/gssapi/check_tok.c
new file mode 100644 (file)
index 0000000..2407349
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * check_tok.c --- Read a GSS API token and do error checking
+ *             checking on it.
+ * 
+ * $Source$
+ * $Author$
+ * $Header$
+ * 
+ * Copyright 1991 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * For copying and distribution information, please see the file
+ * <krb5/copyright.h>.
+ *
+ */
+
+#include <gssapi.h>
+
+OM_uint32 gss_check_token(minor_status, input_token, mechanism, type)
+       OM_uint32       *minor_status;
+       gss_buffer_t    input_token;
+       unsigned char   mechanism;
+       unsigned char   type;
+{
+       char    *buf;
+       
+       *minor_status = 0;
+       
+       if (!input_token)
+               return(gss_make_ce(GSS_CE_CALL_INACCESSIBLE_READ));
+
+       if (input_token->length < 4)
+               return(gss_make_re(GSS_RE_DEFECTIVE_TOKEN));
+
+       buf = input_token->value;
+       
+       if (buf[0] != GSS_API_IMPL_VERSION)
+               return(gss_make_re(GSS_RE_DEFECTIVE_TOKEN));
+       
+       if (mechanism && (mechanism != buf[1]))
+               return(gss_make_re(GSS_RE_BAD_MECH));
+
+       if (type && (type != buf[2]))
+               return(gss_make_re(GSS_RE_FAILURE) | GSS_SS_UNSEQ_TOKEN);
+
+       return(GSS_S_COMPLETE);
+}
diff --git a/src/lib/gssapi/cmp_name.c b/src/lib/gssapi/cmp_name.c
new file mode 100644 (file)
index 0000000..450c5ed
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * imp_name.c --- import_name
+ * 
+ * $Source$
+ * $Author$
+ * $Header$
+ * 
+ * Copyright 1991 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * For copying and distribution information, please see the file
+ * <krb5/copyright.h>.
+ *
+ */
+
+#include <gssapi.h>
+
+OM_uint32 gss_compare_name(minor_status, name1, name2, name_equal)
+       OM_uint32       *minor_status;
+       gss_name_t      name1, name2;
+       int             *name_equal;
+{
+       *minor_status = 0;
+
+       *name_equal = krb5_principal_compare(name1, name2);
+       return(GSS_S_COMPLETE);
+}
+
diff --git a/src/lib/gssapi/comp_oid.c b/src/lib/gssapi/comp_oid.c
new file mode 100644 (file)
index 0000000..3f24f02
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * comp_oid.c --- compare OID's
+ *
+ * $Source$
+ * $Author$
+ * $Header$
+ * 
+ * Copyright 1991 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * For copying and distribution information, please see the file
+ * <krb5/copyright.h>.
+ *
+ */
+
+#include <gssapi.h>
+
+int gss_compare_OID(oid1, oid2)
+       gss_OID oid1, oid2;
+{
+       if (oid1->length != oid2->length)
+               return(0);
+       return (!memcmp(oid1->elements, oid2->elements, oid1->length));
+}
diff --git a/src/lib/gssapi/dsp_name.c b/src/lib/gssapi/dsp_name.c
new file mode 100644 (file)
index 0000000..cdce737
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * dsp_name.c --- display_name
+ * 
+ * $Source$
+ * $Author$
+ * $Header$
+ * 
+ * Copyright 1991 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * For copying and distribution information, please see the file
+ * <krb5/copyright.h>.
+ *
+ */
+
+#include <gssapi.h>
+
+OM_uint32 gss_display_name(minor_status, input_name, output_name_buffer)
+       OM_uint32       *minor_status;
+       gss_name_t      input_name;
+       gss_buffer_t    output_name_buffer;
+{
+       char            *str;
+       
+       if (*minor_status = krb5_unparse_name(input_name, &str))
+               return(gss_make_re(GSS_RE_FAILURE));
+       output_name_buffer->value = str;
+       output_name_buffer->length = strlen(str);
+       return(GSS_S_COMPLETE);
+}
diff --git a/src/lib/gssapi/imp_name.c b/src/lib/gssapi/imp_name.c
new file mode 100644 (file)
index 0000000..62a2433
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * imp_name.c --- import_name
+ * 
+ * $Source$
+ * $Author$
+ * $Header$
+ * 
+ * Copyright 1991 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * For copying and distribution information, please see the file
+ * <krb5/copyright.h>.
+ *
+ */
+
+#include <gssapi.h>
+
+OM_uint32 gss_service_import_name();
+       
+OM_uint32 gss_import_name(minor_status, input_name_buffer, input_name_type,
+                         output_name)
+       OM_uint32       *minor_status;
+       gss_buffer_t    input_name_buffer;
+       gss_OID         input_name_type;
+       gss_name_t      *output_name;
+{
+       *minor_status = 0;
+
+       if ((input_name_type == GSS_C_NULL_OID) ||
+           gss_compare_OID(input_name_type, &gss_OID_krb5)) {
+               /*
+                * Kerberos V5 name
+                */
+               if (!strncasecmp("service:", input_name_buffer->value, 8) &&
+                   input_name_buffer->length >= 8) {
+                       return(gss_service_import_name(minor_status,
+                                                      input_name_buffer,
+                                                      output_name));
+               }
+               if (*minor_status = krb5_parse_name(input_name_buffer->value,
+                                                   output_name))
+                       return(gss_make_re(GSS_RE_FAILURE));
+               else 
+                       return(GSS_S_COMPLETE);
+       }
+       /*
+        * It's of an unknown type.  We don't know how to deal.
+        */
+       return(gss_make_re(GSS_RE_BAD_NAMETYPE));
+}
+       
+                            
+OM_uint32 gss_service_import_name(minor_status, input_name_buffer, output_name)
+       OM_uint32       *minor_status;
+       gss_buffer_t    input_name_buffer;
+       gss_name_t      *output_name;
+{
+       char    *str, *cp;
+       char    *service, *kservice;
+       char    *host;
+       char    buf[512];
+       
+       if (!(str = malloc(input_name_buffer->length+1))) {
+               *minor_status = ENOMEM;
+               return(gss_make_re(GSS_RE_FAILURE));
+       }
+       memcpy(str, input_name_buffer->value, input_name_buffer->length);
+       str[input_name_buffer->length] = '\0';
+       
+       /*
+        * Assume the first eight characters are "service:"
+        */
+       service = cp = str + 8;
+       if (!(cp = index(cp, '@'))) {
+               free(str);
+               return(gss_make_re(GSS_RE_BAD_NAME));
+       }
+       *cp++ = 0;
+       host = cp;
+       /*
+        * We will need to do some mapping here later... XXX
+        */
+       kservice = service;
+       
+       sprintf(buf, "%s/%s", kservice, host);
+       
+       if (*minor_status = krb5_parse_name(buf, output_name)) 
+               return(gss_make_re(GSS_RE_FAILURE));
+       else 
+               return(GSS_S_COMPLETE);
+}      
+
diff --git a/src/lib/gssapi/ind_mechs.c b/src/lib/gssapi/ind_mechs.c
new file mode 100644 (file)
index 0000000..be9ba63
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * ind_mechs.c --- Indicate mechanisms  (also where the OID's are declared)
+ *
+ * $Source$
+ * $Author$
+ * $Header$
+ * 
+ * Copyright 1991 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * For copying and distribution information, please see the file
+ * <krb5/copyright.h>.
+ *
+ */
+
+#include <gssapi.h>
+
+struct gss_OID_desc gss_OID_krb5 = { 15, "KRB5.OSI.SUCKS"};
+struct gss_OID_desc gss_OID_krb5_name = { 20, "KRB5.NAME.OSI.SUCKS" };
+
+OM_uint32 gss_indicate_mechs(minor_status, mech_set)
+       OM_uint32       *minor_status;
+       gss_OID_set     *mech_set;
+{
+       gss_OID_set     set;
+       
+       *minor_status = 0;
+       if (!(set = (gss_OID_set) malloc (sizeof(struct gss_OID_set_desc)))) {
+               *minor_status = ENOMEM;
+               return(gss_make_re(GSS_RE_FAILURE));
+       }
+       set->count = 1;
+       set->elements = &gss_OID_krb5;
+       *mech_set = set;
+       return(GSS_S_COMPLETE);
+}
+       
+                            
diff --git a/src/lib/gssapi/init_sec.c b/src/lib/gssapi/init_sec.c
new file mode 100644 (file)
index 0000000..a4f03c5
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * init_sec.c --- initialize security context
+ * 
+ * $Source$
+ * $Author$
+ * $Header$
+ * 
+ * Copyright 1991 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * For copying and distribution information, please see the file
+ * <krb5/copyright.h>.
+ *
+ */
+
+#include <gssapi.h>
+
+extern krb5_flags    krb5_kdc_default_options;
+
+/*
+ * To do in the future:
+ *
+ *     * Support replay cache
+ *
+ *     * Support delegation of credentials
+ *
+ *     * Do something with time_rec
+ *
+ *     * Should handle Kerberos error packets being sent back and
+ *     forth.
+ */
+
+gss_cred_id_t  gss_default_credentials = {
+       (krb5_principal) NULL, (gss_OID) NULL, 0, (krb5_ccache) NULL,
+       (krb5_kvno) 0, { (krb5_keytype) 0, 0, (krb5_octet *) NULL }
+};
+               
+
+OM_uint32 gss_init_sec_context(minor_status, claimant_cred_handle,
+                              context_handle, target_name,
+                              mech_type, req_flags, time_req,
+                              channel, input_token,
+                              actual_mech_type, output_token,
+                              ret_flags, time_rec)
+       OM_uint32       *minor_status;
+       gss_cred_id_t   claimant_cred_handle;
+       gss_ctx_id_t    *context_handle;
+       gss_name_t      target_name;
+       gss_OID         mech_type;
+       int             req_flags;
+       int             time_req;
+       gss_channel_bindings    channel;
+       gss_buffer_t    input_token;
+       gss_OID         *actual_mech_type;
+       gss_buffer_t    output_token;
+       int             *ret_flags;
+       OM_uint32       *time_rec;
+{
+       krb5_flags              kdc_options = krb5_kdc_default_options;
+       krb5_flags              ap_req_options = 0;
+       krb5_ccache             ccache;
+       krb5_creds              creds;
+       krb5_authenticator      authent;
+       krb5_data               inbuf, outbuf;
+       krb5_ap_rep_enc_part    *repl;
+       OM_uint32               retval;
+       gss_ctx_id_t    context;
+       
+       *minor_status = 0;
+
+       if (!context_handle) {
+               /*
+                * This is first call to init_sec_context
+                *
+                * We only handle Kerberos V5...
+                */
+               if ((mech_type != GSS_C_NULL_OID) &&
+                   !gss_compare_OID(mech_type, &gss_OID_krb5)) {
+                       return(gss_make_re(GSS_RE_BAD_MECH));
+               }
+               if (actual_mech_type)
+                       *actual_mech_type = &gss_OID_krb5;
+               /*
+                * Sanitize the incoming flags
+                *
+                * We don't support delegation or replay detection --- yet.
+                */
+               req_flags &= ~GSS_C_DELEG_FLAG;
+               req_flags &= ~GSS_C_REPLAY_FLAG; 
+               /*
+                * If no credentials were passed in, get our own
+                */
+               if (claimant_cred_handle.ccache)
+                       ccache = claimant_cred_handle.ccache;
+               else {
+                       /*
+                        * Default (or NULL) credentials, we need to
+                        * fill in with defaults.
+                        */
+                       if (*minor_status = krb5_cc_default(&ccache)) {
+                               return(gss_make_re(GSS_RE_FAILURE));
+                       }
+                       claimant_cred_handle.ccache = ccache;
+                       if (*minor_status =
+                           krb5_cc_get_principal(ccache,
+                                                 &claimant_cred_handle.principal))
+                               return(gss_make_re(GSS_RE_FAILURE));
+               }
+               /*
+                * Allocate the context handle structure
+                */
+               if (!(context = malloc(sizeof(struct gss_ctx_id_desc)))) {
+                       *minor_status = ENOMEM;
+                       return(gss_make_re(GSS_RE_FAILURE));
+               }
+               context->mech_type = &gss_OID_krb5;
+               context->state =  GSS_KRB_STATE_DOWN;
+               /*
+                * Fill in context handle structure
+                */
+               if (*minor_status =
+                   krb5_copy_principal(claimant_cred_handle.principal,
+                                       &context->me))
+                       return(gss_make_re(GSS_RE_FAILURE));
+               if (*minor_status =
+                   krb5_copy_principal(target_name,
+                                       &context->him))
+                       return(gss_make_re(GSS_RE_FAILURE));
+               context->flags = req_flags | GSS_C_CONF_FLAG;;
+               context->am_client = 1;
+               context->session_key = NULL;
+               context->my_address.addrtype = channel.sender_addrtype;
+               context->my_address.length = channel.sender_address.length;
+               if (!(context->my_address.contents =
+                     malloc(context->my_address.length))) {
+                       xfree(context);
+                       return(gss_make_re(GSS_RE_FAILURE));
+               }
+               memcpy((char *) context->my_address.contents,
+                      (char *) channel.sender_address.value,
+                      context->my_address.length);
+               context->his_address.addrtype = channel.receiver_addrtype;
+               context->his_address.length = channel.receiver_address.length;
+               if (!(context->his_address.contents =
+                     malloc(context->my_address.length))) {
+                       xfree(context->my_address.contents);
+                       xfree(context);
+                       return(gss_make_re(GSS_RE_FAILURE));
+               }
+               memcpy((char *) context->his_address.contents,
+                      (char *) channel.receiver_address.value,
+                      context->his_address.length);
+               /*
+                * Generate a random sequence number
+                */
+               if (*minor_status =
+                   krb5_generate_seq_number(&creds.keyblock,
+                                            &context->my_seq_num)) {
+                       xfree(context->his_address.contents);
+                       xfree(context->my_address.contents);
+                       free((char *)context);
+                       return(make_gss_re(GSS_RE_FAILURE));
+               }
+               context->his_seq_num = 0;
+               /*
+                * Make a credentials structure
+                */
+               memset((char *)&creds, 0, sizeof(creds));
+               creds.server = context->him;
+               creds.client = context->me;
+               /* creds.times.endtime = 0; -- memset 0 takes care of this
+                                       zero means "as long as possible" */
+               /* creds.keyblock.keytype = 0; -- as well as this.
+                                       zero means no session keytype
+                                       preference */
+               if (*minor_status = krb5_get_credentials(0,
+                                                        ccache,
+                                                        &creds)) {
+                       krb5_free_cred_contents(&creds);
+                       free((char *)context);
+                       return(gss_make_re(GSS_RE_FAILURE));
+               }
+               /*
+                * Setup the ap_req_options
+                */
+               if ((req_flags & GSS_C_MUTUAL_FLAG) ||
+                   (req_flags & GSS_C_SEQUENCE_FLAG))
+                       ap_req_options |= AP_OPTS_MUTUAL_REQUIRED;
+               /*
+                * OK, get the authentication header!
+                */
+               if (*minor_status = krb5_mk_req_extended(ap_req_options, 0,
+                                                 &creds.times,
+                                                 kdc_options,
+                                                 context->my_seq_num, 0,
+                                                 ccache, &creds, &authent,
+                                                 &outbuf)) {
+                       memset((char *)&authent, 0, sizeof(authent));
+                       krb5_free_cred_contents(&creds);
+                       free((char *)context);
+                       return(gss_make_re(GSS_RE_FAILURE));    
+               }
+               context->cusec = authent.cusec;
+               context->ctime = authent.ctime;
+               memset((char *)&authent, 0, sizeof(authent));
+               
+               if (*minor_status =
+                   krb5_copy_keyblock(&creds.keyblock,
+                                      &context->session_key)) {
+                       xfree(outbuf.data);
+                       krb5_free_cred_contents(&creds);
+                       free((char *)context);
+                       return(gss_make_re(GSS_RE_FAILURE));
+               }
+               
+               if (*minor_status = gss_make_token(minor_status,
+                                                  GSS_API_KRB5_TYPE,
+                                                  GSS_API_KRB5_REQ,
+                                                  outbuf.length,
+                                                  outbuf.data,
+                                                  output_token)) {
+                       xfree(outbuf.data);
+                       krb5_free_cred_contents(&creds);
+                       free((char *) context);
+                       return(gss_make_re(GSS_RE_FAILURE));
+               }
+               /*
+                * Send over the requested flags information
+                */
+               ((char *) output_token->value)[4] = context->flags;
+               xfree(outbuf.data);
+               *context_handle = context;
+               context->state = GSS_KRB_STATE_DOWN;
+               *ret_flags = context->flags;
+               /*
+                * Don't free server and client because we need them
+                * for the context structure.
+                */
+               creds.server = 0;
+               creds.client = 0;
+               krb5_free_cred_contents(&creds);
+               if (ap_req_options & AP_OPTS_MUTUAL_REQUIRED) {
+                       context->state = GSS_KRB_STATE_MUTWAIT;
+                       return(GSS_SS_CONTINUE_NEEDED);
+               } else {
+                       context->state = GSS_KRB_STATE_UP;
+                       return(GSS_S_COMPLETE);
+               }
+               
+       } else {
+               context = *context_handle;
+
+               if (context->state != GSS_KRB_STATE_MUTWAIT)
+                       return(gss_make_re(GSS_RE_FAILURE));
+               if (retval = gss_check_token(minor_status, input_token,
+                                            GSS_API_KRB5_TYPE,
+                                            GSS_API_KRB5_REP))
+                       return(retval);
+               inbuf.length = input_token->length-4;
+               inbuf.data = ((char *)input_token->value)+4;
+               
+               if (*minor_status = krb5_rd_rep(&inbuf, context->session_key,
+                                               &repl))
+                       return(gss_make_re(GSS_RE_FAILURE));
+               if ((repl->ctime != context->ctime) ||
+                   (repl->cusec != context->cusec)) {
+                       *minor_status = KRB5_SENDAUTH_MUTUAL_FAILED;
+                       return(gss_make_re(GSS_RE_FAILURE));
+               }
+               context->his_seq_num = repl->seq_number;
+               context->state = GSS_KRB_STATE_UP;
+               krb5_free_ap_rep_enc_part(repl);
+               return(GSS_S_COMPLETE);
+       }
+}
diff --git a/src/lib/gssapi/make_tok.c b/src/lib/gssapi/make_tok.c
new file mode 100644 (file)
index 0000000..97a46be
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * make_tok.c --- Make a GSS API token
+ * 
+ * $Source$
+ * $Author$
+ * $Header$
+ * 
+ * Copyright 1991 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * For copying and distribution information, please see the file
+ * <krb5/copyright.h>.
+ *
+ */
+
+#include <gssapi.h>
+
+OM_uint32 gss_make_token(minor_status, mechanism, type, length, data,
+                        output_token)
+       OM_uint32       *minor_status;
+       unsigned char   mechanism;
+       unsigned char   type;
+       size_t          length;
+       void            *data;
+       gss_buffer_t    output_token;
+{
+       char    *buf;
+       int     offset = 4;
+
+       *minor_status = 0;
+       /*
+        * The Kerberos initial request token needs an extra byte of
+        * flag information, so we reserve it here.
+        */
+       if ((mechanism == GSS_API_KRB5_TYPE) && (type == GSS_API_KRB5_REQ))
+               offset++;
+       if (!(buf = malloc(length+offset))) {
+               *minor_status = ENOMEM;
+               return(gss_make_re(GSS_RE_FAILURE));
+       }
+       output_token->value = buf;
+       output_token->length = length+4;
+       buf[0] = GSS_API_IMPL_VERSION;
+       buf[1] = mechanism;             /* Authentication mechanism */
+       buf[2] = type;                  /* Token type */
+       buf[3] = 0;                     /* Reserved */
+       memcpy(buf+offset, data, length);
+       return(GSS_S_COMPLETE);
+}
diff --git a/src/lib/gssapi/rel_buffer.c b/src/lib/gssapi/rel_buffer.c
new file mode 100644 (file)
index 0000000..022969c
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * rel_buffer.c --- release a gss_buffer_t
+ *
+ * $Source$
+ * $Author$
+ * $Header$
+ * 
+ * Copyright 1991 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * For copying and distribution information, please see the file
+ * <krb5/copyright.h>.
+ *
+ */
+
+#include <gssapi.h>
+
+OM_uint32 gss_release_buffer(minor_status, buffer)
+       OM_uint32       *minor_status;
+       gss_buffer_t    buffer;
+{
+       *minor_status = 0;
+
+       free(buffer->value);
+       return(GSS_S_COMPLETE);
+}
+       
+                            
diff --git a/src/lib/gssapi/rel_cred.c b/src/lib/gssapi/rel_cred.c
new file mode 100644 (file)
index 0000000..e2f09d1
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * rel_cred.c --- release a gss_cred_id_t
+ *
+ * $Source$
+ * $Author$
+ * $Header$
+ * 
+ * Copyright 1991 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * For copying and distribution information, please see the file
+ * <krb5/copyright.h>.
+ *
+ */
+
+#include <gssapi.h>
+
+OM_uint32 gss_release_cred(minor_status, cred_handle)
+       OM_uint32       *minor_status;
+       gss_cred_id_t   *cred_handle;
+{
+       krb5_free_principal(cred_handle->principal);
+       if (*minor_status = krb5_cc_close(cred_handle->ccache))
+               return(gss_make_re(GSS_RE_FAILURE));
+       xfree(cred_handle->srvtab.contents);
+       return(GSS_S_COMPLETE);
+}
+       
+                            
diff --git a/src/lib/gssapi/rel_name.c b/src/lib/gssapi/rel_name.c
new file mode 100644 (file)
index 0000000..f04f5c0
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * rel_name.c --- release a gss_name_t
+ * 
+ * $Source$
+ * $Author$
+ * $Header$
+ * 
+ * Copyright 1991 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * For copying and distribution information, please see the file
+ * <krb5/copyright.h>.
+ *
+ */
+
+#include <gssapi.h>
+
+OM_uint32 gss_release_name(minor_status, name)
+       OM_uint32       *minor_status;
+       gss_name_t      *name;
+{
+       *minor_status = 0;
+
+       krb5_free_principal(*name);
+       return(GSS_S_COMPLETE);
+}
+       
+                            
diff --git a/src/lib/gssapi/rel_oidset.c b/src/lib/gssapi/rel_oidset.c
new file mode 100644 (file)
index 0000000..216d9c9
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * rel_oidset.c --- release a fgss_oid_set
+ *
+ * $Source$
+ * $Author$
+ * $Header$
+ * 
+ * Copyright 1991 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * For copying and distribution information, please see the file
+ * <krb5/copyright.h>.
+ *
+ */
+
+#include <gssapi.h>
+
+OM_uint32 gss_release_oid_set(minor_status, set)
+       OM_uint32       *minor_status;
+       gss_OID_set     set;
+{
+       *minor_status = 0;
+
+       /*
+        * We don't free the OID parts themselves, since they will all
+        * be constant structures in this version
+        */
+       free((char *)set);
+       return(GSS_S_COMPLETE);
+}
+       
+                            
diff --git a/src/lib/gssapi/rel_sec.c b/src/lib/gssapi/rel_sec.c
new file mode 100644 (file)
index 0000000..8702e98
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * rel_buffer.c --- release a security context
+ *
+ * $Source$
+ * $Author$
+ * $Header$
+ * 
+ * Copyright 1991 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * For copying and distribution information, please see the file
+ * <krb5/copyright.h>.
+ *
+ */
+
+#include <gssapi.h>
+
+OM_uint32 gss_release_security_context(minor_status, context)
+       OM_uint32       *minor_status;
+       gss_ctx_id_t    context;
+{
+       *minor_status = 0;
+
+       krb5_free_principal(context->me);
+       krb5_free_principal(context->him);
+       
+       context->state = GSS_KRB5_STATE_DOWN;
+       return(GSS_S_COMPLETE);
+}
+       
+                            
diff --git a/src/lib/gssapi/seal.c b/src/lib/gssapi/seal.c
new file mode 100644 (file)
index 0000000..d6e78e9
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * seal.c --- seal message
+ * 
+ * $Source$
+ * $Author$
+ * $Header$
+ * 
+ * Copyright 1991 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * For copying and distribution information, please see the file
+ * <krb5/copyright.h>.
+ *
+ */
+
+#include <gssapi.h>
+
+OM_uint32 gss_seal(minor_status, context, conf_req_flag, qop_req, 
+                  input_message_buffer, conf_state, output_message_buffer)
+       OM_uint32       *minor_status;
+       gss_ctx_id_t    context;
+       int             conf_req_flag;
+       int             qop_req;
+       gss_buffer_t    input_message_buffer;
+       int             *conf_state;
+       gss_buffer_t    output_message_buffer;
+{
+       krb5_data       inbuf, outbuf;
+       
+       *minor_status = 0;
+
+       inbuf.length = input_message_buffer->length;
+       inbuf.data = input_message_buffer->value;
+       if (conf_req_flag) {
+               int     priv_flags = 0;
+               int             eblock_size;
+               char            *i_vector;
+
+               if (context->flags & GSS_C_SEQUENCE_FLAG)
+                       priv_flags = KRB5_PRIV_DOSEQUENCE|KRB5_PRIV_NOTIME;
+               /*
+                * Initialize the initial vector.
+                */
+               eblock_size =
+                       krb5_keytype_array[context->session_key->keytype]->
+                               system->block_length;
+               if (!(i_vector=malloc(eblock_size))) {
+                       return(gss_make_re(GSS_RE_FAILURE));
+               }
+               memset(i_vector, 0, eblock_size);
+               if (*minor_status = krb5_mk_priv(&inbuf, ETYPE_DES_CBC_CRC,
+                                                context->session_key,
+                                                &context->my_address,
+                                                &context->his_address,
+                                                context->my_seq_num,
+                                                priv_flags,
+                                                0, /* no rcache */
+                                                i_vector,
+                                                &outbuf))
+                       return(gss_make_re(GSS_RE_FAILURE));
+               if (*minor_status = gss_make_token(minor_status,
+                                                  GSS_API_KRB5_TYPE,
+                                                  GSS_API_KRB5_PRIV,
+                                                  outbuf.length,
+                                                  outbuf.data,
+                                                  output_message_buffer)) {
+                       xfree(outbuf.data);
+                       return(gss_make_re(GSS_RE_FAILURE));
+               }
+               if (conf_state)
+                       *conf_state = 1;
+               if (context->flags & GSS_C_SEQUENCE_FLAG)
+                       context->my_seq_num++;
+               return(GSS_S_COMPLETE);
+       } else {
+               int     safe_flags = 0;
+
+               if (context->flags & GSS_C_SEQUENCE_FLAG)
+                       safe_flags = KRB5_SAFE_DOSEQUENCE|KRB5_SAFE_NOTIME;
+               if (*minor_status = krb5_mk_safe(&inbuf,
+                                                CKSUMTYPE_RSA_MD4_DES,
+                                                context->session_key,
+                                                &context->my_address,
+                                                &context->his_address,
+                                                context->my_seq_num,
+                                                safe_flags,
+                                                0, /* no rcache */
+                                                &outbuf))
+                       return(gss_make_re(GSS_RE_FAILURE));
+               if (*minor_status = gss_make_token(minor_status,
+                                                  GSS_API_KRB5_TYPE,
+                                                  GSS_API_KRB5_SAFE,
+                                                  outbuf.length,
+                                                  outbuf.data,
+                                                  output_message_buffer)) {
+                       xfree(outbuf.data);
+                       return(gss_make_re(GSS_RE_FAILURE));
+               }
+               if (conf_state)
+                       *conf_state = 0;
+               if (context->flags & GSS_C_SEQUENCE_FLAG)
+                       context->my_seq_num++;
+               return(GSS_S_COMPLETE);
+       }
+}
+       
+/*
+ * XXX This is done inefficiently; the token in gss_sign does not need
+ * to include the text of the data, just a cryptographic checksum to
+ * act as a checksum.  Nevertheless, this is a quick and dirty way to
+ * get it to work.  When we fix this so that it works for real, we
+ * will need to let gss_verify accept both, and change the servers
+ * first. 
+ */
+
+OM_uint32 gss_sign(minor_status, context, qop_req, 
+                  input_message_buffer, output_message_buffer)
+       OM_uint32       *minor_status;
+       gss_ctx_id_t    context;
+       int             qop_req;
+       gss_buffer_t    input_message_buffer;
+       gss_buffer_t    output_message_buffer;
+{
+       return(gss_seal(minor_status, context, 0, qop_req, 
+                       input_message_buffer, NULL, output_message_buffer));
+}
+
diff --git a/src/lib/gssapi/unseal.c b/src/lib/gssapi/unseal.c
new file mode 100644 (file)
index 0000000..fc51e55
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * seal.c --- seal message
+ * 
+ * $Source$
+ * $Author$
+ * $Header$
+ * 
+ * Copyright 1991 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * For copying and distribution information, please see the file
+ * <krb5/copyright.h>.
+ *
+ */
+
+#include <gssapi.h>
+
+OM_uint32 gss_unseal(minor_status, context, input_message_buffer,
+                    output_message_buffer, conf_state, qop_state)
+       OM_uint32       *minor_status;
+       gss_ctx_id_t    context;
+       gss_buffer_t    input_message_buffer;
+       gss_buffer_t    output_message_buffer;
+       int             *conf_state;
+       int             *qop_state;
+{
+       OM_uint32       retval;
+       krb5_data       inbuf, outbuf;
+       int             token_type;
+
+       *minor_status = 0;
+
+       if (retval = gss_check_token(minor_status, input_message_buffer,
+                                    GSS_API_KRB5_TYPE, 0))
+               return(retval);
+       token_type = ((char *) input_message_buffer->value)[4];
+       if ((token_type != GSS_API_KRB5_SAFE) &&
+           (token_type != GSS_API_KRB5_PRIV))
+               return(gss_make_re(GSS_RE_DEFECTIVE_TOKEN));
+       inbuf.length = input_message_buffer->length-4;
+       inbuf.data = ( (char *) input_message_buffer->value)+4;
+       if (token_type == GSS_API_KRB5_PRIV) {
+               int     priv_flags = 0;
+               int             eblock_size;
+               char            *i_vector;
+
+               if (context->flags & GSS_C_SEQUENCE_FLAG)
+                       priv_flags = KRB5_PRIV_DOSEQUENCE|KRB5_PRIV_NOTIME;
+               /*
+                * Initialize the initial vector.
+                */
+               eblock_size =
+                       krb5_keytype_array[context->session_key->keytype]->
+                               system->block_length;
+               if (!(i_vector=malloc(eblock_size))) {
+                       return(gss_make_re(GSS_RE_FAILURE));
+               }
+               memset(i_vector, 0, eblock_size);
+               if (*minor_status = krb5_rd_priv(&inbuf, 
+                                                context->session_key,
+                                                &context->his_address,
+                                                &context->my_address,
+                                                context->his_seq_num,
+                                                priv_flags,
+                                                i_vector,
+                                                0, /* no rcache */
+                                                &outbuf))
+                       return(gss_make_re(GSS_RE_FAILURE));
+               if (conf_state)
+                       *conf_state = 1;
+       } else {
+               int     safe_flags = 0;
+
+               if (context->flags & GSS_C_SEQUENCE_FLAG)
+                       safe_flags = KRB5_SAFE_DOSEQUENCE|KRB5_SAFE_NOTIME;
+               if (*minor_status = krb5_rd_safe(&inbuf,
+                                                context->session_key,
+                                                &context->his_address,
+                                                &context->my_address,
+                                                context->his_seq_num,
+                                                safe_flags,
+                                                0, /* no rcache */
+                                                &outbuf))
+                       return(gss_make_re(GSS_RE_FAILURE));
+               if (conf_state)
+                       *conf_state = 0;
+       }
+       if (qop_state)
+               *qop_state = 0;
+       output_message_buffer->length = outbuf.length;
+       output_message_buffer->value = outbuf.data;
+       return(GSS_S_COMPLETE);
+}
+       
+OM_uint32 gss_verify(minor_status, context, message_buffer,  
+                  token_buffer, qop_state)
+       OM_uint32       *minor_status;
+       gss_ctx_id_t    context;
+       gss_buffer_t    message_buffer;
+       gss_buffer_t    token_buffer;
+       int             *qop_state;
+{
+       OM_uint32 retval, ret;
+       gss_buffer_desc buf;
+       gss_buffer_t    output_message_buffer = &buf;
+       
+       
+       if (retval = gss_unseal(minor_status, context, message_buffer,
+                    output_message_buffer, NULL, qop_state))
+               return(retval);
+       if (token_buffer->length != output_message_buffer->length)
+               ret = gss_make_re(GSS_RE_BAD_SIG);
+       else if (!memcmp(token_buffer->value, output_message_buffer->value,
+                        token_buffer->length))
+               ret = gss_make_re(GSS_RE_BAD_SIG);
+       if (retval = gss_release_buffer(minor_status, output_message_buffer))
+               return(retval);
+       return(ret);
+}
+