1 /*
2 auth_gss.c
3
4 RPCSEC_GSS client routines.
5
6 Copyright (c) 2000 The Regents of the University of Michigan.
7 All rights reserved.
8
9 Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
10 All rights reserved, all wrongs reversed.
11
12 Redistribution and use in source and binary forms, with or without
13 modification, are permitted provided that the following conditions
14 are met:
15
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.
24
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.
36
37 */
38
39 #include <stdio.h>
40 #include <stdlib.h>
41 //#include <unistd.h>
42 #include <string.h>
43 #include <errno.h>
44 #include <rpc/types.h>
45 #include <rpc/xdr.h>
46 #include <rpc/auth.h>
47 #include <rpc/auth_gss.h>
48 #include <rpc/clnt.h>
49 #include <netinet/in.h>
50 #include <gssapi/gssapi.h>
51
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();
60
61
62 /*
63 * from mit-krb5-1.2.1 mechglue/mglueP.h:
64 * Array of context IDs typed by mechanism OID
65 */
66 typedef struct gss_union_ctx_id_t {
67 gss_OID mech_type;
68 gss_ctx_id_t internal_ctx_id;
69 } gss_union_ctx_id_desc, *gss_union_ctx_id_t;
70
71 static struct auth_ops authgss_ops = {
72 authgss_nextverf,
73 authgss_marshal,
74 authgss_validate,
75 authgss_refresh,
76 authgss_destroy,
77 authgss_wrap,
78 authgss_unwrap
79 };
80
81 #ifdef DEBUG
82
83 /* useful as i add more mechanisms */
84 void
print_rpc_gss_sec(struct rpc_gss_sec * ptr)85 print_rpc_gss_sec(struct rpc_gss_sec *ptr)
86 {
87 int i;
88 char *p;
89
90 log_debug("rpc_gss_sec:");
91 if(ptr->mech == NULL)
92 log_debug("NULL gss_OID mech");
93 else {
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 */
98 if (i == 0) {
99 int first, second;
100 if (*p < 40) {
101 first = 0;
102 second = *p;
103 }
104 else if (40 <= *p && *p < 80) {
105 first = 1;
106 second = *p - 40;
107 }
108 else if (80 <= *p && *p < 127) {
109 first = 2;
110 second = *p - 80;
111 }
112 else {
113 /* Invalid value! */
114 first = -1;
115 second = -1;
116 }
117 fprintf(stderr, " %u %u", first, second);
118 p++;
119 }
120 else {
121 fprintf(stderr, " %u", (unsigned char)*p++);
122 }
123 fprintf(stderr, " }\n");
124 }
125 fprintf(stderr, " qop: %d\n", ptr->qop);
126 fprintf(stderr, " service: %d\n", ptr->svc);
127 fprintf(stderr, " cred: %p\n", ptr->cred);
128 }
129 #endif /*DEBUG*/
130
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 */
141 };
142
143 #define AUTH_PRIVATE(auth) ((struct rpc_gss_data *)auth->ah_private)
144
145 static struct timeval AUTH_TIMEOUT = { 25, 0 };
146
147 AUTH *
authgss_create(CLIENT * clnt,gss_name_t name,struct rpc_gss_sec * sec)148 authgss_create(CLIENT *clnt, gss_name_t name, struct rpc_gss_sec *sec)
149 {
150 AUTH *auth, *save_auth;
151 struct rpc_gss_data *gd;
152 OM_uint32 min_stat = 0;
153
154 log_debug("in authgss_create()");
155
156 memset(&rpc_createerr, 0, sizeof(rpc_createerr));
157
158 if ((auth = calloc(sizeof(*auth), 1)) == NULL) {
159 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
160 rpc_createerr.cf_error.re_errno = ENOMEM;
161 return (NULL);
162 }
163 if ((gd = calloc(sizeof(*gd), 1)) == NULL) {
164 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
165 rpc_createerr.cf_error.re_errno = ENOMEM;
166 free(auth);
167 return (NULL);
168 }
169 #ifdef DEBUG
170 fprintf(stderr, "authgss_create: name is %p\n", name);
171 #endif
172 if (name != GSS_C_NO_NAME) {
173 if (gss_duplicate_name(&min_stat, name, &gd->name)
174 != GSS_S_COMPLETE) {
175 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
176 rpc_createerr.cf_error.re_errno = ENOMEM;
177 free(auth);
178 return (NULL);
179 }
180 }
181 else
182 gd->name = name;
183
184 #ifdef DEBUG
185 fprintf(stderr, "authgss_create: gd->name is %p\n", gd->name);
186 #endif
187 gd->clnt = clnt;
188 gd->ctx = GSS_C_NO_CONTEXT;
189 gd->sec = *sec;
190
191 gd->gc.gc_v = RPCSEC_GSS_VERSION;
192 gd->gc.gc_proc = RPCSEC_GSS_INIT;
193 gd->gc.gc_svc = gd->sec.svc;
194
195 auth->ah_ops = &authgss_ops;
196 auth->ah_private = (caddr_t)gd;
197
198 save_auth = clnt->cl_auth;
199 clnt->cl_auth = auth;
200
201 if (!authgss_refresh(auth))
202 auth = NULL;
203
204 clnt->cl_auth = save_auth;
205
206 return (auth);
207 }
208
209 AUTH *
authgss_create_default(CLIENT * clnt,char * service,struct rpc_gss_sec * sec)210 authgss_create_default(CLIENT *clnt, char *service, struct rpc_gss_sec *sec)
211 {
212 AUTH *auth;
213 OM_uint32 maj_stat = 0, min_stat = 0;
214 gss_buffer_desc sname;
215 gss_name_t name = GSS_C_NO_NAME;
216
217 log_debug("in authgss_create_default()");
218
219
220 sname.value = service;
221 sname.length = strlen(service);
222
223 maj_stat = gss_import_name(&min_stat, &sname,
224 (gss_OID)GSS_C_NT_HOSTBASED_SERVICE,
225 &name);
226
227 if (maj_stat != GSS_S_COMPLETE) {
228 log_status("gss_import_name", maj_stat, min_stat);
229 rpc_createerr.cf_stat = RPC_AUTHERROR;
230 return (NULL);
231 }
232
233 auth = authgss_create(clnt, name, sec);
234
235 if (name != GSS_C_NO_NAME) {
236 #ifdef DEBUG
237 fprintf(stderr, "authgss_create_default: freeing name %p\n", name);
238 #endif
239 gss_release_name(&min_stat, &name);
240 }
241
242 return (auth);
243 }
244
245 bool_t
authgss_get_private_data(AUTH * auth,struct authgss_private_data * pd)246 authgss_get_private_data(AUTH *auth, struct authgss_private_data *pd)
247 {
248 struct rpc_gss_data *gd;
249
250 log_debug("in authgss_get_private_data()");
251
252 if (!auth || !pd)
253 return (FALSE);
254
255 gd = AUTH_PRIVATE(auth);
256
257 if (!gd || !gd->established)
258 return (FALSE);
259
260 pd->pd_ctx = gd->ctx;
261 pd->pd_ctx_hndl = gd->gc.gc_ctx;
262 pd->pd_seq_win = gd->win;
263
264 return (TRUE);
265 }
266
267 static void
authgss_nextverf(AUTH * auth)268 authgss_nextverf(AUTH *auth)
269 {
270 log_debug("in authgss_nextverf()");
271 /* no action necessary */
272 }
273
274 static bool_t
authgss_marshal(AUTH * auth,XDR * xdrs)275 authgss_marshal(AUTH *auth, XDR *xdrs)
276 {
277 XDR tmpxdrs;
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;
282 bool_t xdr_stat;
283
284 log_debug("in authgss_marshal()");
285
286 gd = AUTH_PRIVATE(auth);
287
288 if (gd->established)
289 gd->gc.gc_seq++;
290
291 xdrmem_create(&tmpxdrs, tmp, sizeof(tmp), XDR_ENCODE);
292
293 if (!xdr_rpc_gss_cred(&tmpxdrs, &gd->gc)) {
294 XDR_DESTROY(&tmpxdrs);
295 return (FALSE);
296 }
297 auth->ah_cred.oa_flavor = RPCSEC_GSS;
298 auth->ah_cred.oa_base = tmp;
299 auth->ah_cred.oa_length = XDR_GETPOS(&tmpxdrs);
300
301 XDR_DESTROY(&tmpxdrs);
302
303 if (!xdr_opaque_auth(xdrs, &auth->ah_cred))
304 return (FALSE);
305
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));
309 }
310 /* Checksum serialized RPC header, up to and including credential. */
311 rpcbuf.length = XDR_GETPOS(xdrs);
312 XDR_SETPOS(xdrs, 0);
313 rpcbuf.value = XDR_INLINE(xdrs, rpcbuf.length);
314
315 maj_stat = gss_get_mic(&min_stat, gd->ctx, gd->sec.qop,
316 &rpcbuf, &checksum);
317
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);
323 }
324 return (FALSE);
325 }
326 auth->ah_verf.oa_flavor = RPCSEC_GSS;
327 auth->ah_verf.oa_base = checksum.value;
328 auth->ah_verf.oa_length = checksum.length;
329
330 xdr_stat = xdr_opaque_auth(xdrs, &auth->ah_verf);
331 gss_release_buffer(&min_stat, &checksum);
332
333 return (xdr_stat);
334 }
335
336 static bool_t
authgss_validate(AUTH * auth,struct opaque_auth * verf)337 authgss_validate(AUTH *auth, struct opaque_auth *verf)
338 {
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;
343
344 log_debug("in authgss_validate()");
345
346 gd = AUTH_PRIVATE(auth);
347
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
354 */
355 if ((gd->gc_wire_verf.value =
356 mem_alloc(verf->oa_length)) == NULL) {
357 fprintf(stderr, "gss_validate: out of memory\n");
358 return (FALSE);
359 }
360 memcpy(gd->gc_wire_verf.value, verf->oa_base, verf->oa_length);
361 gd->gc_wire_verf.length = verf->oa_length;
362 return (TRUE);
363 }
364
365 if (gd->gc.gc_proc == RPCSEC_GSS_INIT ||
366 gd->gc.gc_proc == RPCSEC_GSS_CONTINUE_INIT) {
367 num = htonl(gd->win);
368 }
369 else num = htonl(gd->gc.gc_seq);
370
371 signbuf.value = #
372 signbuf.length = sizeof(num);
373
374 checksum.value = verf->oa_base;
375 checksum.length = verf->oa_length;
376
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);
384 }
385 return (FALSE);
386 }
387 return (TRUE);
388 }
389
390 static bool_t
authgss_refresh(AUTH * auth)391 authgss_refresh(AUTH *auth)
392 {
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
398 log_debug("in authgss_refresh()");
399
400 gd = AUTH_PRIVATE(auth);
401
402 if (gd->established)
403 return (TRUE);
404
405 /* GSS context establishment loop. */
406 memset(&gr, 0, sizeof(gr));
407 recv_tokenp = GSS_C_NO_BUFFER;
408
409 #ifdef DEBUG
410 print_rpc_gss_sec(&gd->sec);
411 #endif /*DEBUG*/
412
413 for (;;) {
414 #ifdef DEBUG
415 /* print the token we just received */
416 if (recv_tokenp != GSS_C_NO_BUFFER) {
417 log_debug("The token we just received (length %d):",
418 recv_tokenp->length);
419 log_hexdump(recv_tokenp->value, recv_tokenp->length, 0);
420 }
421 #endif
422 maj_stat = gss_init_sec_context(&min_stat,
423 gd->sec.cred,
424 &gd->ctx,
425 gd->name,
426 gd->sec.mech,
427 gd->sec.req_flags,
428 0, /* time req */
429 NULL, /* channel */
430 recv_tokenp,
431 NULL, /* used mech */
432 &send_token,
433 &ret_flags,
434 NULL); /* time rec */
435
436 if (recv_tokenp != GSS_C_NO_BUFFER) {
437 gss_release_buffer(&min_stat, &gr.gr_token);
438 recv_tokenp = GSS_C_NO_BUFFER;
439 }
440 if (maj_stat != GSS_S_COMPLETE &&
441 maj_stat != GSS_S_CONTINUE_NEEDED) {
442 log_status("gss_init_sec_context", maj_stat, min_stat);
443 break;
444 }
445 if (send_token.length != 0) {
446 memset(&gr, 0, sizeof(gr));
447
448 #ifdef DEBUG
449 /* print the token we are about to send */
450 log_debug("The token being sent (length %d):",
451 send_token.length);
452 log_hexdump(send_token.value, send_token.length, 0);
453 #endif
454
455 call_stat = clnt_call(gd->clnt, NULLPROC,
456 (xdrproc_t)xdr_rpc_gss_init_args,
457 &send_token,
458 (xdrproc_t)xdr_rpc_gss_init_res,
459 (caddr_t)&gr, AUTH_TIMEOUT);
460
461 gss_release_buffer(&min_stat, &send_token);
462
463 if (call_stat != RPC_SUCCESS ||
464 (gr.gr_major != GSS_S_COMPLETE &&
465 gr.gr_major != GSS_S_CONTINUE_NEEDED))
466 return FALSE;
467
468 if (gr.gr_ctx.length != 0) {
469 if (gd->gc.gc_ctx.value)
470 gss_release_buffer(&min_stat,
471 &gd->gc.gc_ctx);
472 gd->gc.gc_ctx = gr.gr_ctx;
473 }
474 if (gr.gr_token.length != 0) {
475 if (maj_stat != GSS_S_CONTINUE_NEEDED)
476 break;
477 recv_tokenp = &gr.gr_token;
478 }
479 gd->gc.gc_proc = RPCSEC_GSS_CONTINUE_INIT;
480 }
481
482 /* GSS_S_COMPLETE => check gss header verifier,
483 * usually checked in gss_validate
484 */
485 if (maj_stat == GSS_S_COMPLETE) {
486 gss_buffer_desc bufin;
487 gss_buffer_desc bufout;
488 u_int seq, qop_state = 0;
489
490 seq = htonl(gr.gr_win);
491 bufin.value = (unsigned char *)&seq;
492 bufin.length = sizeof(seq);
493 bufout.value = (unsigned char *)gd->gc_wire_verf.value;
494 bufout.length = gd->gc_wire_verf.length;
495
496 maj_stat = gss_verify_mic(&min_stat, gd->ctx,
497 &bufin, &bufout, &qop_state);
498
499 if (maj_stat != GSS_S_COMPLETE
500 || qop_state != gd->sec.qop) {
501 log_status("gss_verify_mic", maj_stat, min_stat);
502 if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
503 gd->established = FALSE;
504 authgss_destroy_context(auth);
505 }
506 return (FALSE);
507 }
508 gd->established = TRUE;
509 gd->gc.gc_proc = RPCSEC_GSS_DATA;
510 gd->gc.gc_seq = 0;
511 gd->win = gr.gr_win;
512 break;
513 }
514 }
515 /* End context negotiation loop. */
516 if (gd->gc.gc_proc != RPCSEC_GSS_DATA) {
517 if (gr.gr_token.length != 0)
518 gss_release_buffer(&min_stat, &gr.gr_token);
519
520 authgss_destroy(auth);
521 auth = NULL;
522 rpc_createerr.cf_stat = RPC_AUTHERROR;
523
524 return (FALSE);
525 }
526 return (TRUE);
527 }
528
529 bool_t
authgss_service(AUTH * auth,int svc)530 authgss_service(AUTH *auth, int svc)
531 {
532 struct rpc_gss_data *gd;
533
534 log_debug("in authgss_service()");
535
536 if (!auth)
537 return(FALSE);
538 gd = AUTH_PRIVATE(auth);
539 if (!gd || !gd->established)
540 return (FALSE);
541 gd->sec.svc = svc;
542 gd->gc.gc_svc = svc;
543 return (TRUE);
544 }
545
546 static void
authgss_destroy_context(AUTH * auth)547 authgss_destroy_context(AUTH *auth)
548 {
549 struct rpc_gss_data *gd;
550 OM_uint32 min_stat;
551
552 log_debug("in authgss_destroy_context()");
553
554 gd = AUTH_PRIVATE(auth);
555
556 if (gd->gc.gc_ctx.length != 0) {
557 if (gd->established) {
558 gd->gc.gc_proc = RPCSEC_GSS_DESTROY;
559 clnt_call(gd->clnt, NULLPROC, (xdrproc_t)xdr_void, NULL,
560 (xdrproc_t)xdr_void, NULL, AUTH_TIMEOUT);
561 }
562 gss_release_buffer(&min_stat, &gd->gc.gc_ctx);
563 /* XXX ANDROS check size of context - should be 8 */
564 memset(&gd->gc.gc_ctx, 0, sizeof(gd->gc.gc_ctx));
565 }
566 if (gd->ctx != GSS_C_NO_CONTEXT) {
567 gss_delete_sec_context(&min_stat, &gd->ctx, NULL);
568 gd->ctx = GSS_C_NO_CONTEXT;
569 }
570
571 /* free saved wire verifier (if any) */
572 mem_free(gd->gc_wire_verf.value, gd->gc_wire_verf.length);
573 gd->gc_wire_verf.value = NULL;
574 gd->gc_wire_verf.length = 0;
575
576 gd->established = FALSE;
577 }
578
579 static void
authgss_destroy(AUTH * auth)580 authgss_destroy(AUTH *auth)
581 {
582 struct rpc_gss_data *gd;
583 OM_uint32 min_stat;
584
585 log_debug("in authgss_destroy()");
586
587 gd = AUTH_PRIVATE(auth);
588
589 authgss_destroy_context(auth);
590
591 #ifdef DEBUG
592 fprintf(stderr, "authgss_destroy: freeing name %p\n", gd->name);
593 #endif
594 if (gd->name != GSS_C_NO_NAME)
595 gss_release_name(&min_stat, &gd->name);
596
597 free(gd);
598 free(auth);
599 }
600
601 bool_t
authgss_wrap(AUTH * auth,XDR * xdrs,xdrproc_t xdr_func,caddr_t xdr_ptr)602 authgss_wrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
603 {
604 struct rpc_gss_data *gd;
605
606 log_debug("in authgss_wrap()");
607
608 gd = AUTH_PRIVATE(auth);
609
610 if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) {
611 return ((*xdr_func)(xdrs, xdr_ptr));
612 }
613 return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr,
614 gd->ctx, gd->sec.qop,
615 gd->sec.svc, gd->gc.gc_seq));
616 }
617
618 bool_t
authgss_unwrap(AUTH * auth,XDR * xdrs,xdrproc_t xdr_func,caddr_t xdr_ptr)619 authgss_unwrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
620 {
621 struct rpc_gss_data *gd;
622
623 log_debug("in authgss_unwrap()");
624
625 gd = AUTH_PRIVATE(auth);
626
627 if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) {
628 return ((*xdr_func)(xdrs, xdr_ptr));
629 }
630 return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr,
631 gd->ctx, gd->sec.qop,
632 gd->sec.svc, gd->gc.gc_seq));
633 }
634