1 /*-
2 * Copyright (c) 2008 Doug Rabson
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD$
27 */
28 /*-
29 SPDX-License-Identifier: BSD-3-Clause
30
31 svc_rpcsec_gss.c
32
33 Copyright (c) 2000 The Regents of the University of Michigan.
34 All rights reserved.
35
36 Copyright (c) 2000 Dug Song <[email protected]>.
37 All rights reserved, all wrongs reversed.
38
39 Redistribution and use in source and binary forms, with or without
40 modification, are permitted provided that the following conditions
41 are met:
42
43 1. Redistributions of source code must retain the above copyright
44 notice, this list of conditions and the following disclaimer.
45 2. Redistributions in binary form must reproduce the above copyright
46 notice, this list of conditions and the following disclaimer in the
47 documentation and/or other materials provided with the distribution.
48 3. Neither the name of the University nor the names of its
49 contributors may be used to endorse or promote products derived
50 from this software without specific prior written permission.
51
52 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
53 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
54 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
55 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
57 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
58 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
59 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
60 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
61 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
62 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
63
64 $Id: svc_auth_gss.c,v 1.27 2002/01/15 15:43:00 andros Exp $
65 */
66
67 #include <stdio.h>
68 #include <stdlib.h>
69 #include <string.h>
70 #include <pwd.h>
71 #include <grp.h>
72 #include <errno.h>
73 #include <unistd.h>
74 #include <sys/queue.h>
75 #include <rpc/rpc.h>
76 #include <rpc/rpcsec_gss.h>
77 #include "rpcsec_gss_int.h"
78
79 static bool_t svc_rpc_gss_initialised = FALSE;
80
81 static bool_t svc_rpc_gss_wrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t);
82 static bool_t svc_rpc_gss_unwrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t);
83 static enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *);
84
85 static struct svc_auth_ops svc_auth_gss_ops = {
86 svc_rpc_gss_wrap,
87 svc_rpc_gss_unwrap,
88 };
89
90 struct svc_rpc_gss_callback {
91 SLIST_ENTRY(svc_rpc_gss_callback) cb_link;
92 rpc_gss_callback_t cb_callback;
93 };
94 static SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback)
95 svc_rpc_gss_callbacks = SLIST_HEAD_INITIALIZER(svc_rpc_gss_callbacks);
96
97 struct svc_rpc_gss_svc_name {
98 SLIST_ENTRY(svc_rpc_gss_svc_name) sn_link;
99 char *sn_principal;
100 gss_OID sn_mech;
101 u_int sn_req_time;
102 gss_cred_id_t sn_cred;
103 u_int sn_program;
104 u_int sn_version;
105 };
106 static SLIST_HEAD(svc_rpc_gss_svc_name_list, svc_rpc_gss_svc_name)
107 svc_rpc_gss_svc_names = SLIST_HEAD_INITIALIZER(svc_rpc_gss_svc_names);
108
109 enum svc_rpc_gss_client_state {
110 CLIENT_NEW, /* still authenticating */
111 CLIENT_ESTABLISHED, /* context established */
112 CLIENT_STALE /* garbage to collect */
113 };
114
115 #define SVC_RPC_GSS_SEQWINDOW 128
116
117 struct svc_rpc_gss_client {
118 TAILQ_ENTRY(svc_rpc_gss_client) cl_link;
119 TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink;
120 uint32_t cl_id;
121 time_t cl_expiration; /* when to gc */
122 enum svc_rpc_gss_client_state cl_state; /* client state */
123 bool_t cl_locked; /* fixed service+qop */
124 gss_ctx_id_t cl_ctx; /* context id */
125 gss_cred_id_t cl_creds; /* delegated creds */
126 gss_name_t cl_cname; /* client name */
127 struct svc_rpc_gss_svc_name *cl_sname; /* server name used */
128 rpc_gss_rawcred_t cl_rawcred; /* raw credentials */
129 rpc_gss_ucred_t cl_ucred; /* unix-style credentials */
130 bool_t cl_done_callback; /* TRUE after call */
131 void *cl_cookie; /* user cookie from callback */
132 gid_t cl_gid_storage[NGRPS];
133 gss_OID cl_mech; /* mechanism */
134 gss_qop_t cl_qop; /* quality of protection */
135 u_int cl_seq; /* current sequence number */
136 u_int cl_win; /* sequence window size */
137 u_int cl_seqlast; /* sequence window origin */
138 uint32_t cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */
139 gss_buffer_desc cl_verf; /* buffer for verf checksum */
140 };
141 TAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client);
142
143 #define CLIENT_HASH_SIZE 256
144 #define CLIENT_MAX 128
145 static struct svc_rpc_gss_client_list svc_rpc_gss_client_hash[CLIENT_HASH_SIZE];
146 static struct svc_rpc_gss_client_list svc_rpc_gss_clients;
147 static size_t svc_rpc_gss_client_count;
148 static uint32_t svc_rpc_gss_next_clientid = 1;
149
150 #ifdef __GNUC__
151 static void svc_rpc_gss_init(void) __attribute__ ((constructor));
152 #endif
153
154 static void
svc_rpc_gss_init(void)155 svc_rpc_gss_init(void)
156 {
157 int i;
158
159 if (!svc_rpc_gss_initialised) {
160 for (i = 0; i < CLIENT_HASH_SIZE; i++)
161 TAILQ_INIT(&svc_rpc_gss_client_hash[i]);
162 TAILQ_INIT(&svc_rpc_gss_clients);
163 svc_auth_reg(RPCSEC_GSS, svc_rpc_gss);
164 svc_rpc_gss_initialised = TRUE;
165 }
166 }
167
168 bool_t
rpc_gss_set_callback(rpc_gss_callback_t * cb)169 rpc_gss_set_callback(rpc_gss_callback_t *cb)
170 {
171 struct svc_rpc_gss_callback *scb;
172
173 scb = mem_alloc(sizeof(struct svc_rpc_gss_callback));
174 if (!scb) {
175 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
176 return (FALSE);
177 }
178 scb->cb_callback = *cb;
179 SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link);
180
181 return (TRUE);
182 }
183
184 bool_t
rpc_gss_set_svc_name(const char * principal,const char * mechanism,u_int req_time,u_int program,u_int version)185 rpc_gss_set_svc_name(const char *principal, const char *mechanism,
186 u_int req_time, u_int program, u_int version)
187 {
188 OM_uint32 maj_stat, min_stat;
189 struct svc_rpc_gss_svc_name *sname;
190 gss_buffer_desc namebuf;
191 gss_name_t name;
192 gss_OID mech_oid;
193 gss_OID_set_desc oid_set;
194 gss_cred_id_t cred;
195
196 svc_rpc_gss_init();
197
198 if (!rpc_gss_mech_to_oid(mechanism, &mech_oid))
199 return (FALSE);
200 oid_set.count = 1;
201 oid_set.elements = mech_oid;
202
203 namebuf.value = (void *)(intptr_t) principal;
204 namebuf.length = strlen(principal);
205
206 maj_stat = gss_import_name(&min_stat, &namebuf,
207 GSS_C_NT_HOSTBASED_SERVICE, &name);
208 if (maj_stat != GSS_S_COMPLETE)
209 return (FALSE);
210
211 maj_stat = gss_acquire_cred(&min_stat, name,
212 req_time, &oid_set, GSS_C_ACCEPT, &cred, NULL, NULL);
213 if (maj_stat != GSS_S_COMPLETE)
214 return (FALSE);
215
216 gss_release_name(&min_stat, &name);
217
218 sname = malloc(sizeof(struct svc_rpc_gss_svc_name));
219 if (!sname)
220 return (FALSE);
221 sname->sn_principal = strdup(principal);
222 sname->sn_mech = mech_oid;
223 sname->sn_req_time = req_time;
224 sname->sn_cred = cred;
225 sname->sn_program = program;
226 sname->sn_version = version;
227 SLIST_INSERT_HEAD(&svc_rpc_gss_svc_names, sname, sn_link);
228
229 return (TRUE);
230 }
231
232 bool_t
rpc_gss_get_principal_name(rpc_gss_principal_t * principal,const char * mech,const char * name,const char * node,const char * domain)233 rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
234 const char *mech, const char *name, const char *node, const char *domain)
235 {
236 OM_uint32 maj_stat, min_stat;
237 gss_OID mech_oid;
238 size_t namelen;
239 gss_buffer_desc buf;
240 gss_name_t gss_name, gss_mech_name;
241 rpc_gss_principal_t result;
242
243 svc_rpc_gss_init();
244
245 if (!rpc_gss_mech_to_oid(mech, &mech_oid))
246 return (FALSE);
247
248 /*
249 * Construct a gss_buffer containing the full name formatted
250 * as "name/node@domain" where node and domain are optional.
251 */
252 namelen = strlen(name);
253 if (node) {
254 namelen += strlen(node) + 1;
255 }
256 if (domain) {
257 namelen += strlen(domain) + 1;
258 }
259
260 buf.value = mem_alloc(namelen);
261 buf.length = namelen;
262 strcpy((char *) buf.value, name);
263 if (node) {
264 strcat((char *) buf.value, "/");
265 strcat((char *) buf.value, node);
266 }
267 if (domain) {
268 strcat((char *) buf.value, "@");
269 strcat((char *) buf.value, domain);
270 }
271
272 /*
273 * Convert that to a gss_name_t and then convert that to a
274 * mechanism name in the selected mechanism.
275 */
276 maj_stat = gss_import_name(&min_stat, &buf,
277 GSS_C_NT_USER_NAME, &gss_name);
278 mem_free(buf.value, buf.length);
279 if (maj_stat != GSS_S_COMPLETE) {
280 log_status("gss_import_name", mech_oid, maj_stat, min_stat);
281 return (FALSE);
282 }
283 maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid,
284 &gss_mech_name);
285 if (maj_stat != GSS_S_COMPLETE) {
286 log_status("gss_canonicalize_name", mech_oid, maj_stat,
287 min_stat);
288 gss_release_name(&min_stat, &gss_name);
289 return (FALSE);
290 }
291 gss_release_name(&min_stat, &gss_name);
292
293 /*
294 * Export the mechanism name and use that to construct the
295 * rpc_gss_principal_t result.
296 */
297 maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf);
298 if (maj_stat != GSS_S_COMPLETE) {
299 log_status("gss_export_name", mech_oid, maj_stat, min_stat);
300 gss_release_name(&min_stat, &gss_mech_name);
301 return (FALSE);
302 }
303 gss_release_name(&min_stat, &gss_mech_name);
304
305 result = mem_alloc(sizeof(int) + buf.length);
306 if (!result) {
307 gss_release_buffer(&min_stat, &buf);
308 return (FALSE);
309 }
310 result->len = buf.length;
311 memcpy(result->name, buf.value, buf.length);
312 gss_release_buffer(&min_stat, &buf);
313
314 *principal = result;
315 return (TRUE);
316 }
317
318 bool_t
rpc_gss_getcred(struct svc_req * req,rpc_gss_rawcred_t ** rcred,rpc_gss_ucred_t ** ucred,void ** cookie)319 rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
320 rpc_gss_ucred_t **ucred, void **cookie)
321 {
322 struct svc_rpc_gss_client *client;
323
324 if (req->rq_cred.oa_flavor != RPCSEC_GSS)
325 return (FALSE);
326
327 client = req->rq_clntcred;
328 if (rcred)
329 *rcred = &client->cl_rawcred;
330 if (ucred)
331 *ucred = &client->cl_ucred;
332 if (cookie)
333 *cookie = client->cl_cookie;
334 return (TRUE);
335 }
336
337 int
rpc_gss_svc_max_data_length(struct svc_req * req,int max_tp_unit_len)338 rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
339 {
340 struct svc_rpc_gss_client *client = req->rq_clntcred;
341 int want_conf;
342 OM_uint32 max;
343 OM_uint32 maj_stat, min_stat;
344 int result;
345
346 switch (client->cl_rawcred.service) {
347 case rpc_gss_svc_none:
348 return (max_tp_unit_len);
349 break;
350
351 case rpc_gss_svc_default:
352 case rpc_gss_svc_integrity:
353 want_conf = FALSE;
354 break;
355
356 case rpc_gss_svc_privacy:
357 want_conf = TRUE;
358 break;
359
360 default:
361 return (0);
362 }
363
364 maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf,
365 client->cl_qop, max_tp_unit_len, &max);
366
367 if (maj_stat == GSS_S_COMPLETE) {
368 result = (int) max;
369 if (result < 0)
370 result = 0;
371 return (result);
372 } else {
373 log_status("gss_wrap_size_limit", client->cl_mech,
374 maj_stat, min_stat);
375 return (0);
376 }
377 }
378
379 static struct svc_rpc_gss_client *
svc_rpc_gss_find_client(uint32_t clientid)380 svc_rpc_gss_find_client(uint32_t clientid)
381 {
382 struct svc_rpc_gss_client *client;
383 struct svc_rpc_gss_client_list *list;
384
385
386 log_debug("in svc_rpc_gss_find_client(%d)", clientid);
387
388 list = &svc_rpc_gss_client_hash[clientid % CLIENT_HASH_SIZE];
389 TAILQ_FOREACH(client, list, cl_link) {
390 if (client->cl_id == clientid) {
391 /*
392 * Move this client to the front of the LRU
393 * list.
394 */
395 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
396 TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client,
397 cl_alllink);
398 return client;
399 }
400 }
401
402 return (NULL);
403 }
404
405 static struct svc_rpc_gss_client *
svc_rpc_gss_create_client(void)406 svc_rpc_gss_create_client(void)
407 {
408 struct svc_rpc_gss_client *client;
409 struct svc_rpc_gss_client_list *list;
410
411 log_debug("in svc_rpc_gss_create_client()");
412
413 client = mem_alloc(sizeof(struct svc_rpc_gss_client));
414 memset(client, 0, sizeof(struct svc_rpc_gss_client));
415 client->cl_id = svc_rpc_gss_next_clientid++;
416 list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE];
417 TAILQ_INSERT_HEAD(list, client, cl_link);
418 TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink);
419
420 /*
421 * Start the client off with a short expiration time. We will
422 * try to get a saner value from the client creds later.
423 */
424 client->cl_state = CLIENT_NEW;
425 client->cl_locked = FALSE;
426 client->cl_expiration = time(0) + 5*60;
427 svc_rpc_gss_client_count++;
428
429 return (client);
430 }
431
432 static void
svc_rpc_gss_destroy_client(struct svc_rpc_gss_client * client)433 svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
434 {
435 struct svc_rpc_gss_client_list *list;
436 OM_uint32 min_stat;
437
438 log_debug("in svc_rpc_gss_destroy_client()");
439
440 if (client->cl_ctx)
441 gss_delete_sec_context(&min_stat,
442 &client->cl_ctx, GSS_C_NO_BUFFER);
443
444 if (client->cl_cname)
445 gss_release_name(&min_stat, &client->cl_cname);
446
447 if (client->cl_rawcred.client_principal)
448 mem_free(client->cl_rawcred.client_principal,
449 sizeof(*client->cl_rawcred.client_principal)
450 + client->cl_rawcred.client_principal->len);
451
452 if (client->cl_verf.value)
453 gss_release_buffer(&min_stat, &client->cl_verf);
454
455 list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE];
456 TAILQ_REMOVE(list, client, cl_link);
457 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
458 svc_rpc_gss_client_count--;
459 mem_free(client, sizeof(*client));
460 }
461
462 static void
svc_rpc_gss_timeout_clients(void)463 svc_rpc_gss_timeout_clients(void)
464 {
465 struct svc_rpc_gss_client *client;
466 struct svc_rpc_gss_client *nclient;
467 time_t now = time(0);
468
469 log_debug("in svc_rpc_gss_timeout_clients()");
470 /*
471 * First enforce the max client limit. We keep
472 * svc_rpc_gss_clients in LRU order.
473 */
474 while (svc_rpc_gss_client_count > CLIENT_MAX)
475 svc_rpc_gss_destroy_client(TAILQ_LAST(&svc_rpc_gss_clients,
476 svc_rpc_gss_client_list));
477 TAILQ_FOREACH_SAFE(client, &svc_rpc_gss_clients, cl_alllink, nclient) {
478 if (client->cl_state == CLIENT_STALE
479 || now > client->cl_expiration) {
480 log_debug("expiring client %p", client);
481 svc_rpc_gss_destroy_client(client);
482 }
483 }
484 }
485
486 #ifdef DEBUG
487 /*
488 * OID<->string routines. These are uuuuugly.
489 */
490 static OM_uint32
gss_oid_to_str(OM_uint32 * minor_status,gss_OID oid,gss_buffer_t oid_str)491 gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
492 {
493 char numstr[128];
494 unsigned long number;
495 int numshift;
496 size_t string_length;
497 size_t i;
498 unsigned char *cp;
499 char *bp;
500
501 /* Decoded according to krb5/gssapi_krb5.c */
502
503 /* First determine the size of the string */
504 string_length = 0;
505 number = 0;
506 numshift = 0;
507 cp = (unsigned char *) oid->elements;
508 number = (unsigned long) cp[0];
509 sprintf(numstr, "%ld ", number/40);
510 string_length += strlen(numstr);
511 sprintf(numstr, "%ld ", number%40);
512 string_length += strlen(numstr);
513 for (i=1; i<oid->length; i++) {
514 if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
515 number = (number << 7) | (cp[i] & 0x7f);
516 numshift += 7;
517 }
518 else {
519 *minor_status = 0;
520 return(GSS_S_FAILURE);
521 }
522 if ((cp[i] & 0x80) == 0) {
523 sprintf(numstr, "%ld ", number);
524 string_length += strlen(numstr);
525 number = 0;
526 numshift = 0;
527 }
528 }
529 /*
530 * If we get here, we've calculated the length of "n n n ... n ". Add 4
531 * here for "{ " and "}\0".
532 */
533 string_length += 4;
534 if ((bp = (char *) mem_alloc(string_length))) {
535 strcpy(bp, "{ ");
536 number = (unsigned long) cp[0];
537 sprintf(numstr, "%ld ", number/40);
538 strcat(bp, numstr);
539 sprintf(numstr, "%ld ", number%40);
540 strcat(bp, numstr);
541 number = 0;
542 cp = (unsigned char *) oid->elements;
543 for (i=1; i<oid->length; i++) {
544 number = (number << 7) | (cp[i] & 0x7f);
545 if ((cp[i] & 0x80) == 0) {
546 sprintf(numstr, "%ld ", number);
547 strcat(bp, numstr);
548 number = 0;
549 }
550 }
551 strcat(bp, "}");
552 oid_str->length = strlen(bp)+1;
553 oid_str->value = (void *) bp;
554 *minor_status = 0;
555 return(GSS_S_COMPLETE);
556 }
557 *minor_status = 0;
558 return(GSS_S_FAILURE);
559 }
560 #endif
561
562 static void
svc_rpc_gss_build_ucred(struct svc_rpc_gss_client * client,const gss_name_t name)563 svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
564 const gss_name_t name)
565 {
566 OM_uint32 maj_stat, min_stat;
567 char buf[128];
568 uid_t uid;
569 struct passwd pwd, *pw;
570 rpc_gss_ucred_t *uc = &client->cl_ucred;
571
572 uc->uid = 65534;
573 uc->gid = 65534;
574 uc->gidlen = 0;
575 uc->gidlist = client->cl_gid_storage;
576
577 maj_stat = gss_pname_to_uid(&min_stat, name, client->cl_mech, &uid);
578 if (maj_stat != GSS_S_COMPLETE)
579 return;
580
581 getpwuid_r(uid, &pwd, buf, sizeof(buf), &pw);
582 if (pw) {
583 int len = NGRPS;
584 uc->uid = pw->pw_uid;
585 uc->gid = pw->pw_gid;
586 uc->gidlist = client->cl_gid_storage;
587 getgrouplist(pw->pw_name, pw->pw_gid, uc->gidlist, &len);
588 uc->gidlen = len;
589 }
590 }
591
592 static bool_t
svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client * client,struct svc_req * rqst,struct rpc_gss_init_res * gr,struct rpc_gss_cred * gc)593 svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
594 struct svc_req *rqst,
595 struct rpc_gss_init_res *gr,
596 struct rpc_gss_cred *gc)
597 {
598 gss_buffer_desc recv_tok;
599 gss_OID mech;
600 OM_uint32 maj_stat = 0, min_stat = 0, ret_flags;
601 OM_uint32 cred_lifetime;
602 struct svc_rpc_gss_svc_name *sname;
603
604 log_debug("in svc_rpc_gss_accept_context()");
605
606 /* Deserialize arguments. */
607 memset(&recv_tok, 0, sizeof(recv_tok));
608
609 if (!svc_getargs(rqst->rq_xprt,
610 (xdrproc_t) xdr_gss_buffer_desc,
611 (caddr_t) &recv_tok)) {
612 client->cl_state = CLIENT_STALE;
613 return (FALSE);
614 }
615
616 /*
617 * First time round, try all the server names we have until
618 * one matches. Afterwards, stick with that one.
619 */
620 if (!client->cl_sname) {
621 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
622 if (sname->sn_program == rqst->rq_prog
623 && sname->sn_version == rqst->rq_vers) {
624 gr->gr_major = gss_accept_sec_context(
625 &gr->gr_minor,
626 &client->cl_ctx,
627 sname->sn_cred,
628 &recv_tok,
629 GSS_C_NO_CHANNEL_BINDINGS,
630 &client->cl_cname,
631 &mech,
632 &gr->gr_token,
633 &ret_flags,
634 &cred_lifetime,
635 &client->cl_creds);
636 client->cl_sname = sname;
637 break;
638 }
639 }
640 if (!sname) {
641 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
642 (char *) &recv_tok);
643 return (FALSE);
644 }
645 } else {
646 gr->gr_major = gss_accept_sec_context(
647 &gr->gr_minor,
648 &client->cl_ctx,
649 client->cl_sname->sn_cred,
650 &recv_tok,
651 GSS_C_NO_CHANNEL_BINDINGS,
652 &client->cl_cname,
653 &mech,
654 &gr->gr_token,
655 &ret_flags,
656 &cred_lifetime,
657 NULL);
658 }
659
660 xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
661
662 /*
663 * If we get an error from gss_accept_sec_context, send the
664 * reply anyway so that the client gets a chance to see what
665 * is wrong.
666 */
667 if (gr->gr_major != GSS_S_COMPLETE &&
668 gr->gr_major != GSS_S_CONTINUE_NEEDED) {
669 log_status("accept_sec_context", client->cl_mech,
670 gr->gr_major, gr->gr_minor);
671 client->cl_state = CLIENT_STALE;
672 return (TRUE);
673 }
674
675 gr->gr_handle.value = &client->cl_id;
676 gr->gr_handle.length = sizeof(client->cl_id);
677 gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
678
679 /* Save client info. */
680 client->cl_mech = mech;
681 client->cl_qop = GSS_C_QOP_DEFAULT;
682 client->cl_seq = gc->gc_seq;
683 client->cl_win = gr->gr_win;
684 client->cl_done_callback = FALSE;
685
686 if (gr->gr_major == GSS_S_COMPLETE) {
687 gss_buffer_desc export_name;
688
689 /*
690 * Change client expiration time to be near when the
691 * client creds expire (or 24 hours if we can't figure
692 * that out).
693 */
694 if (cred_lifetime == GSS_C_INDEFINITE)
695 cred_lifetime = time(0) + 24*60*60;
696
697 client->cl_expiration = time(0) + cred_lifetime;
698
699 /*
700 * Fill in cred details in the rawcred structure.
701 */
702 client->cl_rawcred.version = RPCSEC_GSS_VERSION;
703 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
704 maj_stat = gss_export_name(&min_stat, client->cl_cname,
705 &export_name);
706 if (maj_stat != GSS_S_COMPLETE) {
707 log_status("gss_export_name", client->cl_mech,
708 maj_stat, min_stat);
709 return (FALSE);
710 }
711 client->cl_rawcred.client_principal =
712 mem_alloc(sizeof(*client->cl_rawcred.client_principal)
713 + export_name.length);
714 client->cl_rawcred.client_principal->len = export_name.length;
715 memcpy(client->cl_rawcred.client_principal->name,
716 export_name.value, export_name.length);
717 gss_release_buffer(&min_stat, &export_name);
718 client->cl_rawcred.svc_principal =
719 client->cl_sname->sn_principal;
720 client->cl_rawcred.service = gc->gc_svc;
721
722 /*
723 * Use gss_pname_to_uid to map to unix creds. For
724 * kerberos5, this uses krb5_aname_to_localname.
725 */
726 svc_rpc_gss_build_ucred(client, client->cl_cname);
727 gss_release_name(&min_stat, &client->cl_cname);
728
729 #ifdef DEBUG
730 {
731 gss_buffer_desc mechname;
732
733 gss_oid_to_str(&min_stat, mech, &mechname);
734
735 log_debug("accepted context for %s with "
736 "<mech %.*s, qop %d, svc %d>",
737 client->cl_rawcred.client_principal->name,
738 mechname.length, (char *)mechname.value,
739 client->cl_qop, client->rawcred.service);
740
741 gss_release_buffer(&min_stat, &mechname);
742 }
743 #endif /* DEBUG */
744 }
745 return (TRUE);
746 }
747
748 static bool_t
svc_rpc_gss_validate(struct svc_rpc_gss_client * client,struct rpc_msg * msg,gss_qop_t * qop)749 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
750 gss_qop_t *qop)
751 {
752 struct opaque_auth *oa;
753 gss_buffer_desc rpcbuf, checksum;
754 OM_uint32 maj_stat, min_stat;
755 gss_qop_t qop_state;
756 int32_t rpchdr[128 / sizeof(int32_t)];
757 int32_t *buf;
758
759 log_debug("in svc_rpc_gss_validate()");
760
761 memset(rpchdr, 0, sizeof(rpchdr));
762
763 /* Reconstruct RPC header for signing (from xdr_callmsg). */
764 buf = rpchdr;
765 IXDR_PUT_LONG(buf, msg->rm_xid);
766 IXDR_PUT_ENUM(buf, msg->rm_direction);
767 IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
768 IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
769 IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
770 IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
771 oa = &msg->rm_call.cb_cred;
772 IXDR_PUT_ENUM(buf, oa->oa_flavor);
773 IXDR_PUT_LONG(buf, oa->oa_length);
774 if (oa->oa_length) {
775 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
776 buf += RNDUP(oa->oa_length) / sizeof(int32_t);
777 }
778 rpcbuf.value = rpchdr;
779 rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
780
781 checksum.value = msg->rm_call.cb_verf.oa_base;
782 checksum.length = msg->rm_call.cb_verf.oa_length;
783
784 maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
785 &qop_state);
786
787 if (maj_stat != GSS_S_COMPLETE) {
788 log_status("gss_verify_mic", client->cl_mech,
789 maj_stat, min_stat);
790 client->cl_state = CLIENT_STALE;
791 return (FALSE);
792 }
793 *qop = qop_state;
794 return (TRUE);
795 }
796
797 static bool_t
svc_rpc_gss_nextverf(struct svc_rpc_gss_client * client,struct svc_req * rqst,u_int seq)798 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
799 struct svc_req *rqst, u_int seq)
800 {
801 gss_buffer_desc signbuf;
802 OM_uint32 maj_stat, min_stat;
803 uint32_t nseq;
804
805 log_debug("in svc_rpc_gss_nextverf()");
806
807 nseq = htonl(seq);
808 signbuf.value = &nseq;
809 signbuf.length = sizeof(nseq);
810
811 if (client->cl_verf.value)
812 gss_release_buffer(&min_stat, &client->cl_verf);
813
814 maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
815 &signbuf, &client->cl_verf);
816
817 if (maj_stat != GSS_S_COMPLETE) {
818 log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
819 client->cl_state = CLIENT_STALE;
820 return (FALSE);
821 }
822 rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;
823 rqst->rq_xprt->xp_verf.oa_base = (caddr_t)client->cl_verf.value;
824 rqst->rq_xprt->xp_verf.oa_length = (u_int)client->cl_verf.length;
825
826 return (TRUE);
827 }
828
829 static bool_t
svc_rpc_gss_callback(struct svc_rpc_gss_client * client,struct svc_req * rqst)830 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
831 {
832 struct svc_rpc_gss_callback *scb;
833 rpc_gss_lock_t lock;
834 void *cookie;
835 bool_t cb_res;
836 bool_t result;
837
838 /*
839 * See if we have a callback for this guy.
840 */
841 result = TRUE;
842 SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
843 if (scb->cb_callback.program == rqst->rq_prog
844 && scb->cb_callback.version == rqst->rq_vers) {
845 /*
846 * This one matches. Call the callback and see
847 * if it wants to veto or something.
848 */
849 lock.locked = FALSE;
850 lock.raw_cred = &client->cl_rawcred;
851 cb_res = scb->cb_callback.callback(rqst,
852 client->cl_creds,
853 client->cl_ctx,
854 &lock,
855 &cookie);
856
857 if (!cb_res) {
858 client->cl_state = CLIENT_STALE;
859 result = FALSE;
860 break;
861 }
862
863 /*
864 * The callback accepted the connection - it
865 * is responsible for freeing client->cl_creds
866 * now.
867 */
868 client->cl_creds = GSS_C_NO_CREDENTIAL;
869 client->cl_locked = lock.locked;
870 client->cl_cookie = cookie;
871 return (TRUE);
872 }
873 }
874
875 /*
876 * Either no callback exists for this program/version or one
877 * of the callbacks rejected the connection. We just need to
878 * clean up the delegated client creds, if any.
879 */
880 if (client->cl_creds) {
881 OM_uint32 min_ver;
882 gss_release_cred(&min_ver, &client->cl_creds);
883 }
884 return (result);
885 }
886
887 static bool_t
svc_rpc_gss_check_replay(struct svc_rpc_gss_client * client,uint32_t seq)888 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
889 {
890 u_int32_t offset;
891 int word, bit;
892
893 if (seq <= client->cl_seqlast) {
894 /*
895 * The request sequence number is less than
896 * the largest we have seen so far. If it is
897 * outside the window or if we have seen a
898 * request with this sequence before, silently
899 * discard it.
900 */
901 offset = client->cl_seqlast - seq;
902 if (offset >= SVC_RPC_GSS_SEQWINDOW)
903 return (FALSE);
904 word = offset / 32;
905 bit = offset % 32;
906 if (client->cl_seqmask[word] & (1 << bit))
907 return (FALSE);
908 }
909
910 return (TRUE);
911 }
912
913 static void
svc_rpc_gss_update_seq(struct svc_rpc_gss_client * client,uint32_t seq)914 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
915 {
916 int offset, i, word, bit;
917 uint32_t carry, newcarry;
918 uint32_t* maskp;
919
920 maskp = client->cl_seqmask;
921 if (seq > client->cl_seqlast) {
922 /*
923 * This request has a sequence number greater
924 * than any we have seen so far. Advance the
925 * seq window and set bit zero of the window
926 * (which corresponds to the new sequence
927 * number)
928 */
929 offset = seq - client->cl_seqlast;
930 while (offset >= 32) {
931 for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
932 i > 0; i--) {
933 maskp[i] = maskp[i-1];
934 }
935 maskp[0] = 0;
936 offset -= 32;
937 }
938 if (offset > 0) {
939 carry = 0;
940 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
941 newcarry = maskp[i] >> (32 - offset);
942 maskp[i] = (maskp[i] << offset) | carry;
943 carry = newcarry;
944 }
945 }
946 maskp[0] |= 1;
947 client->cl_seqlast = seq;
948 } else {
949 offset = client->cl_seqlast - seq;
950 word = offset / 32;
951 bit = offset % 32;
952 maskp[word] |= (1 << bit);
953 }
954
955 }
956
957 enum auth_stat
svc_rpc_gss(struct svc_req * rqst,struct rpc_msg * msg)958 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
959
960 {
961 OM_uint32 min_stat;
962 XDR xdrs;
963 struct svc_rpc_gss_client *client;
964 struct rpc_gss_cred gc;
965 struct rpc_gss_init_res gr;
966 gss_qop_t qop;
967 int call_stat;
968 enum auth_stat result;
969
970 log_debug("in svc_rpc_gss()");
971
972 /* Garbage collect old clients. */
973 svc_rpc_gss_timeout_clients();
974
975 /* Initialize reply. */
976 rqst->rq_xprt->xp_verf = _null_auth;
977
978 /* Deserialize client credentials. */
979 if (rqst->rq_cred.oa_length <= 0)
980 return (AUTH_BADCRED);
981
982 memset(&gc, 0, sizeof(gc));
983
984 xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
985 rqst->rq_cred.oa_length, XDR_DECODE);
986
987 if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
988 XDR_DESTROY(&xdrs);
989 return (AUTH_BADCRED);
990 }
991 XDR_DESTROY(&xdrs);
992
993 /* Check version. */
994 if (gc.gc_version != RPCSEC_GSS_VERSION) {
995 result = AUTH_BADCRED;
996 goto out;
997 }
998
999 /* Check the proc and find the client (or create it) */
1000 if (gc.gc_proc == RPCSEC_GSS_INIT) {
1001 if (gc.gc_handle.length != 0) {
1002 result = AUTH_BADCRED;
1003 goto out;
1004 }
1005 client = svc_rpc_gss_create_client();
1006 } else {
1007 if (gc.gc_handle.length != sizeof(uint32_t)) {
1008 result = AUTH_BADCRED;
1009 goto out;
1010 }
1011 uint32_t *p = gc.gc_handle.value;
1012 client = svc_rpc_gss_find_client(*p);
1013 if (!client) {
1014 /*
1015 * Can't find the client - we may have
1016 * destroyed it - tell the other side to
1017 * re-authenticate.
1018 */
1019 result = RPCSEC_GSS_CREDPROBLEM;
1020 goto out;
1021 }
1022 }
1023 rqst->rq_clntcred = client;
1024
1025 /*
1026 * The service and sequence number must be ignored for
1027 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1028 */
1029 if (gc.gc_proc != RPCSEC_GSS_INIT
1030 && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1031 /*
1032 * Check for sequence number overflow.
1033 */
1034 if (gc.gc_seq >= MAXSEQ) {
1035 result = RPCSEC_GSS_CTXPROBLEM;
1036 goto out;
1037 }
1038 client->cl_seq = gc.gc_seq;
1039
1040 /*
1041 * Check for valid service.
1042 */
1043 if (gc.gc_svc != rpc_gss_svc_none &&
1044 gc.gc_svc != rpc_gss_svc_integrity &&
1045 gc.gc_svc != rpc_gss_svc_privacy) {
1046 result = AUTH_BADCRED;
1047 goto out;
1048 }
1049 }
1050
1051 /* Handle RPCSEC_GSS control procedure. */
1052 switch (gc.gc_proc) {
1053
1054 case RPCSEC_GSS_INIT:
1055 case RPCSEC_GSS_CONTINUE_INIT:
1056 if (rqst->rq_proc != NULLPROC) {
1057 result = AUTH_REJECTEDCRED;
1058 break;
1059 }
1060
1061 memset(&gr, 0, sizeof(gr));
1062 if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1063 result = AUTH_REJECTEDCRED;
1064 break;
1065 }
1066
1067 if (gr.gr_major == GSS_S_COMPLETE) {
1068 if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1069 result = AUTH_REJECTEDCRED;
1070 break;
1071 }
1072 } else {
1073 rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL;
1074 rqst->rq_xprt->xp_verf.oa_length = 0;
1075 }
1076
1077 call_stat = svc_sendreply(rqst->rq_xprt,
1078 (xdrproc_t) xdr_rpc_gss_init_res,
1079 (caddr_t) &gr);
1080
1081 gss_release_buffer(&min_stat, &gr.gr_token);
1082
1083 if (!call_stat) {
1084 result = AUTH_FAILED;
1085 break;
1086 }
1087
1088 if (gr.gr_major == GSS_S_COMPLETE)
1089 client->cl_state = CLIENT_ESTABLISHED;
1090
1091 result = RPCSEC_GSS_NODISPATCH;
1092 break;
1093
1094 case RPCSEC_GSS_DATA:
1095 case RPCSEC_GSS_DESTROY:
1096 if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1097 result = RPCSEC_GSS_NODISPATCH;
1098 break;
1099 }
1100
1101 if (!svc_rpc_gss_validate(client, msg, &qop)) {
1102 result = RPCSEC_GSS_CREDPROBLEM;
1103 break;
1104 }
1105
1106 if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1107 result = RPCSEC_GSS_CTXPROBLEM;
1108 break;
1109 }
1110
1111 svc_rpc_gss_update_seq(client, gc.gc_seq);
1112
1113 /*
1114 * Change the SVCAUTH ops on the transport to point at
1115 * our own code so that we can unwrap the arguments
1116 * and wrap the result. The caller will re-set this on
1117 * every request to point to a set of null wrap/unwrap
1118 * methods.
1119 */
1120 SVC_AUTH(rqst->rq_xprt).svc_ah_ops = &svc_auth_gss_ops;
1121 SVC_AUTH(rqst->rq_xprt).svc_ah_private = client;
1122
1123 if (gc.gc_proc == RPCSEC_GSS_DATA) {
1124 /*
1125 * We might be ready to do a callback to the server to
1126 * see if it wants to accept/reject the connection.
1127 */
1128 if (!client->cl_done_callback) {
1129 client->cl_done_callback = TRUE;
1130 client->cl_qop = qop;
1131 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1132 client->cl_rawcred.mechanism, qop);
1133 if (!svc_rpc_gss_callback(client, rqst)) {
1134 result = AUTH_REJECTEDCRED;
1135 break;
1136 }
1137 }
1138
1139 /*
1140 * If the server has locked this client to a
1141 * particular service+qop pair, enforce that
1142 * restriction now.
1143 */
1144 if (client->cl_locked) {
1145 if (client->cl_rawcred.service != gc.gc_svc) {
1146 result = AUTH_FAILED;
1147 break;
1148 } else if (client->cl_qop != qop) {
1149 result = AUTH_BADVERF;
1150 break;
1151 }
1152 }
1153
1154 /*
1155 * If the qop changed, look up the new qop
1156 * name for rawcred.
1157 */
1158 if (client->cl_qop != qop) {
1159 client->cl_qop = qop;
1160 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1161 client->cl_rawcred.mechanism, qop);
1162 }
1163
1164 /*
1165 * Make sure we use the right service value
1166 * for unwrap/wrap.
1167 */
1168 client->cl_rawcred.service = gc.gc_svc;
1169
1170 result = AUTH_OK;
1171 } else {
1172 if (rqst->rq_proc != NULLPROC) {
1173 result = AUTH_REJECTEDCRED;
1174 break;
1175 }
1176
1177 call_stat = svc_sendreply(rqst->rq_xprt,
1178 (xdrproc_t) xdr_void, (caddr_t) NULL);
1179
1180 if (!call_stat) {
1181 result = AUTH_FAILED;
1182 break;
1183 }
1184
1185 svc_rpc_gss_destroy_client(client);
1186
1187 result = RPCSEC_GSS_NODISPATCH;
1188 break;
1189 }
1190 break;
1191
1192 default:
1193 result = AUTH_BADCRED;
1194 break;
1195 }
1196 out:
1197 xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1198 return (result);
1199 }
1200
1201 bool_t
svc_rpc_gss_wrap(SVCAUTH * auth,XDR * xdrs,xdrproc_t xdr_func,caddr_t xdr_ptr)1202 svc_rpc_gss_wrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
1203 {
1204 struct svc_rpc_gss_client *client;
1205
1206 log_debug("in svc_rpc_gss_wrap()");
1207
1208 client = (struct svc_rpc_gss_client *) auth->svc_ah_private;
1209 if (client->cl_state != CLIENT_ESTABLISHED
1210 || client->cl_rawcred.service == rpc_gss_svc_none) {
1211 return xdr_func(xdrs, xdr_ptr);
1212 }
1213 return (xdr_rpc_gss_wrap_data(xdrs, xdr_func, xdr_ptr,
1214 client->cl_ctx, client->cl_qop,
1215 client->cl_rawcred.service, client->cl_seq));
1216 }
1217
1218 bool_t
svc_rpc_gss_unwrap(SVCAUTH * auth,XDR * xdrs,xdrproc_t xdr_func,caddr_t xdr_ptr)1219 svc_rpc_gss_unwrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
1220 {
1221 struct svc_rpc_gss_client *client;
1222
1223 log_debug("in svc_rpc_gss_unwrap()");
1224
1225 client = (struct svc_rpc_gss_client *) auth->svc_ah_private;
1226 if (client->cl_state != CLIENT_ESTABLISHED
1227 || client->cl_rawcred.service == rpc_gss_svc_none) {
1228 return xdr_func(xdrs, xdr_ptr);
1229 }
1230 return (xdr_rpc_gss_unwrap_data(xdrs, xdr_func, xdr_ptr,
1231 client->cl_ctx, client->cl_qop,
1232 client->cl_rawcred.service, client->cl_seq));
1233 }
1234