The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/rpc/rpcsec_gss/svc_rpcsec_gss.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2008 Doug Rabson
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  */
   26 /*
   27   svc_rpcsec_gss.c
   28   
   29   Copyright (c) 2000 The Regents of the University of Michigan.
   30   All rights reserved.
   31 
   32   Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
   33   All rights reserved, all wrongs reversed.
   34 
   35   Redistribution and use in source and binary forms, with or without
   36   modification, are permitted provided that the following conditions
   37   are met:
   38 
   39   1. Redistributions of source code must retain the above copyright
   40      notice, this list of conditions and the following disclaimer.
   41   2. Redistributions in binary form must reproduce the above copyright
   42      notice, this list of conditions and the following disclaimer in the
   43      documentation and/or other materials provided with the distribution.
   44   3. Neither the name of the University nor the names of its
   45      contributors may be used to endorse or promote products derived
   46      from this software without specific prior written permission.
   47 
   48   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
   49   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
   50   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   51   DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   52   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   53   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   54   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
   55   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
   56   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
   57   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   58   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   59 
   60   $Id: svc_auth_gss.c,v 1.27 2002/01/15 15:43:00 andros Exp $
   61  */
   62 
   63 #include <sys/cdefs.h>
   64 __FBSDID("$FreeBSD: releng/10.2/sys/rpc/rpcsec_gss/svc_rpcsec_gss.c 242090 2012-10-25 19:30:58Z rmacklem $");
   65 
   66 #include <sys/param.h>
   67 #include <sys/systm.h>
   68 #include <sys/jail.h>
   69 #include <sys/kernel.h>
   70 #include <sys/kobj.h>
   71 #include <sys/lock.h>
   72 #include <sys/malloc.h>
   73 #include <sys/mbuf.h>
   74 #include <sys/mutex.h>
   75 #include <sys/proc.h>
   76 #include <sys/sx.h>
   77 #include <sys/ucred.h>
   78 
   79 #include <rpc/rpc.h>
   80 #include <rpc/rpcsec_gss.h>
   81 
   82 #include "rpcsec_gss_int.h"
   83 
   84 static bool_t   svc_rpc_gss_wrap(SVCAUTH *, struct mbuf **);
   85 static bool_t   svc_rpc_gss_unwrap(SVCAUTH *, struct mbuf **);
   86 static void     svc_rpc_gss_release(SVCAUTH *);
   87 static enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *);
   88 static int rpc_gss_svc_getcred(struct svc_req *, struct ucred **, int *);
   89 
   90 static struct svc_auth_ops svc_auth_gss_ops = {
   91         svc_rpc_gss_wrap,
   92         svc_rpc_gss_unwrap,
   93         svc_rpc_gss_release,
   94 };
   95 
   96 struct sx svc_rpc_gss_lock;
   97 
   98 struct svc_rpc_gss_callback {
   99         SLIST_ENTRY(svc_rpc_gss_callback) cb_link;
  100         rpc_gss_callback_t      cb_callback;
  101 };
  102 static SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback)
  103         svc_rpc_gss_callbacks = SLIST_HEAD_INITIALIZER(svc_rpc_gss_callbacks);
  104 
  105 struct svc_rpc_gss_svc_name {
  106         SLIST_ENTRY(svc_rpc_gss_svc_name) sn_link;
  107         char                    *sn_principal;
  108         gss_OID                 sn_mech;
  109         u_int                   sn_req_time;
  110         gss_cred_id_t           sn_cred;
  111         u_int                   sn_program;
  112         u_int                   sn_version;
  113 };
  114 static SLIST_HEAD(svc_rpc_gss_svc_name_list, svc_rpc_gss_svc_name)
  115         svc_rpc_gss_svc_names = SLIST_HEAD_INITIALIZER(svc_rpc_gss_svc_names);
  116 
  117 enum svc_rpc_gss_client_state {
  118         CLIENT_NEW,                             /* still authenticating */
  119         CLIENT_ESTABLISHED,                     /* context established */
  120         CLIENT_STALE                            /* garbage to collect */
  121 };
  122 
  123 #define SVC_RPC_GSS_SEQWINDOW   128
  124 #ifndef RPCAUTH_UNIXGIDS
  125 #define RPCAUTH_UNIXGIDS        16
  126 #endif
  127 
  128 struct svc_rpc_gss_clientid {
  129         unsigned long           ci_hostid;
  130         uint32_t                ci_boottime;
  131         uint32_t                ci_id;
  132 };
  133 
  134 struct svc_rpc_gss_client {
  135         TAILQ_ENTRY(svc_rpc_gss_client) cl_link;
  136         TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink;
  137         volatile u_int          cl_refs;
  138         struct sx               cl_lock;
  139         struct svc_rpc_gss_clientid cl_id;
  140         time_t                  cl_expiration;  /* when to gc */
  141         enum svc_rpc_gss_client_state cl_state; /* client state */
  142         bool_t                  cl_locked;      /* fixed service+qop */
  143         gss_ctx_id_t            cl_ctx;         /* context id */
  144         gss_cred_id_t           cl_creds;       /* delegated creds */
  145         gss_name_t              cl_cname;       /* client name */
  146         struct svc_rpc_gss_svc_name *cl_sname;  /* server name used */
  147         rpc_gss_rawcred_t       cl_rawcred;     /* raw credentials */
  148         rpc_gss_ucred_t         cl_ucred;       /* unix-style credentials */
  149         struct ucred            *cl_cred;       /* kernel-style credentials */
  150         int                     cl_rpcflavor;   /* RPC pseudo sec flavor */
  151         bool_t                  cl_done_callback; /* TRUE after call */
  152         void                    *cl_cookie;     /* user cookie from callback */
  153         gid_t                   cl_gid_storage[RPCAUTH_UNIXGIDS];
  154         gss_OID                 cl_mech;        /* mechanism */
  155         gss_qop_t               cl_qop;         /* quality of protection */
  156         uint32_t                cl_seqlast;     /* sequence window origin */
  157         uint32_t                cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */
  158 };
  159 TAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client);
  160 
  161 /*
  162  * This structure holds enough information to unwrap arguments or wrap
  163  * results for a given request. We use the rq_clntcred area for this
  164  * (which is a per-request buffer).
  165  */
  166 struct svc_rpc_gss_cookedcred {
  167         struct svc_rpc_gss_client *cc_client;
  168         rpc_gss_service_t       cc_service;
  169         uint32_t                cc_seq;
  170 };
  171 
  172 #define CLIENT_HASH_SIZE        256
  173 #define CLIENT_MAX              128
  174 struct svc_rpc_gss_client_list svc_rpc_gss_client_hash[CLIENT_HASH_SIZE];
  175 struct svc_rpc_gss_client_list svc_rpc_gss_clients;
  176 static size_t svc_rpc_gss_client_count;
  177 static uint32_t svc_rpc_gss_next_clientid = 1;
  178 
  179 static void
  180 svc_rpc_gss_init(void *arg)
  181 {
  182         int i;
  183 
  184         for (i = 0; i < CLIENT_HASH_SIZE; i++)
  185                 TAILQ_INIT(&svc_rpc_gss_client_hash[i]);
  186         TAILQ_INIT(&svc_rpc_gss_clients);
  187         svc_auth_reg(RPCSEC_GSS, svc_rpc_gss, rpc_gss_svc_getcred);
  188         sx_init(&svc_rpc_gss_lock, "gsslock");
  189 }
  190 SYSINIT(svc_rpc_gss_init, SI_SUB_KMEM, SI_ORDER_ANY, svc_rpc_gss_init, NULL);
  191 
  192 bool_t
  193 rpc_gss_set_callback(rpc_gss_callback_t *cb)
  194 {
  195         struct svc_rpc_gss_callback *scb;
  196 
  197         scb = mem_alloc(sizeof(struct svc_rpc_gss_callback));
  198         if (!scb) {
  199                 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
  200                 return (FALSE);
  201         }
  202         scb->cb_callback = *cb;
  203         sx_xlock(&svc_rpc_gss_lock);
  204         SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link);
  205         sx_xunlock(&svc_rpc_gss_lock);
  206 
  207         return (TRUE);
  208 }
  209 
  210 void
  211 rpc_gss_clear_callback(rpc_gss_callback_t *cb)
  212 {
  213         struct svc_rpc_gss_callback *scb;
  214 
  215         sx_xlock(&svc_rpc_gss_lock);
  216         SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
  217                 if (scb->cb_callback.program == cb->program
  218                     && scb->cb_callback.version == cb->version
  219                     && scb->cb_callback.callback == cb->callback) {
  220                         SLIST_REMOVE(&svc_rpc_gss_callbacks, scb,
  221                             svc_rpc_gss_callback, cb_link);
  222                         sx_xunlock(&svc_rpc_gss_lock);
  223                         mem_free(scb, sizeof(*scb));
  224                         return;
  225                 }
  226         }
  227         sx_xunlock(&svc_rpc_gss_lock);
  228 }
  229 
  230 static bool_t
  231 rpc_gss_acquire_svc_cred(struct svc_rpc_gss_svc_name *sname)
  232 {
  233         OM_uint32               maj_stat, min_stat;
  234         gss_buffer_desc         namebuf;
  235         gss_name_t              name;
  236         gss_OID_set_desc        oid_set;
  237 
  238         oid_set.count = 1;
  239         oid_set.elements = sname->sn_mech;
  240 
  241         namebuf.value = (void *) sname->sn_principal;
  242         namebuf.length = strlen(sname->sn_principal);
  243 
  244         maj_stat = gss_import_name(&min_stat, &namebuf,
  245                                    GSS_C_NT_HOSTBASED_SERVICE, &name);
  246         if (maj_stat != GSS_S_COMPLETE)
  247                 return (FALSE);
  248 
  249         if (sname->sn_cred != GSS_C_NO_CREDENTIAL)
  250                 gss_release_cred(&min_stat, &sname->sn_cred);
  251 
  252         maj_stat = gss_acquire_cred(&min_stat, name,
  253             sname->sn_req_time, &oid_set, GSS_C_ACCEPT, &sname->sn_cred,
  254             NULL, NULL);
  255         if (maj_stat != GSS_S_COMPLETE) {
  256                 gss_release_name(&min_stat, &name);
  257                 return (FALSE);
  258         }
  259         gss_release_name(&min_stat, &name);
  260 
  261         return (TRUE);
  262 }
  263 
  264 bool_t
  265 rpc_gss_set_svc_name(const char *principal, const char *mechanism,
  266     u_int req_time, u_int program, u_int version)
  267 {
  268         struct svc_rpc_gss_svc_name *sname;
  269         gss_OID                 mech_oid;
  270 
  271         if (!rpc_gss_mech_to_oid(mechanism, &mech_oid))
  272                 return (FALSE);
  273 
  274         sname = mem_alloc(sizeof(*sname));
  275         if (!sname)
  276                 return (FALSE);
  277         sname->sn_principal = strdup(principal, M_RPC);
  278         sname->sn_mech = mech_oid;
  279         sname->sn_req_time = req_time;
  280         sname->sn_cred = GSS_C_NO_CREDENTIAL;
  281         sname->sn_program = program;
  282         sname->sn_version = version;
  283 
  284         if (!rpc_gss_acquire_svc_cred(sname)) {
  285                 free(sname->sn_principal, M_RPC);
  286                 mem_free(sname, sizeof(*sname));
  287                 return (FALSE);
  288         }
  289 
  290         sx_xlock(&svc_rpc_gss_lock);
  291         SLIST_INSERT_HEAD(&svc_rpc_gss_svc_names, sname, sn_link);
  292         sx_xunlock(&svc_rpc_gss_lock);
  293 
  294         return (TRUE);
  295 }
  296 
  297 void
  298 rpc_gss_clear_svc_name(u_int program, u_int version)
  299 {
  300         OM_uint32               min_stat;
  301         struct svc_rpc_gss_svc_name *sname;
  302 
  303         sx_xlock(&svc_rpc_gss_lock);
  304         SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
  305                 if (sname->sn_program == program
  306                     && sname->sn_version == version) {
  307                         SLIST_REMOVE(&svc_rpc_gss_svc_names, sname,
  308                             svc_rpc_gss_svc_name, sn_link);
  309                         sx_xunlock(&svc_rpc_gss_lock);
  310                         gss_release_cred(&min_stat, &sname->sn_cred);
  311                         free(sname->sn_principal, M_RPC);
  312                         mem_free(sname, sizeof(*sname));
  313                         return;
  314                 }
  315         }
  316         sx_xunlock(&svc_rpc_gss_lock);
  317 }
  318 
  319 bool_t
  320 rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
  321     const char *mech, const char *name, const char *node, const char *domain)
  322 {
  323         OM_uint32               maj_stat, min_stat;
  324         gss_OID                 mech_oid;
  325         size_t                  namelen;
  326         gss_buffer_desc         buf;
  327         gss_name_t              gss_name, gss_mech_name;
  328         rpc_gss_principal_t     result;
  329 
  330         if (!rpc_gss_mech_to_oid(mech, &mech_oid))
  331                 return (FALSE);
  332 
  333         /*
  334          * Construct a gss_buffer containing the full name formatted
  335          * as "name/node@domain" where node and domain are optional.
  336          */
  337         namelen = strlen(name);
  338         if (node) {
  339                 namelen += strlen(node) + 1;
  340         }
  341         if (domain) {
  342                 namelen += strlen(domain) + 1;
  343         }
  344 
  345         buf.value = mem_alloc(namelen);
  346         buf.length = namelen;
  347         strcpy((char *) buf.value, name);
  348         if (node) {
  349                 strcat((char *) buf.value, "/");
  350                 strcat((char *) buf.value, node);
  351         }
  352         if (domain) {
  353                 strcat((char *) buf.value, "@");
  354                 strcat((char *) buf.value, domain);
  355         }
  356 
  357         /*
  358          * Convert that to a gss_name_t and then convert that to a
  359          * mechanism name in the selected mechanism.
  360          */
  361         maj_stat = gss_import_name(&min_stat, &buf,
  362             GSS_C_NT_USER_NAME, &gss_name);
  363         mem_free(buf.value, buf.length);
  364         if (maj_stat != GSS_S_COMPLETE) {
  365                 rpc_gss_log_status("gss_import_name", mech_oid, maj_stat, min_stat);
  366                 return (FALSE);
  367         }
  368         maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid,
  369             &gss_mech_name);
  370         if (maj_stat != GSS_S_COMPLETE) {
  371                 rpc_gss_log_status("gss_canonicalize_name", mech_oid, maj_stat,
  372                     min_stat);
  373                 gss_release_name(&min_stat, &gss_name);
  374                 return (FALSE);
  375         }
  376         gss_release_name(&min_stat, &gss_name);
  377 
  378         /*
  379          * Export the mechanism name and use that to construct the
  380          * rpc_gss_principal_t result.
  381          */
  382         maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf);
  383         if (maj_stat != GSS_S_COMPLETE) {
  384                 rpc_gss_log_status("gss_export_name", mech_oid, maj_stat, min_stat);
  385                 gss_release_name(&min_stat, &gss_mech_name);
  386                 return (FALSE);
  387         }
  388         gss_release_name(&min_stat, &gss_mech_name);
  389 
  390         result = mem_alloc(sizeof(int) + buf.length);
  391         if (!result) {
  392                 gss_release_buffer(&min_stat, &buf);
  393                 return (FALSE);
  394         }
  395         result->len = buf.length;
  396         memcpy(result->name, buf.value, buf.length);
  397         gss_release_buffer(&min_stat, &buf);
  398 
  399         *principal = result;
  400         return (TRUE);
  401 }
  402 
  403 bool_t
  404 rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
  405     rpc_gss_ucred_t **ucred, void **cookie)
  406 {
  407         struct svc_rpc_gss_cookedcred *cc;
  408         struct svc_rpc_gss_client *client;
  409 
  410         if (req->rq_cred.oa_flavor != RPCSEC_GSS)
  411                 return (FALSE);
  412 
  413         cc = req->rq_clntcred;
  414         client = cc->cc_client;
  415         if (rcred)
  416                 *rcred = &client->cl_rawcred;
  417         if (ucred)
  418                 *ucred = &client->cl_ucred;
  419         if (cookie)
  420                 *cookie = client->cl_cookie;
  421         return (TRUE);
  422 }
  423 
  424 /*
  425  * This simpler interface is used by svc_getcred to copy the cred data
  426  * into a kernel cred structure.
  427  */
  428 static int
  429 rpc_gss_svc_getcred(struct svc_req *req, struct ucred **crp, int *flavorp)
  430 {
  431         struct ucred *cr;
  432         struct svc_rpc_gss_cookedcred *cc;
  433         struct svc_rpc_gss_client *client;
  434         rpc_gss_ucred_t *uc;
  435 
  436         if (req->rq_cred.oa_flavor != RPCSEC_GSS)
  437                 return (FALSE);
  438 
  439         cc = req->rq_clntcred;
  440         client = cc->cc_client;
  441 
  442         if (flavorp)
  443                 *flavorp = client->cl_rpcflavor;
  444 
  445         if (client->cl_cred) {
  446                 *crp = crhold(client->cl_cred);
  447                 return (TRUE);
  448         }
  449 
  450         uc = &client->cl_ucred;
  451         cr = client->cl_cred = crget();
  452         cr->cr_uid = cr->cr_ruid = cr->cr_svuid = uc->uid;
  453         cr->cr_rgid = cr->cr_svgid = uc->gid;
  454         crsetgroups(cr, uc->gidlen, uc->gidlist);
  455         cr->cr_prison = &prison0;
  456         prison_hold(cr->cr_prison);
  457         *crp = crhold(cr);
  458 
  459         return (TRUE);
  460 }
  461 
  462 int
  463 rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
  464 {
  465         struct svc_rpc_gss_cookedcred *cc = req->rq_clntcred;
  466         struct svc_rpc_gss_client *client = cc->cc_client;
  467         int                     want_conf;
  468         OM_uint32               max;
  469         OM_uint32               maj_stat, min_stat;
  470         int                     result;
  471 
  472         switch (client->cl_rawcred.service) {
  473         case rpc_gss_svc_none:
  474                 return (max_tp_unit_len);
  475                 break;
  476 
  477         case rpc_gss_svc_default:
  478         case rpc_gss_svc_integrity:
  479                 want_conf = FALSE;
  480                 break;
  481 
  482         case rpc_gss_svc_privacy:
  483                 want_conf = TRUE;
  484                 break;
  485 
  486         default:
  487                 return (0);
  488         }
  489 
  490         maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf,
  491             client->cl_qop, max_tp_unit_len, &max);
  492 
  493         if (maj_stat == GSS_S_COMPLETE) {
  494                 result = (int) max;
  495                 if (result < 0)
  496                         result = 0;
  497                 return (result);
  498         } else {
  499                 rpc_gss_log_status("gss_wrap_size_limit", client->cl_mech,
  500                     maj_stat, min_stat);
  501                 return (0);
  502         }
  503 }
  504 
  505 static struct svc_rpc_gss_client *
  506 svc_rpc_gss_find_client(struct svc_rpc_gss_clientid *id)
  507 {
  508         struct svc_rpc_gss_client *client;
  509         struct svc_rpc_gss_client_list *list;
  510         unsigned long hostid;
  511 
  512         rpc_gss_log_debug("in svc_rpc_gss_find_client(%d)", id->ci_id);
  513 
  514         getcredhostid(curthread->td_ucred, &hostid);
  515         if (id->ci_hostid != hostid || id->ci_boottime != boottime.tv_sec)
  516                 return (NULL);
  517 
  518         list = &svc_rpc_gss_client_hash[id->ci_id % CLIENT_HASH_SIZE];
  519         sx_xlock(&svc_rpc_gss_lock);
  520         TAILQ_FOREACH(client, list, cl_link) {
  521                 if (client->cl_id.ci_id == id->ci_id) {
  522                         /*
  523                          * Move this client to the front of the LRU
  524                          * list.
  525                          */
  526                         TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
  527                         TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client,
  528                             cl_alllink);
  529                         refcount_acquire(&client->cl_refs);
  530                         break;
  531                 }
  532         }
  533         sx_xunlock(&svc_rpc_gss_lock);
  534 
  535         return (client);
  536 }
  537 
  538 static struct svc_rpc_gss_client *
  539 svc_rpc_gss_create_client(void)
  540 {
  541         struct svc_rpc_gss_client *client;
  542         struct svc_rpc_gss_client_list *list;
  543         unsigned long hostid;
  544 
  545         rpc_gss_log_debug("in svc_rpc_gss_create_client()");
  546 
  547         client = mem_alloc(sizeof(struct svc_rpc_gss_client));
  548         memset(client, 0, sizeof(struct svc_rpc_gss_client));
  549         refcount_init(&client->cl_refs, 1);
  550         sx_init(&client->cl_lock, "GSS-client");
  551         getcredhostid(curthread->td_ucred, &hostid);
  552         client->cl_id.ci_hostid = hostid;
  553         client->cl_id.ci_boottime = boottime.tv_sec;
  554         client->cl_id.ci_id = svc_rpc_gss_next_clientid++;
  555         list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
  556         sx_xlock(&svc_rpc_gss_lock);
  557         TAILQ_INSERT_HEAD(list, client, cl_link);
  558         TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink);
  559         svc_rpc_gss_client_count++;
  560         sx_xunlock(&svc_rpc_gss_lock);
  561 
  562         /*
  563          * Start the client off with a short expiration time. We will
  564          * try to get a saner value from the client creds later.
  565          */
  566         client->cl_state = CLIENT_NEW;
  567         client->cl_locked = FALSE;
  568         client->cl_expiration = time_uptime + 5*60;
  569 
  570         return (client);
  571 }
  572 
  573 static void
  574 svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
  575 {
  576         OM_uint32 min_stat;
  577 
  578         rpc_gss_log_debug("in svc_rpc_gss_destroy_client()");
  579 
  580         if (client->cl_ctx)
  581                 gss_delete_sec_context(&min_stat,
  582                     &client->cl_ctx, GSS_C_NO_BUFFER);
  583 
  584         if (client->cl_cname)
  585                 gss_release_name(&min_stat, &client->cl_cname);
  586 
  587         if (client->cl_rawcred.client_principal)
  588                 mem_free(client->cl_rawcred.client_principal,
  589                     sizeof(*client->cl_rawcred.client_principal)
  590                     + client->cl_rawcred.client_principal->len);
  591 
  592         if (client->cl_cred)
  593                 crfree(client->cl_cred);
  594 
  595         sx_destroy(&client->cl_lock);
  596         mem_free(client, sizeof(*client));
  597 }
  598 
  599 /*
  600  * Drop a reference to a client and free it if that was the last reference.
  601  */
  602 static void
  603 svc_rpc_gss_release_client(struct svc_rpc_gss_client *client)
  604 {
  605 
  606         if (!refcount_release(&client->cl_refs))
  607                 return;
  608         svc_rpc_gss_destroy_client(client);
  609 }
  610 
  611 /*
  612  * Remove a client from our global lists.
  613  * Must be called with svc_rpc_gss_lock held.
  614  */
  615 static void
  616 svc_rpc_gss_forget_client_locked(struct svc_rpc_gss_client *client)
  617 {
  618         struct svc_rpc_gss_client_list *list;
  619 
  620         sx_assert(&svc_rpc_gss_lock, SX_XLOCKED);
  621         list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
  622         TAILQ_REMOVE(list, client, cl_link);
  623         TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
  624         svc_rpc_gss_client_count--;
  625 }
  626 
  627 /*
  628  * Remove a client from our global lists and free it if we can.
  629  */
  630 static void
  631 svc_rpc_gss_forget_client(struct svc_rpc_gss_client *client)
  632 {
  633         struct svc_rpc_gss_client_list *list;
  634         struct svc_rpc_gss_client *tclient;
  635 
  636         list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE];
  637         sx_xlock(&svc_rpc_gss_lock);
  638         TAILQ_FOREACH(tclient, list, cl_link) {
  639                 /*
  640                  * Make sure this client has not already been removed
  641                  * from the lists by svc_rpc_gss_forget_client() or
  642                  * svc_rpc_gss_forget_client_locked().
  643                  */
  644                 if (client == tclient) {
  645                         svc_rpc_gss_forget_client_locked(client);
  646                         sx_xunlock(&svc_rpc_gss_lock);
  647                         svc_rpc_gss_release_client(client);
  648                         return;
  649                 }
  650         }
  651         sx_xunlock(&svc_rpc_gss_lock);
  652 }
  653 
  654 static void
  655 svc_rpc_gss_timeout_clients(void)
  656 {
  657         struct svc_rpc_gss_client *client;
  658         time_t now = time_uptime;
  659 
  660         rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()");
  661 
  662         /*
  663          * First enforce the max client limit. We keep
  664          * svc_rpc_gss_clients in LRU order.
  665          */
  666         sx_xlock(&svc_rpc_gss_lock);
  667         client = TAILQ_LAST(&svc_rpc_gss_clients, svc_rpc_gss_client_list);
  668         while (svc_rpc_gss_client_count > CLIENT_MAX && client != NULL) {
  669                 svc_rpc_gss_forget_client_locked(client);
  670                 sx_xunlock(&svc_rpc_gss_lock);
  671                 svc_rpc_gss_release_client(client);
  672                 sx_xlock(&svc_rpc_gss_lock);
  673                 client = TAILQ_LAST(&svc_rpc_gss_clients,
  674                     svc_rpc_gss_client_list);
  675         }
  676 again:
  677         TAILQ_FOREACH(client, &svc_rpc_gss_clients, cl_alllink) {
  678                 if (client->cl_state == CLIENT_STALE
  679                     || now > client->cl_expiration) {
  680                         svc_rpc_gss_forget_client_locked(client);
  681                         sx_xunlock(&svc_rpc_gss_lock);
  682                         rpc_gss_log_debug("expiring client %p", client);
  683                         svc_rpc_gss_release_client(client);
  684                         sx_xlock(&svc_rpc_gss_lock);
  685                         goto again;
  686                 }
  687         }
  688         sx_xunlock(&svc_rpc_gss_lock);
  689 }
  690 
  691 #ifdef DEBUG
  692 /*
  693  * OID<->string routines.  These are uuuuugly.
  694  */
  695 static OM_uint32
  696 gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
  697 {
  698         char            numstr[128];
  699         unsigned long   number;
  700         int             numshift;
  701         size_t          string_length;
  702         size_t          i;
  703         unsigned char   *cp;
  704         char            *bp;
  705 
  706         /* Decoded according to krb5/gssapi_krb5.c */
  707 
  708         /* First determine the size of the string */
  709         string_length = 0;
  710         number = 0;
  711         numshift = 0;
  712         cp = (unsigned char *) oid->elements;
  713         number = (unsigned long) cp[0];
  714         sprintf(numstr, "%ld ", number/40);
  715         string_length += strlen(numstr);
  716         sprintf(numstr, "%ld ", number%40);
  717         string_length += strlen(numstr);
  718         for (i=1; i<oid->length; i++) {
  719                 if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
  720                         number = (number << 7) | (cp[i] & 0x7f);
  721                         numshift += 7;
  722                 }
  723                 else {
  724                         *minor_status = 0;
  725                         return(GSS_S_FAILURE);
  726                 }
  727                 if ((cp[i] & 0x80) == 0) {
  728                         sprintf(numstr, "%ld ", number);
  729                         string_length += strlen(numstr);
  730                         number = 0;
  731                         numshift = 0;
  732                 }
  733         }
  734         /*
  735          * If we get here, we've calculated the length of "n n n ... n ".  Add 4
  736          * here for "{ " and "}\0".
  737          */
  738         string_length += 4;
  739         if ((bp = (char *) mem_alloc(string_length))) {
  740                 strcpy(bp, "{ ");
  741                 number = (unsigned long) cp[0];
  742                 sprintf(numstr, "%ld ", number/40);
  743                 strcat(bp, numstr);
  744                 sprintf(numstr, "%ld ", number%40);
  745                 strcat(bp, numstr);
  746                 number = 0;
  747                 cp = (unsigned char *) oid->elements;
  748                 for (i=1; i<oid->length; i++) {
  749                         number = (number << 7) | (cp[i] & 0x7f);
  750                         if ((cp[i] & 0x80) == 0) {
  751                                 sprintf(numstr, "%ld ", number);
  752                                 strcat(bp, numstr);
  753                                 number = 0;
  754                         }
  755                 }
  756                 strcat(bp, "}");
  757                 oid_str->length = strlen(bp)+1;
  758                 oid_str->value = (void *) bp;
  759                 *minor_status = 0;
  760                 return(GSS_S_COMPLETE);
  761         }
  762         *minor_status = 0;
  763         return(GSS_S_FAILURE);
  764 }
  765 #endif
  766 
  767 static void
  768 svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
  769     const gss_name_t name)
  770 {
  771         OM_uint32               maj_stat, min_stat;
  772         rpc_gss_ucred_t         *uc = &client->cl_ucred;
  773         int                     numgroups;
  774 
  775         uc->uid = 65534;
  776         uc->gid = 65534;
  777         uc->gidlist = client->cl_gid_storage;
  778 
  779         numgroups = RPCAUTH_UNIXGIDS;
  780         maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech,
  781             &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]);
  782         if (GSS_ERROR(maj_stat))
  783                 uc->gidlen = 0;
  784         else
  785                 uc->gidlen = numgroups;
  786 }
  787 
  788 static void
  789 svc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client)
  790 {
  791         static gss_OID_desc krb5_mech_oid =
  792                 {9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
  793 
  794         /*
  795          * Attempt to translate mech type and service into a
  796          * 'pseudo flavor'. Hardwire in krb5 support for now.
  797          */
  798         if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) {
  799                 switch (client->cl_rawcred.service) {
  800                 case rpc_gss_svc_default:
  801                 case rpc_gss_svc_none:
  802                         client->cl_rpcflavor = RPCSEC_GSS_KRB5;
  803                         break;
  804                 case rpc_gss_svc_integrity:
  805                         client->cl_rpcflavor = RPCSEC_GSS_KRB5I;
  806                         break;
  807                 case rpc_gss_svc_privacy:
  808                         client->cl_rpcflavor = RPCSEC_GSS_KRB5P;
  809                         break;
  810                 }
  811         } else {
  812                 client->cl_rpcflavor = RPCSEC_GSS;
  813         }
  814 }
  815 
  816 static bool_t
  817 svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
  818                                struct svc_req *rqst,
  819                                struct rpc_gss_init_res *gr,
  820                                struct rpc_gss_cred *gc)
  821 {
  822         gss_buffer_desc         recv_tok;
  823         gss_OID                 mech;
  824         OM_uint32               maj_stat = 0, min_stat = 0, ret_flags;
  825         OM_uint32               cred_lifetime;
  826         struct svc_rpc_gss_svc_name *sname;
  827 
  828         rpc_gss_log_debug("in svc_rpc_gss_accept_context()");
  829         
  830         /* Deserialize arguments. */
  831         memset(&recv_tok, 0, sizeof(recv_tok));
  832         
  833         if (!svc_getargs(rqst,
  834                 (xdrproc_t) xdr_gss_buffer_desc,
  835                 (caddr_t) &recv_tok)) {
  836                 client->cl_state = CLIENT_STALE;
  837                 return (FALSE);
  838         }
  839 
  840         /*
  841          * First time round, try all the server names we have until
  842          * one matches. Afterwards, stick with that one.
  843          */
  844         sx_xlock(&svc_rpc_gss_lock);
  845         if (!client->cl_sname) {
  846                 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
  847                         if (sname->sn_program == rqst->rq_prog
  848                             && sname->sn_version == rqst->rq_vers) {
  849                         retry:
  850                                 gr->gr_major = gss_accept_sec_context(
  851                                         &gr->gr_minor,
  852                                         &client->cl_ctx,
  853                                         sname->sn_cred,
  854                                         &recv_tok,
  855                                         GSS_C_NO_CHANNEL_BINDINGS,
  856                                         &client->cl_cname,
  857                                         &mech,
  858                                         &gr->gr_token,
  859                                         &ret_flags,
  860                                         &cred_lifetime,
  861                                         &client->cl_creds);
  862                                 if (gr->gr_major == 
  863                                     GSS_S_CREDENTIALS_EXPIRED) {
  864                                         /*
  865                                          * Either our creds really did
  866                                          * expire or gssd was
  867                                          * restarted.
  868                                          */
  869                                         if (rpc_gss_acquire_svc_cred(sname))
  870                                                 goto retry;
  871                                 }
  872                                 client->cl_sname = sname;
  873                                 break;
  874                         }
  875                 }
  876                 if (!sname) {
  877                         xdr_free((xdrproc_t) xdr_gss_buffer_desc,
  878                             (char *) &recv_tok);
  879                         sx_xunlock(&svc_rpc_gss_lock);
  880                         return (FALSE);
  881                 }
  882         } else {
  883                 gr->gr_major = gss_accept_sec_context(
  884                         &gr->gr_minor,
  885                         &client->cl_ctx,
  886                         client->cl_sname->sn_cred,
  887                         &recv_tok,
  888                         GSS_C_NO_CHANNEL_BINDINGS,
  889                         &client->cl_cname,
  890                         &mech,
  891                         &gr->gr_token,
  892                         &ret_flags,
  893                         &cred_lifetime,
  894                         NULL);
  895         }
  896         sx_xunlock(&svc_rpc_gss_lock);
  897         
  898         xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
  899 
  900         /*
  901          * If we get an error from gss_accept_sec_context, send the
  902          * reply anyway so that the client gets a chance to see what
  903          * is wrong.
  904          */
  905         if (gr->gr_major != GSS_S_COMPLETE &&
  906             gr->gr_major != GSS_S_CONTINUE_NEEDED) {
  907                 rpc_gss_log_status("accept_sec_context", client->cl_mech,
  908                     gr->gr_major, gr->gr_minor);
  909                 client->cl_state = CLIENT_STALE;
  910                 return (TRUE);
  911         }
  912 
  913         gr->gr_handle.value = &client->cl_id;
  914         gr->gr_handle.length = sizeof(client->cl_id);
  915         gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
  916         
  917         /* Save client info. */
  918         client->cl_mech = mech;
  919         client->cl_qop = GSS_C_QOP_DEFAULT;
  920         client->cl_done_callback = FALSE;
  921 
  922         if (gr->gr_major == GSS_S_COMPLETE) {
  923                 gss_buffer_desc export_name;
  924 
  925                 /*
  926                  * Change client expiration time to be near when the
  927                  * client creds expire (or 24 hours if we can't figure
  928                  * that out).
  929                  */
  930                 if (cred_lifetime == GSS_C_INDEFINITE)
  931                         cred_lifetime = time_uptime + 24*60*60;
  932 
  933                 client->cl_expiration = time_uptime + cred_lifetime;
  934 
  935                 /*
  936                  * Fill in cred details in the rawcred structure.
  937                  */
  938                 client->cl_rawcred.version = RPCSEC_GSS_VERSION;
  939                 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
  940                 maj_stat = gss_export_name(&min_stat, client->cl_cname,
  941                     &export_name);
  942                 if (maj_stat != GSS_S_COMPLETE) {
  943                         rpc_gss_log_status("gss_export_name", client->cl_mech,
  944                             maj_stat, min_stat);
  945                         return (FALSE);
  946                 }
  947                 client->cl_rawcred.client_principal =
  948                         mem_alloc(sizeof(*client->cl_rawcred.client_principal)
  949                             + export_name.length);
  950                 client->cl_rawcred.client_principal->len = export_name.length;
  951                 memcpy(client->cl_rawcred.client_principal->name,
  952                     export_name.value, export_name.length);
  953                 gss_release_buffer(&min_stat, &export_name);
  954                 client->cl_rawcred.svc_principal =
  955                         client->cl_sname->sn_principal;
  956                 client->cl_rawcred.service = gc->gc_svc;
  957 
  958                 /*
  959                  * Use gss_pname_to_uid to map to unix creds. For
  960                  * kerberos5, this uses krb5_aname_to_localname.
  961                  */
  962                 svc_rpc_gss_build_ucred(client, client->cl_cname);
  963                 svc_rpc_gss_set_flavor(client);
  964                 gss_release_name(&min_stat, &client->cl_cname);
  965 
  966 #ifdef DEBUG
  967                 {
  968                         gss_buffer_desc mechname;
  969 
  970                         gss_oid_to_str(&min_stat, mech, &mechname);
  971                         
  972                         rpc_gss_log_debug("accepted context for %s with "
  973                             "<mech %.*s, qop %d, svc %d>",
  974                             client->cl_rawcred.client_principal->name,
  975                             mechname.length, (char *)mechname.value,
  976                             client->cl_qop, client->cl_rawcred.service);
  977 
  978                         gss_release_buffer(&min_stat, &mechname);
  979                 }
  980 #endif /* DEBUG */
  981         }
  982         return (TRUE);
  983 }
  984 
  985 static bool_t
  986 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
  987     gss_qop_t *qop, rpc_gss_proc_t gcproc)
  988 {
  989         struct opaque_auth      *oa;
  990         gss_buffer_desc          rpcbuf, checksum;
  991         OM_uint32                maj_stat, min_stat;
  992         gss_qop_t                qop_state;
  993         int32_t                  rpchdr[128 / sizeof(int32_t)];
  994         int32_t                 *buf;
  995 
  996         rpc_gss_log_debug("in svc_rpc_gss_validate()");
  997         
  998         memset(rpchdr, 0, sizeof(rpchdr));
  999 
 1000         /* Reconstruct RPC header for signing (from xdr_callmsg). */
 1001         buf = rpchdr;
 1002         IXDR_PUT_LONG(buf, msg->rm_xid);
 1003         IXDR_PUT_ENUM(buf, msg->rm_direction);
 1004         IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
 1005         IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
 1006         IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
 1007         IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
 1008         oa = &msg->rm_call.cb_cred;
 1009         IXDR_PUT_ENUM(buf, oa->oa_flavor);
 1010         IXDR_PUT_LONG(buf, oa->oa_length);
 1011         if (oa->oa_length) {
 1012                 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
 1013                 buf += RNDUP(oa->oa_length) / sizeof(int32_t);
 1014         }
 1015         rpcbuf.value = rpchdr;
 1016         rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
 1017 
 1018         checksum.value = msg->rm_call.cb_verf.oa_base;
 1019         checksum.length = msg->rm_call.cb_verf.oa_length;
 1020         
 1021         maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
 1022                                   &qop_state);
 1023         
 1024         if (maj_stat != GSS_S_COMPLETE) {
 1025                 rpc_gss_log_status("gss_verify_mic", client->cl_mech,
 1026                     maj_stat, min_stat);
 1027                 /*
 1028                  * A bug in some versions of the Linux client generates a
 1029                  * Destroy operation with a bogus encrypted checksum. Deleting
 1030                  * the credential handle for that case causes the mount to fail.
 1031                  * Since the checksum is bogus (gss_verify_mic() failed), it
 1032                  * doesn't make sense to destroy the handle and not doing so
 1033                  * fixes the Linux mount.
 1034                  */
 1035                 if (gcproc != RPCSEC_GSS_DESTROY)
 1036                         client->cl_state = CLIENT_STALE;
 1037                 return (FALSE);
 1038         }
 1039 
 1040         *qop = qop_state;
 1041         return (TRUE);
 1042 }
 1043 
 1044 static bool_t
 1045 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
 1046     struct svc_req *rqst, u_int seq)
 1047 {
 1048         gss_buffer_desc         signbuf;
 1049         gss_buffer_desc         mic;
 1050         OM_uint32               maj_stat, min_stat;
 1051         uint32_t                nseq;       
 1052 
 1053         rpc_gss_log_debug("in svc_rpc_gss_nextverf()");
 1054 
 1055         nseq = htonl(seq);
 1056         signbuf.value = &nseq;
 1057         signbuf.length = sizeof(nseq);
 1058 
 1059         maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
 1060             &signbuf, &mic);
 1061 
 1062         if (maj_stat != GSS_S_COMPLETE) {
 1063                 rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
 1064                 client->cl_state = CLIENT_STALE;
 1065                 return (FALSE);
 1066         }
 1067 
 1068         KASSERT(mic.length <= MAX_AUTH_BYTES,
 1069             ("MIC too large for RPCSEC_GSS"));
 1070 
 1071         rqst->rq_verf.oa_flavor = RPCSEC_GSS;
 1072         rqst->rq_verf.oa_length = mic.length;
 1073         bcopy(mic.value, rqst->rq_verf.oa_base, mic.length);
 1074 
 1075         gss_release_buffer(&min_stat, &mic);
 1076         
 1077         return (TRUE);
 1078 }
 1079 
 1080 static bool_t
 1081 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
 1082 {
 1083         struct svc_rpc_gss_callback *scb;
 1084         rpc_gss_lock_t  lock;
 1085         void            *cookie;
 1086         bool_t          cb_res;
 1087         bool_t          result;
 1088 
 1089         /*
 1090          * See if we have a callback for this guy.
 1091          */
 1092         result = TRUE;
 1093         SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
 1094                 if (scb->cb_callback.program == rqst->rq_prog
 1095                     && scb->cb_callback.version == rqst->rq_vers) {
 1096                         /*
 1097                          * This one matches. Call the callback and see
 1098                          * if it wants to veto or something.
 1099                          */
 1100                         lock.locked = FALSE;
 1101                         lock.raw_cred = &client->cl_rawcred;
 1102                         cb_res = scb->cb_callback.callback(rqst,
 1103                             client->cl_creds,
 1104                             client->cl_ctx,
 1105                             &lock,
 1106                             &cookie);
 1107 
 1108                         if (!cb_res) {
 1109                                 client->cl_state = CLIENT_STALE;
 1110                                 result = FALSE;
 1111                                 break;
 1112                         }
 1113 
 1114                         /*
 1115                          * The callback accepted the connection - it
 1116                          * is responsible for freeing client->cl_creds
 1117                          * now.
 1118                          */
 1119                         client->cl_creds = GSS_C_NO_CREDENTIAL;
 1120                         client->cl_locked = lock.locked;
 1121                         client->cl_cookie = cookie;
 1122                         return (TRUE);
 1123                 }
 1124         }
 1125 
 1126         /*
 1127          * Either no callback exists for this program/version or one
 1128          * of the callbacks rejected the connection. We just need to
 1129          * clean up the delegated client creds, if any.
 1130          */
 1131         if (client->cl_creds) {
 1132                 OM_uint32 min_ver;
 1133                 gss_release_cred(&min_ver, &client->cl_creds);
 1134         }
 1135         return (result);
 1136 }
 1137 
 1138 static bool_t
 1139 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
 1140 {
 1141         u_int32_t offset;
 1142         int word, bit;
 1143         bool_t result;
 1144 
 1145         sx_xlock(&client->cl_lock);
 1146         if (seq <= client->cl_seqlast) {
 1147                 /*
 1148                  * The request sequence number is less than
 1149                  * the largest we have seen so far. If it is
 1150                  * outside the window or if we have seen a
 1151                  * request with this sequence before, silently
 1152                  * discard it.
 1153                  */
 1154                 offset = client->cl_seqlast - seq;
 1155                 if (offset >= SVC_RPC_GSS_SEQWINDOW) {
 1156                         result = FALSE;
 1157                         goto out;
 1158                 }
 1159                 word = offset / 32;
 1160                 bit = offset % 32;
 1161                 if (client->cl_seqmask[word] & (1 << bit)) {
 1162                         result = FALSE;
 1163                         goto out;
 1164                 }
 1165         }
 1166 
 1167         result = TRUE;
 1168 out:
 1169         sx_xunlock(&client->cl_lock);
 1170         return (result);
 1171 }
 1172 
 1173 static void
 1174 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
 1175 {
 1176         int offset, i, word, bit;
 1177         uint32_t carry, newcarry;
 1178 
 1179         sx_xlock(&client->cl_lock);
 1180         if (seq > client->cl_seqlast) {
 1181                 /*
 1182                  * This request has a sequence number greater
 1183                  * than any we have seen so far. Advance the
 1184                  * seq window and set bit zero of the window
 1185                  * (which corresponds to the new sequence
 1186                  * number)
 1187                  */
 1188                 offset = seq - client->cl_seqlast;
 1189                 while (offset > 32) {
 1190                         for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
 1191                              i > 0; i--) {
 1192                                 client->cl_seqmask[i] = client->cl_seqmask[i-1];
 1193                         }
 1194                         client->cl_seqmask[0] = 0;
 1195                         offset -= 32;
 1196                 }
 1197                 carry = 0;
 1198                 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
 1199                         newcarry = client->cl_seqmask[i] >> (32 - offset);
 1200                         client->cl_seqmask[i] =
 1201                                 (client->cl_seqmask[i] << offset) | carry;
 1202                         carry = newcarry;
 1203                 }
 1204                 client->cl_seqmask[0] |= 1;
 1205                 client->cl_seqlast = seq;
 1206         } else {
 1207                 offset = client->cl_seqlast - seq;
 1208                 word = offset / 32;
 1209                 bit = offset % 32;
 1210                 client->cl_seqmask[word] |= (1 << bit);
 1211         }
 1212         sx_xunlock(&client->cl_lock);
 1213 }
 1214 
 1215 enum auth_stat
 1216 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
 1217 
 1218 {
 1219         OM_uint32                min_stat;
 1220         XDR                      xdrs;
 1221         struct svc_rpc_gss_cookedcred *cc;
 1222         struct svc_rpc_gss_client *client;
 1223         struct rpc_gss_cred      gc;
 1224         struct rpc_gss_init_res  gr;
 1225         gss_qop_t                qop;
 1226         int                      call_stat;
 1227         enum auth_stat           result;
 1228         
 1229         rpc_gss_log_debug("in svc_rpc_gss()");
 1230         
 1231         /* Garbage collect old clients. */
 1232         svc_rpc_gss_timeout_clients();
 1233 
 1234         /* Initialize reply. */
 1235         rqst->rq_verf = _null_auth;
 1236 
 1237         /* Deserialize client credentials. */
 1238         if (rqst->rq_cred.oa_length <= 0)
 1239                 return (AUTH_BADCRED);
 1240         
 1241         memset(&gc, 0, sizeof(gc));
 1242         
 1243         xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
 1244             rqst->rq_cred.oa_length, XDR_DECODE);
 1245         
 1246         if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
 1247                 XDR_DESTROY(&xdrs);
 1248                 return (AUTH_BADCRED);
 1249         }
 1250         XDR_DESTROY(&xdrs);
 1251 
 1252         client = NULL;
 1253 
 1254         /* Check version. */
 1255         if (gc.gc_version != RPCSEC_GSS_VERSION) {
 1256                 result = AUTH_BADCRED;
 1257                 goto out;
 1258         }
 1259 
 1260         /* Check the proc and find the client (or create it) */
 1261         if (gc.gc_proc == RPCSEC_GSS_INIT) {
 1262                 if (gc.gc_handle.length != 0) {
 1263                         result = AUTH_BADCRED;
 1264                         goto out;
 1265                 }
 1266                 client = svc_rpc_gss_create_client();
 1267                 refcount_acquire(&client->cl_refs);
 1268         } else {
 1269                 struct svc_rpc_gss_clientid *p;
 1270                 if (gc.gc_handle.length != sizeof(*p)) {
 1271                         result = AUTH_BADCRED;
 1272                         goto out;
 1273                 }
 1274                 p = gc.gc_handle.value;
 1275                 client = svc_rpc_gss_find_client(p);
 1276                 if (!client) {
 1277                         /*
 1278                          * Can't find the client - we may have
 1279                          * destroyed it - tell the other side to
 1280                          * re-authenticate.
 1281                          */
 1282                         result = RPCSEC_GSS_CREDPROBLEM;
 1283                         goto out;
 1284                 }
 1285         }
 1286         cc = rqst->rq_clntcred;
 1287         cc->cc_client = client;
 1288         cc->cc_service = gc.gc_svc;
 1289         cc->cc_seq = gc.gc_seq;
 1290 
 1291         /*
 1292          * The service and sequence number must be ignored for
 1293          * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
 1294          */
 1295         if (gc.gc_proc != RPCSEC_GSS_INIT
 1296             && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
 1297                 /*
 1298                  * Check for sequence number overflow.
 1299                  */
 1300                 if (gc.gc_seq >= MAXSEQ) {
 1301                         result = RPCSEC_GSS_CTXPROBLEM;
 1302                         goto out;
 1303                 }
 1304 
 1305                 /*
 1306                  * Check for valid service.
 1307                  */
 1308                 if (gc.gc_svc != rpc_gss_svc_none &&
 1309                     gc.gc_svc != rpc_gss_svc_integrity &&
 1310                     gc.gc_svc != rpc_gss_svc_privacy) {
 1311                         result = AUTH_BADCRED;
 1312                         goto out;
 1313                 }
 1314         }
 1315 
 1316         /* Handle RPCSEC_GSS control procedure. */
 1317         switch (gc.gc_proc) {
 1318 
 1319         case RPCSEC_GSS_INIT:
 1320         case RPCSEC_GSS_CONTINUE_INIT:
 1321                 if (rqst->rq_proc != NULLPROC) {
 1322                         result = AUTH_REJECTEDCRED;
 1323                         break;
 1324                 }
 1325 
 1326                 memset(&gr, 0, sizeof(gr));
 1327                 if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
 1328                         result = AUTH_REJECTEDCRED;
 1329                         break;
 1330                 }
 1331 
 1332                 if (gr.gr_major == GSS_S_COMPLETE) {
 1333                         /*
 1334                          * We borrow the space for the call verf to
 1335                          * pack our reply verf.
 1336                          */
 1337                         rqst->rq_verf = msg->rm_call.cb_verf;
 1338                         if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
 1339                                 result = AUTH_REJECTEDCRED;
 1340                                 break;
 1341                         }
 1342                 } else {
 1343                         rqst->rq_verf = _null_auth;
 1344                 }
 1345                 
 1346                 call_stat = svc_sendreply(rqst,
 1347                     (xdrproc_t) xdr_rpc_gss_init_res,
 1348                     (caddr_t) &gr);
 1349 
 1350                 gss_release_buffer(&min_stat, &gr.gr_token);
 1351 
 1352                 if (!call_stat) {
 1353                         result = AUTH_FAILED;
 1354                         break;
 1355                 }
 1356 
 1357                 if (gr.gr_major == GSS_S_COMPLETE)
 1358                         client->cl_state = CLIENT_ESTABLISHED;
 1359 
 1360                 result = RPCSEC_GSS_NODISPATCH;
 1361                 break;
 1362                 
 1363         case RPCSEC_GSS_DATA:
 1364         case RPCSEC_GSS_DESTROY:
 1365                 if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
 1366                         result = RPCSEC_GSS_NODISPATCH;
 1367                         break;
 1368                 }
 1369 
 1370                 if (!svc_rpc_gss_validate(client, msg, &qop, gc.gc_proc)) {
 1371                         result = RPCSEC_GSS_CREDPROBLEM;
 1372                         break;
 1373                 }
 1374                 
 1375                 /*
 1376                  * We borrow the space for the call verf to pack our
 1377                  * reply verf.
 1378                  */
 1379                 rqst->rq_verf = msg->rm_call.cb_verf;
 1380                 if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
 1381                         result = RPCSEC_GSS_CTXPROBLEM;
 1382                         break;
 1383                 }
 1384 
 1385                 svc_rpc_gss_update_seq(client, gc.gc_seq);
 1386 
 1387                 /*
 1388                  * Change the SVCAUTH ops on the request to point at
 1389                  * our own code so that we can unwrap the arguments
 1390                  * and wrap the result. The caller will re-set this on
 1391                  * every request to point to a set of null wrap/unwrap
 1392                  * methods. Acquire an extra reference to the client
 1393                  * which will be released by svc_rpc_gss_release()
 1394                  * after the request has finished processing.
 1395                  */
 1396                 refcount_acquire(&client->cl_refs);
 1397                 rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops;
 1398                 rqst->rq_auth.svc_ah_private = cc;
 1399 
 1400                 if (gc.gc_proc == RPCSEC_GSS_DATA) {
 1401                         /*
 1402                          * We might be ready to do a callback to the server to
 1403                          * see if it wants to accept/reject the connection.
 1404                          */
 1405                         sx_xlock(&client->cl_lock);
 1406                         if (!client->cl_done_callback) {
 1407                                 client->cl_done_callback = TRUE;
 1408                                 client->cl_qop = qop;
 1409                                 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
 1410                                         client->cl_rawcred.mechanism, qop);
 1411                                 if (!svc_rpc_gss_callback(client, rqst)) {
 1412                                         result = AUTH_REJECTEDCRED;
 1413                                         sx_xunlock(&client->cl_lock);
 1414                                         break;
 1415                                 }
 1416                         }
 1417                         sx_xunlock(&client->cl_lock);
 1418 
 1419                         /*
 1420                          * If the server has locked this client to a
 1421                          * particular service+qop pair, enforce that
 1422                          * restriction now.
 1423                          */
 1424                         if (client->cl_locked) {
 1425                                 if (client->cl_rawcred.service != gc.gc_svc) {
 1426                                         result = AUTH_FAILED;
 1427                                         break;
 1428                                 } else if (client->cl_qop != qop) {
 1429                                         result = AUTH_BADVERF;
 1430                                         break;
 1431                                 }
 1432                         }
 1433 
 1434                         /*
 1435                          * If the qop changed, look up the new qop
 1436                          * name for rawcred.
 1437                          */
 1438                         if (client->cl_qop != qop) {
 1439                                 client->cl_qop = qop;
 1440                                 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
 1441                                         client->cl_rawcred.mechanism, qop);
 1442                         }
 1443 
 1444                         /*
 1445                          * Make sure we use the right service value
 1446                          * for unwrap/wrap.
 1447                          */
 1448                         if (client->cl_rawcred.service != gc.gc_svc) {
 1449                                 client->cl_rawcred.service = gc.gc_svc;
 1450                                 svc_rpc_gss_set_flavor(client);
 1451                         }
 1452 
 1453                         result = AUTH_OK;
 1454                 } else {
 1455                         if (rqst->rq_proc != NULLPROC) {
 1456                                 result = AUTH_REJECTEDCRED;
 1457                                 break;
 1458                         }
 1459 
 1460                         call_stat = svc_sendreply(rqst,
 1461                             (xdrproc_t) xdr_void, (caddr_t) NULL);
 1462 
 1463                         if (!call_stat) {
 1464                                 result = AUTH_FAILED;
 1465                                 break;
 1466                         }
 1467 
 1468                         svc_rpc_gss_forget_client(client);
 1469 
 1470                         result = RPCSEC_GSS_NODISPATCH;
 1471                         break;
 1472                 }
 1473                 break;
 1474 
 1475         default:
 1476                 result = AUTH_BADCRED;
 1477                 break;
 1478         }
 1479 out:
 1480         if (client)
 1481                 svc_rpc_gss_release_client(client);
 1482 
 1483         xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
 1484         return (result);
 1485 }
 1486 
 1487 static bool_t
 1488 svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp)
 1489 {
 1490         struct svc_rpc_gss_cookedcred *cc;
 1491         struct svc_rpc_gss_client *client;
 1492         
 1493         rpc_gss_log_debug("in svc_rpc_gss_wrap()");
 1494 
 1495         cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
 1496         client = cc->cc_client;
 1497         if (client->cl_state != CLIENT_ESTABLISHED
 1498             || cc->cc_service == rpc_gss_svc_none || *mp == NULL) {
 1499                 return (TRUE);
 1500         }
 1501         
 1502         return (xdr_rpc_gss_wrap_data(mp,
 1503                 client->cl_ctx, client->cl_qop,
 1504                 cc->cc_service, cc->cc_seq));
 1505 }
 1506 
 1507 static bool_t
 1508 svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp)
 1509 {
 1510         struct svc_rpc_gss_cookedcred *cc;
 1511         struct svc_rpc_gss_client *client;
 1512 
 1513         rpc_gss_log_debug("in svc_rpc_gss_unwrap()");
 1514         
 1515         cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
 1516         client = cc->cc_client;
 1517         if (client->cl_state != CLIENT_ESTABLISHED
 1518             || cc->cc_service == rpc_gss_svc_none) {
 1519                 return (TRUE);
 1520         }
 1521 
 1522         return (xdr_rpc_gss_unwrap_data(mp,
 1523                 client->cl_ctx, client->cl_qop,
 1524                 cc->cc_service, cc->cc_seq));
 1525 }
 1526 
 1527 static void
 1528 svc_rpc_gss_release(SVCAUTH *auth)
 1529 {
 1530         struct svc_rpc_gss_cookedcred *cc;
 1531         struct svc_rpc_gss_client *client;
 1532 
 1533         rpc_gss_log_debug("in svc_rpc_gss_release()");
 1534 
 1535         cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
 1536         client = cc->cc_client;
 1537         svc_rpc_gss_release_client(client);
 1538 }

Cache object: bb360d81417706eb39e496cb08b9b3b0


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.