1181344Sdfr/*- 2181344Sdfr * Copyright (c) 2008 Doug Rabson 3181344Sdfr * All rights reserved. 4181344Sdfr * 5181344Sdfr * Redistribution and use in source and binary forms, with or without 6181344Sdfr * modification, are permitted provided that the following conditions 7181344Sdfr * are met: 8181344Sdfr * 1. Redistributions of source code must retain the above copyright 9181344Sdfr * notice, this list of conditions and the following disclaimer. 10181344Sdfr * 2. Redistributions in binary form must reproduce the above copyright 11181344Sdfr * notice, this list of conditions and the following disclaimer in the 12181344Sdfr * documentation and/or other materials provided with the distribution. 13181344Sdfr * 14181344Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15181344Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16181344Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17181344Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18181344Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19181344Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20181344Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21181344Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22181344Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23181344Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24181344Sdfr * SUCH DAMAGE. 25181344Sdfr * 26181344Sdfr * $FreeBSD$ 27181344Sdfr */ 28181344Sdfr/* 29181344Sdfr svc_rpcsec_gss.c 30181344Sdfr 31181344Sdfr Copyright (c) 2000 The Regents of the University of Michigan. 32181344Sdfr All rights reserved. 33181344Sdfr 34181344Sdfr Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>. 35181344Sdfr All rights reserved, all wrongs reversed. 36181344Sdfr 37181344Sdfr Redistribution and use in source and binary forms, with or without 38181344Sdfr modification, are permitted provided that the following conditions 39181344Sdfr are met: 40181344Sdfr 41181344Sdfr 1. Redistributions of source code must retain the above copyright 42181344Sdfr notice, this list of conditions and the following disclaimer. 43181344Sdfr 2. Redistributions in binary form must reproduce the above copyright 44181344Sdfr notice, this list of conditions and the following disclaimer in the 45181344Sdfr documentation and/or other materials provided with the distribution. 46181344Sdfr 3. Neither the name of the University nor the names of its 47181344Sdfr contributors may be used to endorse or promote products derived 48181344Sdfr from this software without specific prior written permission. 49181344Sdfr 50181344Sdfr THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 51181344Sdfr WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 52181344Sdfr MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 53181344Sdfr DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 54181344Sdfr FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 55181344Sdfr CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 56181344Sdfr SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 57181344Sdfr BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 58181344Sdfr LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 59181344Sdfr NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 60181344Sdfr SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 61181344Sdfr 62181344Sdfr $Id: svc_auth_gss.c,v 1.27 2002/01/15 15:43:00 andros Exp $ 63181344Sdfr */ 64181344Sdfr 65181344Sdfr#include <stdio.h> 66181344Sdfr#include <stdlib.h> 67181344Sdfr#include <string.h> 68181344Sdfr#include <pwd.h> 69181344Sdfr#include <grp.h> 70181344Sdfr#include <errno.h> 71181344Sdfr#include <unistd.h> 72181344Sdfr#include <sys/queue.h> 73181344Sdfr#include <rpc/rpc.h> 74181344Sdfr#include <rpc/rpcsec_gss.h> 75181344Sdfr#include "rpcsec_gss_int.h" 76181344Sdfr 77181344Sdfrstatic bool_t svc_rpc_gss_initialised = FALSE; 78181344Sdfr 79181344Sdfrstatic bool_t svc_rpc_gss_wrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t); 80181344Sdfrstatic bool_t svc_rpc_gss_unwrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t); 81181344Sdfrstatic enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *); 82181344Sdfr 83181344Sdfrstatic struct svc_auth_ops svc_auth_gss_ops = { 84181344Sdfr svc_rpc_gss_wrap, 85181344Sdfr svc_rpc_gss_unwrap, 86181344Sdfr}; 87181344Sdfr 88181344Sdfrstruct svc_rpc_gss_callback { 89181344Sdfr SLIST_ENTRY(svc_rpc_gss_callback) cb_link; 90181344Sdfr rpc_gss_callback_t cb_callback; 91181344Sdfr}; 92181344Sdfrstatic SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback) 93201145Santoine svc_rpc_gss_callbacks = SLIST_HEAD_INITIALIZER(svc_rpc_gss_callbacks); 94181344Sdfr 95181344Sdfrstruct svc_rpc_gss_svc_name { 96181344Sdfr SLIST_ENTRY(svc_rpc_gss_svc_name) sn_link; 97181344Sdfr char *sn_principal; 98181344Sdfr gss_OID sn_mech; 99181344Sdfr u_int sn_req_time; 100181344Sdfr gss_cred_id_t sn_cred; 101181344Sdfr u_int sn_program; 102181344Sdfr u_int sn_version; 103181344Sdfr}; 104181344Sdfrstatic SLIST_HEAD(svc_rpc_gss_svc_name_list, svc_rpc_gss_svc_name) 105201145Santoine svc_rpc_gss_svc_names = SLIST_HEAD_INITIALIZER(svc_rpc_gss_svc_names); 106181344Sdfr 107181344Sdfrenum svc_rpc_gss_client_state { 108181344Sdfr CLIENT_NEW, /* still authenticating */ 109181344Sdfr CLIENT_ESTABLISHED, /* context established */ 110181344Sdfr CLIENT_STALE /* garbage to collect */ 111181344Sdfr}; 112181344Sdfr 113181344Sdfr#define SVC_RPC_GSS_SEQWINDOW 128 114181344Sdfr 115181344Sdfrstruct svc_rpc_gss_client { 116181344Sdfr TAILQ_ENTRY(svc_rpc_gss_client) cl_link; 117181344Sdfr TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink; 118181344Sdfr uint32_t cl_id; 119181344Sdfr time_t cl_expiration; /* when to gc */ 120181344Sdfr enum svc_rpc_gss_client_state cl_state; /* client state */ 121181344Sdfr bool_t cl_locked; /* fixed service+qop */ 122181344Sdfr gss_ctx_id_t cl_ctx; /* context id */ 123181344Sdfr gss_cred_id_t cl_creds; /* delegated creds */ 124181344Sdfr gss_name_t cl_cname; /* client name */ 125181344Sdfr struct svc_rpc_gss_svc_name *cl_sname; /* server name used */ 126181344Sdfr rpc_gss_rawcred_t cl_rawcred; /* raw credentials */ 127181344Sdfr rpc_gss_ucred_t cl_ucred; /* unix-style credentials */ 128181344Sdfr bool_t cl_done_callback; /* TRUE after call */ 129181344Sdfr void *cl_cookie; /* user cookie from callback */ 130181344Sdfr gid_t cl_gid_storage[NGRPS]; 131181344Sdfr gss_OID cl_mech; /* mechanism */ 132181344Sdfr gss_qop_t cl_qop; /* quality of protection */ 133181344Sdfr u_int cl_seq; /* current sequence number */ 134181344Sdfr u_int cl_win; /* sequence window size */ 135181344Sdfr u_int cl_seqlast; /* sequence window origin */ 136181344Sdfr uint32_t cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */ 137181344Sdfr gss_buffer_desc cl_verf; /* buffer for verf checksum */ 138181344Sdfr}; 139181344SdfrTAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client); 140181344Sdfr 141181344Sdfr#define CLIENT_HASH_SIZE 256 142181344Sdfr#define CLIENT_MAX 128 143241720Sedstatic struct svc_rpc_gss_client_list svc_rpc_gss_client_hash[CLIENT_HASH_SIZE]; 144241720Sedstatic struct svc_rpc_gss_client_list svc_rpc_gss_clients; 145181344Sdfrstatic size_t svc_rpc_gss_client_count; 146181344Sdfrstatic uint32_t svc_rpc_gss_next_clientid = 1; 147181344Sdfr 148181344Sdfr#ifdef __GNUC__ 149181344Sdfrstatic void svc_rpc_gss_init(void) __attribute__ ((constructor)); 150181344Sdfr#endif 151181344Sdfr 152181344Sdfrstatic void 153181344Sdfrsvc_rpc_gss_init(void) 154181344Sdfr{ 155181344Sdfr int i; 156181344Sdfr 157181344Sdfr if (!svc_rpc_gss_initialised) { 158181344Sdfr for (i = 0; i < CLIENT_HASH_SIZE; i++) 159181344Sdfr TAILQ_INIT(&svc_rpc_gss_client_hash[i]); 160181344Sdfr TAILQ_INIT(&svc_rpc_gss_clients); 161181344Sdfr svc_auth_reg(RPCSEC_GSS, svc_rpc_gss); 162181344Sdfr svc_rpc_gss_initialised = TRUE; 163181344Sdfr } 164181344Sdfr} 165181344Sdfr 166181344Sdfrbool_t 167181344Sdfrrpc_gss_set_callback(rpc_gss_callback_t *cb) 168181344Sdfr{ 169181344Sdfr struct svc_rpc_gss_callback *scb; 170181344Sdfr 171184588Sdfr scb = mem_alloc(sizeof(struct svc_rpc_gss_callback)); 172181344Sdfr if (!scb) { 173181344Sdfr _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); 174181344Sdfr return (FALSE); 175181344Sdfr } 176181344Sdfr scb->cb_callback = *cb; 177181344Sdfr SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link); 178181344Sdfr 179181344Sdfr return (TRUE); 180181344Sdfr} 181181344Sdfr 182181344Sdfrbool_t 183181344Sdfrrpc_gss_set_svc_name(const char *principal, const char *mechanism, 184181344Sdfr u_int req_time, u_int program, u_int version) 185181344Sdfr{ 186181344Sdfr OM_uint32 maj_stat, min_stat; 187181344Sdfr struct svc_rpc_gss_svc_name *sname; 188181344Sdfr gss_buffer_desc namebuf; 189181344Sdfr gss_name_t name; 190181344Sdfr gss_OID mech_oid; 191181344Sdfr gss_OID_set_desc oid_set; 192181344Sdfr gss_cred_id_t cred; 193181344Sdfr 194181344Sdfr svc_rpc_gss_init(); 195181344Sdfr 196181344Sdfr if (!rpc_gss_mech_to_oid(mechanism, &mech_oid)) 197181344Sdfr return (FALSE); 198181344Sdfr oid_set.count = 1; 199181344Sdfr oid_set.elements = mech_oid; 200181344Sdfr 201181344Sdfr namebuf.value = (void *)(intptr_t) principal; 202181344Sdfr namebuf.length = strlen(principal); 203181344Sdfr 204181344Sdfr maj_stat = gss_import_name(&min_stat, &namebuf, 205181344Sdfr GSS_C_NT_HOSTBASED_SERVICE, &name); 206181344Sdfr if (maj_stat != GSS_S_COMPLETE) 207181344Sdfr return (FALSE); 208181344Sdfr 209181344Sdfr maj_stat = gss_acquire_cred(&min_stat, name, 210181344Sdfr req_time, &oid_set, GSS_C_ACCEPT, &cred, NULL, NULL); 211181344Sdfr if (maj_stat != GSS_S_COMPLETE) 212181344Sdfr return (FALSE); 213181344Sdfr 214181344Sdfr gss_release_name(&min_stat, &name); 215181344Sdfr 216181344Sdfr sname = malloc(sizeof(struct svc_rpc_gss_svc_name)); 217181344Sdfr if (!sname) 218181344Sdfr return (FALSE); 219181344Sdfr sname->sn_principal = strdup(principal); 220181344Sdfr sname->sn_mech = mech_oid; 221181344Sdfr sname->sn_req_time = req_time; 222181344Sdfr sname->sn_cred = cred; 223181344Sdfr sname->sn_program = program; 224181344Sdfr sname->sn_version = version; 225181344Sdfr SLIST_INSERT_HEAD(&svc_rpc_gss_svc_names, sname, sn_link); 226181344Sdfr 227181344Sdfr return (TRUE); 228181344Sdfr} 229181344Sdfr 230181344Sdfrbool_t 231181344Sdfrrpc_gss_get_principal_name(rpc_gss_principal_t *principal, 232181344Sdfr const char *mech, const char *name, const char *node, const char *domain) 233181344Sdfr{ 234181344Sdfr OM_uint32 maj_stat, min_stat; 235181344Sdfr gss_OID mech_oid; 236181344Sdfr size_t namelen; 237181344Sdfr gss_buffer_desc buf; 238181344Sdfr gss_name_t gss_name, gss_mech_name; 239181344Sdfr rpc_gss_principal_t result; 240181344Sdfr 241181344Sdfr svc_rpc_gss_init(); 242181344Sdfr 243181344Sdfr if (!rpc_gss_mech_to_oid(mech, &mech_oid)) 244181344Sdfr return (FALSE); 245181344Sdfr 246181344Sdfr /* 247181344Sdfr * Construct a gss_buffer containing the full name formatted 248181344Sdfr * as "name/node@domain" where node and domain are optional. 249181344Sdfr */ 250181344Sdfr namelen = strlen(name); 251181344Sdfr if (node) { 252181344Sdfr namelen += strlen(node) + 1; 253181344Sdfr } 254181344Sdfr if (domain) { 255181344Sdfr namelen += strlen(domain) + 1; 256181344Sdfr } 257181344Sdfr 258184588Sdfr buf.value = mem_alloc(namelen); 259181344Sdfr buf.length = namelen; 260181344Sdfr strcpy((char *) buf.value, name); 261181344Sdfr if (node) { 262181344Sdfr strcat((char *) buf.value, "/"); 263181344Sdfr strcat((char *) buf.value, node); 264181344Sdfr } 265181344Sdfr if (domain) { 266181344Sdfr strcat((char *) buf.value, "@"); 267181344Sdfr strcat((char *) buf.value, domain); 268181344Sdfr } 269181344Sdfr 270181344Sdfr /* 271181344Sdfr * Convert that to a gss_name_t and then convert that to a 272181344Sdfr * mechanism name in the selected mechanism. 273181344Sdfr */ 274181344Sdfr maj_stat = gss_import_name(&min_stat, &buf, 275181344Sdfr GSS_C_NT_USER_NAME, &gss_name); 276184588Sdfr mem_free(buf.value, buf.length); 277181344Sdfr if (maj_stat != GSS_S_COMPLETE) { 278181344Sdfr log_status("gss_import_name", mech_oid, maj_stat, min_stat); 279181344Sdfr return (FALSE); 280181344Sdfr } 281181344Sdfr maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid, 282181344Sdfr &gss_mech_name); 283181344Sdfr if (maj_stat != GSS_S_COMPLETE) { 284181344Sdfr log_status("gss_canonicalize_name", mech_oid, maj_stat, 285181344Sdfr min_stat); 286181344Sdfr gss_release_name(&min_stat, &gss_name); 287181344Sdfr return (FALSE); 288181344Sdfr } 289181344Sdfr gss_release_name(&min_stat, &gss_name); 290181344Sdfr 291181344Sdfr /* 292181344Sdfr * Export the mechanism name and use that to construct the 293181344Sdfr * rpc_gss_principal_t result. 294181344Sdfr */ 295181344Sdfr maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf); 296181344Sdfr if (maj_stat != GSS_S_COMPLETE) { 297181344Sdfr log_status("gss_export_name", mech_oid, maj_stat, min_stat); 298181344Sdfr gss_release_name(&min_stat, &gss_mech_name); 299181344Sdfr return (FALSE); 300181344Sdfr } 301181344Sdfr gss_release_name(&min_stat, &gss_mech_name); 302181344Sdfr 303184588Sdfr result = mem_alloc(sizeof(int) + buf.length); 304181344Sdfr if (!result) { 305181344Sdfr gss_release_buffer(&min_stat, &buf); 306181344Sdfr return (FALSE); 307181344Sdfr } 308181344Sdfr result->len = buf.length; 309181344Sdfr memcpy(result->name, buf.value, buf.length); 310181344Sdfr gss_release_buffer(&min_stat, &buf); 311181344Sdfr 312181344Sdfr *principal = result; 313181344Sdfr return (TRUE); 314181344Sdfr} 315181344Sdfr 316181344Sdfrbool_t 317181344Sdfrrpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred, 318181344Sdfr rpc_gss_ucred_t **ucred, void **cookie) 319181344Sdfr{ 320181344Sdfr struct svc_rpc_gss_client *client; 321181344Sdfr 322181344Sdfr if (req->rq_cred.oa_flavor != RPCSEC_GSS) 323181344Sdfr return (FALSE); 324181344Sdfr 325181344Sdfr client = req->rq_clntcred; 326181344Sdfr if (rcred) 327181344Sdfr *rcred = &client->cl_rawcred; 328181344Sdfr if (ucred) 329181344Sdfr *ucred = &client->cl_ucred; 330181344Sdfr if (cookie) 331181344Sdfr *cookie = client->cl_cookie; 332181344Sdfr return (TRUE); 333181344Sdfr} 334181344Sdfr 335181344Sdfrint 336181344Sdfrrpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len) 337181344Sdfr{ 338181344Sdfr struct svc_rpc_gss_client *client = req->rq_clntcred; 339181344Sdfr int want_conf; 340181344Sdfr OM_uint32 max; 341181344Sdfr OM_uint32 maj_stat, min_stat; 342181344Sdfr int result; 343181344Sdfr 344181344Sdfr switch (client->cl_rawcred.service) { 345181344Sdfr case rpc_gss_svc_none: 346181344Sdfr return (max_tp_unit_len); 347181344Sdfr break; 348181344Sdfr 349181344Sdfr case rpc_gss_svc_default: 350181344Sdfr case rpc_gss_svc_integrity: 351181344Sdfr want_conf = FALSE; 352181344Sdfr break; 353181344Sdfr 354181344Sdfr case rpc_gss_svc_privacy: 355181344Sdfr want_conf = TRUE; 356181344Sdfr break; 357181344Sdfr 358181344Sdfr default: 359181344Sdfr return (0); 360181344Sdfr } 361181344Sdfr 362181344Sdfr maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf, 363181344Sdfr client->cl_qop, max_tp_unit_len, &max); 364181344Sdfr 365181344Sdfr if (maj_stat == GSS_S_COMPLETE) { 366181344Sdfr result = (int) max; 367181344Sdfr if (result < 0) 368181344Sdfr result = 0; 369181344Sdfr return (result); 370181344Sdfr } else { 371181344Sdfr log_status("gss_wrap_size_limit", client->cl_mech, 372181344Sdfr maj_stat, min_stat); 373181344Sdfr return (0); 374181344Sdfr } 375181344Sdfr} 376181344Sdfr 377181344Sdfrstatic struct svc_rpc_gss_client * 378181344Sdfrsvc_rpc_gss_find_client(uint32_t clientid) 379181344Sdfr{ 380181344Sdfr struct svc_rpc_gss_client *client; 381181344Sdfr struct svc_rpc_gss_client_list *list; 382181344Sdfr 383181344Sdfr 384181344Sdfr log_debug("in svc_rpc_gss_find_client(%d)", clientid); 385181344Sdfr 386181344Sdfr list = &svc_rpc_gss_client_hash[clientid % CLIENT_HASH_SIZE]; 387181344Sdfr TAILQ_FOREACH(client, list, cl_link) { 388181344Sdfr if (client->cl_id == clientid) { 389181344Sdfr /* 390181344Sdfr * Move this client to the front of the LRU 391181344Sdfr * list. 392181344Sdfr */ 393181344Sdfr TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink); 394181344Sdfr TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, 395181344Sdfr cl_alllink); 396181344Sdfr return client; 397181344Sdfr } 398181344Sdfr } 399181344Sdfr 400181344Sdfr return (NULL); 401181344Sdfr} 402181344Sdfr 403181344Sdfrstatic struct svc_rpc_gss_client * 404181344Sdfrsvc_rpc_gss_create_client(void) 405181344Sdfr{ 406181344Sdfr struct svc_rpc_gss_client *client; 407181344Sdfr struct svc_rpc_gss_client_list *list; 408181344Sdfr 409181344Sdfr log_debug("in svc_rpc_gss_create_client()"); 410181344Sdfr 411181344Sdfr client = mem_alloc(sizeof(struct svc_rpc_gss_client)); 412181344Sdfr memset(client, 0, sizeof(struct svc_rpc_gss_client)); 413181344Sdfr client->cl_id = svc_rpc_gss_next_clientid++; 414181344Sdfr list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE]; 415181344Sdfr TAILQ_INSERT_HEAD(list, client, cl_link); 416181344Sdfr TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink); 417181344Sdfr 418181344Sdfr /* 419181344Sdfr * Start the client off with a short expiration time. We will 420181344Sdfr * try to get a saner value from the client creds later. 421181344Sdfr */ 422181344Sdfr client->cl_state = CLIENT_NEW; 423181344Sdfr client->cl_locked = FALSE; 424181344Sdfr client->cl_expiration = time(0) + 5*60; 425181344Sdfr svc_rpc_gss_client_count++; 426181344Sdfr 427181344Sdfr return (client); 428181344Sdfr} 429181344Sdfr 430181344Sdfrstatic void 431181344Sdfrsvc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client) 432181344Sdfr{ 433181344Sdfr struct svc_rpc_gss_client_list *list; 434181344Sdfr OM_uint32 min_stat; 435181344Sdfr 436181344Sdfr log_debug("in svc_rpc_gss_destroy_client()"); 437181344Sdfr 438181344Sdfr if (client->cl_ctx) 439181344Sdfr gss_delete_sec_context(&min_stat, 440181344Sdfr &client->cl_ctx, GSS_C_NO_BUFFER); 441181344Sdfr 442181344Sdfr if (client->cl_cname) 443181344Sdfr gss_release_name(&min_stat, &client->cl_cname); 444181344Sdfr 445181344Sdfr if (client->cl_rawcred.client_principal) 446184588Sdfr mem_free(client->cl_rawcred.client_principal, 447184588Sdfr sizeof(*client->cl_rawcred.client_principal) 448184588Sdfr + client->cl_rawcred.client_principal->len); 449181344Sdfr 450181344Sdfr if (client->cl_verf.value) 451181344Sdfr gss_release_buffer(&min_stat, &client->cl_verf); 452181344Sdfr 453181344Sdfr list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE]; 454181344Sdfr TAILQ_REMOVE(list, client, cl_link); 455181344Sdfr TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink); 456181344Sdfr svc_rpc_gss_client_count--; 457181344Sdfr mem_free(client, sizeof(*client)); 458181344Sdfr} 459181344Sdfr 460181344Sdfrstatic void 461181344Sdfrsvc_rpc_gss_timeout_clients(void) 462181344Sdfr{ 463181344Sdfr struct svc_rpc_gss_client *client; 464181344Sdfr struct svc_rpc_gss_client *nclient; 465181344Sdfr time_t now = time(0); 466181344Sdfr 467181344Sdfr log_debug("in svc_rpc_gss_timeout_clients()"); 468181344Sdfr /* 469181344Sdfr * First enforce the max client limit. We keep 470181344Sdfr * svc_rpc_gss_clients in LRU order. 471181344Sdfr */ 472181344Sdfr while (svc_rpc_gss_client_count > CLIENT_MAX) 473181344Sdfr svc_rpc_gss_destroy_client(TAILQ_LAST(&svc_rpc_gss_clients, 474181344Sdfr svc_rpc_gss_client_list)); 475181344Sdfr TAILQ_FOREACH_SAFE(client, &svc_rpc_gss_clients, cl_alllink, nclient) { 476181344Sdfr if (client->cl_state == CLIENT_STALE 477181344Sdfr || now > client->cl_expiration) { 478181344Sdfr log_debug("expiring client %p", client); 479181344Sdfr svc_rpc_gss_destroy_client(client); 480181344Sdfr } 481181344Sdfr } 482181344Sdfr} 483181344Sdfr 484181344Sdfr#ifdef DEBUG 485181344Sdfr/* 486181344Sdfr * OID<->string routines. These are uuuuugly. 487181344Sdfr */ 488181344Sdfrstatic OM_uint32 489181344Sdfrgss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str) 490181344Sdfr{ 491181344Sdfr char numstr[128]; 492181344Sdfr unsigned long number; 493181344Sdfr int numshift; 494181344Sdfr size_t string_length; 495181344Sdfr size_t i; 496181344Sdfr unsigned char *cp; 497181344Sdfr char *bp; 498181344Sdfr 499181344Sdfr /* Decoded according to krb5/gssapi_krb5.c */ 500181344Sdfr 501181344Sdfr /* First determine the size of the string */ 502181344Sdfr string_length = 0; 503181344Sdfr number = 0; 504181344Sdfr numshift = 0; 505181344Sdfr cp = (unsigned char *) oid->elements; 506181344Sdfr number = (unsigned long) cp[0]; 507181344Sdfr sprintf(numstr, "%ld ", number/40); 508181344Sdfr string_length += strlen(numstr); 509181344Sdfr sprintf(numstr, "%ld ", number%40); 510181344Sdfr string_length += strlen(numstr); 511181344Sdfr for (i=1; i<oid->length; i++) { 512181344Sdfr if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) { 513181344Sdfr number = (number << 7) | (cp[i] & 0x7f); 514181344Sdfr numshift += 7; 515181344Sdfr } 516181344Sdfr else { 517181344Sdfr *minor_status = 0; 518181344Sdfr return(GSS_S_FAILURE); 519181344Sdfr } 520181344Sdfr if ((cp[i] & 0x80) == 0) { 521181344Sdfr sprintf(numstr, "%ld ", number); 522181344Sdfr string_length += strlen(numstr); 523181344Sdfr number = 0; 524181344Sdfr numshift = 0; 525181344Sdfr } 526181344Sdfr } 527181344Sdfr /* 528181344Sdfr * If we get here, we've calculated the length of "n n n ... n ". Add 4 529181344Sdfr * here for "{ " and "}\0". 530181344Sdfr */ 531181344Sdfr string_length += 4; 532184588Sdfr if ((bp = (char *) mem_alloc(string_length))) { 533181344Sdfr strcpy(bp, "{ "); 534181344Sdfr number = (unsigned long) cp[0]; 535181344Sdfr sprintf(numstr, "%ld ", number/40); 536181344Sdfr strcat(bp, numstr); 537181344Sdfr sprintf(numstr, "%ld ", number%40); 538181344Sdfr strcat(bp, numstr); 539181344Sdfr number = 0; 540181344Sdfr cp = (unsigned char *) oid->elements; 541181344Sdfr for (i=1; i<oid->length; i++) { 542181344Sdfr number = (number << 7) | (cp[i] & 0x7f); 543181344Sdfr if ((cp[i] & 0x80) == 0) { 544181344Sdfr sprintf(numstr, "%ld ", number); 545181344Sdfr strcat(bp, numstr); 546181344Sdfr number = 0; 547181344Sdfr } 548181344Sdfr } 549181344Sdfr strcat(bp, "}"); 550181344Sdfr oid_str->length = strlen(bp)+1; 551181344Sdfr oid_str->value = (void *) bp; 552181344Sdfr *minor_status = 0; 553181344Sdfr return(GSS_S_COMPLETE); 554181344Sdfr } 555181344Sdfr *minor_status = 0; 556181344Sdfr return(GSS_S_FAILURE); 557181344Sdfr} 558181344Sdfr#endif 559181344Sdfr 560181344Sdfrstatic void 561181344Sdfrsvc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client, 562181344Sdfr const gss_name_t name) 563181344Sdfr{ 564181344Sdfr OM_uint32 maj_stat, min_stat; 565181344Sdfr char buf[128]; 566181344Sdfr uid_t uid; 567181344Sdfr struct passwd pwd, *pw; 568181344Sdfr rpc_gss_ucred_t *uc = &client->cl_ucred; 569181344Sdfr 570181344Sdfr uc->uid = 65534; 571181344Sdfr uc->gid = 65534; 572181344Sdfr uc->gidlen = 0; 573181344Sdfr uc->gidlist = client->cl_gid_storage; 574181344Sdfr 575181344Sdfr maj_stat = gss_pname_to_uid(&min_stat, name, client->cl_mech, &uid); 576181344Sdfr if (maj_stat != GSS_S_COMPLETE) 577181344Sdfr return; 578181344Sdfr 579181344Sdfr getpwuid_r(uid, &pwd, buf, sizeof(buf), &pw); 580181344Sdfr if (pw) { 581181344Sdfr int len = NGRPS; 582181344Sdfr uc->uid = pw->pw_uid; 583181344Sdfr uc->gid = pw->pw_gid; 584181344Sdfr uc->gidlist = client->cl_gid_storage; 585181344Sdfr getgrouplist(pw->pw_name, pw->pw_gid, uc->gidlist, &len); 586181344Sdfr uc->gidlen = len; 587181344Sdfr } 588181344Sdfr} 589181344Sdfr 590181344Sdfrstatic bool_t 591181344Sdfrsvc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client, 592181344Sdfr struct svc_req *rqst, 593181344Sdfr struct rpc_gss_init_res *gr, 594181344Sdfr struct rpc_gss_cred *gc) 595181344Sdfr{ 596181344Sdfr gss_buffer_desc recv_tok; 597181344Sdfr gss_OID mech; 598181344Sdfr OM_uint32 maj_stat = 0, min_stat = 0, ret_flags; 599181344Sdfr OM_uint32 cred_lifetime; 600181344Sdfr struct svc_rpc_gss_svc_name *sname; 601181344Sdfr 602181344Sdfr log_debug("in svc_rpc_gss_accept_context()"); 603181344Sdfr 604181344Sdfr /* Deserialize arguments. */ 605181344Sdfr memset(&recv_tok, 0, sizeof(recv_tok)); 606181344Sdfr 607181344Sdfr if (!svc_getargs(rqst->rq_xprt, 608181344Sdfr (xdrproc_t) xdr_gss_buffer_desc, 609181344Sdfr (caddr_t) &recv_tok)) { 610181344Sdfr client->cl_state = CLIENT_STALE; 611181344Sdfr return (FALSE); 612181344Sdfr } 613181344Sdfr 614181344Sdfr /* 615181344Sdfr * First time round, try all the server names we have until 616181344Sdfr * one matches. Afterwards, stick with that one. 617181344Sdfr */ 618181344Sdfr if (!client->cl_sname) { 619181344Sdfr SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) { 620181344Sdfr if (sname->sn_program == rqst->rq_prog 621181344Sdfr && sname->sn_version == rqst->rq_vers) { 622181344Sdfr gr->gr_major = gss_accept_sec_context( 623181344Sdfr &gr->gr_minor, 624181344Sdfr &client->cl_ctx, 625181344Sdfr sname->sn_cred, 626181344Sdfr &recv_tok, 627181344Sdfr GSS_C_NO_CHANNEL_BINDINGS, 628181344Sdfr &client->cl_cname, 629181344Sdfr &mech, 630181344Sdfr &gr->gr_token, 631181344Sdfr &ret_flags, 632181344Sdfr &cred_lifetime, 633181344Sdfr &client->cl_creds); 634181344Sdfr if (gr->gr_major == GSS_S_COMPLETE 635181344Sdfr || gr->gr_major == GSS_S_CONTINUE_NEEDED) { 636181344Sdfr client->cl_sname = sname; 637181344Sdfr break; 638181344Sdfr } 639184588Sdfr client->cl_sname = sname; 640184588Sdfr break; 641181344Sdfr } 642181344Sdfr } 643184588Sdfr if (!sname) { 644184588Sdfr xdr_free((xdrproc_t) xdr_gss_buffer_desc, 645184588Sdfr (char *) &recv_tok); 646184588Sdfr return (FALSE); 647184588Sdfr } 648181344Sdfr } else { 649181344Sdfr gr->gr_major = gss_accept_sec_context( 650181344Sdfr &gr->gr_minor, 651181344Sdfr &client->cl_ctx, 652181344Sdfr client->cl_sname->sn_cred, 653181344Sdfr &recv_tok, 654181344Sdfr GSS_C_NO_CHANNEL_BINDINGS, 655181344Sdfr &client->cl_cname, 656181344Sdfr &mech, 657181344Sdfr &gr->gr_token, 658181344Sdfr &ret_flags, 659181344Sdfr &cred_lifetime, 660181344Sdfr NULL); 661181344Sdfr } 662181344Sdfr 663181344Sdfr xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok); 664181344Sdfr 665181344Sdfr /* 666181344Sdfr * If we get an error from gss_accept_sec_context, send the 667181344Sdfr * reply anyway so that the client gets a chance to see what 668181344Sdfr * is wrong. 669181344Sdfr */ 670181344Sdfr if (gr->gr_major != GSS_S_COMPLETE && 671181344Sdfr gr->gr_major != GSS_S_CONTINUE_NEEDED) { 672181344Sdfr log_status("accept_sec_context", client->cl_mech, 673181344Sdfr gr->gr_major, gr->gr_minor); 674181344Sdfr client->cl_state = CLIENT_STALE; 675184588Sdfr return (TRUE); 676181344Sdfr } 677181344Sdfr 678181344Sdfr gr->gr_handle.value = &client->cl_id; 679184588Sdfr gr->gr_handle.length = sizeof(client->cl_id); 680181344Sdfr gr->gr_win = SVC_RPC_GSS_SEQWINDOW; 681181344Sdfr 682181344Sdfr /* Save client info. */ 683181344Sdfr client->cl_mech = mech; 684181344Sdfr client->cl_qop = GSS_C_QOP_DEFAULT; 685181344Sdfr client->cl_seq = gc->gc_seq; 686181344Sdfr client->cl_win = gr->gr_win; 687181344Sdfr client->cl_done_callback = FALSE; 688181344Sdfr 689181344Sdfr if (gr->gr_major == GSS_S_COMPLETE) { 690181344Sdfr gss_buffer_desc export_name; 691181344Sdfr 692181344Sdfr /* 693181344Sdfr * Change client expiration time to be near when the 694181344Sdfr * client creds expire (or 24 hours if we can't figure 695181344Sdfr * that out). 696181344Sdfr */ 697181344Sdfr if (cred_lifetime == GSS_C_INDEFINITE) 698181344Sdfr cred_lifetime = time(0) + 24*60*60; 699181344Sdfr 700181344Sdfr client->cl_expiration = time(0) + cred_lifetime; 701181344Sdfr 702181344Sdfr /* 703181344Sdfr * Fill in cred details in the rawcred structure. 704181344Sdfr */ 705181344Sdfr client->cl_rawcred.version = RPCSEC_GSS_VERSION; 706181344Sdfr rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism); 707181344Sdfr maj_stat = gss_export_name(&min_stat, client->cl_cname, 708181344Sdfr &export_name); 709181344Sdfr if (maj_stat != GSS_S_COMPLETE) { 710181344Sdfr log_status("gss_export_name", client->cl_mech, 711181344Sdfr maj_stat, min_stat); 712181344Sdfr return (FALSE); 713181344Sdfr } 714181344Sdfr client->cl_rawcred.client_principal = 715184588Sdfr mem_alloc(sizeof(*client->cl_rawcred.client_principal) 716181344Sdfr + export_name.length); 717181344Sdfr client->cl_rawcred.client_principal->len = export_name.length; 718181344Sdfr memcpy(client->cl_rawcred.client_principal->name, 719181344Sdfr export_name.value, export_name.length); 720181344Sdfr gss_release_buffer(&min_stat, &export_name); 721181344Sdfr client->cl_rawcred.svc_principal = 722181344Sdfr client->cl_sname->sn_principal; 723181344Sdfr client->cl_rawcred.service = gc->gc_svc; 724181344Sdfr 725181344Sdfr /* 726181344Sdfr * Use gss_pname_to_uid to map to unix creds. For 727181344Sdfr * kerberos5, this uses krb5_aname_to_localname. 728181344Sdfr */ 729181344Sdfr svc_rpc_gss_build_ucred(client, client->cl_cname); 730184588Sdfr gss_release_name(&min_stat, &client->cl_cname); 731181344Sdfr 732181344Sdfr#ifdef DEBUG 733181344Sdfr { 734181344Sdfr gss_buffer_desc mechname; 735181344Sdfr 736181344Sdfr gss_oid_to_str(&min_stat, mech, &mechname); 737181344Sdfr 738181344Sdfr log_debug("accepted context for %s with " 739181344Sdfr "<mech %.*s, qop %d, svc %d>", 740181344Sdfr client->cl_rawcred.client_principal->name, 741181344Sdfr mechname.length, (char *)mechname.value, 742181344Sdfr client->cl_qop, client->rawcred.service); 743181344Sdfr 744181344Sdfr gss_release_buffer(&min_stat, &mechname); 745181344Sdfr } 746181344Sdfr#endif /* DEBUG */ 747181344Sdfr } 748181344Sdfr return (TRUE); 749181344Sdfr} 750181344Sdfr 751181344Sdfrstatic bool_t 752181344Sdfrsvc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg, 753181344Sdfr gss_qop_t *qop) 754181344Sdfr{ 755181344Sdfr struct opaque_auth *oa; 756181344Sdfr gss_buffer_desc rpcbuf, checksum; 757181344Sdfr OM_uint32 maj_stat, min_stat; 758181344Sdfr gss_qop_t qop_state; 759181348Sdfr int32_t rpchdr[128 / sizeof(int32_t)]; 760181344Sdfr int32_t *buf; 761181344Sdfr 762181344Sdfr log_debug("in svc_rpc_gss_validate()"); 763181344Sdfr 764181344Sdfr memset(rpchdr, 0, sizeof(rpchdr)); 765181344Sdfr 766181344Sdfr /* Reconstruct RPC header for signing (from xdr_callmsg). */ 767181348Sdfr buf = rpchdr; 768181344Sdfr IXDR_PUT_LONG(buf, msg->rm_xid); 769181344Sdfr IXDR_PUT_ENUM(buf, msg->rm_direction); 770181344Sdfr IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers); 771181344Sdfr IXDR_PUT_LONG(buf, msg->rm_call.cb_prog); 772181344Sdfr IXDR_PUT_LONG(buf, msg->rm_call.cb_vers); 773181344Sdfr IXDR_PUT_LONG(buf, msg->rm_call.cb_proc); 774181344Sdfr oa = &msg->rm_call.cb_cred; 775181344Sdfr IXDR_PUT_ENUM(buf, oa->oa_flavor); 776181344Sdfr IXDR_PUT_LONG(buf, oa->oa_length); 777181344Sdfr if (oa->oa_length) { 778181344Sdfr memcpy((caddr_t)buf, oa->oa_base, oa->oa_length); 779181344Sdfr buf += RNDUP(oa->oa_length) / sizeof(int32_t); 780181344Sdfr } 781181344Sdfr rpcbuf.value = rpchdr; 782181348Sdfr rpcbuf.length = (u_char *)buf - (u_char *)rpchdr; 783181344Sdfr 784181344Sdfr checksum.value = msg->rm_call.cb_verf.oa_base; 785181344Sdfr checksum.length = msg->rm_call.cb_verf.oa_length; 786181344Sdfr 787181344Sdfr maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum, 788181344Sdfr &qop_state); 789181344Sdfr 790181344Sdfr if (maj_stat != GSS_S_COMPLETE) { 791181344Sdfr log_status("gss_verify_mic", client->cl_mech, 792181344Sdfr maj_stat, min_stat); 793181344Sdfr client->cl_state = CLIENT_STALE; 794181344Sdfr return (FALSE); 795181344Sdfr } 796181344Sdfr *qop = qop_state; 797181344Sdfr return (TRUE); 798181344Sdfr} 799181344Sdfr 800181344Sdfrstatic bool_t 801181344Sdfrsvc_rpc_gss_nextverf(struct svc_rpc_gss_client *client, 802181344Sdfr struct svc_req *rqst, u_int seq) 803181344Sdfr{ 804181344Sdfr gss_buffer_desc signbuf; 805181344Sdfr OM_uint32 maj_stat, min_stat; 806181344Sdfr uint32_t nseq; 807181344Sdfr 808181344Sdfr log_debug("in svc_rpc_gss_nextverf()"); 809181344Sdfr 810181344Sdfr nseq = htonl(seq); 811181344Sdfr signbuf.value = &nseq; 812181344Sdfr signbuf.length = sizeof(nseq); 813181344Sdfr 814181344Sdfr if (client->cl_verf.value) 815181344Sdfr gss_release_buffer(&min_stat, &client->cl_verf); 816181344Sdfr 817181344Sdfr maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop, 818181344Sdfr &signbuf, &client->cl_verf); 819181344Sdfr 820181344Sdfr if (maj_stat != GSS_S_COMPLETE) { 821181344Sdfr log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat); 822181344Sdfr client->cl_state = CLIENT_STALE; 823181344Sdfr return (FALSE); 824181344Sdfr } 825181344Sdfr rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS; 826181344Sdfr rqst->rq_xprt->xp_verf.oa_base = (caddr_t)client->cl_verf.value; 827181344Sdfr rqst->rq_xprt->xp_verf.oa_length = (u_int)client->cl_verf.length; 828181344Sdfr 829181344Sdfr return (TRUE); 830181344Sdfr} 831181344Sdfr 832181344Sdfrstatic bool_t 833181344Sdfrsvc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst) 834181344Sdfr{ 835181344Sdfr struct svc_rpc_gss_callback *scb; 836181344Sdfr rpc_gss_lock_t lock; 837181344Sdfr void *cookie; 838181344Sdfr bool_t cb_res; 839181344Sdfr bool_t result; 840181344Sdfr 841181344Sdfr /* 842181344Sdfr * See if we have a callback for this guy. 843181344Sdfr */ 844181344Sdfr result = TRUE; 845181344Sdfr SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) { 846181344Sdfr if (scb->cb_callback.program == rqst->rq_prog 847181344Sdfr && scb->cb_callback.version == rqst->rq_vers) { 848181344Sdfr /* 849181344Sdfr * This one matches. Call the callback and see 850181344Sdfr * if it wants to veto or something. 851181344Sdfr */ 852181344Sdfr lock.locked = FALSE; 853181344Sdfr lock.raw_cred = &client->cl_rawcred; 854181344Sdfr cb_res = scb->cb_callback.callback(rqst, 855181344Sdfr client->cl_creds, 856181344Sdfr client->cl_ctx, 857181344Sdfr &lock, 858181344Sdfr &cookie); 859181344Sdfr 860181344Sdfr if (!cb_res) { 861181344Sdfr client->cl_state = CLIENT_STALE; 862181344Sdfr result = FALSE; 863181344Sdfr break; 864181344Sdfr } 865181344Sdfr 866181344Sdfr /* 867181344Sdfr * The callback accepted the connection - it 868181344Sdfr * is responsible for freeing client->cl_creds 869181344Sdfr * now. 870181344Sdfr */ 871181344Sdfr client->cl_creds = GSS_C_NO_CREDENTIAL; 872181344Sdfr client->cl_locked = lock.locked; 873181344Sdfr client->cl_cookie = cookie; 874181344Sdfr return (TRUE); 875181344Sdfr } 876181344Sdfr } 877181344Sdfr 878181344Sdfr /* 879181344Sdfr * Either no callback exists for this program/version or one 880181344Sdfr * of the callbacks rejected the connection. We just need to 881181344Sdfr * clean up the delegated client creds, if any. 882181344Sdfr */ 883181344Sdfr if (client->cl_creds) { 884181344Sdfr OM_uint32 min_ver; 885181344Sdfr gss_release_cred(&min_ver, &client->cl_creds); 886181344Sdfr } 887181344Sdfr return (result); 888181344Sdfr} 889181344Sdfr 890181344Sdfrstatic bool_t 891181344Sdfrsvc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq) 892181344Sdfr{ 893181344Sdfr u_int32_t offset; 894181344Sdfr int word, bit; 895181344Sdfr 896182758Sdfr if (seq <= client->cl_seqlast) { 897181344Sdfr /* 898181344Sdfr * The request sequence number is less than 899181344Sdfr * the largest we have seen so far. If it is 900181344Sdfr * outside the window or if we have seen a 901181344Sdfr * request with this sequence before, silently 902181344Sdfr * discard it. 903181344Sdfr */ 904181344Sdfr offset = client->cl_seqlast - seq; 905184588Sdfr if (offset >= SVC_RPC_GSS_SEQWINDOW) 906181344Sdfr return (FALSE); 907181344Sdfr word = offset / 32; 908181344Sdfr bit = offset % 32; 909181344Sdfr if (client->cl_seqmask[word] & (1 << bit)) 910181344Sdfr return (FALSE); 911181344Sdfr } 912181344Sdfr 913181344Sdfr return (TRUE); 914181344Sdfr} 915181344Sdfr 916181344Sdfrstatic void 917181344Sdfrsvc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq) 918181344Sdfr{ 919184588Sdfr int offset, i, word, bit; 920181344Sdfr uint32_t carry, newcarry; 921181344Sdfr 922181344Sdfr if (seq > client->cl_seqlast) { 923181344Sdfr /* 924181344Sdfr * This request has a sequence number greater 925181344Sdfr * than any we have seen so far. Advance the 926181344Sdfr * seq window and set bit zero of the window 927181344Sdfr * (which corresponds to the new sequence 928181344Sdfr * number) 929181344Sdfr */ 930181344Sdfr offset = seq - client->cl_seqlast; 931181344Sdfr while (offset > 32) { 932181344Sdfr for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1; 933181344Sdfr i > 0; i--) { 934181344Sdfr client->cl_seqmask[i] = client->cl_seqmask[i-1]; 935181344Sdfr } 936181344Sdfr client->cl_seqmask[0] = 0; 937181344Sdfr offset -= 32; 938181344Sdfr } 939181344Sdfr carry = 0; 940181344Sdfr for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) { 941181344Sdfr newcarry = client->cl_seqmask[i] >> (32 - offset); 942181344Sdfr client->cl_seqmask[i] = 943181344Sdfr (client->cl_seqmask[i] << offset) | carry; 944181344Sdfr carry = newcarry; 945181344Sdfr } 946181344Sdfr client->cl_seqmask[0] |= 1; 947181344Sdfr client->cl_seqlast = seq; 948184588Sdfr } else { 949184588Sdfr offset = client->cl_seqlast - seq; 950184588Sdfr word = offset / 32; 951184588Sdfr bit = offset % 32; 952184588Sdfr client->cl_seqmask[word] |= (1 << bit); 953181344Sdfr } 954184588Sdfr 955181344Sdfr} 956181344Sdfr 957181344Sdfrenum auth_stat 958181344Sdfrsvc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg) 959181344Sdfr 960181344Sdfr{ 961181344Sdfr OM_uint32 min_stat; 962181344Sdfr XDR xdrs; 963181344Sdfr struct svc_rpc_gss_client *client; 964181344Sdfr struct rpc_gss_cred gc; 965181344Sdfr struct rpc_gss_init_res gr; 966181344Sdfr gss_qop_t qop; 967181344Sdfr int call_stat; 968181344Sdfr enum auth_stat result; 969181344Sdfr 970181344Sdfr log_debug("in svc_rpc_gss()"); 971181344Sdfr 972181344Sdfr /* Garbage collect old clients. */ 973181344Sdfr svc_rpc_gss_timeout_clients(); 974181344Sdfr 975181344Sdfr /* Initialize reply. */ 976181344Sdfr rqst->rq_xprt->xp_verf = _null_auth; 977181344Sdfr 978181344Sdfr /* Deserialize client credentials. */ 979181344Sdfr if (rqst->rq_cred.oa_length <= 0) 980181344Sdfr return (AUTH_BADCRED); 981181344Sdfr 982181344Sdfr memset(&gc, 0, sizeof(gc)); 983181344Sdfr 984181344Sdfr xdrmem_create(&xdrs, rqst->rq_cred.oa_base, 985181344Sdfr rqst->rq_cred.oa_length, XDR_DECODE); 986181344Sdfr 987181344Sdfr if (!xdr_rpc_gss_cred(&xdrs, &gc)) { 988181344Sdfr XDR_DESTROY(&xdrs); 989181344Sdfr return (AUTH_BADCRED); 990181344Sdfr } 991181344Sdfr XDR_DESTROY(&xdrs); 992181344Sdfr 993181344Sdfr /* Check version. */ 994181344Sdfr if (gc.gc_version != RPCSEC_GSS_VERSION) { 995181344Sdfr result = AUTH_BADCRED; 996181344Sdfr goto out; 997181344Sdfr } 998181344Sdfr 999181344Sdfr /* Check the proc and find the client (or create it) */ 1000181344Sdfr if (gc.gc_proc == RPCSEC_GSS_INIT) { 1001184588Sdfr if (gc.gc_handle.length != 0) { 1002184588Sdfr result = AUTH_BADCRED; 1003184588Sdfr goto out; 1004184588Sdfr } 1005181344Sdfr client = svc_rpc_gss_create_client(); 1006181344Sdfr } else { 1007181344Sdfr if (gc.gc_handle.length != sizeof(uint32_t)) { 1008181344Sdfr result = AUTH_BADCRED; 1009181344Sdfr goto out; 1010181344Sdfr } 1011181344Sdfr uint32_t *p = gc.gc_handle.value; 1012181344Sdfr client = svc_rpc_gss_find_client(*p); 1013181344Sdfr if (!client) { 1014181344Sdfr /* 1015181344Sdfr * Can't find the client - we may have 1016181344Sdfr * destroyed it - tell the other side to 1017181344Sdfr * re-authenticate. 1018181344Sdfr */ 1019181344Sdfr result = RPCSEC_GSS_CREDPROBLEM; 1020181344Sdfr goto out; 1021181344Sdfr } 1022181344Sdfr } 1023181344Sdfr rqst->rq_clntcred = client; 1024181344Sdfr 1025181344Sdfr /* 1026181344Sdfr * The service and sequence number must be ignored for 1027181344Sdfr * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT. 1028181344Sdfr */ 1029181344Sdfr if (gc.gc_proc != RPCSEC_GSS_INIT 1030181344Sdfr && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) { 1031181344Sdfr /* 1032181344Sdfr * Check for sequence number overflow. 1033181344Sdfr */ 1034181344Sdfr if (gc.gc_seq >= MAXSEQ) { 1035181344Sdfr result = RPCSEC_GSS_CTXPROBLEM; 1036181344Sdfr goto out; 1037181344Sdfr } 1038181344Sdfr client->cl_seq = gc.gc_seq; 1039181344Sdfr 1040181344Sdfr /* 1041181344Sdfr * Check for valid service. 1042181344Sdfr */ 1043181344Sdfr if (gc.gc_svc != rpc_gss_svc_none && 1044181344Sdfr gc.gc_svc != rpc_gss_svc_integrity && 1045181344Sdfr gc.gc_svc != rpc_gss_svc_privacy) { 1046181344Sdfr result = AUTH_BADCRED; 1047181344Sdfr goto out; 1048181344Sdfr } 1049181344Sdfr } 1050181344Sdfr 1051181344Sdfr /* Handle RPCSEC_GSS control procedure. */ 1052181344Sdfr switch (gc.gc_proc) { 1053181344Sdfr 1054181344Sdfr case RPCSEC_GSS_INIT: 1055181344Sdfr case RPCSEC_GSS_CONTINUE_INIT: 1056181344Sdfr if (rqst->rq_proc != NULLPROC) { 1057181344Sdfr result = AUTH_REJECTEDCRED; 1058181344Sdfr break; 1059181344Sdfr } 1060181344Sdfr 1061181344Sdfr memset(&gr, 0, sizeof(gr)); 1062181344Sdfr if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) { 1063181344Sdfr result = AUTH_REJECTEDCRED; 1064181344Sdfr break; 1065181344Sdfr } 1066181344Sdfr 1067181344Sdfr if (gr.gr_major == GSS_S_COMPLETE) { 1068181344Sdfr if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) { 1069181344Sdfr result = AUTH_REJECTEDCRED; 1070181344Sdfr break; 1071181344Sdfr } 1072181344Sdfr } else { 1073181344Sdfr rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL; 1074181344Sdfr rqst->rq_xprt->xp_verf.oa_length = 0; 1075181344Sdfr } 1076181344Sdfr 1077181344Sdfr call_stat = svc_sendreply(rqst->rq_xprt, 1078181344Sdfr (xdrproc_t) xdr_rpc_gss_init_res, 1079181344Sdfr (caddr_t) &gr); 1080181344Sdfr 1081181344Sdfr gss_release_buffer(&min_stat, &gr.gr_token); 1082181344Sdfr 1083181344Sdfr if (!call_stat) { 1084181344Sdfr result = AUTH_FAILED; 1085181344Sdfr break; 1086181344Sdfr } 1087181344Sdfr 1088181344Sdfr if (gr.gr_major == GSS_S_COMPLETE) 1089181344Sdfr client->cl_state = CLIENT_ESTABLISHED; 1090181344Sdfr 1091181344Sdfr result = RPCSEC_GSS_NODISPATCH; 1092181344Sdfr break; 1093181344Sdfr 1094181344Sdfr case RPCSEC_GSS_DATA: 1095181344Sdfr case RPCSEC_GSS_DESTROY: 1096181344Sdfr if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) { 1097181344Sdfr result = RPCSEC_GSS_NODISPATCH; 1098181344Sdfr break; 1099181344Sdfr } 1100181344Sdfr 1101181344Sdfr if (!svc_rpc_gss_validate(client, msg, &qop)) { 1102181344Sdfr result = RPCSEC_GSS_CREDPROBLEM; 1103181344Sdfr break; 1104181344Sdfr } 1105181344Sdfr 1106181344Sdfr if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) { 1107181344Sdfr result = RPCSEC_GSS_CTXPROBLEM; 1108181344Sdfr break; 1109181344Sdfr } 1110181344Sdfr 1111181344Sdfr svc_rpc_gss_update_seq(client, gc.gc_seq); 1112181344Sdfr 1113181344Sdfr /* 1114181344Sdfr * Change the SVCAUTH ops on the transport to point at 1115181344Sdfr * our own code so that we can unwrap the arguments 1116181344Sdfr * and wrap the result. The caller will re-set this on 1117181344Sdfr * every request to point to a set of null wrap/unwrap 1118181344Sdfr * methods. 1119181344Sdfr */ 1120181344Sdfr SVC_AUTH(rqst->rq_xprt).svc_ah_ops = &svc_auth_gss_ops; 1121181344Sdfr SVC_AUTH(rqst->rq_xprt).svc_ah_private = client; 1122181344Sdfr 1123181344Sdfr if (gc.gc_proc == RPCSEC_GSS_DATA) { 1124181344Sdfr /* 1125181344Sdfr * We might be ready to do a callback to the server to 1126181344Sdfr * see if it wants to accept/reject the connection. 1127181344Sdfr */ 1128181344Sdfr if (!client->cl_done_callback) { 1129181344Sdfr client->cl_done_callback = TRUE; 1130181344Sdfr client->cl_qop = qop; 1131181344Sdfr client->cl_rawcred.qop = _rpc_gss_num_to_qop( 1132181344Sdfr client->cl_rawcred.mechanism, qop); 1133181344Sdfr if (!svc_rpc_gss_callback(client, rqst)) { 1134181344Sdfr result = AUTH_REJECTEDCRED; 1135181344Sdfr break; 1136181344Sdfr } 1137181344Sdfr } 1138181344Sdfr 1139181344Sdfr /* 1140181344Sdfr * If the server has locked this client to a 1141181344Sdfr * particular service+qop pair, enforce that 1142181344Sdfr * restriction now. 1143181344Sdfr */ 1144181344Sdfr if (client->cl_locked) { 1145181344Sdfr if (client->cl_rawcred.service != gc.gc_svc) { 1146181344Sdfr result = AUTH_FAILED; 1147181344Sdfr break; 1148181344Sdfr } else if (client->cl_qop != qop) { 1149181344Sdfr result = AUTH_BADVERF; 1150181344Sdfr break; 1151181344Sdfr } 1152181344Sdfr } 1153181344Sdfr 1154181344Sdfr /* 1155181344Sdfr * If the qop changed, look up the new qop 1156181344Sdfr * name for rawcred. 1157181344Sdfr */ 1158181344Sdfr if (client->cl_qop != qop) { 1159181344Sdfr client->cl_qop = qop; 1160181344Sdfr client->cl_rawcred.qop = _rpc_gss_num_to_qop( 1161181344Sdfr client->cl_rawcred.mechanism, qop); 1162181344Sdfr } 1163181344Sdfr 1164181344Sdfr /* 1165181344Sdfr * Make sure we use the right service value 1166181344Sdfr * for unwrap/wrap. 1167181344Sdfr */ 1168181344Sdfr client->cl_rawcred.service = gc.gc_svc; 1169181344Sdfr 1170181344Sdfr result = AUTH_OK; 1171181344Sdfr } else { 1172181344Sdfr if (rqst->rq_proc != NULLPROC) { 1173181344Sdfr result = AUTH_REJECTEDCRED; 1174181344Sdfr break; 1175181344Sdfr } 1176181344Sdfr 1177181344Sdfr call_stat = svc_sendreply(rqst->rq_xprt, 1178181344Sdfr (xdrproc_t) xdr_void, (caddr_t) NULL); 1179181344Sdfr 1180181344Sdfr if (!call_stat) { 1181181344Sdfr result = AUTH_FAILED; 1182181344Sdfr break; 1183181344Sdfr } 1184181344Sdfr 1185181344Sdfr svc_rpc_gss_destroy_client(client); 1186181344Sdfr 1187181344Sdfr result = RPCSEC_GSS_NODISPATCH; 1188181344Sdfr break; 1189181344Sdfr } 1190181344Sdfr break; 1191181344Sdfr 1192181344Sdfr default: 1193181344Sdfr result = AUTH_BADCRED; 1194181344Sdfr break; 1195181344Sdfr } 1196181344Sdfrout: 1197181344Sdfr xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc); 1198181344Sdfr return (result); 1199181344Sdfr} 1200181344Sdfr 1201181344Sdfrbool_t 1202181344Sdfrsvc_rpc_gss_wrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr) 1203181344Sdfr{ 1204181344Sdfr struct svc_rpc_gss_client *client; 1205181344Sdfr 1206181344Sdfr log_debug("in svc_rpc_gss_wrap()"); 1207181344Sdfr 1208181344Sdfr client = (struct svc_rpc_gss_client *) auth->svc_ah_private; 1209181344Sdfr if (client->cl_state != CLIENT_ESTABLISHED 1210181344Sdfr || client->cl_rawcred.service == rpc_gss_svc_none) { 1211181344Sdfr return xdr_func(xdrs, xdr_ptr); 1212181344Sdfr } 1213181344Sdfr return (xdr_rpc_gss_wrap_data(xdrs, xdr_func, xdr_ptr, 1214181344Sdfr client->cl_ctx, client->cl_qop, 1215181344Sdfr client->cl_rawcred.service, client->cl_seq)); 1216181344Sdfr} 1217181344Sdfr 1218181344Sdfrbool_t 1219181344Sdfrsvc_rpc_gss_unwrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr) 1220181344Sdfr{ 1221181344Sdfr struct svc_rpc_gss_client *client; 1222181344Sdfr 1223181344Sdfr log_debug("in svc_rpc_gss_unwrap()"); 1224181344Sdfr 1225181344Sdfr client = (struct svc_rpc_gss_client *) auth->svc_ah_private; 1226181344Sdfr if (client->cl_state != CLIENT_ESTABLISHED 1227181344Sdfr || client->cl_rawcred.service == rpc_gss_svc_none) { 1228181344Sdfr return xdr_func(xdrs, xdr_ptr); 1229181344Sdfr } 1230181344Sdfr return (xdr_rpc_gss_unwrap_data(xdrs, xdr_func, xdr_ptr, 1231181344Sdfr client->cl_ctx, client->cl_qop, 1232181344Sdfr client->cl_rawcred.service, client->cl_seq)); 1233181344Sdfr} 1234