194380Sdfr/*- 294380Sdfr * Copyright (c) 2008 Doug Rabson 394380Sdfr * All rights reserved. 494380Sdfr * 594380Sdfr * Redistribution and use in source and binary forms, with or without 694380Sdfr * modification, are permitted provided that the following conditions 794380Sdfr * are met: 8119332Speter * 1. Redistributions of source code must retain the above copyright 9119332Speter * notice, this list of conditions and the following disclaimer. 1094380Sdfr * 2. Redistributions in binary form must reproduce the above copyright 1194380Sdfr * notice, this list of conditions and the following disclaimer in the 1294380Sdfr * documentation and/or other materials provided with the distribution. 13177613Sjhb * 14227776Slstewart * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15164199Sru * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16113989Sjhb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17255658Sjilles * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18113989Sjhb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19161330Sjhb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20161330Sjhb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2194380Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2294380Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2394380Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2494380Sdfr * SUCH DAMAGE. 2594380Sdfr * 2694380Sdfr * $FreeBSD$ 2794380Sdfr */ 2894380Sdfr/* 2994380Sdfr svc_rpcsec_gss.c 3094380Sdfr 3194380Sdfr Copyright (c) 2000 The Regents of the University of Michigan. 3294380Sdfr All rights reserved. 3394380Sdfr 3494380Sdfr Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>. 3594380Sdfr All rights reserved, all wrongs reversed. 36232449Sjmallett 37205014Snwhitehorn Redistribution and use in source and binary forms, with or without 38205014Snwhitehorn modification, are permitted provided that the following conditions 39119332Speter are met: 4094380Sdfr 4194380Sdfr 1. Redistributions of source code must retain the above copyright 4294380Sdfr notice, this list of conditions and the following disclaimer. 43100385Speter 2. Redistributions in binary form must reproduce the above copyright 4494380Sdfr notice, this list of conditions and the following disclaimer in the 45151360Sps documentation and/or other materials provided with the distribution. 46151360Sps 3. Neither the name of the University nor the names of its 47151360Sps contributors may be used to endorse or promote products derived 48151360Sps from this software without specific prior written permission. 49151360Sps 50151360Sps THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 51151360Sps WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 52151360Sps MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 53151360Sps DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 54151360Sps FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 55151360Sps CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 56151360Sps SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 57236027Sed BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 58236027Sed LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 59151360Sps NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 60236027Sed SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 61236027Sed 62151360Sps $Id: svc_auth_gss.c,v 1.27 2002/01/15 15:43:00 andros Exp $ 63151721Speter */ 64151583Sps 65151583Sps#include <stdio.h> 66119332Speter#include <stdlib.h> 67100385Speter#include <string.h> 68100385Speter#include <pwd.h> 6994380Sdfr#include <grp.h> 70183271Sobrien#include <errno.h> 71183271Sobrien#include <unistd.h> 72183271Sobrien#include <sys/queue.h> 73183271Sobrien#include <rpc/rpc.h> 74183271Sobrien#include <rpc/rpcsec_gss.h> 75119332Speter#include "rpcsec_gss_int.h" 7694380Sdfr 77236027Sedstatic bool_t svc_rpc_gss_initialised = FALSE; 78236027Sed 7994380Sdfrstatic bool_t svc_rpc_gss_wrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t); 80226349Smarcelstatic bool_t svc_rpc_gss_unwrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t); 81226349Smarcelstatic enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *); 82226349Smarcel 83226349Smarcelstatic struct svc_auth_ops svc_auth_gss_ops = { 84226349Smarcel svc_rpc_gss_wrap, 85119332Speter svc_rpc_gss_unwrap, 8694380Sdfr}; 87100385Speter 88100385Speterstruct svc_rpc_gss_callback { 8994380Sdfr SLIST_ENTRY(svc_rpc_gss_callback) cb_link; 90125171Speter rpc_gss_callback_t cb_callback; 91125171Speter}; 92125171Speterstatic SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback) 93125171Speter svc_rpc_gss_callbacks = SLIST_HEAD_INITIALIZER(svc_rpc_gss_callbacks); 94271011Skib 95271011Skibstruct svc_rpc_gss_svc_name { 96271011Skib SLIST_ENTRY(svc_rpc_gss_svc_name) sn_link; 97271011Skib char *sn_principal; 98271011Skib gss_OID sn_mech; 99119332Speter u_int sn_req_time; 10094380Sdfr gss_cred_id_t sn_cred; 10194380Sdfr u_int sn_program; 10294380Sdfr u_int sn_version; 10394380Sdfr}; 104100385Speterstatic SLIST_HEAD(svc_rpc_gss_svc_name_list, svc_rpc_gss_svc_name) 10594380Sdfr svc_rpc_gss_svc_names = SLIST_HEAD_INITIALIZER(svc_rpc_gss_svc_names); 106119332Speter 107100385Speterenum svc_rpc_gss_client_state { 10894380Sdfr CLIENT_NEW, /* still authenticating */ 10994380Sdfr CLIENT_ESTABLISHED, /* context established */ 110119332Speter CLIENT_STALE /* garbage to collect */ 11194380Sdfr}; 112100385Speter 11394380Sdfr#define SVC_RPC_GSS_SEQWINDOW 128 114119332Speter 11594380Sdfrstruct svc_rpc_gss_client { 116100385Speter TAILQ_ENTRY(svc_rpc_gss_client) cl_link; 11794380Sdfr TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink; 11894380Sdfr uint32_t cl_id; 119119332Speter time_t cl_expiration; /* when to gc */ 12094380Sdfr enum svc_rpc_gss_client_state cl_state; /* client state */ 121100385Speter bool_t cl_locked; /* fixed service+qop */ 12294380Sdfr gss_ctx_id_t cl_ctx; /* context id */ 12394380Sdfr gss_cred_id_t cl_creds; /* delegated creds */ 124119332Speter gss_name_t cl_cname; /* client name */ 125100385Speter struct svc_rpc_gss_svc_name *cl_sname; /* server name used */ 12694380Sdfr rpc_gss_rawcred_t cl_rawcred; /* raw credentials */ 12794380Sdfr rpc_gss_ucred_t cl_ucred; /* unix-style credentials */ 128119332Speter bool_t cl_done_callback; /* TRUE after call */ 12994380Sdfr void *cl_cookie; /* user cookie from callback */ 130100385Speter gid_t cl_gid_storage[NGRPS]; 13194380Sdfr gss_OID cl_mech; /* mechanism */ 132119332Speter gss_qop_t cl_qop; /* quality of protection */ 133100385Speter u_int cl_seq; /* current sequence number */ 134100385Speter u_int cl_win; /* sequence window size */ 13594380Sdfr u_int cl_seqlast; /* sequence window origin */ 136190622Skib uint32_t cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */ 137190622Skib gss_buffer_desc cl_verf; /* buffer for verf checksum */ 138190622Skib}; 139190622SkibTAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client); 140119332Speter 14194380Sdfr#define CLIENT_HASH_SIZE 256 14294380Sdfr#define CLIENT_MAX 128 14394380Sdfrstatic struct svc_rpc_gss_client_list svc_rpc_gss_client_hash[CLIENT_HASH_SIZE]; 14494380Sdfrstatic struct svc_rpc_gss_client_list svc_rpc_gss_clients; 14594380Sdfrstatic size_t svc_rpc_gss_client_count; 14694380Sdfrstatic uint32_t svc_rpc_gss_next_clientid = 1; 147119332Speter 14894380Sdfr#ifdef __GNUC__ 14994380Sdfrstatic void svc_rpc_gss_init(void) __attribute__ ((constructor)); 15094380Sdfr#endif 15194380Sdfr 15294380Sdfrstatic void 15394380Sdfrsvc_rpc_gss_init(void) 15494380Sdfr{ 155119332Speter int i; 156157286Sps 157157286Sps if (!svc_rpc_gss_initialised) { 158157286Sps for (i = 0; i < CLIENT_HASH_SIZE; i++) 159157286Sps TAILQ_INIT(&svc_rpc_gss_client_hash[i]); 16094380Sdfr TAILQ_INIT(&svc_rpc_gss_clients); 161119332Speter svc_auth_reg(RPCSEC_GSS, svc_rpc_gss); 16294380Sdfr svc_rpc_gss_initialised = TRUE; 163100385Speter } 16494380Sdfr} 165119332Speter 16694380Sdfrbool_t 167100385Speterrpc_gss_set_callback(rpc_gss_callback_t *cb) 16894380Sdfr{ 169119332Speter struct svc_rpc_gss_callback *scb; 17094380Sdfr 171100385Speter scb = mem_alloc(sizeof(struct svc_rpc_gss_callback)); 17294380Sdfr if (!scb) { 173184184Sjhb _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); 174184184Sjhb return (FALSE); 175184184Sjhb } 176184184Sjhb scb->cb_callback = *cb; 177184184Sjhb SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link); 178184184Sjhb 179119332Speter return (TRUE); 18094380Sdfr} 18194380Sdfr 18294380Sdfrbool_t 183236027Sedrpc_gss_set_svc_name(const char *principal, const char *mechanism, 18494380Sdfr u_int req_time, u_int program, u_int version) 185236027Sed{ 18694380Sdfr OM_uint32 maj_stat, min_stat; 187154596Sambrisko struct svc_rpc_gss_svc_name *sname; 188154596Sambrisko gss_buffer_desc namebuf; 189154596Sambrisko gss_name_t name; 190154596Sambrisko gss_OID mech_oid; 191165406Sjkim gss_OID_set_desc oid_set; 192165406Sjkim gss_cred_id_t cred; 193165406Sjkim 194165406Sjkim svc_rpc_gss_init(); 195165406Sjkim 196165406Sjkim if (!rpc_gss_mech_to_oid(mechanism, &mech_oid)) 197165406Sjkim return (FALSE); 198165406Sjkim oid_set.count = 1; 199165406Sjkim oid_set.elements = mech_oid; 200165406Sjkim 201165406Sjkim namebuf.value = (void *)(intptr_t) principal; 202165406Sjkim namebuf.length = strlen(principal); 203165406Sjkim 204151358Sps maj_stat = gss_import_name(&min_stat, &namebuf, 205151358Sps GSS_C_NT_HOSTBASED_SERVICE, &name); 206151358Sps if (maj_stat != GSS_S_COMPLETE) 207151358Sps return (FALSE); 208151358Sps 209151358Sps maj_stat = gss_acquire_cred(&min_stat, name, 210151358Sps req_time, &oid_set, GSS_C_ACCEPT, &cred, NULL, NULL); 211151358Sps if (maj_stat != GSS_S_COMPLETE) 212151358Sps return (FALSE); 213151358Sps 214151358Sps gss_release_name(&min_stat, &name); 215151358Sps 216253531Skib sname = malloc(sizeof(struct svc_rpc_gss_svc_name)); 217253531Skib if (!sname) 218253531Skib return (FALSE); 219253531Skib sname->sn_principal = strdup(principal); 220253531Skib sname->sn_mech = mech_oid; 221253531Skib sname->sn_req_time = req_time; 222253531Skib sname->sn_cred = cred; 223253531Skib sname->sn_program = program; 224253531Skib sname->sn_version = version; 225253531Skib SLIST_INSERT_HEAD(&svc_rpc_gss_svc_names, sname, sn_link); 226253531Skib 227253531Skib return (TRUE); 228253531Skib} 229253531Skib 230253531Skibbool_t 231140481Spsrpc_gss_get_principal_name(rpc_gss_principal_t *principal, 232151356Sps const char *mech, const char *name, const char *node, const char *domain) 233151356Sps{ 234140481Sps OM_uint32 maj_stat, min_stat; 235253495Skib gss_OID mech_oid; 236253495Skib size_t namelen; 237253495Skib gss_buffer_desc buf; 238253495Skib gss_name_t gss_name, gss_mech_name; 239253495Skib rpc_gss_principal_t result; 240253495Skib 241185879Sjhb svc_rpc_gss_init(); 242185879Sjhb 243185879Sjhb if (!rpc_gss_mech_to_oid(mech, &mech_oid)) 244185879Sjhb return (FALSE); 245185879Sjhb 246185879Sjhb /* 247185879Sjhb * Construct a gss_buffer containing the full name formatted 248185879Sjhb * as "name/node@domain" where node and domain are optional. 249185879Sjhb */ 250185879Sjhb namelen = strlen(name); 251253531Skib if (node) { 252185879Sjhb namelen += strlen(node) + 1; 253154587Sambrisko } 254154587Sambrisko if (domain) { 255154587Sambrisko namelen += strlen(domain) + 1; 256154587Sambrisko } 257147814Sjhb 258147814Sjhb buf.value = mem_alloc(namelen); 259147814Sjhb buf.length = namelen; 260147814Sjhb strcpy((char *) buf.value, name); 261236027Sed if (node) { 262236027Sed strcat((char *) buf.value, "/"); 263147814Sjhb strcat((char *) buf.value, node); 264147814Sjhb } 265147814Sjhb if (domain) { 266147814Sjhb strcat((char *) buf.value, "@"); 267147814Sjhb strcat((char *) buf.value, domain); 268236027Sed } 269236027Sed 270147814Sjhb /* 271140482Sps * Convert that to a gss_name_t and then convert that to a 272140482Sps * mechanism name in the selected mechanism. 273140482Sps */ 274140482Sps maj_stat = gss_import_name(&min_stat, &buf, 275220159Skib GSS_C_NT_USER_NAME, &gss_name); 276220159Skib mem_free(buf.value, buf.length); 277220159Skib if (maj_stat != GSS_S_COMPLETE) { 278220159Skib log_status("gss_import_name", mech_oid, maj_stat, min_stat); 279185879Sjhb return (FALSE); 280185879Sjhb } 281185879Sjhb maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid, 282185879Sjhb &gss_mech_name); 283185879Sjhb if (maj_stat != GSS_S_COMPLETE) { 284185879Sjhb log_status("gss_canonicalize_name", mech_oid, maj_stat, 285185879Sjhb min_stat); 286185879Sjhb gss_release_name(&min_stat, &gss_name); 287185879Sjhb return (FALSE); 288185879Sjhb } 289185879Sjhb gss_release_name(&min_stat, &gss_name); 290185879Sjhb 291185879Sjhb /* 292185879Sjhb * Export the mechanism name and use that to construct the 293185879Sjhb * rpc_gss_principal_t result. 294185879Sjhb */ 295185879Sjhb maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf); 296185879Sjhb if (maj_stat != GSS_S_COMPLETE) { 297185879Sjhb log_status("gss_export_name", mech_oid, maj_stat, min_stat); 298185879Sjhb gss_release_name(&min_stat, &gss_mech_name); 299185879Sjhb return (FALSE); 300185879Sjhb } 301185879Sjhb gss_release_name(&min_stat, &gss_mech_name); 302185879Sjhb 303185879Sjhb result = mem_alloc(sizeof(int) + buf.length); 304185879Sjhb if (!result) { 305185879Sjhb gss_release_buffer(&min_stat, &buf); 306185436Sbz return (FALSE); 307185436Sbz } 308185436Sbz result->len = buf.length; 309163020Sdavidxu memcpy(result->name, buf.value, buf.length); 310163020Sdavidxu gss_release_buffer(&min_stat, &buf); 311163020Sdavidxu 312163020Sdavidxu *principal = result; 313163020Sdavidxu return (TRUE); 314163020Sdavidxu} 315163020Sdavidxu 316163020Sdavidxubool_t 317163020Sdavidxurpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred, 318185879Sjhb rpc_gss_ucred_t **ucred, void **cookie) 319185879Sjhb{ 320185879Sjhb struct svc_rpc_gss_client *client; 321185879Sjhb 322119332Speter if (req->rq_cred.oa_flavor != RPCSEC_GSS) 323114988Speter return (FALSE); 324142874Sps 325114988Speter client = req->rq_clntcred; 326142874Sps if (rcred) 327114988Speter *rcred = &client->cl_rawcred; 328142874Sps if (ucred) 329104739Speter *ucred = &client->cl_ucred; 330183189Sobrien if (cookie) 331183189Sobrien *cookie = client->cl_cookie; 332183189Sobrien return (TRUE); 333183189Sobrien} 334183189Sobrien 335119332Speterint 33694380Sdfrrpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len) 33794380Sdfr{ 338236027Sed struct svc_rpc_gss_client *client = req->rq_clntcred; 339236027Sed int want_conf; 34094380Sdfr OM_uint32 max; 341156115Sps OM_uint32 maj_stat, min_stat; 34294380Sdfr int result; 34394380Sdfr 34494380Sdfr switch (client->cl_rawcred.service) { 345205328Skib case rpc_gss_svc_none: 346205328Skib return (max_tp_unit_len); 347205328Skib break; 348205328Skib 349205328Skib case rpc_gss_svc_default: 350205328Skib case rpc_gss_svc_integrity: 351205328Skib want_conf = FALSE; 352205328Skib break; 353205328Skib 354205328Skib case rpc_gss_svc_privacy: 355205328Skib want_conf = TRUE; 356119332Speter break; 357114988Speter 358114988Speter default: 359114988Speter return (0); 360114988Speter } 361119332Speter 362119332Speter maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf, 363119194Speter client->cl_qop, max_tp_unit_len, &max); 364150632Speter 365150632Speter if (maj_stat == GSS_S_COMPLETE) { 366150632Speter result = (int) max; 367150632Speter if (result < 0) 368150632Speter result = 0; 369150632Speter return (result); 370150632Speter } else { 371150632Speter log_status("gss_wrap_size_limit", client->cl_mech, 372150632Speter maj_stat, min_stat); 373150632Speter return (0); 374163047Sdavidxu } 375163047Sdavidxu} 376163047Sdavidxu 377163047Sdavidxustatic struct svc_rpc_gss_client * 378163047Sdavidxusvc_rpc_gss_find_client(uint32_t clientid) 379163047Sdavidxu{ 380205328Skib struct svc_rpc_gss_client *client; 381205328Skib struct svc_rpc_gss_client_list *list; 382205328Skib 383205328Skib 384162552Sdavidxu log_debug("in svc_rpc_gss_find_client(%d)", clientid); 385162552Sdavidxu 386162552Sdavidxu list = &svc_rpc_gss_client_hash[clientid % CLIENT_HASH_SIZE]; 387162537Sdavidxu TAILQ_FOREACH(client, list, cl_link) { 388162537Sdavidxu if (client->cl_id == clientid) { 389162537Sdavidxu /* 390163451Sdavidxu * Move this client to the front of the LRU 391162537Sdavidxu * list. 392162537Sdavidxu */ 393162537Sdavidxu TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink); 394162552Sdavidxu TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, 395162552Sdavidxu cl_alllink); 396162552Sdavidxu return client; 397162552Sdavidxu } 398318323Sbrooks } 399318323Sbrooks 400318323Sbrooks return (NULL); 401318323Sbrooks} 402318323Sbrooks 403205328Skibstatic struct svc_rpc_gss_client * 404205328Skibsvc_rpc_gss_create_client(void) 405205328Skib{ 406205328Skib struct svc_rpc_gss_client *client; 407205328Skib struct svc_rpc_gss_client_list *list; 408205328Skib 409205328Skib log_debug("in svc_rpc_gss_create_client()"); 410205328Skib 411205328Skib client = mem_alloc(sizeof(struct svc_rpc_gss_client)); 412205328Skib memset(client, 0, sizeof(struct svc_rpc_gss_client)); 413205328Skib client->cl_id = svc_rpc_gss_next_clientid++; 414205328Skib list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE]; 415205328Skib TAILQ_INSERT_HEAD(list, client, cl_link); 416205328Skib TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink); 417205328Skib 418205328Skib /* 419205328Skib * Start the client off with a short expiration time. We will 420205328Skib * try to get a saner value from the client creds later. 421205328Skib */ 422205328Skib client->cl_state = CLIENT_NEW; 423205328Skib client->cl_locked = FALSE; 424205328Skib client->cl_expiration = time(0) + 5*60; 425205328Skib svc_rpc_gss_client_count++; 426205328Skib 427205328Skib return (client); 428253531Skib} 429253531Skib 430253531Skibstatic void 431253531Skibsvc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client) 432185879Sjhb{ 433185879Sjhb struct svc_rpc_gss_client_list *list; 434185879Sjhb OM_uint32 min_stat; 435185879Sjhb 436205014Snwhitehorn log_debug("in svc_rpc_gss_destroy_client()"); 437171214Speter 438171214Speter if (client->cl_ctx) 439171214Speter gss_delete_sec_context(&min_stat, 440171214Speter &client->cl_ctx, GSS_C_NO_BUFFER); 441205014Snwhitehorn 442236027Sed if (client->cl_cname) 443236027Sed gss_release_name(&min_stat, &client->cl_cname); 444171214Speter 445171214Speter if (client->cl_rawcred.client_principal) 446171214Speter mem_free(client->cl_rawcred.client_principal, 447171214Speter sizeof(*client->cl_rawcred.client_principal) 448171214Speter + client->cl_rawcred.client_principal->len); 449205014Snwhitehorn 450236027Sed if (client->cl_verf.value) 451236027Sed gss_release_buffer(&min_stat, &client->cl_verf); 452171214Speter 453171214Speter list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE]; 454171214Speter TAILQ_REMOVE(list, client, cl_link); 455171214Speter TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink); 456171214Speter svc_rpc_gss_client_count--; 457171214Speter mem_free(client, sizeof(*client)); 458171214Speter} 459205014Snwhitehorn 460236027Sedstatic void 461236027Sedsvc_rpc_gss_timeout_clients(void) 462171214Speter{ 463171214Speter struct svc_rpc_gss_client *client; 464171214Speter struct svc_rpc_gss_client *nclient; 465205014Snwhitehorn time_t now = time(0); 466236027Sed 467236027Sed log_debug("in svc_rpc_gss_timeout_clients()"); 468171214Speter /* 469171214Speter * First enforce the max client limit. We keep 470171214Speter * svc_rpc_gss_clients in LRU order. 471171214Speter */ 472205014Snwhitehorn while (svc_rpc_gss_client_count > CLIENT_MAX) 473236027Sed svc_rpc_gss_destroy_client(TAILQ_LAST(&svc_rpc_gss_clients, 474236027Sed svc_rpc_gss_client_list)); 475171214Speter TAILQ_FOREACH_SAFE(client, &svc_rpc_gss_clients, cl_alllink, nclient) { 476171214Speter if (client->cl_state == CLIENT_STALE 477171214Speter || now > client->cl_expiration) { 478205014Snwhitehorn log_debug("expiring client %p", client); 479236027Sed svc_rpc_gss_destroy_client(client); 480236027Sed } 481171214Speter } 482205014Snwhitehorn} 483205014Snwhitehorn 484205014Snwhitehorn#ifdef DEBUG 485205014Snwhitehorn/* 486205014Snwhitehorn * OID<->string routines. These are uuuuugly. 487236027Sed */ 488236027Sedstatic OM_uint32 489205014Snwhitehorngss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str) 490205014Snwhitehorn{ 491205014Snwhitehorn char numstr[128]; 492205014Snwhitehorn unsigned long number; 493205014Snwhitehorn int numshift; 494236027Sed size_t string_length; 495236027Sed size_t i; 496205014Snwhitehorn unsigned char *cp; 497205014Snwhitehorn char *bp; 498205014Snwhitehorn 499205014Snwhitehorn /* Decoded according to krb5/gssapi_krb5.c */ 500205014Snwhitehorn 501205014Snwhitehorn /* First determine the size of the string */ 502205014Snwhitehorn string_length = 0; 503236027Sed number = 0; 504236027Sed numshift = 0; 505205014Snwhitehorn cp = (unsigned char *) oid->elements; 506205014Snwhitehorn number = (unsigned long) cp[0]; 507205014Snwhitehorn sprintf(numstr, "%ld ", number/40); 508236027Sed string_length += strlen(numstr); 509236027Sed sprintf(numstr, "%ld ", number%40); 510205014Snwhitehorn string_length += strlen(numstr); 511205014Snwhitehorn for (i=1; i<oid->length; i++) { 512205014Snwhitehorn if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) { 513205014Snwhitehorn number = (number << 7) | (cp[i] & 0x7f); 514236027Sed numshift += 7; 515236027Sed } 516205014Snwhitehorn else { 517205014Snwhitehorn *minor_status = 0; 518205014Snwhitehorn return(GSS_S_FAILURE); 519236027Sed } 520236027Sed if ((cp[i] & 0x80) == 0) { 521205014Snwhitehorn sprintf(numstr, "%ld ", number); 522205014Snwhitehorn string_length += strlen(numstr); 523205014Snwhitehorn number = 0; 524180434Sbrooks numshift = 0; 525180434Sbrooks } 526205014Snwhitehorn } 527236027Sed /* 528236027Sed * If we get here, we've calculated the length of "n n n ... n ". Add 4 529180434Sbrooks * here for "{ " and "}\0". 530180434Sbrooks */ 531205014Snwhitehorn string_length += 4; 532205014Snwhitehorn if ((bp = (char *) mem_alloc(string_length))) { 533205014Snwhitehorn strcpy(bp, "{ "); 534236027Sed number = (unsigned long) cp[0]; 535236027Sed sprintf(numstr, "%ld ", number/40); 536205014Snwhitehorn strcat(bp, numstr); 537205014Snwhitehorn sprintf(numstr, "%ld ", number%40); 538205014Snwhitehorn strcat(bp, numstr); 539180434Sbrooks number = 0; 540180434Sbrooks cp = (unsigned char *) oid->elements; 541180434Sbrooks for (i=1; i<oid->length; i++) { 542236027Sed number = (number << 7) | (cp[i] & 0x7f); 543236027Sed if ((cp[i] & 0x80) == 0) { 544180434Sbrooks sprintf(numstr, "%ld ", number); 545180434Sbrooks strcat(bp, numstr); 546180434Sbrooks number = 0; 547180434Sbrooks } 548180434Sbrooks } 549236027Sed strcat(bp, "}"); 550236027Sed oid_str->length = strlen(bp)+1; 551180434Sbrooks oid_str->value = (void *) bp; 552180434Sbrooks *minor_status = 0; 553180434Sbrooks return(GSS_S_COMPLETE); 554180434Sbrooks } 555180434Sbrooks *minor_status = 0; 556180434Sbrooks return(GSS_S_FAILURE); 557236027Sed} 558236027Sed#endif 559180434Sbrooks 560180434Sbrooksstatic void 561180434Sbrookssvc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client, 562177790Skib const gss_name_t name) 563177790Skib{ 564236027Sed OM_uint32 maj_stat, min_stat; 565236027Sed char buf[128]; 566177790Skib uid_t uid; 567177790Skib struct passwd pwd, *pw; 568177790Skib rpc_gss_ucred_t *uc = &client->cl_ucred; 569177790Skib 570177790Skib uc->uid = 65534; 571177790Skib uc->gid = 65534; 572177790Skib uc->gidlen = 0; 573177790Skib uc->gidlist = client->cl_gid_storage; 574177790Skib 575177790Skib maj_stat = gss_pname_to_uid(&min_stat, name, client->cl_mech, &uid); 576177790Skib if (maj_stat != GSS_S_COMPLETE) 577177790Skib return; 578191675Sjamie 579191675Sjamie getpwuid_r(uid, &pwd, buf, sizeof(buf), &pw); 580191675Sjamie if (pw) { 581191675Sjamie int len = NGRPS; 582191675Sjamie uc->uid = pw->pw_uid; 583191675Sjamie uc->gid = pw->pw_gid; 584191675Sjamie uc->gidlist = client->cl_gid_storage; 585191675Sjamie getgrouplist(pw->pw_name, pw->pw_gid, uc->gidlist, &len); 586191675Sjamie uc->gidlen = len; 587191675Sjamie } 588194919Sjhb} 589194919Sjhb 590194919Sjhbstatic bool_t 591194919Sjhbsvc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client, 592194919Sjhb struct svc_req *rqst, 593194919Sjhb struct rpc_gss_init_res *gr, 594194919Sjhb struct rpc_gss_cred *gc) 595194919Sjhb{ 596194919Sjhb gss_buffer_desc recv_tok; 597194919Sjhb gss_OID mech; 598194919Sjhb OM_uint32 maj_stat = 0, min_stat = 0, ret_flags; 599194919Sjhb OM_uint32 cred_lifetime; 600194919Sjhb struct svc_rpc_gss_svc_name *sname; 601194919Sjhb 602194919Sjhb log_debug("in svc_rpc_gss_accept_context()"); 603194919Sjhb 604255658Sjilles /* Deserialize arguments. */ 605255658Sjilles memset(&recv_tok, 0, sizeof(recv_tok)); 606255658Sjilles 607198512Skib if (!svc_getargs(rqst->rq_xprt, 608198512Skib (xdrproc_t) xdr_gss_buffer_desc, 609198512Skib (caddr_t) &recv_tok)) { 610198512Skib client->cl_state = CLIENT_STALE; 611198512Skib return (FALSE); 612198512Skib } 613198512Skib 614198512Skib /* 615250854Skib * First time round, try all the server names we have until 616220792Smdf * one matches. Afterwards, stick with that one. 617220792Smdf */ 618250854Skib if (!client->cl_sname) { 619226365Sjhb SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) { 620226365Sjhb if (sname->sn_program == rqst->rq_prog 621226365Sjhb && sname->sn_version == rqst->rq_vers) { 622226365Sjhb gr->gr_major = gss_accept_sec_context( 623220792Smdf &gr->gr_minor, 624227071Sjhb &client->cl_ctx, 625227071Sjhb sname->sn_cred, 626250854Skib &recv_tok, 627227071Sjhb GSS_C_NO_CHANNEL_BINDINGS, 628227071Sjhb &client->cl_cname, 629227071Sjhb &mech, 630227071Sjhb &gr->gr_token, 631227071Sjhb &ret_flags, 632227071Sjhb &cred_lifetime, 633242959Skib &client->cl_creds); 634242959Skib if (gr->gr_major == GSS_S_COMPLETE 635250854Skib || gr->gr_major == GSS_S_CONTINUE_NEEDED) { 636250854Skib client->cl_sname = sname; 637250854Skib break; 638242959Skib } 639242959Skib client->cl_sname = sname; 640242959Skib break; 641242959Skib } 642242959Skib } 643250854Skib if (!sname) { 644250854Skib xdr_free((xdrproc_t) xdr_gss_buffer_desc, 645250854Skib (char *) &recv_tok); 646250854Skib return (FALSE); 647250854Skib } 648250854Skib } else { 649250854Skib gr->gr_major = gss_accept_sec_context( 650250854Skib &gr->gr_minor, 651250854Skib &client->cl_ctx, 652250854Skib client->cl_sname->sn_cred, 653250854Skib &recv_tok, 654250854Skib GSS_C_NO_CHANNEL_BINDINGS, 655250854Skib &client->cl_cname, 656250854Skib &mech, 657250854Skib &gr->gr_token, 658250854Skib &ret_flags, 659250854Skib &cred_lifetime, 660250854Skib NULL); 661250854Skib } 662250854Skib 663250854Skib xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok); 664250854Skib 665250854Skib /* 666250854Skib * If we get an error from gss_accept_sec_context, send the 667250854Skib * reply anyway so that the client gets a chance to see what 668250854Skib * is wrong. 669254482Spjd */ 670254482Spjd if (gr->gr_major != GSS_S_COMPLETE && 671254482Spjd gr->gr_major != GSS_S_CONTINUE_NEEDED) { 672254482Spjd log_status("accept_sec_context", client->cl_mech, 673254482Spjd gr->gr_major, gr->gr_minor); 674254482Spjd client->cl_state = CLIENT_STALE; 675254482Spjd return (TRUE); 676254482Spjd } 677254482Spjd 678254482Spjd gr->gr_handle.value = &client->cl_id; 679251527Sglebius gr->gr_handle.length = sizeof(client->cl_id); 680251527Sglebius gr->gr_win = SVC_RPC_GSS_SEQWINDOW; 681251527Sglebius 682255709Sjhb /* Save client info. */ 683255709Sjhb client->cl_mech = mech; 684255709Sjhb client->cl_qop = GSS_C_QOP_DEFAULT; 685255709Sjhb client->cl_seq = gc->gc_seq; 686255709Sjhb client->cl_win = gr->gr_win; 687255709Sjhb client->cl_done_callback = FALSE; 688255709Sjhb 689255709Sjhb if (gr->gr_major == GSS_S_COMPLETE) { 690255709Sjhb gss_buffer_desc export_name; 691255709Sjhb 692255709Sjhb /* 693255709Sjhb * Change client expiration time to be near when the 694255709Sjhb * client creds expire (or 24 hours if we can't figure 695255709Sjhb * that out). 696255709Sjhb */ 697255709Sjhb if (cred_lifetime == GSS_C_INDEFINITE) 698255709Sjhb cred_lifetime = time(0) + 24*60*60; 699255709Sjhb 700275987Sdchagin client->cl_expiration = time(0) + cred_lifetime; 701275987Sdchagin 702275987Sdchagin /* 703275987Sdchagin * Fill in cred details in the rawcred structure. 704275987Sdchagin */ 705275987Sdchagin client->cl_rawcred.version = RPCSEC_GSS_VERSION; 706293475Sdchagin rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism); 707293475Sdchagin maj_stat = gss_export_name(&min_stat, client->cl_cname, 708293475Sdchagin &export_name); 709293475Sdchagin if (maj_stat != GSS_S_COMPLETE) { 710293475Sdchagin log_status("gss_export_name", client->cl_mech, 711293475Sdchagin maj_stat, min_stat); 712293475Sdchagin return (FALSE); 713293475Sdchagin } 714293475Sdchagin client->cl_rawcred.client_principal = 715293475Sdchagin mem_alloc(sizeof(*client->cl_rawcred.client_principal) 716232449Sjmallett + export_name.length); 717205014Snwhitehorn client->cl_rawcred.client_principal->len = export_name.length; 718205014Snwhitehorn memcpy(client->cl_rawcred.client_principal->name, 719119332Speter export_name.value, export_name.length); 720151360Sps gss_release_buffer(&min_stat, &export_name); 721151360Sps client->cl_rawcred.svc_principal = 722151360Sps client->cl_sname->sn_principal; 723119332Speter client->cl_rawcred.service = gc->gc_svc; 724183271Sobrien 725119332Speter /* 726226349Smarcel * Use gss_pname_to_uid to map to unix creds. For 727119332Speter * kerberos5, this uses krb5_aname_to_localname. 728125171Speter */ 729271011Skib svc_rpc_gss_build_ucred(client, client->cl_cname); 730119332Speter gss_release_name(&min_stat, &client->cl_cname); 731119332Speter 732119332Speter#ifdef DEBUG 733119332Speter { 734119332Speter gss_buffer_desc mechname; 735119332Speter 736119332Speter gss_oid_to_str(&min_stat, mech, &mechname); 737119332Speter 738190622Skib log_debug("accepted context for %s with " 739119332Speter "<mech %.*s, qop %d, svc %d>", 740119332Speter client->cl_rawcred.client_principal->name, 741119332Speter mechname.length, (char *)mechname.value, 742119332Speter client->cl_qop, client->rawcred.service); 743119332Speter 744119332Speter gss_release_buffer(&min_stat, &mechname); 745184184Sjhb } 746119332Speter#endif /* DEBUG */ 747154596Sambrisko } 748165406Sjkim return (TRUE); 749165406Sjkim} 750151358Sps 751151358Spsstatic bool_t 752151358Spssvc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg, 753253531Skib gss_qop_t *qop) 754253531Skib{ 755253531Skib struct opaque_auth *oa; 756140481Sps gss_buffer_desc rpcbuf, checksum; 757253495Skib OM_uint32 maj_stat, min_stat; 758185879Sjhb gss_qop_t qop_state; 759185879Sjhb int32_t rpchdr[128 / sizeof(int32_t)]; 760185879Sjhb int32_t *buf; 761154587Sambrisko 762147814Sjhb log_debug("in svc_rpc_gss_validate()"); 763147814Sjhb 764140482Sps memset(rpchdr, 0, sizeof(rpchdr)); 765220159Skib 766185879Sjhb /* Reconstruct RPC header for signing (from xdr_callmsg). */ 767185879Sjhb buf = rpchdr; 768185879Sjhb IXDR_PUT_LONG(buf, msg->rm_xid); 769185879Sjhb IXDR_PUT_ENUM(buf, msg->rm_direction); 770185879Sjhb IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers); 771185879Sjhb IXDR_PUT_LONG(buf, msg->rm_call.cb_prog); 772185879Sjhb IXDR_PUT_LONG(buf, msg->rm_call.cb_vers); 773185436Sbz IXDR_PUT_LONG(buf, msg->rm_call.cb_proc); 774163020Sdavidxu oa = &msg->rm_call.cb_cred; 775163020Sdavidxu IXDR_PUT_ENUM(buf, oa->oa_flavor); 776185879Sjhb IXDR_PUT_LONG(buf, oa->oa_length); 777119332Speter if (oa->oa_length) { 778183189Sobrien memcpy((caddr_t)buf, oa->oa_base, oa->oa_length); 779119332Speter buf += RNDUP(oa->oa_length) / sizeof(int32_t); 780205328Skib } 781205328Skib rpcbuf.value = rpchdr; 782119332Speter rpcbuf.length = (u_char *)buf - (u_char *)rpchdr; 783119332Speter 784150632Speter checksum.value = msg->rm_call.cb_verf.oa_base; 785150632Speter checksum.length = msg->rm_call.cb_verf.oa_length; 786150632Speter 787163047Sdavidxu maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum, 788163047Sdavidxu &qop_state); 789205328Skib 790162552Sdavidxu if (maj_stat != GSS_S_COMPLETE) { 791162537Sdavidxu log_status("gss_verify_mic", client->cl_mech, 792162552Sdavidxu maj_stat, min_stat); 793318323Sbrooks client->cl_state = CLIENT_STALE; 794205328Skib return (FALSE); 795205328Skib } 796205328Skib *qop = qop_state; 797205328Skib return (TRUE); 798253531Skib} 799185879Sjhb 800205014Snwhitehornstatic bool_t 801171214Spetersvc_rpc_gss_nextverf(struct svc_rpc_gss_client *client, 802171214Speter struct svc_req *rqst, u_int seq) 803171214Speter{ 804171214Speter gss_buffer_desc signbuf; 805171214Speter OM_uint32 maj_stat, min_stat; 806171214Speter uint32_t nseq; 807205014Snwhitehorn 808205014Snwhitehorn log_debug("in svc_rpc_gss_nextverf()"); 809205014Snwhitehorn 810205014Snwhitehorn nseq = htonl(seq); 811205014Snwhitehorn signbuf.value = &nseq; 812205014Snwhitehorn signbuf.length = sizeof(nseq); 813205014Snwhitehorn 814205014Snwhitehorn if (client->cl_verf.value) 815205014Snwhitehorn gss_release_buffer(&min_stat, &client->cl_verf); 816180434Sbrooks 817205014Snwhitehorn maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop, 818205014Snwhitehorn &signbuf, &client->cl_verf); 819205014Snwhitehorn 820180434Sbrooks if (maj_stat != GSS_S_COMPLETE) { 821180434Sbrooks log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat); 822180434Sbrooks client->cl_state = CLIENT_STALE; 823177790Skib return (FALSE); 824177790Skib } 825177790Skib rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS; 826191675Sjamie rqst->rq_xprt->xp_verf.oa_base = (caddr_t)client->cl_verf.value; 827191675Sjamie rqst->rq_xprt->xp_verf.oa_length = (u_int)client->cl_verf.length; 828194919Sjhb 829194919Sjhb return (TRUE); 830194919Sjhb} 831255658Sjilles 832198512Skibstatic bool_t 833250854Skibsvc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst) 834220792Smdf{ 835227071Sjhb struct svc_rpc_gss_callback *scb; 836242959Skib rpc_gss_lock_t lock; 837250854Skib void *cookie; 838250854Skib bool_t cb_res; 839250854Skib bool_t result; 840250854Skib 841250854Skib /* 842254482Spjd * See if we have a callback for this guy. 843254482Spjd */ 844251527Sglebius result = TRUE; 845255709Sjhb SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) { 846255709Sjhb if (scb->cb_callback.program == rqst->rq_prog 847255709Sjhb && scb->cb_callback.version == rqst->rq_vers) { 848255709Sjhb /* 849255709Sjhb * This one matches. Call the callback and see 850275987Sdchagin * if it wants to veto or something. 851293475Sdchagin */ 852293475Sdchagin lock.locked = FALSE; 85394380Sdfr lock.raw_cred = &client->cl_rawcred; 85494380Sdfr cb_res = scb->cb_callback.callback(rqst, 85594380Sdfr client->cl_creds, 856232449Sjmallett client->cl_ctx, 857205014Snwhitehorn &lock, 858205014Snwhitehorn &cookie); 859223167Skib 860223167Skib if (!cb_res) { 861223167Skib client->cl_state = CLIENT_STALE; 862223167Skib result = FALSE; 863223167Skib break; 864220239Skib } 865220239Skib 866220239Skib /* 867220239Skib * The callback accepted the connection - it 868220239Skib * is responsible for freeing client->cl_creds 869220239Skib * now. 870220239Skib */ 871220239Skib client->cl_creds = GSS_C_NO_CREDENTIAL; 872151721Speter client->cl_locked = lock.locked; 873151721Speter client->cl_cookie = cookie; 874151721Speter return (TRUE); 875151721Speter } 876151721Speter } 877151721Speter 878151721Speter /* 879151721Speter * Either no callback exists for this program/version or one 880151721Speter * of the callbacks rejected the connection. We just need to 881220239Skib * clean up the delegated client creds, if any. 882220239Skib */ 883220239Skib if (client->cl_creds) { 884220239Skib OM_uint32 min_ver; 885220239Skib gss_release_cred(&min_ver, &client->cl_creds); 886220239Skib } 887220239Skib return (result); 888220239Skib} 889220239Skib 890220239Skibstatic bool_t 891151721Spetersvc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq) 892151721Speter{ 893151721Speter u_int32_t offset; 894151721Speter int word, bit; 895151721Speter 896151721Speter if (seq <= client->cl_seqlast) { 897151721Speter /* 898151721Speter * The request sequence number is less than 899151721Speter * the largest we have seen so far. If it is 900151721Speter * outside the window or if we have seen a 901151721Speter * request with this sequence before, silently 902151721Speter * discard it. 903151721Speter */ 904151721Speter offset = client->cl_seqlast - seq; 905151721Speter if (offset >= SVC_RPC_GSS_SEQWINDOW) 906151721Speter return (FALSE); 907151721Speter word = offset / 32; 908151721Speter bit = offset % 32; 909220239Skib if (client->cl_seqmask[word] & (1 << bit)) 910220239Skib return (FALSE); 911220239Skib } 912220239Skib 913220239Skib return (TRUE); 914220239Skib} 915205014Snwhitehorn 916205014Snwhitehornstatic void 917205014Snwhitehornsvc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq) 918205014Snwhitehorn{ 919205014Snwhitehorn int offset, i, word, bit; 920205014Snwhitehorn uint32_t carry, newcarry; 921250854Skib 922250854Skib if (seq > client->cl_seqlast) { 923250854Skib /* 924255709Sjhb * This request has a sequence number greater 925255709Sjhb * than any we have seen so far. Advance the 926255709Sjhb * seq window and set bit zero of the window 927223167Skib * (which corresponds to the new sequence 928220239Skib * number) 929220239Skib */ 930151721Speter offset = seq - client->cl_seqlast; 931151721Speter while (offset > 32) { 932151721Speter for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1; 933220239Skib i > 0; i--) { 934220239Skib client->cl_seqmask[i] = client->cl_seqmask[i-1]; 935220239Skib } 936151721Speter client->cl_seqmask[0] = 0; 937151721Speter offset -= 32; 938151721Speter } 939151721Speter carry = 0; 940151721Speter for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) { 941220239Skib newcarry = client->cl_seqmask[i] >> (32 - offset); 94294380Sdfr client->cl_seqmask[i] = 94394380Sdfr (client->cl_seqmask[i] << offset) | carry; 94494380Sdfr carry = newcarry; 945100385Speter } 946100385Speter client->cl_seqmask[0] |= 1; 947100385Speter client->cl_seqlast = seq; 948232449Sjmallett } else { 949205014Snwhitehorn offset = client->cl_seqlast - seq; 950205014Snwhitehorn word = offset / 32; 951128261Speter bit = offset % 32; 952128261Speter client->cl_seqmask[word] |= (1 << bit); 953128261Speter } 954128261Speter 955128261Speter} 956128261Speter 957128261Speterenum auth_stat 958128261Spetersvc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg) 959128261Speter 960128261Speter{ 961128261Speter OM_uint32 min_stat; 962128261Speter XDR xdrs; 963128261Speter struct svc_rpc_gss_client *client; 964128261Speter struct rpc_gss_cred gc; 965128261Speter struct rpc_gss_init_res gr; 966128261Speter gss_qop_t qop; 967128261Speter int call_stat; 968119332Speter enum auth_stat result; 969104739Speter 970104739Speter log_debug("in svc_rpc_gss()"); 971236027Sed 972236027Sed /* Garbage collect old clients. */ 973104739Speter svc_rpc_gss_timeout_clients(); 974156115Sps 975104739Speter /* Initialize reply. */ 976104739Speter rqst->rq_xprt->xp_verf = _null_auth; 977104739Speter 978119332Speter /* Deserialize client credentials. */ 979114988Speter if (rqst->rq_cred.oa_length <= 0) 980114988Speter return (AUTH_BADCRED); 981114988Speter 982114988Speter memset(&gc, 0, sizeof(gc)); 983119332Speter 984126093Speter xdrmem_create(&xdrs, rqst->rq_cred.oa_base, 985114988Speter rqst->rq_cred.oa_length, XDR_DECODE); 986205014Snwhitehorn 987205014Snwhitehorn if (!xdr_rpc_gss_cred(&xdrs, &gc)) { 988205014Snwhitehorn XDR_DESTROY(&xdrs); 989205014Snwhitehorn return (AUTH_BADCRED); 990205014Snwhitehorn } 991205014Snwhitehorn XDR_DESTROY(&xdrs); 992250854Skib 993250854Skib /* Check version. */ 994250854Skib if (gc.gc_version != RPCSEC_GSS_VERSION) { 995255709Sjhb result = AUTH_BADCRED; 996255709Sjhb goto out; 997255709Sjhb } 998128261Speter 999128261Speter /* Check the proc and find the client (or create it) */ 1000128261Speter if (gc.gc_proc == RPCSEC_GSS_INIT) { 1001128261Speter if (gc.gc_handle.length != 0) { 1002119332Speter result = AUTH_BADCRED; 1003119332Speter goto out; 1004119332Speter } 1005100385Speter client = svc_rpc_gss_create_client(); 1006100385Speter } else { 1007100385Speter if (gc.gc_handle.length != sizeof(uint32_t)) { 1008171214Speter result = AUTH_BADCRED; 1009197637Srwatson goto out; 1010171214Speter } 1011232449Sjmallett uint32_t *p = gc.gc_handle.value; 1012205014Snwhitehorn client = svc_rpc_gss_find_client(*p); 1013205014Snwhitehorn if (!client) { 1014171214Speter /* 1015171214Speter * Can't find the client - we may have 1016171214Speter * destroyed it - tell the other side to 1017171214Speter * re-authenticate. 1018171214Speter */ 1019236027Sed result = RPCSEC_GSS_CREDPROBLEM; 1020236027Sed goto out; 1021171214Speter } 1022171214Speter } 1023171214Speter rqst->rq_clntcred = client; 1024171214Speter 1025171214Speter /* 1026171214Speter * The service and sequence number must be ignored for 1027236027Sed * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT. 1028236027Sed */ 1029171214Speter if (gc.gc_proc != RPCSEC_GSS_INIT 1030171214Speter && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) { 1031171214Speter /* 1032171214Speter * Check for sequence number overflow. 1033171214Speter */ 1034171214Speter if (gc.gc_seq >= MAXSEQ) { 1035171214Speter result = RPCSEC_GSS_CTXPROBLEM; 1036171214Speter goto out; 1037236027Sed } 1038236027Sed client->cl_seq = gc.gc_seq; 1039171214Speter 1040171214Speter /* 1041171214Speter * Check for valid service. 1042171214Speter */ 1043236027Sed if (gc.gc_svc != rpc_gss_svc_none && 1044236027Sed gc.gc_svc != rpc_gss_svc_integrity && 1045171214Speter gc.gc_svc != rpc_gss_svc_privacy) { 1046171214Speter result = AUTH_BADCRED; 1047171214Speter goto out; 1048171214Speter } 1049171214Speter } 1050236027Sed 1051236027Sed /* Handle RPCSEC_GSS control procedure. */ 1052171214Speter switch (gc.gc_proc) { 1053171214Speter 1054171214Speter case RPCSEC_GSS_INIT: 1055171214Speter case RPCSEC_GSS_CONTINUE_INIT: 1056236027Sed if (rqst->rq_proc != NULLPROC) { 1057236027Sed result = AUTH_REJECTEDCRED; 1058171214Speter break; 1059205014Snwhitehorn } 1060205014Snwhitehorn 1061205014Snwhitehorn memset(&gr, 0, sizeof(gr)); 1062205014Snwhitehorn if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) { 1063205014Snwhitehorn result = AUTH_REJECTEDCRED; 1064205014Snwhitehorn break; 1065250854Skib } 1066250854Skib 1067250854Skib if (gr.gr_major == GSS_S_COMPLETE) { 1068255709Sjhb if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) { 1069255709Sjhb result = AUTH_REJECTEDCRED; 1070255709Sjhb break; 1071171214Speter } 1072171214Speter } else { 1073171214Speter rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL; 1074171214Speter rqst->rq_xprt->xp_verf.oa_length = 0; 1075171214Speter } 1076171214Speter 1077171214Speter call_stat = svc_sendreply(rqst->rq_xprt, 1078197637Srwatson (xdrproc_t) xdr_rpc_gss_init_res, 1079171214Speter (caddr_t) &gr); 1080194919Sjhb 1081200619Simp gss_release_buffer(&min_stat, &gr.gr_token); 1082194919Sjhb 1083232449Sjmallett if (!call_stat) { 1084205014Snwhitehorn result = AUTH_FAILED; 1085205014Snwhitehorn break; 1086194919Sjhb } 1087194919Sjhb 1088194919Sjhb if (gr.gr_major == GSS_S_COMPLETE) 1089194919Sjhb client->cl_state = CLIENT_ESTABLISHED; 1090194919Sjhb 1091194919Sjhb result = RPCSEC_GSS_NODISPATCH; 1092194919Sjhb break; 1093194919Sjhb 1094194919Sjhb case RPCSEC_GSS_DATA: 1095194919Sjhb case RPCSEC_GSS_DESTROY: 1096194919Sjhb if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) { 1097194919Sjhb result = RPCSEC_GSS_NODISPATCH; 1098194919Sjhb break; 1099194919Sjhb } 1100194919Sjhb 1101194919Sjhb if (!svc_rpc_gss_validate(client, msg, &qop)) { 1102205014Snwhitehorn result = RPCSEC_GSS_CREDPROBLEM; 1103205014Snwhitehorn break; 1104205014Snwhitehorn } 1105205014Snwhitehorn 1106205014Snwhitehorn if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) { 1107205014Snwhitehorn result = RPCSEC_GSS_CTXPROBLEM; 1108250854Skib break; 1109250854Skib } 1110250854Skib 1111255709Sjhb svc_rpc_gss_update_seq(client, gc.gc_seq); 1112255709Sjhb 1113255709Sjhb /* 1114194919Sjhb * Change the SVCAUTH ops on the transport to point at 1115194919Sjhb * our own code so that we can unwrap the arguments 1116194919Sjhb * and wrap the result. The caller will re-set this on 1117194919Sjhb * every request to point to a set of null wrap/unwrap 1118200619Simp * methods. 1119194919Sjhb */ 1120161330Sjhb SVC_AUTH(rqst->rq_xprt).svc_ah_ops = &svc_auth_gss_ops; 1121194647Sjhb SVC_AUTH(rqst->rq_xprt).svc_ah_private = client; 1122223167Skib 1123161330Sjhb if (gc.gc_proc == RPCSEC_GSS_DATA) { 1124161330Sjhb /* 1125161330Sjhb * We might be ready to do a callback to the server to 1126220239Skib * see if it wants to accept/reject the connection. 1127220239Skib */ 1128194647Sjhb if (!client->cl_done_callback) { 1129194647Sjhb client->cl_done_callback = TRUE; 1130194647Sjhb client->cl_qop = qop; 1131162374Srwatson client->cl_rawcred.qop = _rpc_gss_num_to_qop( 1132183271Sobrien client->cl_rawcred.mechanism, qop); 1133161330Sjhb if (!svc_rpc_gss_callback(client, rqst)) { 1134220239Skib result = AUTH_REJECTEDCRED; 1135220239Skib break; 1136226349Smarcel } 1137161330Sjhb } 1138161330Sjhb 1139271011Skib /* 1140161330Sjhb * If the server has locked this client to a 1141220239Skib * particular service+qop pair, enforce that 1142194647Sjhb * restriction now. 1143194647Sjhb */ 1144194647Sjhb if (client->cl_locked) { 1145194647Sjhb if (client->cl_rawcred.service != gc.gc_svc) { 1146194647Sjhb result = AUTH_FAILED; 1147161330Sjhb break; 1148161330Sjhb } else if (client->cl_qop != qop) { 1149161330Sjhb result = AUTH_BADVERF; 1150161330Sjhb break; 1151161330Sjhb } 1152161330Sjhb } 1153161330Sjhb 1154220239Skib /* 1155194647Sjhb * If the qop changed, look up the new qop 1156194647Sjhb * name for rawcred. 1157190622Skib */ 1158161330Sjhb if (client->cl_qop != qop) { 1159161330Sjhb client->cl_qop = qop; 1160161330Sjhb client->cl_rawcred.qop = _rpc_gss_num_to_qop( 1161194647Sjhb client->cl_rawcred.mechanism, qop); 1162194647Sjhb } 1163161330Sjhb 1164161330Sjhb /* 1165161330Sjhb * Make sure we use the right service value 1166184184Sjhb * for unwrap/wrap. 1167194647Sjhb */ 1168194647Sjhb client->cl_rawcred.service = gc.gc_svc; 1169194647Sjhb 1170194647Sjhb result = AUTH_OK; 1171161330Sjhb } else { 1172161330Sjhb if (rqst->rq_proc != NULLPROC) { 1173194919Sjhb result = AUTH_REJECTEDCRED; 1174194919Sjhb break; 1175165406Sjkim } 1176165406Sjkim 1177194919Sjhb call_stat = svc_sendreply(rqst->rq_xprt, 1178161330Sjhb (xdrproc_t) xdr_void, (caddr_t) NULL); 1179161330Sjhb 1180161330Sjhb if (!call_stat) { 1181253531Skib result = AUTH_FAILED; 1182253531Skib break; 1183253531Skib } 1184161330Sjhb 1185253495Skib svc_rpc_gss_destroy_client(client); 1186185879Sjhb 1187185879Sjhb result = RPCSEC_GSS_NODISPATCH; 1188185879Sjhb break; 1189161330Sjhb } 1190161330Sjhb break; 1191161330Sjhb 1192194647Sjhb default: 1193161330Sjhb result = AUTH_BADCRED; 1194220159Skib break; 1195185879Sjhb } 1196185879Sjhbout: 1197185879Sjhb xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc); 1198185879Sjhb return (result); 1199185879Sjhb} 1200185879Sjhb 1201185879Sjhbbool_t 1202194647Sjhbsvc_rpc_gss_wrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr) 1203185436Sbz{ 1204194647Sjhb struct svc_rpc_gss_client *client; 1205194647Sjhb 1206163020Sdavidxu log_debug("in svc_rpc_gss_wrap()"); 1207163020Sdavidxu 1208185879Sjhb client = (struct svc_rpc_gss_client *) auth->svc_ah_private; 1209161330Sjhb if (client->cl_state != CLIENT_ESTABLISHED 1210183189Sobrien || client->cl_rawcred.service == rpc_gss_svc_none) { 1211161960Srwatson return xdr_func(xdrs, xdr_ptr); 1212205328Skib } 1213205328Skib return (xdr_rpc_gss_wrap_data(xdrs, xdr_func, xdr_ptr, 1214161330Sjhb client->cl_ctx, client->cl_qop, 1215161330Sjhb client->cl_rawcred.service, client->cl_seq)); 1216161330Sjhb} 1217161330Sjhb 1218161330Sjhbbool_t 1219163047Sdavidxusvc_rpc_gss_unwrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr) 1220163047Sdavidxu{ 1221205328Skib struct svc_rpc_gss_client *client; 1222162552Sdavidxu 1223162537Sdavidxu log_debug("in svc_rpc_gss_unwrap()"); 1224162552Sdavidxu 1225318323Sbrooks client = (struct svc_rpc_gss_client *) auth->svc_ah_private; 1226205328Skib if (client->cl_state != CLIENT_ESTABLISHED 1227205328Skib || client->cl_rawcred.service == rpc_gss_svc_none) { 1228205328Skib return xdr_func(xdrs, xdr_ptr); 1229205328Skib } 1230253531Skib return (xdr_rpc_gss_unwrap_data(xdrs, xdr_func, xdr_ptr, 1231185879Sjhb client->cl_ctx, client->cl_qop, 1232171214Speter client->cl_rawcred.service, client->cl_seq)); 1233171214Speter} 1234171214Speter