4 RPCSEC_GSS client routines.
6 Copyright (c) 2000 The Regents of the University of Michigan.
9 Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
10 All rights reserved, all wrongs reversed.
12 Redistribution and use in source and binary forms, with or without
13 modification, are permitted provided that the following conditions
16 1. Redistributions of source code must retain the above copyright
17 notice, this list of conditions and the following disclaimer.
18 2. Redistributions in binary form must reproduce the above copyright
19 notice, this list of conditions and the following disclaimer in the
20 documentation and/or other materials provided with the distribution.
21 3. Neither the name of the University nor the names of its
22 contributors may be used to endorse or promote products derived
23 from this software without specific prior written permission.
25 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
26 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
27 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
32 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44 #include <rpc/types.h>
47 #include <rpc/auth_gss.h>
49 #include <netinet/in.h>
50 #include <gssapi/gssapi.h>
54 static void authgss_nextverf(AUTH *);
55 static bool_t authgss_marshal(AUTH *, XDR *);
56 static bool_t authgss_refresh(AUTH *, void *);
57 static bool_t authgss_validate(AUTH *, struct opaque_auth *);
58 static void authgss_destroy(AUTH *);
59 static void authgss_destroy_context(AUTH *);
60 static bool_t authgss_wrap(AUTH *, XDR *, xdrproc_t, caddr_t);
61 static bool_t authgss_unwrap(AUTH *, XDR *, xdrproc_t, caddr_t);
65 * from mit-krb5-1.2.1 mechglue/mglueP.h:
66 * Array of context IDs typed by mechanism OID
68 typedef struct gss_union_ctx_id_t {
70 gss_ctx_id_t internal_ctx_id;
71 } gss_union_ctx_id_desc, *gss_union_ctx_id_t;
73 static struct auth_ops authgss_ops = {
84 /* useful as i add more mechanisms */
86 print_rpc_gss_sec(struct rpc_gss_sec *ptr)
91 if (libtirpc_debug_level < 4 || log_stderr == 0)
94 gss_log_debug("rpc_gss_sec:");
96 gss_log_debug("NULL gss_OID mech");
98 fprintf(stderr, " mechanism_OID: {");
99 p = (char *)ptr->mech->elements;
100 for (i=0; i < ptr->mech->length; i++)
101 /* First byte of OIDs encoded to save a byte */
108 else if (40 <= *p && *p < 80) {
112 else if (80 <= *p && *p < 127) {
121 fprintf(stderr, " %u %u", first, second);
125 fprintf(stderr, " %u", (unsigned char)*p++);
127 fprintf(stderr, " }\n");
129 fprintf(stderr, " qop: %d\n", ptr->qop);
130 fprintf(stderr, " service: %d\n", ptr->svc);
131 fprintf(stderr, " cred: %p\n", ptr->cred);
134 struct rpc_gss_data {
135 bool_t established; /* context established */
136 gss_buffer_desc gc_wire_verf; /* save GSS_S_COMPLETE NULL RPC verfier
137 * to process at end of context negotiation*/
138 CLIENT *clnt; /* client handle */
139 gss_name_t name; /* service name */
140 struct rpc_gss_sec sec; /* security tuple */
141 gss_ctx_id_t ctx; /* context id */
142 struct rpc_gss_cred gc; /* client credentials */
143 u_int win; /* sequence window */
146 #define AUTH_PRIVATE(auth) ((struct rpc_gss_data *)auth->ah_private)
148 static struct timeval AUTH_TIMEOUT = { 25, 0 };
151 authgss_create(CLIENT *clnt, gss_name_t name, struct rpc_gss_sec *sec)
153 AUTH *auth, *save_auth;
154 struct rpc_gss_data *gd;
155 OM_uint32 min_stat = 0;
157 gss_log_debug("in authgss_create()");
159 memset(&rpc_createerr, 0, sizeof(rpc_createerr));
161 if ((auth = calloc(sizeof(*auth), 1)) == NULL) {
162 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
163 rpc_createerr.cf_error.re_errno = ENOMEM;
166 if ((gd = calloc(sizeof(*gd), 1)) == NULL) {
167 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
168 rpc_createerr.cf_error.re_errno = ENOMEM;
172 LIBTIRPC_DEBUG(3, ("authgss_create: name is %p", name));
173 if (name != GSS_C_NO_NAME) {
174 if (gss_duplicate_name(&min_stat, name, &gd->name)
176 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
177 rpc_createerr.cf_error.re_errno = ENOMEM;
185 LIBTIRPC_DEBUG(3, ("authgss_create: gd->name is %p", gd->name));
187 gd->ctx = GSS_C_NO_CONTEXT;
190 gd->gc.gc_v = RPCSEC_GSS_VERSION;
191 gd->gc.gc_proc = RPCSEC_GSS_INIT;
192 gd->gc.gc_svc = gd->sec.svc;
194 auth->ah_ops = &authgss_ops;
195 auth->ah_private = (caddr_t)gd;
197 save_auth = clnt->cl_auth;
198 clnt->cl_auth = auth;
200 if (!authgss_refresh(auth, NULL))
203 auth_get(auth); /* Reference for caller */
205 clnt->cl_auth = save_auth;
211 authgss_create_default(CLIENT *clnt, char *service, struct rpc_gss_sec *sec)
214 OM_uint32 maj_stat = 0, min_stat = 0;
215 gss_buffer_desc sname;
216 gss_name_t name = GSS_C_NO_NAME;
218 gss_log_debug("in authgss_create_default()");
221 sname.value = service;
222 sname.length = strlen(service);
224 maj_stat = gss_import_name(&min_stat, &sname,
225 (gss_OID)GSS_C_NT_HOSTBASED_SERVICE,
228 if (maj_stat != GSS_S_COMPLETE) {
229 gss_log_status("authgss_create_default: gss_import_name",
231 rpc_createerr.cf_stat = RPC_AUTHERROR;
235 auth = authgss_create(clnt, name, sec);
237 if (name != GSS_C_NO_NAME) {
238 LIBTIRPC_DEBUG(3, ("authgss_create_default: freeing name %p", name));
239 gss_release_name(&min_stat, &name);
246 authgss_get_private_data(AUTH *auth, struct authgss_private_data *pd)
248 struct rpc_gss_data *gd;
250 gss_log_debug("in authgss_get_private_data()");
255 gd = AUTH_PRIVATE(auth);
257 if (!gd || !gd->established)
260 pd->pd_ctx = gd->ctx;
261 pd->pd_ctx_hndl = gd->gc.gc_ctx;
262 pd->pd_seq_win = gd->win;
264 * We've given this away -- don't try to use it ourself any more
265 * Caller should call authgss_free_private_data to free data.
266 * This also ensures that authgss_destroy_context() won't try to
267 * send an RPCSEC_GSS_DESTROY request which might inappropriately
268 * destroy the context.
270 gd->ctx = GSS_C_NO_CONTEXT;
271 gd->gc.gc_ctx.length = 0;
272 gd->gc.gc_ctx.value = NULL;
278 authgss_free_private_data(struct authgss_private_data *pd)
281 gss_log_debug("in authgss_free_private_data()");
286 if (pd->pd_ctx != GSS_C_NO_CONTEXT)
287 gss_delete_sec_context(&min_stat, &pd->pd_ctx, NULL);
288 gss_release_buffer(&min_stat, &pd->pd_ctx_hndl);
289 memset(&pd->pd_ctx_hndl, 0, sizeof(pd->pd_ctx_hndl));
296 authgss_nextverf(AUTH *auth)
298 gss_log_debug("in authgss_nextverf()");
299 /* no action necessary */
303 authgss_marshal(AUTH *auth, XDR *xdrs)
306 char tmp[MAX_AUTH_BYTES];
307 struct rpc_gss_data *gd;
308 gss_buffer_desc rpcbuf, checksum;
309 OM_uint32 maj_stat, min_stat;
312 gss_log_debug("in authgss_marshal()");
314 gd = AUTH_PRIVATE(auth);
319 xdrmem_create(&tmpxdrs, tmp, sizeof(tmp), XDR_ENCODE);
321 if (!xdr_rpc_gss_cred(&tmpxdrs, &gd->gc)) {
322 XDR_DESTROY(&tmpxdrs);
325 auth->ah_cred.oa_flavor = RPCSEC_GSS;
326 auth->ah_cred.oa_base = tmp;
327 auth->ah_cred.oa_length = XDR_GETPOS(&tmpxdrs);
329 XDR_DESTROY(&tmpxdrs);
331 if (!xdr_opaque_auth(xdrs, &auth->ah_cred))
334 if (gd->gc.gc_proc == RPCSEC_GSS_INIT ||
335 gd->gc.gc_proc == RPCSEC_GSS_CONTINUE_INIT) {
336 return (xdr_opaque_auth(xdrs, &_null_auth));
338 /* Checksum serialized RPC header, up to and including credential. */
339 rpcbuf.length = XDR_GETPOS(xdrs);
341 rpcbuf.value = XDR_INLINE(xdrs, rpcbuf.length);
343 maj_stat = gss_get_mic(&min_stat, gd->ctx, gd->sec.qop,
346 if (maj_stat != GSS_S_COMPLETE) {
347 gss_log_status("authgss_marshal: gss_get_mic",
349 if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
350 gd->established = FALSE;
351 authgss_destroy_context(auth);
355 auth->ah_verf.oa_flavor = RPCSEC_GSS;
356 auth->ah_verf.oa_base = checksum.value;
357 auth->ah_verf.oa_length = checksum.length;
359 xdr_stat = xdr_opaque_auth(xdrs, &auth->ah_verf);
360 gss_release_buffer(&min_stat, &checksum);
366 authgss_validate(AUTH *auth, struct opaque_auth *verf)
368 struct rpc_gss_data *gd;
369 u_int num, qop_state;
370 gss_buffer_desc signbuf, checksum;
371 OM_uint32 maj_stat, min_stat;
373 gss_log_debug("in authgss_validate()");
375 gd = AUTH_PRIVATE(auth);
377 if (gd->established == FALSE) {
378 /* would like to do this only on NULL rpc --
379 * gc->established is good enough.
380 * save the on the wire verifier to validate last
381 * INIT phase packet after decode if the major
382 * status is GSS_S_COMPLETE
384 if ((gd->gc_wire_verf.value =
385 mem_alloc(verf->oa_length)) == NULL) {
386 fprintf(stderr, "gss_validate: out of memory\n");
389 memcpy(gd->gc_wire_verf.value, verf->oa_base, verf->oa_length);
390 gd->gc_wire_verf.length = verf->oa_length;
394 if (gd->gc.gc_proc == RPCSEC_GSS_INIT ||
395 gd->gc.gc_proc == RPCSEC_GSS_CONTINUE_INIT) {
396 num = htonl(gd->win);
398 else num = htonl(gd->gc.gc_seq);
400 signbuf.value = #
401 signbuf.length = sizeof(num);
403 checksum.value = verf->oa_base;
404 checksum.length = verf->oa_length;
406 maj_stat = gss_verify_mic(&min_stat, gd->ctx, &signbuf,
407 &checksum, &qop_state);
409 if (maj_stat != GSS_S_COMPLETE || qop_state != gd->sec.qop) {
410 gss_log_status("authgss_validate: gss_verify_mic",
412 if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
413 gd->established = FALSE;
414 authgss_destroy_context(auth);
422 authgss_refresh(AUTH *auth, void *dummy)
424 struct rpc_gss_data *gd;
425 struct rpc_gss_init_res gr;
426 gss_buffer_desc *recv_tokenp, send_token;
427 OM_uint32 maj_stat, min_stat, call_stat, ret_flags;
429 gss_log_debug("in authgss_refresh()");
431 gd = AUTH_PRIVATE(auth);
436 /* GSS context establishment loop. */
437 memset(&gr, 0, sizeof(gr));
438 recv_tokenp = GSS_C_NO_BUFFER;
440 print_rpc_gss_sec(&gd->sec);
443 /* print the token we just received */
444 if (recv_tokenp != GSS_C_NO_BUFFER) {
445 gss_log_debug("The token we just received (length %d):",
446 recv_tokenp->length);
447 gss_log_hexdump(recv_tokenp->value, recv_tokenp->length, 0);
449 maj_stat = gss_init_sec_context(&min_stat,
458 NULL, /* used mech */
461 NULL); /* time rec */
463 if (recv_tokenp != GSS_C_NO_BUFFER) {
464 gss_release_buffer(&min_stat, &gr.gr_token);
465 recv_tokenp = GSS_C_NO_BUFFER;
467 if (maj_stat != GSS_S_COMPLETE &&
468 maj_stat != GSS_S_CONTINUE_NEEDED) {
469 gss_log_status("authgss_refresh: gss_init_sec_context",
473 if (send_token.length != 0) {
474 memset(&gr, 0, sizeof(gr));
476 /* print the token we are about to send */
477 gss_log_debug("The token being sent (length %d):",
479 gss_log_hexdump(send_token.value, send_token.length, 0);
481 call_stat = clnt_call(gd->clnt, NULLPROC,
482 (xdrproc_t)xdr_rpc_gss_init_args,
484 (xdrproc_t)xdr_rpc_gss_init_res,
485 (caddr_t)&gr, AUTH_TIMEOUT);
487 gss_release_buffer(&min_stat, &send_token);
489 if (call_stat != RPC_SUCCESS ||
490 (gr.gr_major != GSS_S_COMPLETE &&
491 gr.gr_major != GSS_S_CONTINUE_NEEDED))
494 if (gr.gr_ctx.length != 0) {
495 if (gd->gc.gc_ctx.value)
496 gss_release_buffer(&min_stat,
498 gd->gc.gc_ctx = gr.gr_ctx;
500 if (gr.gr_token.length != 0) {
501 if (maj_stat != GSS_S_CONTINUE_NEEDED)
503 recv_tokenp = &gr.gr_token;
505 gd->gc.gc_proc = RPCSEC_GSS_CONTINUE_INIT;
508 /* GSS_S_COMPLETE => check gss header verifier,
509 * usually checked in gss_validate
511 if (maj_stat == GSS_S_COMPLETE) {
512 gss_buffer_desc bufin;
513 gss_buffer_desc bufout;
514 u_int seq, qop_state = 0;
516 seq = htonl(gr.gr_win);
517 bufin.value = (unsigned char *)&seq;
518 bufin.length = sizeof(seq);
519 bufout.value = (unsigned char *)gd->gc_wire_verf.value;
520 bufout.length = gd->gc_wire_verf.length;
522 maj_stat = gss_verify_mic(&min_stat, gd->ctx,
523 &bufin, &bufout, &qop_state);
525 if (maj_stat != GSS_S_COMPLETE
526 || qop_state != gd->sec.qop) {
527 gss_log_status("authgss_refresh: gss_verify_mic",
529 if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
530 gd->established = FALSE;
531 authgss_destroy_context(auth);
535 gd->established = TRUE;
536 gd->gc.gc_proc = RPCSEC_GSS_DATA;
542 /* End context negotiation loop. */
543 if (gd->gc.gc_proc != RPCSEC_GSS_DATA) {
544 if (gr.gr_token.length != 0)
545 gss_release_buffer(&min_stat, &gr.gr_token);
547 authgss_destroy(auth);
549 rpc_createerr.cf_stat = RPC_AUTHERROR;
557 authgss_service(AUTH *auth, int svc)
559 struct rpc_gss_data *gd;
561 gss_log_debug("in authgss_service()");
565 gd = AUTH_PRIVATE(auth);
566 if (!gd || !gd->established)
574 authgss_destroy_context(AUTH *auth)
576 struct rpc_gss_data *gd;
579 gss_log_debug("in authgss_destroy_context()");
581 gd = AUTH_PRIVATE(auth);
583 if (gd->gc.gc_ctx.length != 0) {
584 if (gd->established) {
585 AUTH *save_auth = NULL;
587 /* Make sure we use the right auth_ops */
588 if (gd->clnt->cl_auth != auth) {
589 save_auth = gd->clnt->cl_auth;
590 gd->clnt->cl_auth = auth;
593 gd->gc.gc_proc = RPCSEC_GSS_DESTROY;
594 clnt_call(gd->clnt, NULLPROC, (xdrproc_t)xdr_void, NULL,
595 (xdrproc_t)xdr_void, NULL, AUTH_TIMEOUT);
597 if (save_auth != NULL)
598 gd->clnt->cl_auth = save_auth;
600 gss_release_buffer(&min_stat, &gd->gc.gc_ctx);
601 /* XXX ANDROS check size of context - should be 8 */
602 memset(&gd->gc.gc_ctx, 0, sizeof(gd->gc.gc_ctx));
604 if (gd->ctx != GSS_C_NO_CONTEXT) {
605 gss_delete_sec_context(&min_stat, &gd->ctx, NULL);
606 gd->ctx = GSS_C_NO_CONTEXT;
609 /* free saved wire verifier (if any) */
610 mem_free(gd->gc_wire_verf.value, gd->gc_wire_verf.length);
611 gd->gc_wire_verf.value = NULL;
612 gd->gc_wire_verf.length = 0;
614 gd->established = FALSE;
618 authgss_destroy(AUTH *auth)
620 struct rpc_gss_data *gd;
623 gss_log_debug("in authgss_destroy()");
625 gd = AUTH_PRIVATE(auth);
627 authgss_destroy_context(auth);
629 LIBTIRPC_DEBUG(3, ("authgss_destroy: freeing name %p", gd->name));
630 if (gd->name != GSS_C_NO_NAME)
631 gss_release_name(&min_stat, &gd->name);
638 authgss_wrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
640 struct rpc_gss_data *gd;
642 gss_log_debug("in authgss_wrap()");
644 gd = AUTH_PRIVATE(auth);
646 if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) {
647 return ((*xdr_func)(xdrs, xdr_ptr));
649 return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr,
650 gd->ctx, gd->sec.qop,
651 gd->sec.svc, gd->gc.gc_seq));
655 authgss_unwrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
657 struct rpc_gss_data *gd;
659 gss_log_debug("in authgss_unwrap()");
661 gd = AUTH_PRIVATE(auth);
663 if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) {
664 return ((*xdr_func)(xdrs, xdr_ptr));
666 return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr,
667 gd->ctx, gd->sec.qop,
668 gd->sec.svc, gd->gc.gc_seq));