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>
52 static void authgss_nextverf();
53 static bool_t authgss_marshal();
54 static bool_t authgss_refresh();
55 static bool_t authgss_validate();
56 static void authgss_destroy();
57 static void authgss_destroy_context();
58 static bool_t authgss_wrap();
59 static bool_t authgss_unwrap();
63 * from mit-krb5-1.2.1 mechglue/mglueP.h:
64 * Array of context IDs typed by mechanism OID
66 typedef struct gss_union_ctx_id_t {
68 gss_ctx_id_t internal_ctx_id;
69 } gss_union_ctx_id_desc, *gss_union_ctx_id_t;
71 static struct auth_ops authgss_ops = {
83 /* useful as i add more mechanisms */
85 print_rpc_gss_sec(struct rpc_gss_sec *ptr)
90 log_debug("rpc_gss_sec:");
92 log_debug("NULL gss_OID mech");
94 fprintf(stderr, " mechanism_OID: {");
95 p = (char *)ptr->mech->elements;
96 for (i=0; i < ptr->mech->length; i++)
97 /* First byte of OIDs encoded to save a byte */
104 else if (40 <= *p && *p < 80) {
108 else if (80 <= *p && *p < 127) {
117 fprintf(stderr, " %u %u", first, second);
121 fprintf(stderr, " %u", (unsigned char)*p++);
123 fprintf(stderr, " }\n");
125 fprintf(stderr, " qop: %d\n", ptr->qop);
126 fprintf(stderr, " service: %d\n", ptr->svc);
127 fprintf(stderr, " cred: %p\n", ptr->cred);
131 struct rpc_gss_data {
132 bool_t established; /* context established */
133 gss_buffer_desc gc_wire_verf; /* save GSS_S_COMPLETE NULL RPC verfier
134 * to process at end of context negotiation*/
135 CLIENT *clnt; /* client handle */
136 gss_name_t name; /* service name */
137 struct rpc_gss_sec sec; /* security tuple */
138 gss_ctx_id_t ctx; /* context id */
139 struct rpc_gss_cred gc; /* client credentials */
140 u_int win; /* sequence window */
143 #define AUTH_PRIVATE(auth) ((struct rpc_gss_data *)auth->ah_private)
145 static struct timeval AUTH_TIMEOUT = { 25, 0 };
148 authgss_create(CLIENT *clnt, gss_name_t name, struct rpc_gss_sec *sec)
150 AUTH *auth, *save_auth;
151 struct rpc_gss_data *gd;
152 OM_uint32 min_stat = 0;
154 log_debug("in authgss_create()");
156 memset(&rpc_createerr, 0, sizeof(rpc_createerr));
158 if ((auth = calloc(sizeof(*auth), 1)) == NULL) {
159 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
160 rpc_createerr.cf_error.re_errno = ENOMEM;
163 if ((gd = calloc(sizeof(*gd), 1)) == NULL) {
164 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
165 rpc_createerr.cf_error.re_errno = ENOMEM;
170 fprintf(stderr, "authgss_create: name is %p\n", name);
172 if (name != GSS_C_NO_NAME) {
173 if (gss_duplicate_name(&min_stat, name, &gd->name)
175 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
176 rpc_createerr.cf_error.re_errno = ENOMEM;
185 fprintf(stderr, "authgss_create: gd->name is %p\n", gd->name);
188 gd->ctx = GSS_C_NO_CONTEXT;
191 gd->gc.gc_v = RPCSEC_GSS_VERSION;
192 gd->gc.gc_proc = RPCSEC_GSS_INIT;
193 gd->gc.gc_svc = gd->sec.svc;
195 auth->ah_ops = &authgss_ops;
196 auth->ah_private = (caddr_t)gd;
198 save_auth = clnt->cl_auth;
199 clnt->cl_auth = auth;
201 if (!authgss_refresh(auth))
204 clnt->cl_auth = save_auth;
210 authgss_create_default(CLIENT *clnt, char *service, struct rpc_gss_sec *sec)
213 OM_uint32 maj_stat = 0, min_stat = 0;
214 gss_buffer_desc sname;
215 gss_name_t name = GSS_C_NO_NAME;
217 log_debug("in authgss_create_default()");
220 sname.value = service;
221 sname.length = strlen(service);
223 maj_stat = gss_import_name(&min_stat, &sname,
224 GSS_C_NT_HOSTBASED_SERVICE,
227 if (maj_stat != GSS_S_COMPLETE) {
228 log_status("gss_import_name", maj_stat, min_stat);
229 rpc_createerr.cf_stat = RPC_AUTHERROR;
233 auth = authgss_create(clnt, name, sec);
235 if (name != GSS_C_NO_NAME) {
237 fprintf(stderr, "authgss_create_default: freeing name %p\n", 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 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;
268 authgss_nextverf(AUTH *auth)
270 log_debug("in authgss_nextverf()");
271 /* no action necessary */
275 authgss_marshal(AUTH *auth, XDR *xdrs)
278 char tmp[MAX_AUTH_BYTES];
279 struct rpc_gss_data *gd;
280 gss_buffer_desc rpcbuf, checksum;
281 OM_uint32 maj_stat, min_stat;
284 log_debug("in authgss_marshal()");
286 gd = AUTH_PRIVATE(auth);
291 xdrmem_create(&tmpxdrs, tmp, sizeof(tmp), XDR_ENCODE);
293 if (!xdr_rpc_gss_cred(&tmpxdrs, &gd->gc)) {
294 XDR_DESTROY(&tmpxdrs);
297 auth->ah_cred.oa_flavor = RPCSEC_GSS;
298 auth->ah_cred.oa_base = tmp;
299 auth->ah_cred.oa_length = XDR_GETPOS(&tmpxdrs);
301 XDR_DESTROY(&tmpxdrs);
303 if (!xdr_opaque_auth(xdrs, &auth->ah_cred))
306 if (gd->gc.gc_proc == RPCSEC_GSS_INIT ||
307 gd->gc.gc_proc == RPCSEC_GSS_CONTINUE_INIT) {
308 return (xdr_opaque_auth(xdrs, &_null_auth));
310 /* Checksum serialized RPC header, up to and including credential. */
311 rpcbuf.length = XDR_GETPOS(xdrs);
313 rpcbuf.value = XDR_INLINE(xdrs, rpcbuf.length);
315 maj_stat = gss_get_mic(&min_stat, gd->ctx, gd->sec.qop,
318 if (maj_stat != GSS_S_COMPLETE) {
319 log_status("gss_get_mic", maj_stat, min_stat);
320 if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
321 gd->established = FALSE;
322 authgss_destroy_context(auth);
326 auth->ah_verf.oa_flavor = RPCSEC_GSS;
327 auth->ah_verf.oa_base = checksum.value;
328 auth->ah_verf.oa_length = checksum.length;
330 xdr_stat = xdr_opaque_auth(xdrs, &auth->ah_verf);
331 gss_release_buffer(&min_stat, &checksum);
337 authgss_validate(AUTH *auth, struct opaque_auth *verf)
339 struct rpc_gss_data *gd;
340 u_int num, qop_state;
341 gss_buffer_desc signbuf, checksum;
342 OM_uint32 maj_stat, min_stat;
344 log_debug("in authgss_validate()");
346 gd = AUTH_PRIVATE(auth);
348 if (gd->established == FALSE) {
349 /* would like to do this only on NULL rpc --
350 * gc->established is good enough.
351 * save the on the wire verifier to validate last
352 * INIT phase packet after decode if the major
353 * status is GSS_S_COMPLETE
355 if ((gd->gc_wire_verf.value =
356 mem_alloc(verf->oa_length)) == NULL) {
357 fprintf(stderr, "gss_validate: out of memory\n");
360 memcpy(gd->gc_wire_verf.value, verf->oa_base, verf->oa_length);
361 gd->gc_wire_verf.length = verf->oa_length;
365 if (gd->gc.gc_proc == RPCSEC_GSS_INIT ||
366 gd->gc.gc_proc == RPCSEC_GSS_CONTINUE_INIT) {
367 num = htonl(gd->win);
369 else num = htonl(gd->gc.gc_seq);
371 signbuf.value = #
372 signbuf.length = sizeof(num);
374 checksum.value = verf->oa_base;
375 checksum.length = verf->oa_length;
377 maj_stat = gss_verify_mic(&min_stat, gd->ctx, &signbuf,
378 &checksum, &qop_state);
379 if (maj_stat != GSS_S_COMPLETE || qop_state != gd->sec.qop) {
380 log_status("gss_verify_mic", maj_stat, min_stat);
381 if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
382 gd->established = FALSE;
383 authgss_destroy_context(auth);
391 authgss_refresh(AUTH *auth)
393 struct rpc_gss_data *gd;
394 struct rpc_gss_init_res gr;
395 gss_buffer_desc *recv_tokenp, send_token;
396 OM_uint32 maj_stat, min_stat, call_stat, ret_flags;
397 OM_uint32 req_flags=0;
399 log_debug("in authgss_refresh()");
401 gd = AUTH_PRIVATE(auth);
406 /* GSS context establishment loop. */
407 memset(&gr, 0, sizeof(gr));
408 recv_tokenp = GSS_C_NO_BUFFER;
411 print_rpc_gss_sec(&gd->sec);
416 /* print the token we just received */
417 if (recv_tokenp != GSS_C_NO_BUFFER) {
418 log_debug("The token we just received (length %d):",
419 recv_tokenp->length);
420 log_hexdump(recv_tokenp->value, recv_tokenp->length, 0);
423 maj_stat = gss_init_sec_context(&min_stat,
432 NULL, /* used mech */
435 NULL); /* time rec */
437 if (recv_tokenp != GSS_C_NO_BUFFER) {
438 gss_release_buffer(&min_stat, &gr.gr_token);
439 recv_tokenp = GSS_C_NO_BUFFER;
441 if (maj_stat != GSS_S_COMPLETE &&
442 maj_stat != GSS_S_CONTINUE_NEEDED) {
443 log_status("gss_init_sec_context", maj_stat, min_stat);
446 if (send_token.length != 0) {
447 memset(&gr, 0, sizeof(gr));
450 /* print the token we are about to send */
451 log_debug("The token being sent (length %d):",
453 log_hexdump(send_token.value, send_token.length, 0);
456 call_stat = clnt_call(gd->clnt, NULLPROC,
457 xdr_rpc_gss_init_args,
459 xdr_rpc_gss_init_res,
460 (caddr_t)&gr, AUTH_TIMEOUT);
462 gss_release_buffer(&min_stat, &send_token);
464 if (call_stat != RPC_SUCCESS ||
465 (gr.gr_major != GSS_S_COMPLETE &&
466 gr.gr_major != GSS_S_CONTINUE_NEEDED))
469 if (gr.gr_ctx.length != 0) {
470 if (gd->gc.gc_ctx.value)
471 gss_release_buffer(&min_stat,
473 gd->gc.gc_ctx = gr.gr_ctx;
475 if (gr.gr_token.length != 0) {
476 if (maj_stat != GSS_S_CONTINUE_NEEDED)
478 recv_tokenp = &gr.gr_token;
480 gd->gc.gc_proc = RPCSEC_GSS_CONTINUE_INIT;
483 /* GSS_S_COMPLETE => check gss header verifier,
484 * usually checked in gss_validate
486 if (maj_stat == GSS_S_COMPLETE) {
487 gss_buffer_desc bufin;
488 gss_buffer_desc bufout;
489 u_int seq, qop_state = 0;
491 seq = htonl(gr.gr_win);
492 bufin.value = (unsigned char *)&seq;
493 bufin.length = sizeof(seq);
494 bufout.value = (unsigned char *)gd->gc_wire_verf.value;
495 bufout.length = gd->gc_wire_verf.length;
497 maj_stat = gss_verify_mic(&min_stat, gd->ctx,
498 &bufin, &bufout, &qop_state);
500 if (maj_stat != GSS_S_COMPLETE
501 || qop_state != gd->sec.qop) {
502 log_status("gss_verify_mic", maj_stat, min_stat);
503 if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
504 gd->established = FALSE;
505 authgss_destroy_context(auth);
509 gd->established = TRUE;
510 gd->gc.gc_proc = RPCSEC_GSS_DATA;
516 /* End context negotiation loop. */
517 if (gd->gc.gc_proc != RPCSEC_GSS_DATA) {
518 if (gr.gr_token.length != 0)
519 gss_release_buffer(&min_stat, &gr.gr_token);
521 authgss_destroy(auth);
523 rpc_createerr.cf_stat = RPC_AUTHERROR;
531 authgss_service(AUTH *auth, int svc)
533 struct rpc_gss_data *gd;
535 log_debug("in authgss_service()");
539 gd = AUTH_PRIVATE(auth);
540 if (!gd || !gd->established)
548 authgss_destroy_context(AUTH *auth)
550 struct rpc_gss_data *gd;
553 log_debug("in authgss_destroy_context()");
555 gd = AUTH_PRIVATE(auth);
557 if (gd->gc.gc_ctx.length != 0) {
558 if (gd->established) {
559 gd->gc.gc_proc = RPCSEC_GSS_DESTROY;
560 clnt_call(gd->clnt, NULLPROC, xdr_void, NULL,
561 xdr_void, NULL, AUTH_TIMEOUT);
563 gss_release_buffer(&min_stat, &gd->gc.gc_ctx);
564 /* XXX ANDROS check size of context - should be 8 */
565 memset(&gd->gc.gc_ctx, 0, sizeof(gd->gc.gc_ctx));
567 if (gd->ctx != GSS_C_NO_CONTEXT) {
568 gss_delete_sec_context(&min_stat, &gd->ctx, NULL);
569 gd->ctx = GSS_C_NO_CONTEXT;
571 gd->established = FALSE;
575 authgss_destroy(AUTH *auth)
577 struct rpc_gss_data *gd;
580 log_debug("in authgss_destroy()");
582 gd = AUTH_PRIVATE(auth);
584 authgss_destroy_context(auth);
587 fprintf(stderr, "authgss_destroy: freeing name %p\n", gd->name);
589 if (gd->name != GSS_C_NO_NAME)
590 gss_release_name(&min_stat, &gd->name);
597 authgss_wrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
599 struct rpc_gss_data *gd;
601 log_debug("in authgss_wrap()");
603 gd = AUTH_PRIVATE(auth);
605 if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) {
606 return ((*xdr_func)(xdrs, xdr_ptr));
608 return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr,
609 gd->ctx, gd->sec.qop,
610 gd->sec.svc, gd->gc.gc_seq));
614 authgss_unwrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
616 struct rpc_gss_data *gd;
618 log_debug("in authgss_unwrap()");
620 gd = AUTH_PRIVATE(auth);
622 if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) {
623 return ((*xdr_func)(xdrs, xdr_ptr));
625 return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr,
626 gd->ctx, gd->sec.qop,
627 gd->sec.svc, gd->gc.gc_seq));