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 #ifdef VNET_NFSD
  482         if (jailed(curthread->td_ucred))
  483                 cr->cr_prison = curthread->td_ucred->cr_prison;
  484         else
  485 #endif
  486                 cr->cr_prison = &prison0;
  487         prison_hold(cr->cr_prison);
  488         *crp = crhold(cr);
  489 
  490         return (TRUE);
  491 }
  492 
  493 int
  494 rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
  495 {
  496         struct svc_rpc_gss_cookedcred *cc = req->rq_clntcred;
  497         struct svc_rpc_gss_client *client = cc->cc_client;
  498         int                     want_conf;
  499         OM_uint32               max;
  500         OM_uint32               maj_stat, min_stat;
  501         int                     result;
  502 
  503         switch (client->cl_rawcred.service) {
  504         case rpc_gss_svc_none:
  505                 return (max_tp_unit_len);
  506                 break;
  507 
  508         case rpc_gss_svc_default:
  509         case rpc_gss_svc_integrity:
  510                 want_conf = FALSE;
  511                 break;
  512 
  513         case rpc_gss_svc_privacy:
  514                 want_conf = TRUE;
  515                 break;
  516 
  517         default:
  518                 return (0);
  519         }
  520 
  521         maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf,
  522             client->cl_qop, max_tp_unit_len, &max);
  523 
  524         if (maj_stat == GSS_S_COMPLETE) {
  525                 result = (int) max;
  526                 if (result < 0)
  527                         result = 0;
  528                 return (result);
  529         } else {
  530                 rpc_gss_log_status("gss_wrap_size_limit", client->cl_mech,
  531                     maj_stat, min_stat);
  532                 return (0);
  533         }
  534 }
  535 
  536 static struct svc_rpc_gss_client *
  537 svc_rpc_gss_find_client(struct svc_rpc_gss_clientid *id)
  538 {
  539         struct svc_rpc_gss_client *client;
  540         struct svc_rpc_gss_client_list *list;
  541         struct timeval boottime;
  542         unsigned long hostid;
  543 
  544         rpc_gss_log_debug("in svc_rpc_gss_find_client(%d)", id->ci_id);
  545 
  546         getcredhostid(curthread->td_ucred, &hostid);
  547         getboottime(&boottime);
  548         if (id->ci_hostid != hostid || id->ci_boottime != boottime.tv_sec)
  549                 return (NULL);
  550 
  551         list = &svc_rpc_gss_client_hash[id->ci_id % svc_rpc_gss_client_hash_size];
  552         sx_xlock(&svc_rpc_gss_lock);
  553         TAILQ_FOREACH(client, list, cl_link) {
  554                 if (client->cl_id.ci_id == id->ci_id) {
  555                         /*
  556                          * Move this client to the front of the LRU
  557                          * list.
  558                          */
  559                         TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
  560                         TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client,
  561                             cl_alllink);
  562                         refcount_acquire(&client->cl_refs);
  563                         break;
  564                 }
  565         }
  566         sx_xunlock(&svc_rpc_gss_lock);
  567 
  568         return (client);
  569 }
  570 
  571 static struct svc_rpc_gss_client *
  572 svc_rpc_gss_create_client(void)
  573 {
  574         struct svc_rpc_gss_client *client;
  575         struct svc_rpc_gss_client_list *list;
  576         struct timeval boottime;
  577         unsigned long hostid;
  578 
  579         rpc_gss_log_debug("in svc_rpc_gss_create_client()");
  580 
  581         client = mem_alloc(sizeof(struct svc_rpc_gss_client));
  582         memset(client, 0, sizeof(struct svc_rpc_gss_client));
  583 
  584         /*
  585          * Set the initial value of cl_refs to two.  One for the caller
  586          * and the other to hold onto the client structure until it expires.
  587          */
  588         refcount_init(&client->cl_refs, 2);
  589         sx_init(&client->cl_lock, "GSS-client");
  590         getcredhostid(curthread->td_ucred, &hostid);
  591         client->cl_id.ci_hostid = hostid;
  592         getboottime(&boottime);
  593         client->cl_id.ci_boottime = boottime.tv_sec;
  594         client->cl_id.ci_id = svc_rpc_gss_next_clientid++;
  595 
  596         /*
  597          * Start the client off with a short expiration time. We will
  598          * try to get a saner value from the client creds later.
  599          */
  600         client->cl_state = CLIENT_NEW;
  601         client->cl_locked = FALSE;
  602         client->cl_expiration = time_uptime + 5*60;
  603 
  604         list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
  605         sx_xlock(&svc_rpc_gss_lock);
  606         TAILQ_INSERT_HEAD(list, client, cl_link);
  607         TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink);
  608         svc_rpc_gss_client_count++;
  609         sx_xunlock(&svc_rpc_gss_lock);
  610         return (client);
  611 }
  612 
  613 static void
  614 svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
  615 {
  616         OM_uint32 min_stat;
  617 
  618         rpc_gss_log_debug("in svc_rpc_gss_destroy_client()");
  619 
  620         if (client->cl_ctx)
  621                 gss_delete_sec_context(&min_stat,
  622                     &client->cl_ctx, GSS_C_NO_BUFFER);
  623 
  624         if (client->cl_cname)
  625                 gss_release_name(&min_stat, &client->cl_cname);
  626 
  627         if (client->cl_rawcred.client_principal)
  628                 mem_free(client->cl_rawcred.client_principal,
  629                     sizeof(*client->cl_rawcred.client_principal)
  630                     + client->cl_rawcred.client_principal->len);
  631 
  632         if (client->cl_cred)
  633                 crfree(client->cl_cred);
  634 
  635         sx_destroy(&client->cl_lock);
  636         mem_free(client, sizeof(*client));
  637 }
  638 
  639 /*
  640  * Drop a reference to a client and free it if that was the last reference.
  641  */
  642 static void
  643 svc_rpc_gss_release_client(struct svc_rpc_gss_client *client)
  644 {
  645 
  646         if (!refcount_release(&client->cl_refs))
  647                 return;
  648         svc_rpc_gss_destroy_client(client);
  649 }
  650 
  651 /*
  652  * Remove a client from our global lists.
  653  * Must be called with svc_rpc_gss_lock held.
  654  */
  655 static void
  656 svc_rpc_gss_forget_client_locked(struct svc_rpc_gss_client *client)
  657 {
  658         struct svc_rpc_gss_client_list *list;
  659 
  660         sx_assert(&svc_rpc_gss_lock, SX_XLOCKED);
  661         list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
  662         TAILQ_REMOVE(list, client, cl_link);
  663         TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
  664         svc_rpc_gss_client_count--;
  665 }
  666 
  667 /*
  668  * Remove a client from our global lists and free it if we can.
  669  */
  670 static void
  671 svc_rpc_gss_forget_client(struct svc_rpc_gss_client *client)
  672 {
  673         struct svc_rpc_gss_client_list *list;
  674         struct svc_rpc_gss_client *tclient;
  675 
  676         list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % svc_rpc_gss_client_hash_size];
  677         sx_xlock(&svc_rpc_gss_lock);
  678         TAILQ_FOREACH(tclient, list, cl_link) {
  679                 /*
  680                  * Make sure this client has not already been removed
  681                  * from the lists by svc_rpc_gss_forget_client() or
  682                  * svc_rpc_gss_forget_client_locked().
  683                  */
  684                 if (client == tclient) {
  685                         svc_rpc_gss_forget_client_locked(client);
  686                         sx_xunlock(&svc_rpc_gss_lock);
  687                         svc_rpc_gss_release_client(client);
  688                         return;
  689                 }
  690         }
  691         sx_xunlock(&svc_rpc_gss_lock);
  692 }
  693 
  694 static void
  695 svc_rpc_gss_timeout_clients(void)
  696 {
  697         struct svc_rpc_gss_client *client;
  698         time_t now = time_uptime;
  699 
  700         rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()");
  701 
  702         /*
  703          * First enforce the max client limit. We keep
  704          * svc_rpc_gss_clients in LRU order.
  705          */
  706         sx_xlock(&svc_rpc_gss_lock);
  707         client = TAILQ_LAST(&svc_rpc_gss_clients, svc_rpc_gss_client_list);
  708         while (svc_rpc_gss_client_count > svc_rpc_gss_client_max && client != NULL) {
  709                 svc_rpc_gss_forget_client_locked(client);
  710                 sx_xunlock(&svc_rpc_gss_lock);
  711                 svc_rpc_gss_release_client(client);
  712                 sx_xlock(&svc_rpc_gss_lock);
  713                 client = TAILQ_LAST(&svc_rpc_gss_clients,
  714                     svc_rpc_gss_client_list);
  715         }
  716 again:
  717         TAILQ_FOREACH(client, &svc_rpc_gss_clients, cl_alllink) {
  718                 if (client->cl_state == CLIENT_STALE
  719                     || now > client->cl_expiration) {
  720                         svc_rpc_gss_forget_client_locked(client);
  721                         sx_xunlock(&svc_rpc_gss_lock);
  722                         rpc_gss_log_debug("expiring client %p", client);
  723                         svc_rpc_gss_release_client(client);
  724                         sx_xlock(&svc_rpc_gss_lock);
  725                         goto again;
  726                 }
  727         }
  728         sx_xunlock(&svc_rpc_gss_lock);
  729 }
  730 
  731 #ifdef DEBUG
  732 /*
  733  * OID<->string routines.  These are uuuuugly.
  734  */
  735 static OM_uint32
  736 gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
  737 {
  738         char            numstr[128];
  739         unsigned long   number;
  740         int             numshift;
  741         size_t          string_length;
  742         size_t          i;
  743         unsigned char   *cp;
  744         char            *bp;
  745 
  746         /* Decoded according to krb5/gssapi_krb5.c */
  747 
  748         /* First determine the size of the string */
  749         string_length = 0;
  750         number = 0;
  751         numshift = 0;
  752         cp = (unsigned char *) oid->elements;
  753         number = (unsigned long) cp[0];
  754         sprintf(numstr, "%ld ", number/40);
  755         string_length += strlen(numstr);
  756         sprintf(numstr, "%ld ", number%40);
  757         string_length += strlen(numstr);
  758         for (i=1; i<oid->length; i++) {
  759                 if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
  760                         number = (number << 7) | (cp[i] & 0x7f);
  761                         numshift += 7;
  762                 }
  763                 else {
  764                         *minor_status = 0;
  765                         return(GSS_S_FAILURE);
  766                 }
  767                 if ((cp[i] & 0x80) == 0) {
  768                         sprintf(numstr, "%ld ", number);
  769                         string_length += strlen(numstr);
  770                         number = 0;
  771                         numshift = 0;
  772                 }
  773         }
  774         /*
  775          * If we get here, we've calculated the length of "n n n ... n ".  Add 4
  776          * here for "{ " and "}\0".
  777          */
  778         string_length += 4;
  779         if ((bp = malloc(string_length, M_GSSAPI, M_WAITOK | M_ZERO))) {
  780                 strcpy(bp, "{ ");
  781                 number = (unsigned long) cp[0];
  782                 sprintf(numstr, "%ld ", number/40);
  783                 strcat(bp, numstr);
  784                 sprintf(numstr, "%ld ", number%40);
  785                 strcat(bp, numstr);
  786                 number = 0;
  787                 cp = (unsigned char *) oid->elements;
  788                 for (i=1; i<oid->length; i++) {
  789                         number = (number << 7) | (cp[i] & 0x7f);
  790                         if ((cp[i] & 0x80) == 0) {
  791                                 sprintf(numstr, "%ld ", number);
  792                                 strcat(bp, numstr);
  793                                 number = 0;
  794                         }
  795                 }
  796                 strcat(bp, "}");
  797                 oid_str->length = strlen(bp)+1;
  798                 oid_str->value = (void *) bp;
  799                 *minor_status = 0;
  800                 return(GSS_S_COMPLETE);
  801         }
  802         *minor_status = 0;
  803         return(GSS_S_FAILURE);
  804 }
  805 #endif
  806 
  807 static void
  808 svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
  809     const gss_name_t name)
  810 {
  811         OM_uint32               maj_stat, min_stat;
  812         rpc_gss_ucred_t         *uc = &client->cl_ucred;
  813         int                     numgroups;
  814 
  815         uc->uid = 65534;
  816         uc->gid = 65534;
  817         uc->gidlist = client->cl_gid_storage;
  818 
  819         numgroups = NGROUPS;
  820         maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech,
  821             &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]);
  822         if (GSS_ERROR(maj_stat))
  823                 uc->gidlen = 0;
  824         else
  825                 uc->gidlen = numgroups;
  826 }
  827 
  828 static void
  829 svc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client)
  830 {
  831         static gss_OID_desc krb5_mech_oid =
  832                 {9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
  833 
  834         /*
  835          * Attempt to translate mech type and service into a
  836          * 'pseudo flavor'. Hardwire in krb5 support for now.
  837          */
  838         if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) {
  839                 switch (client->cl_rawcred.service) {
  840                 case rpc_gss_svc_default:
  841                 case rpc_gss_svc_none:
  842                         client->cl_rpcflavor = RPCSEC_GSS_KRB5;
  843                         break;
  844                 case rpc_gss_svc_integrity:
  845                         client->cl_rpcflavor = RPCSEC_GSS_KRB5I;
  846                         break;
  847                 case rpc_gss_svc_privacy:
  848                         client->cl_rpcflavor = RPCSEC_GSS_KRB5P;
  849                         break;
  850                 }
  851         } else {
  852                 client->cl_rpcflavor = RPCSEC_GSS;
  853         }
  854 }
  855 
  856 static bool_t
  857 svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
  858                                struct svc_req *rqst,
  859                                struct rpc_gss_init_res *gr,
  860                                struct rpc_gss_cred *gc)
  861 {
  862         gss_buffer_desc         recv_tok;
  863         gss_OID                 mech;
  864         OM_uint32               maj_stat = 0, min_stat = 0, ret_flags;
  865         OM_uint32               cred_lifetime;
  866         struct svc_rpc_gss_svc_name *sname;
  867 
  868         rpc_gss_log_debug("in svc_rpc_gss_accept_context()");
  869         
  870         /* Deserialize arguments. */
  871         memset(&recv_tok, 0, sizeof(recv_tok));
  872         
  873         if (!svc_getargs(rqst,
  874                 (xdrproc_t) xdr_gss_buffer_desc,
  875                 (caddr_t) &recv_tok)) {
  876                 client->cl_state = CLIENT_STALE;
  877                 return (FALSE);
  878         }
  879 
  880         /*
  881          * First time round, try all the server names we have until
  882          * one matches. Afterwards, stick with that one.
  883          */
  884         sx_xlock(&svc_rpc_gss_lock);
  885         if (!client->cl_sname) {
  886                 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
  887                         if (sname->sn_program == rqst->rq_prog
  888                             && sname->sn_version == rqst->rq_vers) {
  889                         retry:
  890                                 gr->gr_major = gss_accept_sec_context(
  891                                         &gr->gr_minor,
  892                                         &client->cl_ctx,
  893                                         sname->sn_cred,
  894                                         &recv_tok,
  895                                         GSS_C_NO_CHANNEL_BINDINGS,
  896                                         &client->cl_cname,
  897                                         &mech,
  898                                         &gr->gr_token,
  899                                         &ret_flags,
  900                                         &cred_lifetime,
  901                                         &client->cl_creds);
  902                                 if (gr->gr_major == 
  903                                     GSS_S_CREDENTIALS_EXPIRED) {
  904                                         /*
  905                                          * Either our creds really did
  906                                          * expire or gssd was
  907                                          * restarted.
  908                                          */
  909                                         if (rpc_gss_acquire_svc_cred(sname))
  910                                                 goto retry;
  911                                 }
  912                                 client->cl_sname = sname;
  913                                 break;
  914                         }
  915                 }
  916                 if (!sname) {
  917                         xdr_free((xdrproc_t) xdr_gss_buffer_desc,
  918                             (char *) &recv_tok);
  919                         sx_xunlock(&svc_rpc_gss_lock);
  920                         return (FALSE);
  921                 }
  922         } else {
  923                 gr->gr_major = gss_accept_sec_context(
  924                         &gr->gr_minor,
  925                         &client->cl_ctx,
  926                         client->cl_sname->sn_cred,
  927                         &recv_tok,
  928                         GSS_C_NO_CHANNEL_BINDINGS,
  929                         &client->cl_cname,
  930                         &mech,
  931                         &gr->gr_token,
  932                         &ret_flags,
  933                         &cred_lifetime,
  934                         NULL);
  935         }
  936         sx_xunlock(&svc_rpc_gss_lock);
  937         
  938         xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
  939 
  940         /*
  941          * If we get an error from gss_accept_sec_context, send the
  942          * reply anyway so that the client gets a chance to see what
  943          * is wrong.
  944          */
  945         if (gr->gr_major != GSS_S_COMPLETE &&
  946             gr->gr_major != GSS_S_CONTINUE_NEEDED) {
  947                 rpc_gss_log_status("accept_sec_context", client->cl_mech,
  948                     gr->gr_major, gr->gr_minor);
  949                 client->cl_state = CLIENT_STALE;
  950                 return (TRUE);
  951         }
  952 
  953         gr->gr_handle.value = &client->cl_id;
  954         gr->gr_handle.length = sizeof(client->cl_id);
  955         gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
  956         
  957         /* Save client info. */
  958         client->cl_mech = mech;
  959         client->cl_qop = GSS_C_QOP_DEFAULT;
  960         client->cl_done_callback = FALSE;
  961 
  962         if (gr->gr_major == GSS_S_COMPLETE) {
  963                 gss_buffer_desc export_name;
  964 
  965                 /*
  966                  * Change client expiration time to be near when the
  967                  * client creds expire (or 24 hours if we can't figure
  968                  * that out).
  969                  */
  970                 if (cred_lifetime == GSS_C_INDEFINITE)
  971                         cred_lifetime = 24*60*60;
  972 
  973                 /*
  974                  * Cap cred_lifetime if sysctl kern.rpc.gss.lifetime_max is set.
  975                  */
  976                 if (svc_rpc_gss_lifetime_max > 0 && cred_lifetime >
  977                     svc_rpc_gss_lifetime_max)
  978                         cred_lifetime = svc_rpc_gss_lifetime_max;
  979                 
  980                 client->cl_expiration = time_uptime + cred_lifetime;
  981 
  982                 /*
  983                  * Fill in cred details in the rawcred structure.
  984                  */
  985                 client->cl_rawcred.version = RPCSEC_GSS_VERSION;
  986                 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
  987                 maj_stat = gss_export_name(&min_stat, client->cl_cname,
  988                     &export_name);
  989                 if (maj_stat != GSS_S_COMPLETE) {
  990                         rpc_gss_log_status("gss_export_name", client->cl_mech,
  991                             maj_stat, min_stat);
  992                         return (FALSE);
  993                 }
  994                 client->cl_rawcred.client_principal =
  995                         mem_alloc(sizeof(*client->cl_rawcred.client_principal)
  996                             + export_name.length);
  997                 client->cl_rawcred.client_principal->len = export_name.length;
  998                 memcpy(client->cl_rawcred.client_principal->name,
  999                     export_name.value, export_name.length);
 1000                 gss_release_buffer(&min_stat, &export_name);
 1001                 client->cl_rawcred.svc_principal =
 1002                         client->cl_sname->sn_principal;
 1003                 client->cl_rawcred.service = gc->gc_svc;
 1004 
 1005                 /*
 1006                  * Use gss_pname_to_uid to map to unix creds. For
 1007                  * kerberos5, this uses krb5_aname_to_localname.
 1008                  */
 1009                 svc_rpc_gss_build_ucred(client, client->cl_cname);
 1010                 svc_rpc_gss_set_flavor(client);
 1011                 gss_release_name(&min_stat, &client->cl_cname);
 1012 
 1013 #ifdef DEBUG
 1014                 {
 1015                         gss_buffer_desc mechname;
 1016 
 1017                         gss_oid_to_str(&min_stat, mech, &mechname);
 1018                         
 1019                         rpc_gss_log_debug("accepted context for %s with "
 1020                             "<mech %.*s, qop %d, svc %d>",
 1021                             client->cl_rawcred.client_principal->name,
 1022                             mechname.length, (char *)mechname.value,
 1023                             client->cl_qop, client->cl_rawcred.service);
 1024 
 1025                         gss_release_buffer(&min_stat, &mechname);
 1026                 }
 1027 #endif /* DEBUG */
 1028         }
 1029         return (TRUE);
 1030 }
 1031 
 1032 static bool_t
 1033 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
 1034     gss_qop_t *qop, rpc_gss_proc_t gcproc)
 1035 {
 1036         struct opaque_auth      *oa;
 1037         gss_buffer_desc          rpcbuf, checksum;
 1038         OM_uint32                maj_stat, min_stat;
 1039         gss_qop_t                qop_state;
 1040         int32_t                  rpchdr[128 / sizeof(int32_t)];
 1041         int32_t                 *buf;
 1042 
 1043         rpc_gss_log_debug("in svc_rpc_gss_validate()");
 1044         
 1045         memset(rpchdr, 0, sizeof(rpchdr));
 1046 
 1047         /* Reconstruct RPC header for signing (from xdr_callmsg). */
 1048         buf = rpchdr;
 1049         IXDR_PUT_LONG(buf, msg->rm_xid);
 1050         IXDR_PUT_ENUM(buf, msg->rm_direction);
 1051         IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
 1052         IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
 1053         IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
 1054         IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
 1055         oa = &msg->rm_call.cb_cred;
 1056         IXDR_PUT_ENUM(buf, oa->oa_flavor);
 1057         IXDR_PUT_LONG(buf, oa->oa_length);
 1058         if (oa->oa_length) {
 1059                 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
 1060                 buf += RNDUP(oa->oa_length) / sizeof(int32_t);
 1061         }
 1062         rpcbuf.value = rpchdr;
 1063         rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
 1064 
 1065         checksum.value = msg->rm_call.cb_verf.oa_base;
 1066         checksum.length = msg->rm_call.cb_verf.oa_length;
 1067         
 1068         maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
 1069                                   &qop_state);
 1070         
 1071         if (maj_stat != GSS_S_COMPLETE) {
 1072                 rpc_gss_log_status("gss_verify_mic", client->cl_mech,
 1073                     maj_stat, min_stat);
 1074                 /*
 1075                  * A bug in some versions of the Linux client generates a
 1076                  * Destroy operation with a bogus encrypted checksum. Deleting
 1077                  * the credential handle for that case causes the mount to fail.
 1078                  * Since the checksum is bogus (gss_verify_mic() failed), it
 1079                  * doesn't make sense to destroy the handle and not doing so
 1080                  * fixes the Linux mount.
 1081                  */
 1082                 if (gcproc != RPCSEC_GSS_DESTROY)
 1083                         client->cl_state = CLIENT_STALE;
 1084                 return (FALSE);
 1085         }
 1086 
 1087         *qop = qop_state;
 1088         return (TRUE);
 1089 }
 1090 
 1091 static bool_t
 1092 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
 1093     struct svc_req *rqst, u_int seq)
 1094 {
 1095         gss_buffer_desc         signbuf;
 1096         gss_buffer_desc         mic;
 1097         OM_uint32               maj_stat, min_stat;
 1098         uint32_t                nseq;       
 1099 
 1100         rpc_gss_log_debug("in svc_rpc_gss_nextverf()");
 1101 
 1102         nseq = htonl(seq);
 1103         signbuf.value = &nseq;
 1104         signbuf.length = sizeof(nseq);
 1105 
 1106         maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
 1107             &signbuf, &mic);
 1108 
 1109         if (maj_stat != GSS_S_COMPLETE) {
 1110                 rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
 1111                 client->cl_state = CLIENT_STALE;
 1112                 return (FALSE);
 1113         }
 1114 
 1115         KASSERT(mic.length <= MAX_AUTH_BYTES,
 1116             ("MIC too large for RPCSEC_GSS"));
 1117 
 1118         rqst->rq_verf.oa_flavor = RPCSEC_GSS;
 1119         rqst->rq_verf.oa_length = mic.length;
 1120         bcopy(mic.value, rqst->rq_verf.oa_base, mic.length);
 1121 
 1122         gss_release_buffer(&min_stat, &mic);
 1123         
 1124         return (TRUE);
 1125 }
 1126 
 1127 static bool_t
 1128 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
 1129 {
 1130         struct svc_rpc_gss_callback *scb;
 1131         rpc_gss_lock_t  lock;
 1132         void            *cookie;
 1133         bool_t          cb_res;
 1134         bool_t          result;
 1135 
 1136         /*
 1137          * See if we have a callback for this guy.
 1138          */
 1139         result = TRUE;
 1140         SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
 1141                 if (scb->cb_callback.program == rqst->rq_prog
 1142                     && scb->cb_callback.version == rqst->rq_vers) {
 1143                         /*
 1144                          * This one matches. Call the callback and see
 1145                          * if it wants to veto or something.
 1146                          */
 1147                         lock.locked = FALSE;
 1148                         lock.raw_cred = &client->cl_rawcred;
 1149                         cb_res = scb->cb_callback.callback(rqst,
 1150                             client->cl_creds,
 1151                             client->cl_ctx,
 1152                             &lock,
 1153                             &cookie);
 1154 
 1155                         if (!cb_res) {
 1156                                 client->cl_state = CLIENT_STALE;
 1157                                 result = FALSE;
 1158                                 break;
 1159                         }
 1160 
 1161                         /*
 1162                          * The callback accepted the connection - it
 1163                          * is responsible for freeing client->cl_creds
 1164                          * now.
 1165                          */
 1166                         client->cl_creds = GSS_C_NO_CREDENTIAL;
 1167                         client->cl_locked = lock.locked;
 1168                         client->cl_cookie = cookie;
 1169                         return (TRUE);
 1170                 }
 1171         }
 1172 
 1173         /*
 1174          * Either no callback exists for this program/version or one
 1175          * of the callbacks rejected the connection. We just need to
 1176          * clean up the delegated client creds, if any.
 1177          */
 1178         if (client->cl_creds) {
 1179                 OM_uint32 min_ver;
 1180                 gss_release_cred(&min_ver, &client->cl_creds);
 1181         }
 1182         return (result);
 1183 }
 1184 
 1185 static bool_t
 1186 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
 1187 {
 1188         uint32_t offset;
 1189         int word, bit;
 1190         bool_t result;
 1191 
 1192         sx_xlock(&client->cl_lock);
 1193         if (seq <= client->cl_seqlast) {
 1194                 /*
 1195                  * The request sequence number is less than
 1196                  * the largest we have seen so far. If it is
 1197                  * outside the window or if we have seen a
 1198                  * request with this sequence before, silently
 1199                  * discard it.
 1200                  */
 1201                 offset = client->cl_seqlast - seq;
 1202                 if (offset >= SVC_RPC_GSS_SEQWINDOW) {
 1203                         result = FALSE;
 1204                         goto out;
 1205                 }
 1206                 word = offset / 32;
 1207                 bit = offset % 32;
 1208                 if (client->cl_seqmask[word] & (1 << bit)) {
 1209                         result = FALSE;
 1210                         goto out;
 1211                 }
 1212         }
 1213 
 1214         result = TRUE;
 1215 out:
 1216         sx_xunlock(&client->cl_lock);
 1217         return (result);
 1218 }
 1219 
 1220 static void
 1221 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
 1222 {
 1223         int offset, i, word, bit;
 1224         uint32_t carry, newcarry;
 1225 
 1226         sx_xlock(&client->cl_lock);
 1227         if (seq > client->cl_seqlast) {
 1228                 /*
 1229                  * This request has a sequence number greater
 1230                  * than any we have seen so far. Advance the
 1231                  * seq window and set bit zero of the window
 1232                  * (which corresponds to the new sequence
 1233                  * number)
 1234                  */
 1235                 offset = seq - client->cl_seqlast;
 1236                 while (offset > 32) {
 1237                         for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
 1238                              i > 0; i--) {
 1239                                 client->cl_seqmask[i] = client->cl_seqmask[i-1];
 1240                         }
 1241                         client->cl_seqmask[0] = 0;
 1242                         offset -= 32;
 1243                 }
 1244                 carry = 0;
 1245                 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
 1246                         newcarry = client->cl_seqmask[i] >> (32 - offset);
 1247                         client->cl_seqmask[i] =
 1248                                 (client->cl_seqmask[i] << offset) | carry;
 1249                         carry = newcarry;
 1250                 }
 1251                 client->cl_seqmask[0] |= 1;
 1252                 client->cl_seqlast = seq;
 1253         } else {
 1254                 offset = client->cl_seqlast - seq;
 1255                 word = offset / 32;
 1256                 bit = offset % 32;
 1257                 client->cl_seqmask[word] |= (1 << bit);
 1258         }
 1259         sx_xunlock(&client->cl_lock);
 1260 }
 1261 
 1262 enum auth_stat
 1263 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
 1264 
 1265 {
 1266         OM_uint32                min_stat;
 1267         XDR                      xdrs;
 1268         struct svc_rpc_gss_cookedcred *cc;
 1269         struct svc_rpc_gss_client *client;
 1270         struct rpc_gss_cred      gc;
 1271         struct rpc_gss_init_res  gr;
 1272         gss_qop_t                qop;
 1273         int                      call_stat;
 1274         enum auth_stat           result;
 1275         
 1276         rpc_gss_log_debug("in svc_rpc_gss()");
 1277         
 1278         /* Garbage collect old clients. */
 1279         svc_rpc_gss_timeout_clients();
 1280 
 1281         /* Initialize reply. */
 1282         rqst->rq_verf = _null_auth;
 1283 
 1284         /* Deserialize client credentials. */
 1285         if (rqst->rq_cred.oa_length <= 0)
 1286                 return (AUTH_BADCRED);
 1287         
 1288         memset(&gc, 0, sizeof(gc));
 1289         
 1290         xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
 1291             rqst->rq_cred.oa_length, XDR_DECODE);
 1292         
 1293         if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
 1294                 XDR_DESTROY(&xdrs);
 1295                 return (AUTH_BADCRED);
 1296         }
 1297         XDR_DESTROY(&xdrs);
 1298 
 1299         client = NULL;
 1300 
 1301         /* Check version. */
 1302         if (gc.gc_version != RPCSEC_GSS_VERSION) {
 1303                 result = AUTH_BADCRED;
 1304                 goto out;
 1305         }
 1306 
 1307         /* Check the proc and find the client (or create it) */
 1308         if (gc.gc_proc == RPCSEC_GSS_INIT) {
 1309                 if (gc.gc_handle.length != 0) {
 1310                         result = AUTH_BADCRED;
 1311                         goto out;
 1312                 }
 1313                 client = svc_rpc_gss_create_client();
 1314         } else {
 1315                 struct svc_rpc_gss_clientid *p;
 1316                 if (gc.gc_handle.length != sizeof(*p)) {
 1317                         result = AUTH_BADCRED;
 1318                         goto out;
 1319                 }
 1320                 p = gc.gc_handle.value;
 1321                 client = svc_rpc_gss_find_client(p);
 1322                 if (!client) {
 1323                         /*
 1324                          * Can't find the client - we may have
 1325                          * destroyed it - tell the other side to
 1326                          * re-authenticate.
 1327                          */
 1328                         result = RPCSEC_GSS_CREDPROBLEM;
 1329                         goto out;
 1330                 }
 1331         }
 1332         cc = rqst->rq_clntcred;
 1333         cc->cc_client = client;
 1334         cc->cc_service = gc.gc_svc;
 1335         cc->cc_seq = gc.gc_seq;
 1336 
 1337         /*
 1338          * The service and sequence number must be ignored for
 1339          * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
 1340          */
 1341         if (gc.gc_proc != RPCSEC_GSS_INIT
 1342             && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
 1343                 /*
 1344                  * Check for sequence number overflow.
 1345                  */
 1346                 if (gc.gc_seq >= MAXSEQ) {
 1347                         result = RPCSEC_GSS_CTXPROBLEM;
 1348                         goto out;
 1349                 }
 1350 
 1351                 /*
 1352                  * Check for valid service.
 1353                  */
 1354                 if (gc.gc_svc != rpc_gss_svc_none &&
 1355                     gc.gc_svc != rpc_gss_svc_integrity &&
 1356                     gc.gc_svc != rpc_gss_svc_privacy) {
 1357                         result = AUTH_BADCRED;
 1358                         goto out;
 1359                 }
 1360         }
 1361 
 1362         /* Handle RPCSEC_GSS control procedure. */
 1363         switch (gc.gc_proc) {
 1364 
 1365         case RPCSEC_GSS_INIT:
 1366         case RPCSEC_GSS_CONTINUE_INIT:
 1367                 if (rqst->rq_proc != NULLPROC) {
 1368                         result = AUTH_REJECTEDCRED;
 1369                         break;
 1370                 }
 1371 
 1372                 memset(&gr, 0, sizeof(gr));
 1373                 if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
 1374                         result = AUTH_REJECTEDCRED;
 1375                         break;
 1376                 }
 1377 
 1378                 if (gr.gr_major == GSS_S_COMPLETE) {
 1379                         /*
 1380                          * We borrow the space for the call verf to
 1381                          * pack our reply verf.
 1382                          */
 1383                         rqst->rq_verf = msg->rm_call.cb_verf;
 1384                         if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
 1385                                 result = AUTH_REJECTEDCRED;
 1386                                 break;
 1387                         }
 1388                 } else {
 1389                         rqst->rq_verf = _null_auth;
 1390                 }
 1391                 
 1392                 call_stat = svc_sendreply(rqst,
 1393                     (xdrproc_t) xdr_rpc_gss_init_res,
 1394                     (caddr_t) &gr);
 1395 
 1396                 gss_release_buffer(&min_stat, &gr.gr_token);
 1397 
 1398                 if (!call_stat) {
 1399                         result = AUTH_FAILED;
 1400                         break;
 1401                 }
 1402 
 1403                 if (gr.gr_major == GSS_S_COMPLETE)
 1404                         client->cl_state = CLIENT_ESTABLISHED;
 1405 
 1406                 result = RPCSEC_GSS_NODISPATCH;
 1407                 break;
 1408                 
 1409         case RPCSEC_GSS_DATA:
 1410         case RPCSEC_GSS_DESTROY:
 1411                 if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
 1412                         result = RPCSEC_GSS_NODISPATCH;
 1413                         break;
 1414                 }
 1415 
 1416                 if (!svc_rpc_gss_validate(client, msg, &qop, gc.gc_proc)) {
 1417                         result = RPCSEC_GSS_CREDPROBLEM;
 1418                         break;
 1419                 }
 1420                 
 1421                 /*
 1422                  * We borrow the space for the call verf to pack our
 1423                  * reply verf.
 1424                  */
 1425                 rqst->rq_verf = msg->rm_call.cb_verf;
 1426                 if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
 1427                         result = RPCSEC_GSS_CTXPROBLEM;
 1428                         break;
 1429                 }
 1430 
 1431                 svc_rpc_gss_update_seq(client, gc.gc_seq);
 1432 
 1433                 /*
 1434                  * Change the SVCAUTH ops on the request to point at
 1435                  * our own code so that we can unwrap the arguments
 1436                  * and wrap the result. The caller will re-set this on
 1437                  * every request to point to a set of null wrap/unwrap
 1438                  * methods. Acquire an extra reference to the client
 1439                  * which will be released by svc_rpc_gss_release()
 1440                  * after the request has finished processing.
 1441                  */
 1442                 refcount_acquire(&client->cl_refs);
 1443                 rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops;
 1444                 rqst->rq_auth.svc_ah_private = cc;
 1445 
 1446                 if (gc.gc_proc == RPCSEC_GSS_DATA) {
 1447                         /*
 1448                          * We might be ready to do a callback to the server to
 1449                          * see if it wants to accept/reject the connection.
 1450                          */
 1451                         sx_xlock(&client->cl_lock);
 1452                         if (!client->cl_done_callback) {
 1453                                 client->cl_done_callback = TRUE;
 1454                                 client->cl_qop = qop;
 1455                                 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
 1456                                         client->cl_rawcred.mechanism, qop);
 1457                                 if (!svc_rpc_gss_callback(client, rqst)) {
 1458                                         result = AUTH_REJECTEDCRED;
 1459                                         sx_xunlock(&client->cl_lock);
 1460                                         break;
 1461                                 }
 1462                         }
 1463                         sx_xunlock(&client->cl_lock);
 1464 
 1465                         /*
 1466                          * If the server has locked this client to a
 1467                          * particular service+qop pair, enforce that
 1468                          * restriction now.
 1469                          */
 1470                         if (client->cl_locked) {
 1471                                 if (client->cl_rawcred.service != gc.gc_svc) {
 1472                                         result = AUTH_FAILED;
 1473                                         break;
 1474                                 } else if (client->cl_qop != qop) {
 1475                                         result = AUTH_BADVERF;
 1476                                         break;
 1477                                 }
 1478                         }
 1479 
 1480                         /*
 1481                          * If the qop changed, look up the new qop
 1482                          * name for rawcred.
 1483                          */
 1484                         if (client->cl_qop != qop) {
 1485                                 client->cl_qop = qop;
 1486                                 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
 1487                                         client->cl_rawcred.mechanism, qop);
 1488                         }
 1489 
 1490                         /*
 1491                          * Make sure we use the right service value
 1492                          * for unwrap/wrap.
 1493                          */
 1494                         if (client->cl_rawcred.service != gc.gc_svc) {
 1495                                 client->cl_rawcred.service = gc.gc_svc;
 1496                                 svc_rpc_gss_set_flavor(client);
 1497                         }
 1498 
 1499                         result = AUTH_OK;
 1500                 } else {
 1501                         if (rqst->rq_proc != NULLPROC) {
 1502                                 result = AUTH_REJECTEDCRED;
 1503                                 break;
 1504                         }
 1505 
 1506                         call_stat = svc_sendreply(rqst,
 1507                             (xdrproc_t) xdr_void, (caddr_t) NULL);
 1508 
 1509                         if (!call_stat) {
 1510                                 result = AUTH_FAILED;
 1511                                 break;
 1512                         }
 1513 
 1514                         svc_rpc_gss_forget_client(client);
 1515 
 1516                         result = RPCSEC_GSS_NODISPATCH;
 1517                         break;
 1518                 }
 1519                 break;
 1520 
 1521         default:
 1522                 result = AUTH_BADCRED;
 1523                 break;
 1524         }
 1525 out:
 1526         if (client)
 1527                 svc_rpc_gss_release_client(client);
 1528 
 1529         xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
 1530         return (result);
 1531 }
 1532 
 1533 static bool_t
 1534 svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp)
 1535 {
 1536         struct svc_rpc_gss_cookedcred *cc;
 1537         struct svc_rpc_gss_client *client;
 1538         
 1539         rpc_gss_log_debug("in svc_rpc_gss_wrap()");
 1540 
 1541         cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
 1542         client = cc->cc_client;
 1543         if (client->cl_state != CLIENT_ESTABLISHED
 1544             || cc->cc_service == rpc_gss_svc_none || *mp == NULL) {
 1545                 return (TRUE);
 1546         }
 1547         
 1548         return (xdr_rpc_gss_wrap_data(mp,
 1549                 client->cl_ctx, client->cl_qop,
 1550                 cc->cc_service, cc->cc_seq));
 1551 }
 1552 
 1553 static bool_t
 1554 svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp)
 1555 {
 1556         struct svc_rpc_gss_cookedcred *cc;
 1557         struct svc_rpc_gss_client *client;
 1558 
 1559         rpc_gss_log_debug("in svc_rpc_gss_unwrap()");
 1560         
 1561         cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
 1562         client = cc->cc_client;
 1563         if (client->cl_state != CLIENT_ESTABLISHED
 1564             || cc->cc_service == rpc_gss_svc_none) {
 1565                 return (TRUE);
 1566         }
 1567 
 1568         return (xdr_rpc_gss_unwrap_data(mp,
 1569                 client->cl_ctx, client->cl_qop,
 1570                 cc->cc_service, cc->cc_seq));
 1571 }
 1572 
 1573 static void
 1574 svc_rpc_gss_release(SVCAUTH *auth)
 1575 {
 1576         struct svc_rpc_gss_cookedcred *cc;
 1577         struct svc_rpc_gss_client *client;
 1578 
 1579         rpc_gss_log_debug("in svc_rpc_gss_release()");
 1580 
 1581         cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private;
 1582         client = cc->cc_client;
 1583         svc_rpc_gss_release_client(client);
 1584 }

Cache object: 38464fff9d1c07ec7bbb38eb3316b2c7


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