1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
29 *
30 * $Header:
31 * /afs/gza.com/product/secure/rel-eng/src/1.1/rpc/RCS/auth_gssapi.c,v
32 * 1.14 1995/03/22 22:07:55 jik Exp $
33 */
34
35 #pragma ident "%Z%%M% %I% %E% SMI"
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <strings.h>
40 #include <errno.h>
41 #include <pthread.h>
42 #include <thread.h>
43 #include <syslog.h>
44 #include <gssapi/gssapi.h>
45 #include <rpc/rpc.h>
46 #include <rpc/rpcsec_defs.h>
47
48 static void rpc_gss_nextverf();
49 static bool_t rpc_gss_marshall();
50 static bool_t rpc_gss_validate();
51 static bool_t rpc_gss_refresh();
52 static void rpc_gss_destroy();
53 static void rpc_gss_destroy_pvt();
54 static bool_t rpc_gss_seccreate_pvt();
55 static bool_t validate_seqwin();
56
57 /*
58 * Globals that should have header files but don't.
59 */
60 extern bool_t xdr_opaque_auth(XDR *, struct opaque_auth *);
61
62
63 static struct auth_ops rpc_gss_ops = {
64 rpc_gss_nextverf,
65 rpc_gss_marshall,
66 rpc_gss_validate,
67 rpc_gss_refresh,
68 rpc_gss_destroy
69 };
70
71 /*
72 * Private data for RPCSEC_GSS.
73 */
74 typedef struct _rpc_gss_data {
75 bool_t established; /* TRUE when established */
76 CLIENT *clnt; /* associated client handle */
77 uint_t version; /* RPCSEC version */
78 gss_ctx_id_t context; /* GSS context id */
79 gss_buffer_desc ctx_handle; /* RPCSEC context handle */
80 uint_t seq_num; /* last sequence number rcvd */
81 gss_cred_id_t my_cred; /* GSS credentials */
82 OM_uint32 qop; /* requested QOP */
83 rpc_gss_service_t service; /* requested service */
84 uint_t gss_proc; /* GSS control procedure */
85 gss_name_t target_name; /* target server */
86 int req_flags; /* GSS request bits */
87 gss_OID mech_type; /* GSS mechanism */
88 OM_uint32 time_req; /* requested cred lifetime */
89 bool_t invalid; /* can't use this any more */
90 OM_uint32 seq_window; /* server sequence window */
91 struct opaque_auth *verifier; /* rpc reply verifier saved for */
92 /* validating the sequence window */
93 gss_channel_bindings_t icb;
94 } rpc_gss_data;
95 #define AUTH_PRIVATE(auth) ((rpc_gss_data *)auth->ah_private)
96
97 /*
98 * Create a context.
99 */
100 AUTH *
__rpc_gss_seccreate(clnt,server_name,mech,service,qop,options_req,options_ret)101 __rpc_gss_seccreate(clnt, server_name, mech, service, qop, options_req,
102 options_ret)
103 CLIENT *clnt; /* associated client handle */
104 char *server_name; /* target server */
105 char *mech; /* security mechanism */
106 rpc_gss_service_t service; /* security service */
107 char *qop; /* requested QOP */
108 rpc_gss_options_req_t *options_req; /* requested options */
109 rpc_gss_options_ret_t *options_ret; /* returned options */
110 {
111 OM_uint32 gssstat;
112 OM_uint32 minor_stat;
113 gss_name_t target_name;
114 gss_OID mech_type;
115 OM_uint32 ret_flags;
116 OM_uint32 time_rec;
117 gss_buffer_desc input_name;
118 AUTH *auth = NULL;
119 rpc_gss_data *ap = NULL;
120 OM_uint32 qop_num;
121
122 /*
123 * convert ascii strings to GSS values
124 */
125 if (!__rpc_gss_qop_to_num(qop, mech, &qop_num)) {
126 return (NULL);
127 }
128
129 if (!__rpc_gss_mech_to_oid(mech, &mech_type)) {
130 return (NULL);
131 }
132
133 /*
134 * convert name to GSS internal type
135 */
136 input_name.value = server_name;
137 input_name.length = strlen(server_name);
138 gssstat = gss_import_name(&minor_stat, &input_name,
139 (gss_OID)GSS_C_NT_HOSTBASED_SERVICE,
140 &target_name);
141 if (gssstat != GSS_S_COMPLETE) {
142 rpc_gss_err.rpc_gss_error = RPC_GSS_ER_SYSTEMERROR;
143 rpc_gss_err.system_error = ENOMEM;
144 return (NULL);
145 }
146
147 /*
148 * Create AUTH handle. Save the necessary interface information
149 * so that the client can refresh the handle later if needed.
150 */
151 if ((auth = (AUTH *) malloc(sizeof (*auth))) != NULL)
152 ap = (rpc_gss_data *) malloc(sizeof (*ap));
153 if (auth == NULL || ap == NULL) {
154 rpc_gss_err.rpc_gss_error = RPC_GSS_ER_SYSTEMERROR;
155 rpc_gss_err.system_error = ENOMEM;
156 if (auth != NULL)
157 free((char *)auth);
158 (void) gss_release_name(&minor_stat, &target_name);
159 return (NULL);
160 }
161
162 memset((char *)ap, 0, sizeof (*ap));
163 ap->clnt = clnt;
164 ap->version = RPCSEC_GSS_VERSION;
165 if (options_req != NULL) {
166 ap->my_cred = options_req->my_cred;
167 ap->req_flags = options_req->req_flags;
168 ap->time_req = options_req->time_req;
169 ap->icb = options_req->input_channel_bindings;
170 } else {
171 ap->my_cred = GSS_C_NO_CREDENTIAL;
172 ap->req_flags = GSS_C_MUTUAL_FLAG;
173 ap->time_req = 0;
174 ap->icb = NULL;
175 }
176 if ((ap->service = service) == rpc_gss_svc_default)
177 ap->service = rpc_gss_svc_integrity;
178 ap->qop = qop_num;
179 ap->target_name = target_name;
180 ap->mech_type = mech_type;
181
182 /*
183 * Now invoke the real interface that sets up the context from
184 * the information stashed away in the private data.
185 */
186 if (!rpc_gss_seccreate_pvt(&gssstat, &minor_stat, auth, ap,
187 &mech_type, &ret_flags, &time_rec)) {
188 if (ap->target_name)
189 (void) gss_release_name(&minor_stat, &ap->target_name);
190 free((char *)ap);
191 free((char *)auth);
192 return (NULL);
193 }
194
195 /*
196 * Make sure that the requested service is supported. In all
197 * cases, integrity service must be available.
198 */
199 if ((ap->service == rpc_gss_svc_privacy &&
200 !(ret_flags & GSS_C_CONF_FLAG)) ||
201 !(ret_flags & GSS_C_INTEG_FLAG)) {
202 rpc_gss_destroy(auth);
203 rpc_gss_err.rpc_gss_error = RPC_GSS_ER_SYSTEMERROR;
204 rpc_gss_err.system_error = EPROTONOSUPPORT;
205 return (NULL);
206 }
207
208 /*
209 * return option values if requested
210 */
211 if (options_ret != NULL) {
212 char *s;
213
214 options_ret->major_status = gssstat;
215 options_ret->minor_status = minor_stat;
216 options_ret->rpcsec_version = ap->version;
217 options_ret->ret_flags = ret_flags;
218 options_ret->time_ret = time_rec;
219 options_ret->gss_context = ap->context;
220 if ((s = __rpc_gss_oid_to_mech(mech_type)) != NULL)
221 strcpy(options_ret->actual_mechanism, s);
222 else
223 options_ret->actual_mechanism[0] = '\0';
224 }
225 return (auth);
226 }
227
228 /*
229 * Private interface to create a context. This is the interface
230 * that's invoked when the context has to be refreshed.
231 */
232 static bool_t
rpc_gss_seccreate_pvt(gssstat,minor_stat,auth,ap,actual_mech_type,ret_flags,time_rec)233 rpc_gss_seccreate_pvt(gssstat, minor_stat, auth, ap, actual_mech_type,
234 ret_flags, time_rec)
235 OM_uint32 *gssstat;
236 OM_uint32 *minor_stat;
237 AUTH *auth;
238 rpc_gss_data *ap;
239 gss_OID *actual_mech_type;
240 OM_uint32 *ret_flags;
241 OM_uint32 *time_rec;
242 {
243 CLIENT *clnt = ap->clnt;
244 AUTH *save_auth;
245 enum clnt_stat callstat;
246 rpc_gss_init_arg call_arg;
247 rpc_gss_init_res call_res;
248 gss_buffer_desc *input_token_p, input_token;
249 bool_t free_results = FALSE;
250
251 /*
252 * initialize error
253 */
254 memset(&rpc_createerr, 0, sizeof (rpc_createerr));
255
256 /*
257 * (re)initialize AUTH handle and private data.
258 */
259 memset((char *)auth, 0, sizeof (*auth));
260 auth->ah_ops = &rpc_gss_ops;
261 auth->ah_private = (caddr_t)ap;
262 auth->ah_cred.oa_flavor = RPCSEC_GSS;
263
264 ap->established = FALSE;
265 ap->ctx_handle.length = 0;
266 ap->ctx_handle.value = NULL;
267 ap->context = GSS_C_NO_CONTEXT;
268 ap->seq_num = 0;
269 ap->gss_proc = RPCSEC_GSS_INIT;
270
271 /*
272 * should not change clnt->cl_auth at this time, so save
273 * old handle
274 */
275 save_auth = clnt->cl_auth;
276 clnt->cl_auth = auth;
277
278 /*
279 * set state for starting context setup
280 */
281 input_token_p = GSS_C_NO_BUFFER;
282
283 next_token:
284 *gssstat = gss_init_sec_context(minor_stat,
285 ap->my_cred,
286 &ap->context,
287 ap->target_name,
288 ap->mech_type,
289 ap->req_flags,
290 ap->time_req,
291 NULL,
292 input_token_p,
293 actual_mech_type,
294 &call_arg,
295 ret_flags,
296 time_rec);
297
298 if (input_token_p != GSS_C_NO_BUFFER) {
299 OM_uint32 minor_stat2;
300
301 (void) gss_release_buffer(&minor_stat2, input_token_p);
302 input_token_p = GSS_C_NO_BUFFER;
303 }
304
305 if (*gssstat != GSS_S_COMPLETE && *gssstat != GSS_S_CONTINUE_NEEDED) {
306
307 goto cleanup;
308 }
309
310 /*
311 * if we got a token, pass it on
312 */
313 if (call_arg.length != 0) {
314 struct timeval timeout = {30, 0};
315
316 memset((char *)&call_res, 0, sizeof (call_res));
317 callstat = clnt_call(clnt, NULLPROC,
318 __xdr_rpc_gss_init_arg, (caddr_t)&call_arg,
319 __xdr_rpc_gss_init_res, (caddr_t)&call_res,
320 timeout);
321 (void) gss_release_buffer(minor_stat, &call_arg);
322
323 if (callstat != RPC_SUCCESS) {
324 goto cleanup;
325 }
326 /*
327 * we have results - note that these need to be freed
328 */
329 free_results = TRUE;
330
331 if (call_res.gss_major != GSS_S_COMPLETE &&
332 call_res.gss_major != GSS_S_CONTINUE_NEEDED)
333 goto cleanup;
334
335 ap->gss_proc = RPCSEC_GSS_CONTINUE_INIT;
336
337 /*
338 * check for ctx_handle
339 */
340 if (ap->ctx_handle.length == 0) {
341 if (call_res.ctx_handle.length == 0)
342 goto cleanup;
343 GSS_DUP_BUFFER(ap->ctx_handle,
344 call_res.ctx_handle);
345 } else if (!GSS_BUFFERS_EQUAL(ap->ctx_handle,
346 call_res.ctx_handle))
347 goto cleanup;
348
349 /*
350 * check for token
351 */
352 if (call_res.token.length != 0) {
353 if (*gssstat == GSS_S_COMPLETE)
354 goto cleanup;
355 GSS_DUP_BUFFER(input_token, call_res.token);
356 input_token_p = &input_token;
357
358 } else if (*gssstat != GSS_S_COMPLETE)
359 goto cleanup;
360
361 /* save the sequence window value; validate later */
362 ap->seq_window = call_res.seq_window;
363 xdr_free(__xdr_rpc_gss_init_res, (caddr_t)&call_res);
364 free_results = FALSE;
365 }
366
367 /*
368 * results were okay.. continue if necessary
369 */
370 if (*gssstat == GSS_S_CONTINUE_NEEDED)
371 goto next_token;
372
373 /*
374 * Validate the sequence window - RFC 2203 section 5.2.3.1
375 */
376 if (!validate_seqwin(ap)) {
377 goto cleanup;
378 }
379
380 /*
381 * Done! Security context creation is successful.
382 * Ready for exchanging data.
383 */
384 ap->established = TRUE;
385 ap->seq_num = 1;
386 ap->gss_proc = RPCSEC_GSS_DATA;
387 ap->invalid = FALSE;
388
389 clnt->cl_auth = save_auth; /* restore cl_auth */
390 return (TRUE);
391
392 cleanup:
393 if (ap->context != GSS_C_NO_CONTEXT)
394 rpc_gss_destroy_pvt(auth);
395 if (free_results)
396 xdr_free(__xdr_rpc_gss_init_res, (caddr_t)&call_res);
397 clnt->cl_auth = save_auth; /* restore cl_auth */
398
399 /*
400 * if (rpc_createerr.cf_stat == 0)
401 * rpc_createerr.cf_stat = RPC_AUTHERROR;
402 */
403 if (rpc_createerr.cf_stat == 0) {
404 rpc_gss_err.rpc_gss_error = RPC_GSS_ER_SYSTEMERROR;
405 rpc_gss_err.system_error = RPC_AUTHERROR;
406 }
407
408 return (FALSE);
409 }
410
411 /*
412 * Set service defaults.
413 */
414 bool_t
__rpc_gss_set_defaults(auth,service,qop)415 __rpc_gss_set_defaults(auth, service, qop)
416 AUTH *auth;
417 rpc_gss_service_t service;
418 char *qop;
419 {
420 /*LINTED*/
421 rpc_gss_data *ap = AUTH_PRIVATE(auth);
422 char *mech;
423 OM_uint32 qop_num;
424
425 switch (service) {
426 case rpc_gss_svc_integrity:
427 case rpc_gss_svc_privacy:
428 case rpc_gss_svc_none:
429 break;
430 case rpc_gss_svc_default:
431 service = rpc_gss_svc_integrity;
432 break;
433 default:
434 return (FALSE);
435 }
436
437 if ((mech = __rpc_gss_oid_to_mech(ap->mech_type)) == NULL)
438 return (FALSE);
439
440 if (!__rpc_gss_qop_to_num(qop, mech, &qop_num))
441 return (FALSE);
442
443 ap->qop = qop_num;
444 ap->service = service;
445 return (TRUE);
446 }
447
448 /*
449 * Marshall credentials.
450 */
451 static bool_t
marshall_creds(ap,xdrs)452 marshall_creds(ap, xdrs)
453 rpc_gss_data *ap;
454 XDR *xdrs;
455 {
456 rpc_gss_creds ag_creds;
457 char cred_buf[MAX_AUTH_BYTES];
458 struct opaque_auth creds;
459 XDR cred_xdrs;
460
461 ag_creds.version = ap->version;
462 ag_creds.gss_proc = ap->gss_proc;
463 ag_creds.seq_num = ap->seq_num;
464 ag_creds.service = ap->service;
465
466 /*
467 * If context has not been set up yet, use NULL handle.
468 */
469 if (ap->ctx_handle.length > 0)
470 ag_creds.ctx_handle = ap->ctx_handle;
471 else {
472 ag_creds.ctx_handle.length = 0;
473 ag_creds.ctx_handle.value = NULL;
474 }
475
476 xdrmem_create(&cred_xdrs, (caddr_t)cred_buf, MAX_AUTH_BYTES,
477 XDR_ENCODE);
478 if (!__xdr_rpc_gss_creds(&cred_xdrs, &ag_creds)) {
479 XDR_DESTROY(&cred_xdrs);
480 return (FALSE);
481 }
482
483 creds.oa_flavor = RPCSEC_GSS;
484 creds.oa_base = cred_buf;
485 creds.oa_length = xdr_getpos(&cred_xdrs);
486 XDR_DESTROY(&cred_xdrs);
487
488 if (!xdr_opaque_auth(xdrs, &creds))
489 return (FALSE);
490
491 return (TRUE);
492 }
493
494 /*
495 * Marshall verifier. The verifier is the checksum of the RPC header
496 * up to and including the credential field. The XDR handle that's
497 * passed in has the header up to and including the credential field
498 * encoded. A pointer to the transmit buffer is also passed in.
499 */
500 static bool_t
marshall_verf(ap,xdrs,buf)501 marshall_verf(ap, xdrs, buf)
502 rpc_gss_data *ap;
503 XDR *xdrs; /* send XDR */
504 char *buf; /* pointer of send buffer */
505 {
506 struct opaque_auth verf;
507 OM_uint32 major, minor;
508 gss_buffer_desc in_buf, out_buf;
509 bool_t ret = FALSE;
510
511 /*
512 * If context is not established yet, use NULL verifier.
513 */
514 if (!ap->established) {
515 verf.oa_flavor = AUTH_NONE;
516 verf.oa_base = NULL;
517 verf.oa_length = 0;
518 return (xdr_opaque_auth(xdrs, &verf));
519 }
520
521 verf.oa_flavor = RPCSEC_GSS;
522 in_buf.length = xdr_getpos(xdrs);
523 in_buf.value = buf;
524 if ((major = gss_sign(&minor, ap->context, ap->qop, &in_buf,
525 &out_buf)) != GSS_S_COMPLETE) {
526 if (major == GSS_S_CONTEXT_EXPIRED) {
527 ap->invalid = TRUE;
528 }
529 return (FALSE);
530 }
531 verf.oa_base = out_buf.value;
532 verf.oa_length = out_buf.length;
533 ret = xdr_opaque_auth(xdrs, &verf);
534 (void) gss_release_buffer(&minor, &out_buf);
535
536 return (ret);
537 }
538
539 /*
540 * Function: rpc_gss_nextverf. Not used.
541 */
542 static void
rpc_gss_nextverf()543 rpc_gss_nextverf()
544 {
545 }
546
547 /*
548 * Function: rpc_gss_marshall - not used.
549 */
550 static bool_t
rpc_gss_marshall(auth,xdrs)551 rpc_gss_marshall(auth, xdrs)
552 AUTH *auth;
553 XDR *xdrs;
554 {
555 if (!xdr_opaque_auth(xdrs, &auth->ah_cred) ||
556 !xdr_opaque_auth(xdrs, &auth->ah_verf))
557 return (FALSE);
558 return (TRUE);
559 }
560
561 /*
562 * Validate sequence window upon a successful RPCSEC_GSS INIT session.
563 * The sequence window sent back by the server should be verifiable by
564 * the verifier which is a checksum of the sequence window.
565 */
566 static bool_t
validate_seqwin(rpc_gss_data * ap)567 validate_seqwin(rpc_gss_data *ap)
568 {
569 uint_t seq_win_net;
570 OM_uint32 major = 0, minor = 0;
571 gss_buffer_desc msg_buf, tok_buf;
572 int qop_state = 0;
573
574 seq_win_net = (uint_t)htonl(ap->seq_window);
575 msg_buf.length = sizeof (seq_win_net);
576 msg_buf.value = (char *)&seq_win_net;
577 tok_buf.length = ap->verifier->oa_length;
578 tok_buf.value = ap->verifier->oa_base;
579 major = gss_verify(&minor, ap->context, &msg_buf, &tok_buf, &qop_state);
580 if (major != GSS_S_COMPLETE)
581 return (FALSE);
582 return (TRUE);
583 }
584
585 /*
586 * Validate RPC response verifier from server. The response verifier
587 * is the checksum of the request sequence number.
588 */
589 static bool_t
rpc_gss_validate(auth,verf)590 rpc_gss_validate(auth, verf)
591 AUTH *auth;
592 struct opaque_auth *verf;
593 {
594 /*LINTED*/
595 rpc_gss_data *ap = AUTH_PRIVATE(auth);
596 uint_t seq_num_net;
597 OM_uint32 major, minor;
598 gss_buffer_desc msg_buf, tok_buf;
599 int qop_state;
600
601 /*
602 * If context is not established yet, save the verifier for
603 * validating the sequence window later at the end of context
604 * creation session.
605 */
606 if (!ap->established) {
607 if (ap->verifier == NULL) {
608 ap->verifier = malloc(sizeof (struct opaque_auth));
609 memset(ap->verifier, 0, sizeof (struct opaque_auth));
610 if (verf->oa_length > 0)
611 ap->verifier->oa_base = malloc(verf->oa_length);
612 } else {
613 if (ap->verifier->oa_length > 0)
614 free(ap->verifier->oa_base);
615 if (verf->oa_length > 0)
616 ap->verifier->oa_base = malloc(verf->oa_length);
617 }
618 ap->verifier->oa_length = verf->oa_length;
619 bcopy(verf->oa_base, ap->verifier->oa_base, verf->oa_length);
620 return (TRUE);
621 }
622
623 seq_num_net = (uint_t)htonl(ap->seq_num);
624 msg_buf.length = sizeof (seq_num_net);
625 msg_buf.value = (char *)&seq_num_net;
626 tok_buf.length = verf->oa_length;
627 tok_buf.value = verf->oa_base;
628 major = gss_verify(&minor, ap->context, &msg_buf, &tok_buf, &qop_state);
629 if (major != GSS_S_COMPLETE)
630 return (FALSE);
631 return (TRUE);
632 }
633
634 /*
635 * Refresh client context. This is necessary sometimes because the
636 * server will ocassionally destroy contexts based on LRU method, or
637 * because of expired credentials.
638 */
639 static bool_t
rpc_gss_refresh(auth,msg)640 rpc_gss_refresh(auth, msg)
641 AUTH *auth;
642 struct rpc_msg *msg;
643 {
644 /*LINTED*/
645 rpc_gss_data *ap = AUTH_PRIVATE(auth);
646 OM_uint32 gssstat, minor_stat;
647
648 /*
649 * The context needs to be recreated only when the error status
650 * returned from the server is one of the following:
651 * RPCSEC_GSS_NOCRED and RPCSEC_GSS_FAILED
652 * The existing context should not be destroyed unless the above
653 * error status codes are received or if the context has not
654 * been set up.
655 */
656
657 if (msg->rjcted_rply.rj_why == RPCSEC_GSS_NOCRED ||
658 msg->rjcted_rply.rj_why == RPCSEC_GSS_FAILED ||
659 !ap->established) {
660 /*
661 * Destroy the context if necessary. Use the same memory
662 * for the new context since we've already passed a pointer
663 * to it to the user.
664 */
665 if (ap->context != GSS_C_NO_CONTEXT) {
666 (void) gss_delete_sec_context(&minor_stat, &ap->context,
667 NULL);
668 ap->context = GSS_C_NO_CONTEXT;
669 }
670 if (ap->ctx_handle.length != 0) {
671 (void) gss_release_buffer(&minor_stat,
672 &ap->ctx_handle);
673 ap->ctx_handle.length = 0;
674 ap->ctx_handle.value = NULL;
675 }
676
677 /*
678 * If the context was not already established, don't try to
679 * recreate it.
680 */
681 if (!ap->established) {
682 ap->invalid = TRUE;
683 return (FALSE);
684 }
685
686 /*
687 * Recreate context.
688 */
689 if (rpc_gss_seccreate_pvt(&gssstat, &minor_stat, auth, ap,
690 (gss_OID *)0, (OM_uint32 *)0, (OM_uint32 *)0))
691 return (TRUE);
692 else {
693 ap->invalid = TRUE;
694 return (FALSE);
695 }
696 }
697 return (FALSE);
698 }
699
700 /*
701 * Destroy a context.
702 */
703 static void
rpc_gss_destroy(auth)704 rpc_gss_destroy(auth)
705 AUTH *auth;
706 {
707 /*LINTED*/
708 rpc_gss_data *ap = AUTH_PRIVATE(auth);
709
710 rpc_gss_destroy_pvt(auth);
711 free((char *)ap);
712 free(auth);
713 }
714
715 /*
716 * Private interface to destroy a context without freeing up
717 * the memory used by it. We need to do this when a refresh
718 * fails, for example, so the user will still have a handle.
719 */
720 static void
rpc_gss_destroy_pvt(auth)721 rpc_gss_destroy_pvt(auth)
722 AUTH *auth;
723 {
724 struct timeval timeout;
725 OM_uint32 minor_stat;
726 /*LINTED*/
727 rpc_gss_data *ap = AUTH_PRIVATE(auth);
728
729 /*
730 * If we have a server context id, inform server that we are
731 * destroying the context.
732 */
733 if (ap->ctx_handle.length != 0) {
734 ap->gss_proc = RPCSEC_GSS_DESTROY;
735 timeout.tv_sec = 1;
736 timeout.tv_usec = 0;
737 (void) clnt_call(ap->clnt, NULLPROC, xdr_void, NULL,
738 xdr_void, NULL, timeout);
739
740 (void) gss_release_buffer(&minor_stat, &ap->ctx_handle);
741 ap->ctx_handle.length = 0;
742 ap->ctx_handle.value = NULL;
743 }
744
745 /*
746 * Destroy local GSS context.
747 */
748 if (ap->context != GSS_C_NO_CONTEXT) {
749 (void) gss_delete_sec_context(&minor_stat, &ap->context, NULL);
750 ap->context = GSS_C_NO_CONTEXT;
751 }
752
753 /*
754 * Looks like we need to release default credentials if we use it.
755 * Non-default creds need to be released by user.
756 */
757 if (ap->my_cred == GSS_C_NO_CREDENTIAL)
758 (void) gss_release_cred(&minor_stat, &ap->my_cred);
759
760 /*
761 * Release any internal name structures.
762 */
763 if (ap->target_name != NULL) {
764 (void) gss_release_name(&minor_stat, &ap->target_name);
765 ap->target_name = NULL;
766 }
767
768 /*
769 * Free the verifier saved for sequence window checking.
770 */
771 if (ap->verifier != NULL) {
772 if (ap->verifier->oa_length > 0)
773 free(ap->verifier->oa_base);
774 free(ap->verifier);
775 ap->verifier = NULL;
776 }
777 }
778
779 /*
780 * Wrap client side data. The encoded header is passed in through
781 * buf and buflen. The header is up to but not including the
782 * credential field.
783 */
784 bool_t
__rpc_gss_wrap(auth,buf,buflen,out_xdrs,xdr_func,xdr_ptr)785 __rpc_gss_wrap(auth, buf, buflen, out_xdrs, xdr_func, xdr_ptr)
786 AUTH *auth;
787 char *buf; /* encoded header */
788 uint_t buflen; /* encoded header length */
789 XDR *out_xdrs;
790 bool_t (*xdr_func)();
791 caddr_t xdr_ptr;
792 {
793 /*LINTED*/
794 rpc_gss_data *ap = AUTH_PRIVATE(auth);
795 XDR xdrs;
796 char tmp_buf[512];
797
798
799 /*
800 * Reject an invalid context.
801 */
802 if (ap->invalid)
803 return (FALSE);
804
805 /*
806 * If context is established, bump up sequence number.
807 */
808 if (ap->established)
809 ap->seq_num++;
810
811 /*
812 * Create the header in a temporary XDR context and buffer
813 * before putting it out.
814 */
815 xdrmem_create(&xdrs, tmp_buf, sizeof (tmp_buf), XDR_ENCODE);
816 if (!XDR_PUTBYTES(&xdrs, buf, buflen))
817 return (FALSE);
818
819 /*
820 * create cred field
821 */
822 if (!marshall_creds(ap, &xdrs))
823 return (FALSE);
824
825 /*
826 * create verifier
827 */
828 if (!marshall_verf(ap, &xdrs, tmp_buf))
829 return (FALSE);
830
831 /*
832 * write out header and destroy temp structures
833 */
834 if (!XDR_PUTBYTES(out_xdrs, tmp_buf, XDR_GETPOS(&xdrs)))
835 return (FALSE);
836 XDR_DESTROY(&xdrs);
837
838 /*
839 * If context is not established, or if neither integrity
840 * nor privacy is used, just XDR encode data.
841 */
842 if (!ap->established || ap->service == rpc_gss_svc_none)
843 return ((*xdr_func)(out_xdrs, xdr_ptr));
844
845 return (__rpc_gss_wrap_data(ap->service, ap->qop, ap->context,
846 ap->seq_num, out_xdrs, xdr_func, xdr_ptr));
847 }
848
849 /*
850 * Unwrap received data.
851 */
852 bool_t
__rpc_gss_unwrap(auth,in_xdrs,xdr_func,xdr_ptr)853 __rpc_gss_unwrap(auth, in_xdrs, xdr_func, xdr_ptr)
854 AUTH *auth;
855 XDR *in_xdrs;
856 bool_t (*xdr_func)();
857 caddr_t xdr_ptr;
858 {
859 /*LINTED*/
860 rpc_gss_data *ap = AUTH_PRIVATE(auth);
861
862 /*
863 * If context is not established, of if neither integrity
864 * nor privacy is used, just XDR encode data.
865 */
866 if (!ap->established || ap->service == rpc_gss_svc_none)
867 return ((*xdr_func)(in_xdrs, xdr_ptr));
868
869 return (__rpc_gss_unwrap_data(ap->service,
870 ap->context,
871 ap->seq_num,
872 ap->qop,
873 in_xdrs, xdr_func, xdr_ptr));
874 }
875
876 int
__rpc_gss_max_data_length(auth,max_tp_unit_len)877 __rpc_gss_max_data_length(auth, max_tp_unit_len)
878 AUTH *auth;
879 int max_tp_unit_len;
880 {
881 /*LINTED*/
882 rpc_gss_data *ap = AUTH_PRIVATE(auth);
883
884 if (!ap->established || max_tp_unit_len <= 0)
885 return (0);
886
887 return (__find_max_data_length(ap->service,
888 ap->context,
889 ap->qop,
890 max_tp_unit_len));
891 }
892
893 void
__rpc_gss_get_error(rpc_gss_error_t * error)894 __rpc_gss_get_error(rpc_gss_error_t *error)
895 {
896 *error = rpc_gss_err;
897 }
898
899 #undef rpc_gss_err
900
901 rpc_gss_error_t rpc_gss_err;
902
903 rpc_gss_error_t *
__rpc_gss_err()904 __rpc_gss_err()
905 {
906 static thread_key_t rpc_gss_err_key = THR_ONCE_KEY;
907 rpc_gss_error_t *tsd;
908
909 if (thr_main())
910 return (&rpc_gss_err);
911 if (thr_keycreate_once(&rpc_gss_err_key, free) != 0)
912 return (&rpc_gss_err);
913 tsd = pthread_getspecific(rpc_gss_err_key);
914 if (tsd == NULL) {
915 tsd = (rpc_gss_error_t *)calloc(1, sizeof (rpc_gss_error_t));
916 if (thr_setspecific(rpc_gss_err_key, tsd) != 0) {
917 if (tsd)
918 free(tsd);
919 return (&rpc_gss_err);
920 }
921 }
922 return (tsd);
923 }
924