1191005Sdelphij/*- 2204977Simp * Copyright (c) 2008 Doug Rabson 331899Ssef * All rights reserved. 431899Ssef * 531899Ssef * Redistribution and use in source and binary forms, with or without 631899Ssef * modification, are permitted provided that the following conditions 731899Ssef * are met: 831899Ssef * 1. Redistributions of source code must retain the above copyright 931899Ssef * notice, this list of conditions and the following disclaimer. 1031899Ssef * 2. Redistributions in binary form must reproduce the above copyright 1131899Ssef * notice, this list of conditions and the following disclaimer in the 1231899Ssef * documentation and/or other materials provided with the distribution. 1331899Ssef * 1431899Ssef * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1531899Ssef * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1631899Ssef * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1731899Ssef * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1831899Ssef * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1931899Ssef * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2031899Ssef * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2131899Ssef * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2231899Ssef * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2331899Ssef * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2431899Ssef * SUCH DAMAGE. 2531899Ssef */ 2631899Ssef/* 2731899Ssef svc_rpcsec_gss.c 2831899Ssef 2931899Ssef Copyright (c) 2000 The Regents of the University of Michigan. 3031899Ssef All rights reserved. 3131899Ssef 32119852Scharnier Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>. 33119852Scharnier All rights reserved, all wrongs reversed. 3432275Scharnier 3531899Ssef Redistribution and use in source and binary forms, with or without 3631567Ssef modification, are permitted provided that the following conditions 3731567Ssef are met: 3831567Ssef 3931567Ssef 1. Redistributions of source code must retain the above copyright 4031567Ssef notice, this list of conditions and the following disclaimer. 4185301Sdes 2. Redistributions in binary form must reproduce the above copyright 42123916Scracauer notice, this list of conditions and the following disclaimer in the 43104581Smike documentation and/or other materials provided with the distribution. 44123916Scracauer 3. Neither the name of the University nor the names of its 45168569Sdelphij contributors may be used to endorse or promote products derived 46191005Sdelphij from this software without specific prior written permission. 4785301Sdes 48132306Salfred THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 4932275Scharnier WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 50200462Sdelphij MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 51200462Sdelphij DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 5232275Scharnier FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 5331567Ssef CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 5431567Ssef SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 5531567Ssef BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 56101423Smdodd LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 5731579Speter NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 5831567Ssef SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 59101282Smdodd 6087703Smarkm $Id: svc_auth_gss.c,v 1.27 2002/01/15 15:43:00 andros Exp $ 61192025Sdds */ 6231567Ssef 63171645Smarcel#include <sys/cdefs.h> 6431567Ssef__FBSDID("$FreeBSD: stable/11/sys/rpc/rpcsec_gss/svc_rpcsec_gss.c 355967 2019-12-20 23:08:10Z rmacklem $"); 65144177Salfred 6632275Scharnier#include <sys/param.h> 6732275Scharnier#include <sys/systm.h> 68144177Salfred#include <sys/jail.h> 69192025Sdds#include <sys/kernel.h> 70192025Sdds#include <sys/kobj.h> 71144177Salfred#include <sys/lock.h> 7231567Ssef#include <sys/malloc.h> 7331567Ssef#include <sys/mbuf.h> 7438897Ssef#include <sys/mutex.h> 7538897Ssef#include <sys/proc.h> 7638897Ssef#include <sys/sx.h> 7738897Ssef#include <sys/ucred.h> 7831567Ssef 79144177Salfred#include <rpc/rpc.h> 80144177Salfred#include <rpc/rpcsec_gss.h> 81144177Salfred 8231567Ssef#include "rpcsec_gss_int.h" 83130394Sdwmalone 84144177Salfredstatic bool_t svc_rpc_gss_wrap(SVCAUTH *, struct mbuf **); 85179051Sjhbstatic bool_t svc_rpc_gss_unwrap(SVCAUTH *, struct mbuf **); 86179051Sjhbstatic void svc_rpc_gss_release(SVCAUTH *); 87130394Sdwmalonestatic enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *); 8839908Ssefstatic int rpc_gss_svc_getcred(struct svc_req *, struct ucred **, int *); 89144177Salfred 90144177Salfredstatic struct svc_auth_ops svc_auth_gss_ops = { 91144177Salfred svc_rpc_gss_wrap, 92144177Salfred svc_rpc_gss_unwrap, 9339908Ssef svc_rpc_gss_release, 94106716Smarcel}; 95144177Salfred 96106716Smarcelstruct sx svc_rpc_gss_lock; 97154047Sgrehan 98154047Sgrehanstruct svc_rpc_gss_callback { 99154047Sgrehan SLIST_ENTRY(svc_rpc_gss_callback) cb_link; 100154047Sgrehan rpc_gss_callback_t cb_callback; 101101320Sjake}; 102144177Salfredstatic SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback) 103101320Sjake svc_rpc_gss_callbacks = SLIST_HEAD_INITIALIZER(svc_rpc_gss_callbacks); 104188628Simp 105188628Simpstruct svc_rpc_gss_svc_name { 106188628Simp SLIST_ENTRY(svc_rpc_gss_svc_name) sn_link; 107188628Simp char *sn_principal; 108188628Simp gss_OID sn_mech; 109144177Salfred u_int sn_req_time; 11031567Ssef gss_cred_id_t sn_cred; 11131567Ssef u_int sn_program; 11231567Ssef u_int sn_version; 11331567Ssef}; 114168569Sdelphijstatic SLIST_HEAD(svc_rpc_gss_svc_name_list, svc_rpc_gss_svc_name) 11531567Ssef svc_rpc_gss_svc_names = SLIST_HEAD_INITIALIZER(svc_rpc_gss_svc_names); 11631567Ssef 11731567Ssefenum svc_rpc_gss_client_state { 118144177Salfred CLIENT_NEW, /* still authenticating */ 119144177Salfred CLIENT_ESTABLISHED, /* context established */ 120144177Salfred CLIENT_STALE /* garbage to collect */ 121144177Salfred}; 122168569Sdelphij 123168569Sdelphij#define SVC_RPC_GSS_SEQWINDOW 128 124168569Sdelphij 125168569Sdelphijstruct svc_rpc_gss_clientid { 12631567Ssef unsigned long ci_hostid; 127168569Sdelphij uint32_t ci_boottime; 128168569Sdelphij uint32_t ci_id; 129168569Sdelphij}; 130168569Sdelphij 131168569Sdelphijstruct svc_rpc_gss_client { 132168569Sdelphij TAILQ_ENTRY(svc_rpc_gss_client) cl_link; 133168569Sdelphij TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink; 13431567Ssef volatile u_int cl_refs; 135144177Salfred struct sx cl_lock; 136144177Salfred struct svc_rpc_gss_clientid cl_id; 137144177Salfred time_t cl_expiration; /* when to gc */ 13831567Ssef enum svc_rpc_gss_client_state cl_state; /* client state */ 139144177Salfred bool_t cl_locked; /* fixed service+qop */ 140144177Salfred gss_ctx_id_t cl_ctx; /* context id */ 141144177Salfred gss_cred_id_t cl_creds; /* delegated creds */ 142144177Salfred gss_name_t cl_cname; /* client name */ 143144177Salfred struct svc_rpc_gss_svc_name *cl_sname; /* server name used */ 144144177Salfred rpc_gss_rawcred_t cl_rawcred; /* raw credentials */ 14531567Ssef rpc_gss_ucred_t cl_ucred; /* unix-style credentials */ 14631567Ssef struct ucred *cl_cred; /* kernel-style credentials */ 147132306Salfred int cl_rpcflavor; /* RPC pseudo sec flavor */ 148132306Salfred bool_t cl_done_callback; /* TRUE after call */ 149132306Salfred void *cl_cookie; /* user cookie from callback */ 150132306Salfred gid_t cl_gid_storage[NGROUPS]; 151132306Salfred gss_OID cl_mech; /* mechanism */ 152132306Salfred gss_qop_t cl_qop; /* quality of protection */ 153132306Salfred uint32_t cl_seqlast; /* sequence window origin */ 154132306Salfred uint32_t cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */ 155132306Salfred}; 156132306SalfredTAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client); 157132306Salfred 158132306Salfred/* 159132306Salfred * This structure holds enough information to unwrap arguments or wrap 160132306Salfred * results for a given request. We use the rq_clntcred area for this 161132306Salfred * (which is a per-request buffer). 162132306Salfred */ 163132306Salfredstruct svc_rpc_gss_cookedcred { 16432275Scharnier struct svc_rpc_gss_client *cc_client; 165144177Salfred rpc_gss_service_t cc_service; 166144177Salfred uint32_t cc_seq; 167144177Salfred}; 168144177Salfred 169191005Sdelphij#define CLIENT_HASH_SIZE 256 170191005Sdelphij#define CLIENT_MAX 128 171144177Salfredu_int svc_rpc_gss_client_max = CLIENT_MAX; 172144177Salfred 173171055SdelphijSYSCTL_NODE(_kern, OID_AUTO, rpc, CTLFLAG_RW, 0, "RPC"); 174144178SalfredSYSCTL_NODE(_kern_rpc, OID_AUTO, gss, CTLFLAG_RW, 0, "GSS"); 175144177Salfred 176144177SalfredSYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_max, CTLFLAG_RW, 17731567Ssef &svc_rpc_gss_client_max, 0, 178144178Salfred "Max number of rpc-gss clients"); 179144178Salfred 180144178Salfredstatic u_int svc_rpc_gss_lifetime_max = 0; 181144177SalfredSYSCTL_UINT(_kern_rpc_gss, OID_AUTO, lifetime_max, CTLFLAG_RW, 182192153Sdelphij &svc_rpc_gss_lifetime_max, 0, 183144177Salfred "Maximum lifetime (seconds) of rpc-gss clients"); 184192153Sdelphij 185192153Sdelphijstatic u_int svc_rpc_gss_client_count; 186144177SalfredSYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_count, CTLFLAG_RD, 187153963Sbrian &svc_rpc_gss_client_count, 0, 188168569Sdelphij "Number of rpc-gss clients"); 189168569Sdelphij 190168569Sdelphijstruct svc_rpc_gss_client_list svc_rpc_gss_client_hash[CLIENT_HASH_SIZE]; 191192025Sddsstruct svc_rpc_gss_client_list svc_rpc_gss_clients; 192144177Salfredstatic uint32_t svc_rpc_gss_next_clientid = 1; 193144177Salfred 194144177Salfredstatic void 195171055Sdelphijsvc_rpc_gss_init(void *arg) 196171055Sdelphij{ 197171055Sdelphij int i; 198171055Sdelphij 199171055Sdelphij for (i = 0; i < CLIENT_HASH_SIZE; i++) 200144177Salfred TAILQ_INIT(&svc_rpc_gss_client_hash[i]); 201144177Salfred TAILQ_INIT(&svc_rpc_gss_clients); 202144177Salfred svc_auth_reg(RPCSEC_GSS, svc_rpc_gss, rpc_gss_svc_getcred); 203144177Salfred sx_init(&svc_rpc_gss_lock, "gsslock"); 204144177Salfred} 205144177SalfredSYSINIT(svc_rpc_gss_init, SI_SUB_KMEM, SI_ORDER_ANY, svc_rpc_gss_init, NULL); 206144177Salfred 207192025Sddsbool_t 208192025Sddsrpc_gss_set_callback(rpc_gss_callback_t *cb) 209192025Sdds{ 210144177Salfred struct svc_rpc_gss_callback *scb; 211144177Salfred 212144177Salfred scb = mem_alloc(sizeof(struct svc_rpc_gss_callback)); 213144177Salfred if (!scb) { 214144177Salfred _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); 215144177Salfred return (FALSE); 216144177Salfred } 217144177Salfred scb->cb_callback = *cb; 218144177Salfred sx_xlock(&svc_rpc_gss_lock); 219144177Salfred SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link); 220144177Salfred sx_xunlock(&svc_rpc_gss_lock); 221144177Salfred 222153963Sbrian return (TRUE); 223153963Sbrian} 224153963Sbrian 225144177Salfredvoid 226144177Salfredrpc_gss_clear_callback(rpc_gss_callback_t *cb) 227144177Salfred{ 228144177Salfred struct svc_rpc_gss_callback *scb; 229144177Salfred 230144177Salfred sx_xlock(&svc_rpc_gss_lock); 231144177Salfred SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) { 23231567Ssef if (scb->cb_callback.program == cb->program 233144177Salfred && scb->cb_callback.version == cb->version 234144177Salfred && scb->cb_callback.callback == cb->callback) { 235144177Salfred SLIST_REMOVE(&svc_rpc_gss_callbacks, scb, 236144177Salfred svc_rpc_gss_callback, cb_link); 23731567Ssef sx_xunlock(&svc_rpc_gss_lock); 238144177Salfred mem_free(scb, sizeof(*scb)); 239144177Salfred return; 240144177Salfred } 241144177Salfred } 242200752Sjh sx_xunlock(&svc_rpc_gss_lock); 243200752Sjh} 244200752Sjh 245200752Sjhstatic bool_t 246200752Sjhrpc_gss_acquire_svc_cred(struct svc_rpc_gss_svc_name *sname) 247200752Sjh{ 24832275Scharnier OM_uint32 maj_stat, min_stat; 249144177Salfred gss_buffer_desc namebuf; 250144177Salfred gss_name_t name; 251144177Salfred gss_OID_set_desc oid_set; 252144177Salfred 253144177Salfred oid_set.count = 1; 254144177Salfred oid_set.elements = sname->sn_mech; 25531567Ssef 256144177Salfred namebuf.value = (void *) sname->sn_principal; 257144177Salfred namebuf.length = strlen(sname->sn_principal); 258144177Salfred 259144177Salfred maj_stat = gss_import_name(&min_stat, &namebuf, 260144177Salfred GSS_C_NT_HOSTBASED_SERVICE, &name); 261144177Salfred if (maj_stat != GSS_S_COMPLETE) 262144177Salfred return (FALSE); 263168569Sdelphij 264144177Salfred if (sname->sn_cred != GSS_C_NO_CREDENTIAL) 265144177Salfred gss_release_cred(&min_stat, &sname->sn_cred); 266144177Salfred 267144177Salfred maj_stat = gss_acquire_cred(&min_stat, name, 26831567Ssef sname->sn_req_time, &oid_set, GSS_C_ACCEPT, &sname->sn_cred, 26931567Ssef NULL, NULL); 270144177Salfred if (maj_stat != GSS_S_COMPLETE) { 271144177Salfred gss_release_name(&min_stat, &name); 272144177Salfred return (FALSE); 273144177Salfred } 27431567Ssef gss_release_name(&min_stat, &name); 275101283Smdodd 276168569Sdelphij return (TRUE); 277168569Sdelphij} 278144178Salfred 279144177Salfredbool_t 280144177Salfredrpc_gss_set_svc_name(const char *principal, const char *mechanism, 281144177Salfred u_int req_time, u_int program, u_int version) 282144177Salfred{ 283144177Salfred struct svc_rpc_gss_svc_name *sname; 28431567Ssef gss_OID mech_oid; 285144177Salfred 286101285Smdodd if (!rpc_gss_mech_to_oid(mechanism, &mech_oid)) 287144177Salfred return (FALSE); 288158630Spav 289168569Sdelphij sname = mem_alloc(sizeof(*sname)); 29031567Ssef if (!sname) 291168569Sdelphij return (FALSE); 292168569Sdelphij sname->sn_principal = strdup(principal, M_RPC); 293168569Sdelphij sname->sn_mech = mech_oid; 294168569Sdelphij sname->sn_req_time = req_time; 295168569Sdelphij sname->sn_cred = GSS_C_NO_CREDENTIAL; 296168569Sdelphij sname->sn_program = program; 297168569Sdelphij sname->sn_version = version; 298168569Sdelphij 299168569Sdelphij if (!rpc_gss_acquire_svc_cred(sname)) { 30031567Ssef free(sname->sn_principal, M_RPC); 301168569Sdelphij mem_free(sname, sizeof(*sname)); 302168569Sdelphij return (FALSE); 303168569Sdelphij } 304168569Sdelphij 305168569Sdelphij sx_xlock(&svc_rpc_gss_lock); 306168569Sdelphij SLIST_INSERT_HEAD(&svc_rpc_gss_svc_names, sname, sn_link); 307101283Smdodd sx_xunlock(&svc_rpc_gss_lock); 308168569Sdelphij 309168569Sdelphij return (TRUE); 310168569Sdelphij} 311168569Sdelphij 312168569Sdelphijvoid 313168569Sdelphijrpc_gss_clear_svc_name(u_int program, u_int version) 314168569Sdelphij{ 315168569Sdelphij OM_uint32 min_stat; 316168569Sdelphij struct svc_rpc_gss_svc_name *sname; 317144177Salfred 318144177Salfred sx_xlock(&svc_rpc_gss_lock); 319168569Sdelphij SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) { 320168569Sdelphij if (sname->sn_program == program 321168569Sdelphij && sname->sn_version == version) { 322168569Sdelphij SLIST_REMOVE(&svc_rpc_gss_svc_names, sname, 323168569Sdelphij svc_rpc_gss_svc_name, sn_link); 324144177Salfred sx_xunlock(&svc_rpc_gss_lock); 325168569Sdelphij gss_release_cred(&min_stat, &sname->sn_cred); 326168569Sdelphij free(sname->sn_principal, M_RPC); 327168569Sdelphij mem_free(sname, sizeof(*sname)); 328168569Sdelphij return; 329168569Sdelphij } 330168569Sdelphij } 331168569Sdelphij sx_xunlock(&svc_rpc_gss_lock); 332168569Sdelphij} 333168569Sdelphij 334144177Salfredbool_t 335168569Sdelphijrpc_gss_get_principal_name(rpc_gss_principal_t *principal, 336168569Sdelphij const char *mech, const char *name, const char *node, const char *domain) 337168569Sdelphij{ 338168569Sdelphij OM_uint32 maj_stat, min_stat; 339168569Sdelphij gss_OID mech_oid; 340168569Sdelphij size_t namelen; 341168569Sdelphij gss_buffer_desc buf; 342168569Sdelphij gss_name_t gss_name, gss_mech_name; 343168569Sdelphij rpc_gss_principal_t result; 344168569Sdelphij 345168569Sdelphij if (!rpc_gss_mech_to_oid(mech, &mech_oid)) 346168569Sdelphij return (FALSE); 347168569Sdelphij 348168569Sdelphij /* 349192025Sdds * Construct a gss_buffer containing the full name formatted 350192025Sdds * as "name/node@domain" where node and domain are optional. 351168569Sdelphij */ 352168569Sdelphij namelen = strlen(name) + 1; 353168569Sdelphij if (node) { 354168569Sdelphij namelen += strlen(node) + 1; 355168569Sdelphij } 356168569Sdelphij if (domain) { 357168569Sdelphij namelen += strlen(domain) + 1; 358168569Sdelphij } 359168569Sdelphij 360168569Sdelphij buf.value = mem_alloc(namelen); 361168569Sdelphij buf.length = namelen; 362168569Sdelphij strcpy((char *) buf.value, name); 363168569Sdelphij if (node) { 364168569Sdelphij strcat((char *) buf.value, "/"); 365168569Sdelphij strcat((char *) buf.value, node); 366168569Sdelphij } 367168569Sdelphij if (domain) { 368168569Sdelphij strcat((char *) buf.value, "@"); 369168569Sdelphij strcat((char *) buf.value, domain); 370168569Sdelphij } 371168569Sdelphij 372144177Salfred /* 373168569Sdelphij * Convert that to a gss_name_t and then convert that to a 374191005Sdelphij * mechanism name in the selected mechanism. 375191005Sdelphij */ 376191005Sdelphij maj_stat = gss_import_name(&min_stat, &buf, 377191005Sdelphij GSS_C_NT_USER_NAME, &gss_name); 378191005Sdelphij mem_free(buf.value, buf.length); 379191005Sdelphij if (maj_stat != GSS_S_COMPLETE) { 380192025Sdds rpc_gss_log_status("gss_import_name", mech_oid, maj_stat, min_stat); 381192025Sdds return (FALSE); 382192025Sdds } 383192025Sdds maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid, 384192025Sdds &gss_mech_name); 385144177Salfred if (maj_stat != GSS_S_COMPLETE) { 38631567Ssef rpc_gss_log_status("gss_canonicalize_name", mech_oid, maj_stat, 387 min_stat); 388 gss_release_name(&min_stat, &gss_name); 389 return (FALSE); 390 } 391 gss_release_name(&min_stat, &gss_name); 392 393 /* 394 * Export the mechanism name and use that to construct the 395 * rpc_gss_principal_t result. 396 */ 397 maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf); 398 if (maj_stat != GSS_S_COMPLETE) { 399 rpc_gss_log_status("gss_export_name", mech_oid, maj_stat, min_stat); 400 gss_release_name(&min_stat, &gss_mech_name); 401 return (FALSE); 402 } 403 gss_release_name(&min_stat, &gss_mech_name); 404 405 result = mem_alloc(sizeof(int) + buf.length); 406 if (!result) { 407 gss_release_buffer(&min_stat, &buf); 408 return (FALSE); 409 } 410 result->len = buf.length; 411 memcpy(result->name, buf.value, buf.length); 412 gss_release_buffer(&min_stat, &buf); 413 414 *principal = result; 415 return (TRUE); 416} 417 418bool_t 419rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred, 420 rpc_gss_ucred_t **ucred, void **cookie) 421{ 422 struct svc_rpc_gss_cookedcred *cc; 423 struct svc_rpc_gss_client *client; 424 425 if (req->rq_cred.oa_flavor != RPCSEC_GSS) 426 return (FALSE); 427 428 cc = req->rq_clntcred; 429 client = cc->cc_client; 430 if (rcred) 431 *rcred = &client->cl_rawcred; 432 if (ucred) 433 *ucred = &client->cl_ucred; 434 if (cookie) 435 *cookie = client->cl_cookie; 436 return (TRUE); 437} 438 439/* 440 * This simpler interface is used by svc_getcred to copy the cred data 441 * into a kernel cred structure. 442 */ 443static int 444rpc_gss_svc_getcred(struct svc_req *req, struct ucred **crp, int *flavorp) 445{ 446 struct ucred *cr; 447 struct svc_rpc_gss_cookedcred *cc; 448 struct svc_rpc_gss_client *client; 449 rpc_gss_ucred_t *uc; 450 451 if (req->rq_cred.oa_flavor != RPCSEC_GSS) 452 return (FALSE); 453 454 cc = req->rq_clntcred; 455 client = cc->cc_client; 456 457 if (flavorp) 458 *flavorp = client->cl_rpcflavor; 459 460 if (client->cl_cred) { 461 *crp = crhold(client->cl_cred); 462 return (TRUE); 463 } 464 465 uc = &client->cl_ucred; 466 cr = client->cl_cred = crget(); 467 cr->cr_uid = cr->cr_ruid = cr->cr_svuid = uc->uid; 468 cr->cr_rgid = cr->cr_svgid = uc->gid; 469 crsetgroups(cr, uc->gidlen, uc->gidlist); 470 cr->cr_prison = &prison0; 471 prison_hold(cr->cr_prison); 472 *crp = crhold(cr); 473 474 return (TRUE); 475} 476 477int 478rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len) 479{ 480 struct svc_rpc_gss_cookedcred *cc = req->rq_clntcred; 481 struct svc_rpc_gss_client *client = cc->cc_client; 482 int want_conf; 483 OM_uint32 max; 484 OM_uint32 maj_stat, min_stat; 485 int result; 486 487 switch (client->cl_rawcred.service) { 488 case rpc_gss_svc_none: 489 return (max_tp_unit_len); 490 break; 491 492 case rpc_gss_svc_default: 493 case rpc_gss_svc_integrity: 494 want_conf = FALSE; 495 break; 496 497 case rpc_gss_svc_privacy: 498 want_conf = TRUE; 499 break; 500 501 default: 502 return (0); 503 } 504 505 maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf, 506 client->cl_qop, max_tp_unit_len, &max); 507 508 if (maj_stat == GSS_S_COMPLETE) { 509 result = (int) max; 510 if (result < 0) 511 result = 0; 512 return (result); 513 } else { 514 rpc_gss_log_status("gss_wrap_size_limit", client->cl_mech, 515 maj_stat, min_stat); 516 return (0); 517 } 518} 519 520static struct svc_rpc_gss_client * 521svc_rpc_gss_find_client(struct svc_rpc_gss_clientid *id) 522{ 523 struct svc_rpc_gss_client *client; 524 struct svc_rpc_gss_client_list *list; 525 struct timeval boottime; 526 unsigned long hostid; 527 528 rpc_gss_log_debug("in svc_rpc_gss_find_client(%d)", id->ci_id); 529 530 getcredhostid(curthread->td_ucred, &hostid); 531 getboottime(&boottime); 532 if (id->ci_hostid != hostid || id->ci_boottime != boottime.tv_sec) 533 return (NULL); 534 535 list = &svc_rpc_gss_client_hash[id->ci_id % CLIENT_HASH_SIZE]; 536 sx_xlock(&svc_rpc_gss_lock); 537 TAILQ_FOREACH(client, list, cl_link) { 538 if (client->cl_id.ci_id == id->ci_id) { 539 /* 540 * Move this client to the front of the LRU 541 * list. 542 */ 543 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink); 544 TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, 545 cl_alllink); 546 refcount_acquire(&client->cl_refs); 547 break; 548 } 549 } 550 sx_xunlock(&svc_rpc_gss_lock); 551 552 return (client); 553} 554 555static struct svc_rpc_gss_client * 556svc_rpc_gss_create_client(void) 557{ 558 struct svc_rpc_gss_client *client; 559 struct svc_rpc_gss_client_list *list; 560 struct timeval boottime; 561 unsigned long hostid; 562 563 rpc_gss_log_debug("in svc_rpc_gss_create_client()"); 564 565 client = mem_alloc(sizeof(struct svc_rpc_gss_client)); 566 memset(client, 0, sizeof(struct svc_rpc_gss_client)); 567 568 /* 569 * Set the initial value of cl_refs to two. One for the caller 570 * and the other to hold onto the client structure until it expires. 571 */ 572 refcount_init(&client->cl_refs, 2); 573 sx_init(&client->cl_lock, "GSS-client"); 574 getcredhostid(curthread->td_ucred, &hostid); 575 client->cl_id.ci_hostid = hostid; 576 getboottime(&boottime); 577 client->cl_id.ci_boottime = boottime.tv_sec; 578 client->cl_id.ci_id = svc_rpc_gss_next_clientid++; 579 580 /* 581 * Start the client off with a short expiration time. We will 582 * try to get a saner value from the client creds later. 583 */ 584 client->cl_state = CLIENT_NEW; 585 client->cl_locked = FALSE; 586 client->cl_expiration = time_uptime + 5*60; 587 588 list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE]; 589 sx_xlock(&svc_rpc_gss_lock); 590 TAILQ_INSERT_HEAD(list, client, cl_link); 591 TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink); 592 svc_rpc_gss_client_count++; 593 sx_xunlock(&svc_rpc_gss_lock); 594 return (client); 595} 596 597static void 598svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client) 599{ 600 OM_uint32 min_stat; 601 602 rpc_gss_log_debug("in svc_rpc_gss_destroy_client()"); 603 604 if (client->cl_ctx) 605 gss_delete_sec_context(&min_stat, 606 &client->cl_ctx, GSS_C_NO_BUFFER); 607 608 if (client->cl_cname) 609 gss_release_name(&min_stat, &client->cl_cname); 610 611 if (client->cl_rawcred.client_principal) 612 mem_free(client->cl_rawcred.client_principal, 613 sizeof(*client->cl_rawcred.client_principal) 614 + client->cl_rawcred.client_principal->len); 615 616 if (client->cl_cred) 617 crfree(client->cl_cred); 618 619 sx_destroy(&client->cl_lock); 620 mem_free(client, sizeof(*client)); 621} 622 623/* 624 * Drop a reference to a client and free it if that was the last reference. 625 */ 626static void 627svc_rpc_gss_release_client(struct svc_rpc_gss_client *client) 628{ 629 630 if (!refcount_release(&client->cl_refs)) 631 return; 632 svc_rpc_gss_destroy_client(client); 633} 634 635/* 636 * Remove a client from our global lists. 637 * Must be called with svc_rpc_gss_lock held. 638 */ 639static void 640svc_rpc_gss_forget_client_locked(struct svc_rpc_gss_client *client) 641{ 642 struct svc_rpc_gss_client_list *list; 643 644 sx_assert(&svc_rpc_gss_lock, SX_XLOCKED); 645 list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE]; 646 TAILQ_REMOVE(list, client, cl_link); 647 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink); 648 svc_rpc_gss_client_count--; 649} 650 651/* 652 * Remove a client from our global lists and free it if we can. 653 */ 654static void 655svc_rpc_gss_forget_client(struct svc_rpc_gss_client *client) 656{ 657 struct svc_rpc_gss_client_list *list; 658 struct svc_rpc_gss_client *tclient; 659 660 list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE]; 661 sx_xlock(&svc_rpc_gss_lock); 662 TAILQ_FOREACH(tclient, list, cl_link) { 663 /* 664 * Make sure this client has not already been removed 665 * from the lists by svc_rpc_gss_forget_client() or 666 * svc_rpc_gss_forget_client_locked(). 667 */ 668 if (client == tclient) { 669 svc_rpc_gss_forget_client_locked(client); 670 sx_xunlock(&svc_rpc_gss_lock); 671 svc_rpc_gss_release_client(client); 672 return; 673 } 674 } 675 sx_xunlock(&svc_rpc_gss_lock); 676} 677 678static void 679svc_rpc_gss_timeout_clients(void) 680{ 681 struct svc_rpc_gss_client *client; 682 time_t now = time_uptime; 683 684 rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()"); 685 686 /* 687 * First enforce the max client limit. We keep 688 * svc_rpc_gss_clients in LRU order. 689 */ 690 sx_xlock(&svc_rpc_gss_lock); 691 client = TAILQ_LAST(&svc_rpc_gss_clients, svc_rpc_gss_client_list); 692 while (svc_rpc_gss_client_count > svc_rpc_gss_client_max && client != NULL) { 693 svc_rpc_gss_forget_client_locked(client); 694 sx_xunlock(&svc_rpc_gss_lock); 695 svc_rpc_gss_release_client(client); 696 sx_xlock(&svc_rpc_gss_lock); 697 client = TAILQ_LAST(&svc_rpc_gss_clients, 698 svc_rpc_gss_client_list); 699 } 700again: 701 TAILQ_FOREACH(client, &svc_rpc_gss_clients, cl_alllink) { 702 if (client->cl_state == CLIENT_STALE 703 || now > client->cl_expiration) { 704 svc_rpc_gss_forget_client_locked(client); 705 sx_xunlock(&svc_rpc_gss_lock); 706 rpc_gss_log_debug("expiring client %p", client); 707 svc_rpc_gss_release_client(client); 708 sx_xlock(&svc_rpc_gss_lock); 709 goto again; 710 } 711 } 712 sx_xunlock(&svc_rpc_gss_lock); 713} 714 715#ifdef DEBUG 716/* 717 * OID<->string routines. These are uuuuugly. 718 */ 719static OM_uint32 720gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str) 721{ 722 char numstr[128]; 723 unsigned long number; 724 int numshift; 725 size_t string_length; 726 size_t i; 727 unsigned char *cp; 728 char *bp; 729 730 /* Decoded according to krb5/gssapi_krb5.c */ 731 732 /* First determine the size of the string */ 733 string_length = 0; 734 number = 0; 735 numshift = 0; 736 cp = (unsigned char *) oid->elements; 737 number = (unsigned long) cp[0]; 738 sprintf(numstr, "%ld ", number/40); 739 string_length += strlen(numstr); 740 sprintf(numstr, "%ld ", number%40); 741 string_length += strlen(numstr); 742 for (i=1; i<oid->length; i++) { 743 if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) { 744 number = (number << 7) | (cp[i] & 0x7f); 745 numshift += 7; 746 } 747 else { 748 *minor_status = 0; 749 return(GSS_S_FAILURE); 750 } 751 if ((cp[i] & 0x80) == 0) { 752 sprintf(numstr, "%ld ", number); 753 string_length += strlen(numstr); 754 number = 0; 755 numshift = 0; 756 } 757 } 758 /* 759 * If we get here, we've calculated the length of "n n n ... n ". Add 4 760 * here for "{ " and "}\0". 761 */ 762 string_length += 4; 763 if ((bp = malloc(string_length, M_GSSAPI, M_WAITOK | M_ZERO))) { 764 strcpy(bp, "{ "); 765 number = (unsigned long) cp[0]; 766 sprintf(numstr, "%ld ", number/40); 767 strcat(bp, numstr); 768 sprintf(numstr, "%ld ", number%40); 769 strcat(bp, numstr); 770 number = 0; 771 cp = (unsigned char *) oid->elements; 772 for (i=1; i<oid->length; i++) { 773 number = (number << 7) | (cp[i] & 0x7f); 774 if ((cp[i] & 0x80) == 0) { 775 sprintf(numstr, "%ld ", number); 776 strcat(bp, numstr); 777 number = 0; 778 } 779 } 780 strcat(bp, "}"); 781 oid_str->length = strlen(bp)+1; 782 oid_str->value = (void *) bp; 783 *minor_status = 0; 784 return(GSS_S_COMPLETE); 785 } 786 *minor_status = 0; 787 return(GSS_S_FAILURE); 788} 789#endif 790 791static void 792svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client, 793 const gss_name_t name) 794{ 795 OM_uint32 maj_stat, min_stat; 796 rpc_gss_ucred_t *uc = &client->cl_ucred; 797 int numgroups; 798 799 uc->uid = 65534; 800 uc->gid = 65534; 801 uc->gidlist = client->cl_gid_storage; 802 803 numgroups = NGROUPS; 804 maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech, 805 &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]); 806 if (GSS_ERROR(maj_stat)) 807 uc->gidlen = 0; 808 else 809 uc->gidlen = numgroups; 810} 811 812static void 813svc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client) 814{ 815 static gss_OID_desc krb5_mech_oid = 816 {9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" }; 817 818 /* 819 * Attempt to translate mech type and service into a 820 * 'pseudo flavor'. Hardwire in krb5 support for now. 821 */ 822 if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) { 823 switch (client->cl_rawcred.service) { 824 case rpc_gss_svc_default: 825 case rpc_gss_svc_none: 826 client->cl_rpcflavor = RPCSEC_GSS_KRB5; 827 break; 828 case rpc_gss_svc_integrity: 829 client->cl_rpcflavor = RPCSEC_GSS_KRB5I; 830 break; 831 case rpc_gss_svc_privacy: 832 client->cl_rpcflavor = RPCSEC_GSS_KRB5P; 833 break; 834 } 835 } else { 836 client->cl_rpcflavor = RPCSEC_GSS; 837 } 838} 839 840static bool_t 841svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client, 842 struct svc_req *rqst, 843 struct rpc_gss_init_res *gr, 844 struct rpc_gss_cred *gc) 845{ 846 gss_buffer_desc recv_tok; 847 gss_OID mech; 848 OM_uint32 maj_stat = 0, min_stat = 0, ret_flags; 849 OM_uint32 cred_lifetime; 850 struct svc_rpc_gss_svc_name *sname; 851 852 rpc_gss_log_debug("in svc_rpc_gss_accept_context()"); 853 854 /* Deserialize arguments. */ 855 memset(&recv_tok, 0, sizeof(recv_tok)); 856 857 if (!svc_getargs(rqst, 858 (xdrproc_t) xdr_gss_buffer_desc, 859 (caddr_t) &recv_tok)) { 860 client->cl_state = CLIENT_STALE; 861 return (FALSE); 862 } 863 864 /* 865 * First time round, try all the server names we have until 866 * one matches. Afterwards, stick with that one. 867 */ 868 sx_xlock(&svc_rpc_gss_lock); 869 if (!client->cl_sname) { 870 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) { 871 if (sname->sn_program == rqst->rq_prog 872 && sname->sn_version == rqst->rq_vers) { 873 retry: 874 gr->gr_major = gss_accept_sec_context( 875 &gr->gr_minor, 876 &client->cl_ctx, 877 sname->sn_cred, 878 &recv_tok, 879 GSS_C_NO_CHANNEL_BINDINGS, 880 &client->cl_cname, 881 &mech, 882 &gr->gr_token, 883 &ret_flags, 884 &cred_lifetime, 885 &client->cl_creds); 886 if (gr->gr_major == 887 GSS_S_CREDENTIALS_EXPIRED) { 888 /* 889 * Either our creds really did 890 * expire or gssd was 891 * restarted. 892 */ 893 if (rpc_gss_acquire_svc_cred(sname)) 894 goto retry; 895 } 896 client->cl_sname = sname; 897 break; 898 } 899 } 900 if (!sname) { 901 xdr_free((xdrproc_t) xdr_gss_buffer_desc, 902 (char *) &recv_tok); 903 sx_xunlock(&svc_rpc_gss_lock); 904 return (FALSE); 905 } 906 } else { 907 gr->gr_major = gss_accept_sec_context( 908 &gr->gr_minor, 909 &client->cl_ctx, 910 client->cl_sname->sn_cred, 911 &recv_tok, 912 GSS_C_NO_CHANNEL_BINDINGS, 913 &client->cl_cname, 914 &mech, 915 &gr->gr_token, 916 &ret_flags, 917 &cred_lifetime, 918 NULL); 919 } 920 sx_xunlock(&svc_rpc_gss_lock); 921 922 xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok); 923 924 /* 925 * If we get an error from gss_accept_sec_context, send the 926 * reply anyway so that the client gets a chance to see what 927 * is wrong. 928 */ 929 if (gr->gr_major != GSS_S_COMPLETE && 930 gr->gr_major != GSS_S_CONTINUE_NEEDED) { 931 rpc_gss_log_status("accept_sec_context", client->cl_mech, 932 gr->gr_major, gr->gr_minor); 933 client->cl_state = CLIENT_STALE; 934 return (TRUE); 935 } 936 937 gr->gr_handle.value = &client->cl_id; 938 gr->gr_handle.length = sizeof(client->cl_id); 939 gr->gr_win = SVC_RPC_GSS_SEQWINDOW; 940 941 /* Save client info. */ 942 client->cl_mech = mech; 943 client->cl_qop = GSS_C_QOP_DEFAULT; 944 client->cl_done_callback = FALSE; 945 946 if (gr->gr_major == GSS_S_COMPLETE) { 947 gss_buffer_desc export_name; 948 949 /* 950 * Change client expiration time to be near when the 951 * client creds expire (or 24 hours if we can't figure 952 * that out). 953 */ 954 if (cred_lifetime == GSS_C_INDEFINITE) 955 cred_lifetime = 24*60*60; 956 957 /* 958 * Cap cred_lifetime if sysctl kern.rpc.gss.lifetime_max is set. 959 */ 960 if (svc_rpc_gss_lifetime_max > 0 && cred_lifetime > 961 svc_rpc_gss_lifetime_max) 962 cred_lifetime = svc_rpc_gss_lifetime_max; 963 964 client->cl_expiration = time_uptime + cred_lifetime; 965 966 /* 967 * Fill in cred details in the rawcred structure. 968 */ 969 client->cl_rawcred.version = RPCSEC_GSS_VERSION; 970 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism); 971 maj_stat = gss_export_name(&min_stat, client->cl_cname, 972 &export_name); 973 if (maj_stat != GSS_S_COMPLETE) { 974 rpc_gss_log_status("gss_export_name", client->cl_mech, 975 maj_stat, min_stat); 976 return (FALSE); 977 } 978 client->cl_rawcred.client_principal = 979 mem_alloc(sizeof(*client->cl_rawcred.client_principal) 980 + export_name.length); 981 client->cl_rawcred.client_principal->len = export_name.length; 982 memcpy(client->cl_rawcred.client_principal->name, 983 export_name.value, export_name.length); 984 gss_release_buffer(&min_stat, &export_name); 985 client->cl_rawcred.svc_principal = 986 client->cl_sname->sn_principal; 987 client->cl_rawcred.service = gc->gc_svc; 988 989 /* 990 * Use gss_pname_to_uid to map to unix creds. For 991 * kerberos5, this uses krb5_aname_to_localname. 992 */ 993 svc_rpc_gss_build_ucred(client, client->cl_cname); 994 svc_rpc_gss_set_flavor(client); 995 gss_release_name(&min_stat, &client->cl_cname); 996 997#ifdef DEBUG 998 { 999 gss_buffer_desc mechname; 1000 1001 gss_oid_to_str(&min_stat, mech, &mechname); 1002 1003 rpc_gss_log_debug("accepted context for %s with " 1004 "<mech %.*s, qop %d, svc %d>", 1005 client->cl_rawcred.client_principal->name, 1006 mechname.length, (char *)mechname.value, 1007 client->cl_qop, client->cl_rawcred.service); 1008 1009 gss_release_buffer(&min_stat, &mechname); 1010 } 1011#endif /* DEBUG */ 1012 } 1013 return (TRUE); 1014} 1015 1016static bool_t 1017svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg, 1018 gss_qop_t *qop, rpc_gss_proc_t gcproc) 1019{ 1020 struct opaque_auth *oa; 1021 gss_buffer_desc rpcbuf, checksum; 1022 OM_uint32 maj_stat, min_stat; 1023 gss_qop_t qop_state; 1024 int32_t rpchdr[128 / sizeof(int32_t)]; 1025 int32_t *buf; 1026 1027 rpc_gss_log_debug("in svc_rpc_gss_validate()"); 1028 1029 memset(rpchdr, 0, sizeof(rpchdr)); 1030 1031 /* Reconstruct RPC header for signing (from xdr_callmsg). */ 1032 buf = rpchdr; 1033 IXDR_PUT_LONG(buf, msg->rm_xid); 1034 IXDR_PUT_ENUM(buf, msg->rm_direction); 1035 IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers); 1036 IXDR_PUT_LONG(buf, msg->rm_call.cb_prog); 1037 IXDR_PUT_LONG(buf, msg->rm_call.cb_vers); 1038 IXDR_PUT_LONG(buf, msg->rm_call.cb_proc); 1039 oa = &msg->rm_call.cb_cred; 1040 IXDR_PUT_ENUM(buf, oa->oa_flavor); 1041 IXDR_PUT_LONG(buf, oa->oa_length); 1042 if (oa->oa_length) { 1043 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length); 1044 buf += RNDUP(oa->oa_length) / sizeof(int32_t); 1045 } 1046 rpcbuf.value = rpchdr; 1047 rpcbuf.length = (u_char *)buf - (u_char *)rpchdr; 1048 1049 checksum.value = msg->rm_call.cb_verf.oa_base; 1050 checksum.length = msg->rm_call.cb_verf.oa_length; 1051 1052 maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum, 1053 &qop_state); 1054 1055 if (maj_stat != GSS_S_COMPLETE) { 1056 rpc_gss_log_status("gss_verify_mic", client->cl_mech, 1057 maj_stat, min_stat); 1058 /* 1059 * A bug in some versions of the Linux client generates a 1060 * Destroy operation with a bogus encrypted checksum. Deleting 1061 * the credential handle for that case causes the mount to fail. 1062 * Since the checksum is bogus (gss_verify_mic() failed), it 1063 * doesn't make sense to destroy the handle and not doing so 1064 * fixes the Linux mount. 1065 */ 1066 if (gcproc != RPCSEC_GSS_DESTROY) 1067 client->cl_state = CLIENT_STALE; 1068 return (FALSE); 1069 } 1070 1071 *qop = qop_state; 1072 return (TRUE); 1073} 1074 1075static bool_t 1076svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client, 1077 struct svc_req *rqst, u_int seq) 1078{ 1079 gss_buffer_desc signbuf; 1080 gss_buffer_desc mic; 1081 OM_uint32 maj_stat, min_stat; 1082 uint32_t nseq; 1083 1084 rpc_gss_log_debug("in svc_rpc_gss_nextverf()"); 1085 1086 nseq = htonl(seq); 1087 signbuf.value = &nseq; 1088 signbuf.length = sizeof(nseq); 1089 1090 maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop, 1091 &signbuf, &mic); 1092 1093 if (maj_stat != GSS_S_COMPLETE) { 1094 rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat); 1095 client->cl_state = CLIENT_STALE; 1096 return (FALSE); 1097 } 1098 1099 KASSERT(mic.length <= MAX_AUTH_BYTES, 1100 ("MIC too large for RPCSEC_GSS")); 1101 1102 rqst->rq_verf.oa_flavor = RPCSEC_GSS; 1103 rqst->rq_verf.oa_length = mic.length; 1104 bcopy(mic.value, rqst->rq_verf.oa_base, mic.length); 1105 1106 gss_release_buffer(&min_stat, &mic); 1107 1108 return (TRUE); 1109} 1110 1111static bool_t 1112svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst) 1113{ 1114 struct svc_rpc_gss_callback *scb; 1115 rpc_gss_lock_t lock; 1116 void *cookie; 1117 bool_t cb_res; 1118 bool_t result; 1119 1120 /* 1121 * See if we have a callback for this guy. 1122 */ 1123 result = TRUE; 1124 SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) { 1125 if (scb->cb_callback.program == rqst->rq_prog 1126 && scb->cb_callback.version == rqst->rq_vers) { 1127 /* 1128 * This one matches. Call the callback and see 1129 * if it wants to veto or something. 1130 */ 1131 lock.locked = FALSE; 1132 lock.raw_cred = &client->cl_rawcred; 1133 cb_res = scb->cb_callback.callback(rqst, 1134 client->cl_creds, 1135 client->cl_ctx, 1136 &lock, 1137 &cookie); 1138 1139 if (!cb_res) { 1140 client->cl_state = CLIENT_STALE; 1141 result = FALSE; 1142 break; 1143 } 1144 1145 /* 1146 * The callback accepted the connection - it 1147 * is responsible for freeing client->cl_creds 1148 * now. 1149 */ 1150 client->cl_creds = GSS_C_NO_CREDENTIAL; 1151 client->cl_locked = lock.locked; 1152 client->cl_cookie = cookie; 1153 return (TRUE); 1154 } 1155 } 1156 1157 /* 1158 * Either no callback exists for this program/version or one 1159 * of the callbacks rejected the connection. We just need to 1160 * clean up the delegated client creds, if any. 1161 */ 1162 if (client->cl_creds) { 1163 OM_uint32 min_ver; 1164 gss_release_cred(&min_ver, &client->cl_creds); 1165 } 1166 return (result); 1167} 1168 1169static bool_t 1170svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq) 1171{ 1172 u_int32_t offset; 1173 int word, bit; 1174 bool_t result; 1175 1176 sx_xlock(&client->cl_lock); 1177 if (seq <= client->cl_seqlast) { 1178 /* 1179 * The request sequence number is less than 1180 * the largest we have seen so far. If it is 1181 * outside the window or if we have seen a 1182 * request with this sequence before, silently 1183 * discard it. 1184 */ 1185 offset = client->cl_seqlast - seq; 1186 if (offset >= SVC_RPC_GSS_SEQWINDOW) { 1187 result = FALSE; 1188 goto out; 1189 } 1190 word = offset / 32; 1191 bit = offset % 32; 1192 if (client->cl_seqmask[word] & (1 << bit)) { 1193 result = FALSE; 1194 goto out; 1195 } 1196 } 1197 1198 result = TRUE; 1199out: 1200 sx_xunlock(&client->cl_lock); 1201 return (result); 1202} 1203 1204static void 1205svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq) 1206{ 1207 int offset, i, word, bit; 1208 uint32_t carry, newcarry; 1209 1210 sx_xlock(&client->cl_lock); 1211 if (seq > client->cl_seqlast) { 1212 /* 1213 * This request has a sequence number greater 1214 * than any we have seen so far. Advance the 1215 * seq window and set bit zero of the window 1216 * (which corresponds to the new sequence 1217 * number) 1218 */ 1219 offset = seq - client->cl_seqlast; 1220 while (offset > 32) { 1221 for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1; 1222 i > 0; i--) { 1223 client->cl_seqmask[i] = client->cl_seqmask[i-1]; 1224 } 1225 client->cl_seqmask[0] = 0; 1226 offset -= 32; 1227 } 1228 carry = 0; 1229 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) { 1230 newcarry = client->cl_seqmask[i] >> (32 - offset); 1231 client->cl_seqmask[i] = 1232 (client->cl_seqmask[i] << offset) | carry; 1233 carry = newcarry; 1234 } 1235 client->cl_seqmask[0] |= 1; 1236 client->cl_seqlast = seq; 1237 } else { 1238 offset = client->cl_seqlast - seq; 1239 word = offset / 32; 1240 bit = offset % 32; 1241 client->cl_seqmask[word] |= (1 << bit); 1242 } 1243 sx_xunlock(&client->cl_lock); 1244} 1245 1246enum auth_stat 1247svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg) 1248 1249{ 1250 OM_uint32 min_stat; 1251 XDR xdrs; 1252 struct svc_rpc_gss_cookedcred *cc; 1253 struct svc_rpc_gss_client *client; 1254 struct rpc_gss_cred gc; 1255 struct rpc_gss_init_res gr; 1256 gss_qop_t qop; 1257 int call_stat; 1258 enum auth_stat result; 1259 1260 rpc_gss_log_debug("in svc_rpc_gss()"); 1261 1262 /* Garbage collect old clients. */ 1263 svc_rpc_gss_timeout_clients(); 1264 1265 /* Initialize reply. */ 1266 rqst->rq_verf = _null_auth; 1267 1268 /* Deserialize client credentials. */ 1269 if (rqst->rq_cred.oa_length <= 0) 1270 return (AUTH_BADCRED); 1271 1272 memset(&gc, 0, sizeof(gc)); 1273 1274 xdrmem_create(&xdrs, rqst->rq_cred.oa_base, 1275 rqst->rq_cred.oa_length, XDR_DECODE); 1276 1277 if (!xdr_rpc_gss_cred(&xdrs, &gc)) { 1278 XDR_DESTROY(&xdrs); 1279 return (AUTH_BADCRED); 1280 } 1281 XDR_DESTROY(&xdrs); 1282 1283 client = NULL; 1284 1285 /* Check version. */ 1286 if (gc.gc_version != RPCSEC_GSS_VERSION) { 1287 result = AUTH_BADCRED; 1288 goto out; 1289 } 1290 1291 /* Check the proc and find the client (or create it) */ 1292 if (gc.gc_proc == RPCSEC_GSS_INIT) { 1293 if (gc.gc_handle.length != 0) { 1294 result = AUTH_BADCRED; 1295 goto out; 1296 } 1297 client = svc_rpc_gss_create_client(); 1298 } else { 1299 struct svc_rpc_gss_clientid *p; 1300 if (gc.gc_handle.length != sizeof(*p)) { 1301 result = AUTH_BADCRED; 1302 goto out; 1303 } 1304 p = gc.gc_handle.value; 1305 client = svc_rpc_gss_find_client(p); 1306 if (!client) { 1307 /* 1308 * Can't find the client - we may have 1309 * destroyed it - tell the other side to 1310 * re-authenticate. 1311 */ 1312 result = RPCSEC_GSS_CREDPROBLEM; 1313 goto out; 1314 } 1315 } 1316 cc = rqst->rq_clntcred; 1317 cc->cc_client = client; 1318 cc->cc_service = gc.gc_svc; 1319 cc->cc_seq = gc.gc_seq; 1320 1321 /* 1322 * The service and sequence number must be ignored for 1323 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT. 1324 */ 1325 if (gc.gc_proc != RPCSEC_GSS_INIT 1326 && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) { 1327 /* 1328 * Check for sequence number overflow. 1329 */ 1330 if (gc.gc_seq >= MAXSEQ) { 1331 result = RPCSEC_GSS_CTXPROBLEM; 1332 goto out; 1333 } 1334 1335 /* 1336 * Check for valid service. 1337 */ 1338 if (gc.gc_svc != rpc_gss_svc_none && 1339 gc.gc_svc != rpc_gss_svc_integrity && 1340 gc.gc_svc != rpc_gss_svc_privacy) { 1341 result = AUTH_BADCRED; 1342 goto out; 1343 } 1344 } 1345 1346 /* Handle RPCSEC_GSS control procedure. */ 1347 switch (gc.gc_proc) { 1348 1349 case RPCSEC_GSS_INIT: 1350 case RPCSEC_GSS_CONTINUE_INIT: 1351 if (rqst->rq_proc != NULLPROC) { 1352 result = AUTH_REJECTEDCRED; 1353 break; 1354 } 1355 1356 memset(&gr, 0, sizeof(gr)); 1357 if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) { 1358 result = AUTH_REJECTEDCRED; 1359 break; 1360 } 1361 1362 if (gr.gr_major == GSS_S_COMPLETE) { 1363 /* 1364 * We borrow the space for the call verf to 1365 * pack our reply verf. 1366 */ 1367 rqst->rq_verf = msg->rm_call.cb_verf; 1368 if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) { 1369 result = AUTH_REJECTEDCRED; 1370 break; 1371 } 1372 } else { 1373 rqst->rq_verf = _null_auth; 1374 } 1375 1376 call_stat = svc_sendreply(rqst, 1377 (xdrproc_t) xdr_rpc_gss_init_res, 1378 (caddr_t) &gr); 1379 1380 gss_release_buffer(&min_stat, &gr.gr_token); 1381 1382 if (!call_stat) { 1383 result = AUTH_FAILED; 1384 break; 1385 } 1386 1387 if (gr.gr_major == GSS_S_COMPLETE) 1388 client->cl_state = CLIENT_ESTABLISHED; 1389 1390 result = RPCSEC_GSS_NODISPATCH; 1391 break; 1392 1393 case RPCSEC_GSS_DATA: 1394 case RPCSEC_GSS_DESTROY: 1395 if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) { 1396 result = RPCSEC_GSS_NODISPATCH; 1397 break; 1398 } 1399 1400 if (!svc_rpc_gss_validate(client, msg, &qop, gc.gc_proc)) { 1401 result = RPCSEC_GSS_CREDPROBLEM; 1402 break; 1403 } 1404 1405 /* 1406 * We borrow the space for the call verf to pack our 1407 * reply verf. 1408 */ 1409 rqst->rq_verf = msg->rm_call.cb_verf; 1410 if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) { 1411 result = RPCSEC_GSS_CTXPROBLEM; 1412 break; 1413 } 1414 1415 svc_rpc_gss_update_seq(client, gc.gc_seq); 1416 1417 /* 1418 * Change the SVCAUTH ops on the request to point at 1419 * our own code so that we can unwrap the arguments 1420 * and wrap the result. The caller will re-set this on 1421 * every request to point to a set of null wrap/unwrap 1422 * methods. Acquire an extra reference to the client 1423 * which will be released by svc_rpc_gss_release() 1424 * after the request has finished processing. 1425 */ 1426 refcount_acquire(&client->cl_refs); 1427 rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops; 1428 rqst->rq_auth.svc_ah_private = cc; 1429 1430 if (gc.gc_proc == RPCSEC_GSS_DATA) { 1431 /* 1432 * We might be ready to do a callback to the server to 1433 * see if it wants to accept/reject the connection. 1434 */ 1435 sx_xlock(&client->cl_lock); 1436 if (!client->cl_done_callback) { 1437 client->cl_done_callback = TRUE; 1438 client->cl_qop = qop; 1439 client->cl_rawcred.qop = _rpc_gss_num_to_qop( 1440 client->cl_rawcred.mechanism, qop); 1441 if (!svc_rpc_gss_callback(client, rqst)) { 1442 result = AUTH_REJECTEDCRED; 1443 sx_xunlock(&client->cl_lock); 1444 break; 1445 } 1446 } 1447 sx_xunlock(&client->cl_lock); 1448 1449 /* 1450 * If the server has locked this client to a 1451 * particular service+qop pair, enforce that 1452 * restriction now. 1453 */ 1454 if (client->cl_locked) { 1455 if (client->cl_rawcred.service != gc.gc_svc) { 1456 result = AUTH_FAILED; 1457 break; 1458 } else if (client->cl_qop != qop) { 1459 result = AUTH_BADVERF; 1460 break; 1461 } 1462 } 1463 1464 /* 1465 * If the qop changed, look up the new qop 1466 * name for rawcred. 1467 */ 1468 if (client->cl_qop != qop) { 1469 client->cl_qop = qop; 1470 client->cl_rawcred.qop = _rpc_gss_num_to_qop( 1471 client->cl_rawcred.mechanism, qop); 1472 } 1473 1474 /* 1475 * Make sure we use the right service value 1476 * for unwrap/wrap. 1477 */ 1478 if (client->cl_rawcred.service != gc.gc_svc) { 1479 client->cl_rawcred.service = gc.gc_svc; 1480 svc_rpc_gss_set_flavor(client); 1481 } 1482 1483 result = AUTH_OK; 1484 } else { 1485 if (rqst->rq_proc != NULLPROC) { 1486 result = AUTH_REJECTEDCRED; 1487 break; 1488 } 1489 1490 call_stat = svc_sendreply(rqst, 1491 (xdrproc_t) xdr_void, (caddr_t) NULL); 1492 1493 if (!call_stat) { 1494 result = AUTH_FAILED; 1495 break; 1496 } 1497 1498 svc_rpc_gss_forget_client(client); 1499 1500 result = RPCSEC_GSS_NODISPATCH; 1501 break; 1502 } 1503 break; 1504 1505 default: 1506 result = AUTH_BADCRED; 1507 break; 1508 } 1509out: 1510 if (client) 1511 svc_rpc_gss_release_client(client); 1512 1513 xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc); 1514 return (result); 1515} 1516 1517static bool_t 1518svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp) 1519{ 1520 struct svc_rpc_gss_cookedcred *cc; 1521 struct svc_rpc_gss_client *client; 1522 1523 rpc_gss_log_debug("in svc_rpc_gss_wrap()"); 1524 1525 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private; 1526 client = cc->cc_client; 1527 if (client->cl_state != CLIENT_ESTABLISHED 1528 || cc->cc_service == rpc_gss_svc_none || *mp == NULL) { 1529 return (TRUE); 1530 } 1531 1532 return (xdr_rpc_gss_wrap_data(mp, 1533 client->cl_ctx, client->cl_qop, 1534 cc->cc_service, cc->cc_seq)); 1535} 1536 1537static bool_t 1538svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp) 1539{ 1540 struct svc_rpc_gss_cookedcred *cc; 1541 struct svc_rpc_gss_client *client; 1542 1543 rpc_gss_log_debug("in svc_rpc_gss_unwrap()"); 1544 1545 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private; 1546 client = cc->cc_client; 1547 if (client->cl_state != CLIENT_ESTABLISHED 1548 || cc->cc_service == rpc_gss_svc_none) { 1549 return (TRUE); 1550 } 1551 1552 return (xdr_rpc_gss_unwrap_data(mp, 1553 client->cl_ctx, client->cl_qop, 1554 cc->cc_service, cc->cc_seq)); 1555} 1556 1557static void 1558svc_rpc_gss_release(SVCAUTH *auth) 1559{ 1560 struct svc_rpc_gss_cookedcred *cc; 1561 struct svc_rpc_gss_client *client; 1562 1563 rpc_gss_log_debug("in svc_rpc_gss_release()"); 1564 1565 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private; 1566 client = cc->cc_client; 1567 svc_rpc_gss_release_client(client); 1568} 1569