1 /* lib/rpc/auth_gss.c */
2 /*
3 Copyright (c) 2000 The Regents of the University of Michigan.
4 All rights reserved.
5
6 Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
7 All rights reserved, all wrongs reversed.
8
9 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions
11 are met:
12
13 1. Redistributions of source code must retain the above copyright
14 notice, this list of conditions and the following disclaimer.
15 2. Redistributions in binary form must reproduce the above copyright
16 notice, this list of conditions and the following disclaimer in the
17 documentation and/or other materials provided with the distribution.
18 3. Neither the name of the University nor the names of its
19 contributors may be used to endorse or promote products derived
20 from this software without specific prior written permission.
21
22 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
23 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
24 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
29 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
34 Id: auth_gss.c,v 1.35 2002/10/15 21:25:25 kwc Exp
35 */
36
37 /* RPCSEC_GSS client routines. */
38
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <unistd.h>
42 #include <string.h>
43 #include <errno.h>
44 #include <gssrpc/types.h>
45 #include <gssrpc/xdr.h>
46 #include <gssrpc/auth.h>
47 #include <gssrpc/auth_gss.h>
48 #include <gssrpc/clnt.h>
49 #include <netinet/in.h>
50 #ifdef HAVE_HEIMDAL
51 #include <gssapi.h>
52 #define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE
53 #else
54 #include <gssapi/gssapi.h>
55 #include <gssapi/gssapi_generic.h>
56 #endif
57
58 #ifdef DEBUG_GSSAPI
59 int auth_debug_gss = DEBUG_GSSAPI;
60 int misc_debug_gss = DEBUG_GSSAPI;
61 #endif
62
63 static void authgss_nextverf(AUTH *);
64 static bool_t authgss_marshal(AUTH *, XDR *);
65 static bool_t authgss_refresh(AUTH *, struct rpc_msg *);
66 static bool_t authgss_validate(AUTH *, struct opaque_auth *);
67 static void authgss_destroy(AUTH *);
68 static void authgss_destroy_context(AUTH *);
69 static bool_t authgss_wrap(AUTH *, XDR *, xdrproc_t, caddr_t);
70 static bool_t authgss_unwrap(AUTH *, XDR *, xdrproc_t, caddr_t);
71
72
73 /*
74 * from mit-krb5-1.2.1 mechglue/mglueP.h:
75 * Array of context IDs typed by mechanism OID
76 */
77 typedef struct gss_union_ctx_id_t {
78 gss_OID mech_type;
79 gss_ctx_id_t internal_ctx_id;
80 } gss_union_ctx_id_desc, *gss_union_ctx_id_t;
81
82 static struct auth_ops authgss_ops = {
83 authgss_nextverf,
84 authgss_marshal,
85 authgss_validate,
86 authgss_refresh,
87 authgss_destroy,
88 authgss_wrap,
89 authgss_unwrap
90 };
91
92 #ifdef DEBUG
93
94 /* useful as i add more mechanisms */
95 void
print_rpc_gss_sec(struct rpc_gss_sec * ptr)96 print_rpc_gss_sec(struct rpc_gss_sec *ptr)
97 {
98 int i;
99 char *p;
100
101 log_debug("rpc_gss_sec:");
102 if(ptr->mech == NULL)
103 log_debug("NULL gss_OID mech");
104 else {
105 fprintf(stderr, " mechanism_OID: {");
106 p = (char *)ptr->mech->elements;
107 for (i=0; i < ptr->mech->length; i++)
108 /* First byte of OIDs encoded to save a byte */
109 if (i == 0) {
110 int first, second;
111 if (*p < 40) {
112 first = 0;
113 second = *p;
114 }
115 else if (40 <= *p && *p < 80) {
116 first = 1;
117 second = *p - 40;
118 }
119 else if (80 <= *p && *p < 127) {
120 first = 2;
121 second = *p - 80;
122 }
123 else {
124 /* Invalid value! */
125 first = -1;
126 second = -1;
127 }
128 fprintf(stderr, " %u %u", first, second);
129 p++;
130 }
131 else {
132 fprintf(stderr, " %u", (unsigned char)*p++);
133 }
134 fprintf(stderr, " }\n");
135 }
136 fprintf(stderr, " qop: %d\n", ptr->qop);
137 fprintf(stderr, " service: %d\n", ptr->svc);
138 fprintf(stderr, " cred: %p\n", ptr->cred);
139 fprintf(stderr, " req_flags: 0x%08x", ptr->req_flags);
140 }
141 #endif /*DEBUG*/
142
143 struct rpc_gss_data {
144 bool_t established; /* context established */
145 bool_t inprogress;
146 gss_buffer_desc gc_wire_verf; /* save GSS_S_COMPLETE NULL RPC verfier
147 * to process at end of context negotiation*/
148 CLIENT *clnt; /* client handle */
149 gss_name_t name; /* service name */
150 struct rpc_gss_sec sec; /* security tuple */
151 gss_ctx_id_t ctx; /* context id */
152 struct rpc_gss_cred gc; /* client credentials */
153 uint32_t win; /* sequence window */
154 };
155
156 #define AUTH_PRIVATE(auth) ((struct rpc_gss_data *)auth->ah_private)
157
158 static struct timeval AUTH_TIMEOUT = { 25, 0 };
159
160 AUTH *
authgss_create(CLIENT * clnt,gss_name_t name,struct rpc_gss_sec * sec)161 authgss_create(CLIENT *clnt, gss_name_t name, struct rpc_gss_sec *sec)
162 {
163 AUTH *auth, *save_auth;
164 struct rpc_gss_data *gd;
165 OM_uint32 min_stat = 0;
166
167 log_debug("in authgss_create()");
168
169 memset(&rpc_createerr, 0, sizeof(rpc_createerr));
170
171 if ((auth = calloc(sizeof(*auth), 1)) == NULL) {
172 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
173 rpc_createerr.cf_error.re_errno = ENOMEM;
174 return (NULL);
175 }
176 if ((gd = calloc(sizeof(*gd), 1)) == NULL) {
177 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
178 rpc_createerr.cf_error.re_errno = ENOMEM;
179 free(auth);
180 return (NULL);
181 }
182 if (name != GSS_C_NO_NAME) {
183 if (gss_duplicate_name(&min_stat, name, &gd->name)
184 != GSS_S_COMPLETE) {
185 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
186 rpc_createerr.cf_error.re_errno = ENOMEM;
187 free(auth);
188 free(gd);
189 return (NULL);
190 }
191 }
192 else
193 gd->name = name;
194
195 gd->clnt = clnt;
196 gd->ctx = GSS_C_NO_CONTEXT;
197 gd->sec = *sec;
198
199 gd->gc.gc_v = RPCSEC_GSS_VERSION;
200 gd->gc.gc_proc = RPCSEC_GSS_INIT;
201 gd->gc.gc_svc = gd->sec.svc;
202
203 auth->ah_ops = &authgss_ops;
204 auth->ah_private = (caddr_t)gd;
205
206 save_auth = clnt->cl_auth;
207 clnt->cl_auth = auth;
208
209 if (!authgss_refresh(auth, NULL))
210 auth = NULL;
211
212 clnt->cl_auth = save_auth;
213
214 log_debug("authgss_create returning auth 0x%08x", auth);
215 return (auth);
216 }
217
218 AUTH *
authgss_create_default(CLIENT * clnt,char * service,struct rpc_gss_sec * sec)219 authgss_create_default(CLIENT *clnt, char *service, struct rpc_gss_sec *sec)
220 {
221 AUTH *auth;
222 OM_uint32 maj_stat = 0, min_stat = 0;
223 gss_buffer_desc sname;
224 gss_name_t name;
225
226 log_debug("in authgss_create_default()");
227
228
229 sname.value = service;
230 sname.length = strlen(service);
231
232 maj_stat = gss_import_name(&min_stat, &sname,
233 (gss_OID)gss_nt_service_name,
234 &name);
235
236 if (maj_stat != GSS_S_COMPLETE) {
237 log_status("gss_import_name", maj_stat, min_stat);
238 rpc_createerr.cf_stat = RPC_AUTHERROR;
239 return (NULL);
240 }
241
242 auth = authgss_create(clnt, name, sec);
243
244 if (name != GSS_C_NO_NAME)
245 gss_release_name(&min_stat, &name);
246
247 log_debug("authgss_create_default returning auth 0x%08x", auth);
248 return (auth);
249 }
250
251 bool_t
authgss_get_private_data(AUTH * auth,struct authgss_private_data * pd)252 authgss_get_private_data(AUTH *auth, struct authgss_private_data *pd)
253 {
254 struct rpc_gss_data *gd;
255
256 log_debug("in authgss_get_private_data()");
257
258 if (!auth || !pd)
259 return (FALSE);
260
261 gd = AUTH_PRIVATE(auth);
262
263 if (!gd || !gd->established)
264 return (FALSE);
265
266 pd->pd_ctx = gd->ctx;
267 pd->pd_ctx_hndl = gd->gc.gc_ctx;
268 pd->pd_seq_win = gd->win;
269
270 return (TRUE);
271 }
272
273 static void
authgss_nextverf(AUTH * auth)274 authgss_nextverf(AUTH *auth)
275 {
276 log_debug("in authgss_nextverf()\n");
277 /* no action necessary */
278 }
279
280 static bool_t
authgss_marshal(AUTH * auth,XDR * xdrs)281 authgss_marshal(AUTH *auth, XDR *xdrs)
282 {
283 XDR tmpxdrs;
284 char tmp[MAX_AUTH_BYTES];
285 struct rpc_gss_data *gd;
286 gss_buffer_desc rpcbuf, checksum;
287 OM_uint32 maj_stat, min_stat;
288 bool_t xdr_stat;
289
290 log_debug("in authgss_marshal()");
291
292 gd = AUTH_PRIVATE(auth);
293
294 if (gd->established)
295 gd->gc.gc_seq++;
296
297 xdrmem_create(&tmpxdrs, tmp, sizeof(tmp), XDR_ENCODE);
298
299 if (!xdr_rpc_gss_cred(&tmpxdrs, &gd->gc)) {
300 XDR_DESTROY(&tmpxdrs);
301 return (FALSE);
302 }
303 auth->ah_cred.oa_flavor = RPCSEC_GSS;
304 auth->ah_cred.oa_base = tmp;
305 auth->ah_cred.oa_length = XDR_GETPOS(&tmpxdrs);
306
307 XDR_DESTROY(&tmpxdrs);
308
309 if (!xdr_opaque_auth(xdrs, &auth->ah_cred))
310 return (FALSE);
311
312 if (gd->gc.gc_proc == RPCSEC_GSS_INIT ||
313 gd->gc.gc_proc == RPCSEC_GSS_CONTINUE_INIT) {
314 return (xdr_opaque_auth(xdrs, &gssrpc__null_auth));
315 }
316 /* Checksum serialized RPC header, up to and including credential. */
317 rpcbuf.length = XDR_GETPOS(xdrs);
318 XDR_SETPOS(xdrs, 0);
319 rpcbuf.value = XDR_INLINE(xdrs, (int)rpcbuf.length);
320
321 maj_stat = gss_get_mic(&min_stat, gd->ctx, gd->sec.qop,
322 &rpcbuf, &checksum);
323
324 if (maj_stat != GSS_S_COMPLETE) {
325 log_status("gss_get_mic", maj_stat, min_stat);
326 if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
327 gd->established = FALSE;
328 authgss_destroy_context(auth);
329 }
330 return (FALSE);
331 }
332 auth->ah_verf.oa_flavor = RPCSEC_GSS;
333 auth->ah_verf.oa_base = checksum.value;
334 auth->ah_verf.oa_length = checksum.length;
335
336 xdr_stat = xdr_opaque_auth(xdrs, &auth->ah_verf);
337 gss_release_buffer(&min_stat, &checksum);
338
339 return (xdr_stat);
340 }
341
342 static bool_t
authgss_validate(AUTH * auth,struct opaque_auth * verf)343 authgss_validate(AUTH *auth, struct opaque_auth *verf)
344 {
345 struct rpc_gss_data *gd;
346 uint32_t num;
347 gss_qop_t qop_state;
348 gss_buffer_desc signbuf, checksum;
349 OM_uint32 maj_stat, min_stat;
350
351 log_debug("in authgss_validate()");
352
353 gd = AUTH_PRIVATE(auth);
354
355 if (gd->established == FALSE) {
356 /* would like to do this only on NULL rpc - gc->established is good enough.
357 * save the on the wire verifier to validate last INIT phase packet
358 * after decode if the major status is GSS_S_COMPLETE
359 */
360 if ((gd->gc_wire_verf.value = mem_alloc(verf->oa_length)) == NULL) {
361 fprintf(stderr, "gss_validate: out of memory\n");
362 return (FALSE);
363 }
364 memcpy(gd->gc_wire_verf.value, verf->oa_base, verf->oa_length);
365 gd->gc_wire_verf.length = verf->oa_length;
366 return (TRUE);
367 }
368
369 if (gd->gc.gc_proc == RPCSEC_GSS_INIT ||
370 gd->gc.gc_proc == RPCSEC_GSS_CONTINUE_INIT) {
371 num = htonl(gd->win);
372 }
373 else num = htonl(gd->gc.gc_seq);
374
375 signbuf.value = #
376 signbuf.length = sizeof(num);
377
378 checksum.value = verf->oa_base;
379 checksum.length = verf->oa_length;
380
381 maj_stat = gss_verify_mic(&min_stat, gd->ctx, &signbuf,
382 &checksum, &qop_state);
383 if (maj_stat != GSS_S_COMPLETE || qop_state != gd->sec.qop) {
384 log_status("gss_verify_mic", maj_stat, min_stat);
385 if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
386 gd->established = FALSE;
387 authgss_destroy_context(auth);
388 }
389 return (FALSE);
390 }
391 return (TRUE);
392 }
393
394 static bool_t
authgss_refresh(AUTH * auth,struct rpc_msg * msg)395 authgss_refresh(AUTH *auth, struct rpc_msg *msg)
396 {
397 struct rpc_gss_data *gd;
398 struct rpc_gss_init_res gr;
399 gss_buffer_desc *recv_tokenp, send_token;
400 OM_uint32 maj_stat, min_stat, call_stat, ret_flags;
401
402 log_debug("in authgss_refresh()");
403
404 gd = AUTH_PRIVATE(auth);
405
406 if (gd->established || gd->inprogress)
407 return (TRUE);
408
409 /* GSS context establishment loop. */
410 memset(&gr, 0, sizeof(gr));
411 recv_tokenp = GSS_C_NO_BUFFER;
412
413 #ifdef DEBUG
414 print_rpc_gss_sec(&gd->sec);
415 #endif /*DEBUG*/
416
417 for (;;) {
418 gd->inprogress = TRUE;
419 maj_stat = gss_init_sec_context(&min_stat,
420 gd->sec.cred,
421 &gd->ctx,
422 gd->name,
423 gd->sec.mech,
424 gd->sec.req_flags,
425 0, /* time req */
426 GSS_C_NO_CHANNEL_BINDINGS,
427 recv_tokenp,
428 NULL, /* used mech */
429 &send_token,
430 &ret_flags,
431 NULL); /* time rec */
432
433 log_status("gss_init_sec_context", maj_stat, min_stat);
434 if (recv_tokenp != GSS_C_NO_BUFFER) {
435 free(gr.gr_token.value);
436 gr.gr_token.value = NULL;
437 recv_tokenp = GSS_C_NO_BUFFER;
438 }
439 if (maj_stat != GSS_S_COMPLETE &&
440 maj_stat != GSS_S_CONTINUE_NEEDED) {
441 log_status("gss_init_sec_context (error)", maj_stat, min_stat);
442 break;
443 }
444 if (send_token.length != 0) {
445 memset(&gr, 0, sizeof(gr));
446
447 call_stat = clnt_call(gd->clnt, NULLPROC,
448 xdr_rpc_gss_init_args,
449 &send_token,
450 xdr_rpc_gss_init_res,
451 (caddr_t)&gr, AUTH_TIMEOUT);
452
453 gss_release_buffer(&min_stat, &send_token);
454
455 log_debug("authgss_refresh: call_stat=%d", call_stat);
456 log_debug("%s", clnt_sperror(gd->clnt, "authgss_refresh"));
457 if (call_stat != RPC_SUCCESS ||
458 (gr.gr_major != GSS_S_COMPLETE &&
459 gr.gr_major != GSS_S_CONTINUE_NEEDED))
460 break;
461
462 if (gr.gr_ctx.length != 0) {
463 free(gd->gc.gc_ctx.value);
464 gd->gc.gc_ctx = gr.gr_ctx;
465 }
466 if (gr.gr_token.length != 0) {
467 if (maj_stat != GSS_S_CONTINUE_NEEDED)
468 break;
469 recv_tokenp = &gr.gr_token;
470 }
471 gd->gc.gc_proc = RPCSEC_GSS_CONTINUE_INIT;
472 }
473
474 /* GSS_S_COMPLETE => check gss header verifier, usually checked in
475 * gss_validate
476 */
477 if (maj_stat == GSS_S_COMPLETE) {
478 gss_buffer_desc bufin;
479 gss_buffer_desc bufout;
480 uint32_t seq;
481 gss_qop_t qop_state = 0;
482
483 seq = htonl(gr.gr_win);
484 bufin.value = (u_char *)&seq;
485 bufin.length = sizeof(seq);
486 bufout.value = (u_char *)gd->gc_wire_verf.value;
487 bufout.length = gd->gc_wire_verf.length;
488
489 log_debug("authgss_refresh: GSS_S_COMPLETE: calling verify_mic");
490 maj_stat = gss_verify_mic(&min_stat,gd->ctx,
491 &bufin, &bufout, &qop_state);
492 free(gd->gc_wire_verf.value);
493 gd->gc_wire_verf.length = 0;
494 gd->gc_wire_verf.value = NULL;
495
496 if (maj_stat != GSS_S_COMPLETE || qop_state != gd->sec.qop) {
497 log_status("gss_verify_mic", maj_stat, min_stat);
498 if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
499 gd->established = FALSE;
500 authgss_destroy_context(auth);
501 }
502 return (FALSE);
503 }
504 gd->established = TRUE;
505 gd->inprogress = FALSE;
506 gd->gc.gc_proc = RPCSEC_GSS_DATA;
507 gd->gc.gc_seq = 0;
508 gd->win = gr.gr_win;
509 break;
510 }
511 }
512 log_status("authgss_refresh: at end of context negotiation", maj_stat, min_stat);
513 /* End context negotiation loop. */
514 if (gd->gc.gc_proc != RPCSEC_GSS_DATA) {
515 log_debug("authgss_refresh: returning ERROR (gc_proc %d)", gd->gc.gc_proc);
516 free(gr.gr_token.value);
517 authgss_destroy(auth);
518 auth = NULL;
519 rpc_createerr.cf_stat = RPC_AUTHERROR;
520
521 return (FALSE);
522 }
523 log_debug("authgss_refresh: returning SUCCESS");
524 return (TRUE);
525 }
526
527 bool_t
authgss_service(AUTH * auth,int svc)528 authgss_service(AUTH *auth, int svc)
529 {
530 struct rpc_gss_data *gd;
531
532 log_debug("in authgss_service()");
533
534 if (!auth)
535 return(FALSE);
536 gd = AUTH_PRIVATE(auth);
537 if (!gd || !gd->established)
538 return (FALSE);
539 gd->sec.svc = svc;
540 gd->gc.gc_svc = svc;
541 return (TRUE);
542 }
543
544 static void
authgss_destroy_context(AUTH * auth)545 authgss_destroy_context(AUTH *auth)
546 {
547 struct rpc_gss_data *gd;
548 OM_uint32 min_stat;
549
550 log_debug("in authgss_destroy_context()");
551
552 gd = AUTH_PRIVATE(auth);
553
554 if (gd->gc.gc_ctx.length != 0) {
555 if (gd->established) {
556 gd->gc.gc_proc = RPCSEC_GSS_DESTROY;
557 (void)clnt_call(gd->clnt, NULLPROC, xdr_void, NULL,
558 xdr_void, NULL, AUTH_TIMEOUT);
559 log_debug("%s",
560 clnt_sperror(gd->clnt,
561 "authgss_destroy_context"));
562 }
563 free(gd->gc.gc_ctx.value);
564 /* XXX ANDROS check size of context - should be 8 */
565 memset(&gd->gc.gc_ctx, 0, sizeof(gd->gc.gc_ctx));
566 }
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;
570 }
571 gd->established = FALSE;
572
573 log_debug("finished authgss_destroy_context()");
574 }
575
576 static void
authgss_destroy(AUTH * auth)577 authgss_destroy(AUTH *auth)
578 {
579 struct rpc_gss_data *gd;
580 OM_uint32 min_stat;
581
582 log_debug("in authgss_destroy()");
583
584 gd = AUTH_PRIVATE(auth);
585
586 authgss_destroy_context(auth);
587
588 if (gd->name != GSS_C_NO_NAME)
589 gss_release_name(&min_stat, &gd->name);
590
591 free(gd);
592 free(auth);
593 }
594
595 bool_t
authgss_wrap(AUTH * auth,XDR * xdrs,xdrproc_t xdr_func,caddr_t xdr_ptr)596 authgss_wrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
597 {
598 struct rpc_gss_data *gd;
599
600 log_debug("in authgss_wrap()");
601
602 gd = AUTH_PRIVATE(auth);
603
604 if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) {
605 return ((*xdr_func)(xdrs, xdr_ptr));
606 }
607 return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr,
608 gd->ctx, gd->sec.qop,
609 gd->sec.svc, gd->gc.gc_seq));
610 }
611
612 bool_t
authgss_unwrap(AUTH * auth,XDR * xdrs,xdrproc_t xdr_func,caddr_t xdr_ptr)613 authgss_unwrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
614 {
615 struct rpc_gss_data *gd;
616
617 log_debug("in authgss_unwrap()");
618
619 gd = AUTH_PRIVATE(auth);
620
621 if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) {
622 return ((*xdr_func)(xdrs, xdr_ptr));
623 }
624 return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr,
625 gd->ctx, gd->sec.qop,
626 gd->sec.svc, gd->gc.gc_seq));
627 }
628