xref: /reactos/dll/3rdparty/libtirpc/src/auth_gss.c (revision 1734f297)
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
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 *
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 *
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
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
268 authgss_nextverf(AUTH *auth)
269 {
270 	log_debug("in authgss_nextverf()");
271 	/* no action necessary */
272 }
273 
274 static bool_t
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
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 = &num;
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
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
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
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
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
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
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