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

Cache object: b75b9a8e83d02d4344c6152e84a1344c


[ 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.