1 /*
2   svc_auth_gss.c
3 
4   Copyright (c) 2000 The Regents of the University of Michigan.
5   All rights reserved.
6 
7   Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
8   All rights reserved, all wrongs reversed.
9 
10   Redistribution and use in source and binary forms, with or without
11   modification, are permitted provided that the following conditions
12   are met:
13 
14   1. Redistributions of source code must retain the above copyright
15      notice, this list of conditions and the following disclaimer.
16   2. Redistributions in binary form must reproduce the above copyright
17      notice, this list of conditions and the following disclaimer in the
18      documentation and/or other materials provided with the distribution.
19   3. Neither the name of the University nor the names of its
20      contributors may be used to endorse or promote products derived
21      from this software without specific prior written permission.
22 
23   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
24   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26   DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
30   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 
35  */
36 #include <wintirpc.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <rpc/rpc.h>
41 #include <gssapi/gssapi.h>
42 
43 extern SVCAUTH svc_auth_none;
44 
45 /*
46  * from mit-krb5-1.2.1 mechglue/mglueP.h:
47  * Array of context IDs typed by mechanism OID
48  */
49 typedef struct gss_union_ctx_id_t {
50   gss_OID     mech_type;
51   gss_ctx_id_t    internal_ctx_id;
52 } gss_union_ctx_id_desc, *gss_union_ctx_id_t;
53 
54 
55 
56 static bool_t	svcauth_gss_destroy();
57 static bool_t   svcauth_gss_wrap();
58 static bool_t   svcauth_gss_unwrap();
59 
60 struct svc_auth_ops svc_auth_gss_ops = {
61 	svcauth_gss_wrap,
62 	svcauth_gss_unwrap,
63 	svcauth_gss_destroy
64 };
65 
66 struct svc_rpc_gss_data {
67 	bool_t			established;	/* context established */
68 	gss_ctx_id_t		ctx;		/* context id */
69 	struct rpc_gss_sec	sec;		/* security triple */
70 	gss_buffer_desc		cname;		/* GSS client name */
71 	u_int			seq;		/* sequence number */
72 	u_int			win;		/* sequence window */
73 	u_int			seqlast;	/* last sequence number */
74 	u_int32_t		seqmask;	/* bitmask of seqnums */
75 	gss_name_t		client_name;	/* unparsed name string */
76 };
77 
78 #define SVCAUTH_PRIVATE(auth) \
79 	((struct svc_rpc_gss_data *)(auth)->svc_ah_private)
80 
81 /* Global server credentials. */
82 gss_cred_id_t		_svcauth_gss_creds;
83 static gss_name_t	_svcauth_gss_name = NULL;
84 
85 bool_t
86 svcauth_gss_set_svc_name(gss_name_t name)
87 {
88 	OM_uint32	maj_stat, min_stat;
89 
90 	log_debug("in svcauth_gss_set_svc_name()");
91 
92 	if (_svcauth_gss_name != NULL) {
93 		maj_stat = gss_release_name(&min_stat, &_svcauth_gss_name);
94 
95 		if (maj_stat != GSS_S_COMPLETE) {
96 			log_status("gss_release_name", maj_stat, min_stat);
97 			return (FALSE);
98 		}
99 		_svcauth_gss_name = NULL;
100 	}
101 	maj_stat = gss_duplicate_name(&min_stat, name, &_svcauth_gss_name);
102 
103 	if (maj_stat != GSS_S_COMPLETE) {
104 		log_status("gss_duplicate_name", maj_stat, min_stat);
105 		return (FALSE);
106 	}
107 
108 	return (TRUE);
109 }
110 
111 static bool_t
112 svcauth_gss_import_name(char *service)
113 {
114 	gss_name_t	name;
115 	gss_buffer_desc	namebuf;
116 	OM_uint32	maj_stat, min_stat;
117 
118 	log_debug("in svcauth_gss_import_name()");
119 
120 	namebuf.value = service;
121 	namebuf.length = strlen(service);
122 
123 	maj_stat = gss_import_name(&min_stat, &namebuf,
124 				   (gss_OID)GSS_C_NT_HOSTBASED_SERVICE, &name);
125 
126 	if (maj_stat != GSS_S_COMPLETE) {
127 		log_status("gss_import_name", maj_stat, min_stat);
128 		return (FALSE);
129 	}
130 	if (svcauth_gss_set_svc_name(name) != TRUE) {
131 		gss_release_name(&min_stat, &name);
132 		return (FALSE);
133 	}
134 	return (TRUE);
135 }
136 
137 static bool_t
138 svcauth_gss_acquire_cred(void)
139 {
140 	OM_uint32	maj_stat, min_stat;
141 
142 	log_debug("in svcauth_gss_acquire_cred()");
143 
144 	maj_stat = gss_acquire_cred(&min_stat, _svcauth_gss_name, 0,
145 				    GSS_C_NULL_OID_SET, GSS_C_ACCEPT,
146 				    &_svcauth_gss_creds, NULL, NULL);
147 
148 	if (maj_stat != GSS_S_COMPLETE) {
149 		log_status("gss_acquire_cred", maj_stat, min_stat);
150 		return (FALSE);
151 	}
152 	return (TRUE);
153 }
154 
155 static bool_t
156 svcauth_gss_release_cred(void)
157 {
158 	OM_uint32	maj_stat, min_stat;
159 
160 	log_debug("in svcauth_gss_release_cred()");
161 
162 	maj_stat = gss_release_cred(&min_stat, &_svcauth_gss_creds);
163 
164 	if (maj_stat != GSS_S_COMPLETE) {
165 		log_status("gss_release_cred", maj_stat, min_stat);
166 		return (FALSE);
167 	}
168 
169 	_svcauth_gss_creds = NULL;
170 
171 	return (TRUE);
172 }
173 
174 static bool_t
175 svcauth_gss_accept_sec_context(struct svc_req *rqst,
176 			       struct rpc_gss_init_res *gr)
177 {
178 	struct svc_rpc_gss_data	*gd;
179 	struct rpc_gss_cred	*gc;
180 	gss_buffer_desc		 recv_tok, seqbuf, checksum;
181 	gss_OID			 mech;
182 	OM_uint32		 maj_stat = 0, min_stat = 0, ret_flags, seq;
183 
184 	log_debug("in svcauth_gss_accept_context()");
185 
186 	gd = SVCAUTH_PRIVATE(rqst->rq_xprt->xp_auth);
187 	gc = (struct rpc_gss_cred *)rqst->rq_clntcred;
188 	memset(gr, 0, sizeof(*gr));
189 
190 	/* Deserialize arguments. */
191 	memset(&recv_tok, 0, sizeof(recv_tok));
192 
193 	if (!svc_getargs(rqst->rq_xprt, (xdrproc_t)xdr_rpc_gss_init_args,
194 			 (caddr_t)&recv_tok))
195 		return (FALSE);
196 
197 	gr->gr_major = gss_accept_sec_context(&gr->gr_minor,
198 					      &gd->ctx,
199 					      _svcauth_gss_creds,
200 					      &recv_tok,
201 					      GSS_C_NO_CHANNEL_BINDINGS,
202 					      &gd->client_name,
203 					      &mech,
204 					      &gr->gr_token,
205 					      &ret_flags,
206 					      NULL,
207 					      NULL);
208 
209 	if (gr->gr_major != GSS_S_COMPLETE &&
210 	    gr->gr_major != GSS_S_CONTINUE_NEEDED) {
211 		log_status("accept_sec_context", gr->gr_major, gr->gr_minor);
212 		gd->ctx = GSS_C_NO_CONTEXT;
213 		gss_release_buffer(&min_stat, &gr->gr_token);
214 		return (FALSE);
215 	}
216 	/* ANDROS: krb5 mechglue returns ctx of size 8 - two pointers,
217 	 * one to the mechanism oid, one to the internal_ctx_id */
218 	if ((gr->gr_ctx.value = mem_alloc(sizeof(gss_union_ctx_id_desc))) == NULL) {
219 		fprintf(stderr, "svcauth_gss_accept_context: out of memory\n");
220 		return (FALSE);
221 	}
222 	memcpy(gr->gr_ctx.value, gd->ctx, sizeof(gss_union_ctx_id_desc));
223 	gr->gr_ctx.length = sizeof(gss_union_ctx_id_desc);
224 
225 	/* ANDROS: change for debugging linux kernel version...
226 	gr->gr_win = sizeof(gd->seqmask) * 8;
227 	*/
228 	gr->gr_win = 0x00000005;
229 
230 	/* Save client info. */
231 	gd->sec.mech = mech;
232 	gd->sec.qop = GSS_C_QOP_DEFAULT;
233 	gd->sec.svc = gc->gc_svc;
234 	gd->seq = gc->gc_seq;
235 	gd->win = gr->gr_win;
236 
237 	if (gr->gr_major == GSS_S_COMPLETE) {
238 		maj_stat = gss_display_name(&min_stat, gd->client_name,
239 					    &gd->cname, &gd->sec.mech);
240 		if (maj_stat != GSS_S_COMPLETE) {
241 			log_status("display_name", maj_stat, min_stat);
242 			return (FALSE);
243 		}
244 #ifdef DEBUG
245 #ifdef HAVE_KRB5
246 		{
247 			gss_buffer_desc mechname;
248 
249 			gss_oid_to_str(&min_stat, mech, &mechname);
250 
251 			log_debug("accepted context for %.*s with "
252 				  "<mech %.*s, qop %d, svc %d>",
253 				  gd->cname.length, (char *)gd->cname.value,
254 				  mechname.length, (char *)mechname.value,
255 				  gd->sec.qop, gd->sec.svc);
256 
257 			gss_release_buffer(&min_stat, &mechname);
258 		}
259 #elif HAVE_HEIMDAL
260 		log_debug("accepted context for %.*s with "
261 			  "<mech {}, qop %d, svc %d>",
262 			  gd->cname.length, (char *)gd->cname.value,
263 			  gd->sec.qop, gd->sec.svc);
264 #endif
265 #endif /* DEBUG */
266 		seq = htonl(gr->gr_win);
267 		seqbuf.value = &seq;
268 		seqbuf.length = sizeof(seq);
269 
270 		maj_stat = gss_sign(&min_stat, gd->ctx, GSS_C_QOP_DEFAULT,
271 				    &seqbuf, &checksum);
272 
273 		if (maj_stat != GSS_S_COMPLETE)
274 			return (FALSE);
275 
276 		rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;
277 		rqst->rq_xprt->xp_verf.oa_base = checksum.value;
278 		rqst->rq_xprt->xp_verf.oa_length = checksum.length;
279 	}
280 	return (TRUE);
281 }
282 
283 static bool_t
284 svcauth_gss_validate(struct svc_rpc_gss_data *gd, struct rpc_msg *msg)
285 {
286 	struct opaque_auth	*oa;
287 	gss_buffer_desc		 rpcbuf, checksum;
288 	OM_uint32		 maj_stat, min_stat, qop_state;
289 	u_char			 rpchdr[128];
290 	int32_t			*buf;
291 
292 	log_debug("in svcauth_gss_validate()");
293 
294 	memset(rpchdr, 0, sizeof(rpchdr));
295 
296 	/* XXX - Reconstruct RPC header for signing (from xdr_callmsg). */
297 	oa = &msg->rm_call.cb_cred;
298 	if (oa->oa_length > MAX_AUTH_BYTES)
299 		return (FALSE);
300 
301 	/* 8 XDR units from the IXDR macro calls. */
302 	if (sizeof(rpchdr) < (8 * BYTES_PER_XDR_UNIT +
303 			RNDUP(oa->oa_length)))
304 		return (FALSE);
305 
306 	buf = (int32_t *)rpchdr;
307 	IXDR_PUT_LONG(buf, msg->rm_xid);
308 	IXDR_PUT_ENUM(buf, msg->rm_direction);
309 	IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
310 	IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
311 	IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
312 	IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
313 	IXDR_PUT_ENUM(buf, oa->oa_flavor);
314 	IXDR_PUT_LONG(buf, oa->oa_length);
315 	if (oa->oa_length) {
316 		memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
317 		buf += RNDUP(oa->oa_length) / sizeof(int32_t);
318 	}
319 	rpcbuf.value = rpchdr;
320 	rpcbuf.length = (u_char *)buf - rpchdr;
321 
322 	checksum.value = msg->rm_call.cb_verf.oa_base;
323 	checksum.length = msg->rm_call.cb_verf.oa_length;
324 
325 	maj_stat = gss_verify_mic(&min_stat, gd->ctx, &rpcbuf, &checksum,
326 				  &qop_state);
327 
328 	if (maj_stat != GSS_S_COMPLETE) {
329 		log_status("gss_verify_mic", maj_stat, min_stat);
330 		return (FALSE);
331 	}
332 	return (TRUE);
333 }
334 
335 bool_t
336 svcauth_gss_nextverf(struct svc_req *rqst, u_int num)
337 {
338 	struct svc_rpc_gss_data	*gd;
339 	gss_buffer_desc		 signbuf, checksum;
340 	OM_uint32		 maj_stat, min_stat;
341 
342 	log_debug("in svcauth_gss_nextverf()");
343 
344 	if (rqst->rq_xprt->xp_auth == NULL)
345 		return (FALSE);
346 
347 	gd = SVCAUTH_PRIVATE(rqst->rq_xprt->xp_auth);
348 
349 	signbuf.value = &num;
350 	signbuf.length = sizeof(num);
351 
352 	maj_stat = gss_get_mic(&min_stat, gd->ctx, gd->sec.qop,
353 			       &signbuf, &checksum);
354 
355 	if (maj_stat != GSS_S_COMPLETE) {
356 		log_status("gss_get_mic", maj_stat, min_stat);
357 		return (FALSE);
358 	}
359 	rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;
360 	rqst->rq_xprt->xp_verf.oa_base = (caddr_t)checksum.value;
361 	rqst->rq_xprt->xp_verf.oa_length = (u_int)checksum.length;
362 
363 	return (TRUE);
364 }
365 
366 enum auth_stat
367 _svcauth_gss(struct svc_req *rqst, struct rpc_msg *msg, bool_t *no_dispatch)
368 {
369 	XDR	 		 xdrs;
370 	SVCAUTH			*auth;
371 	struct svc_rpc_gss_data	*gd;
372 	struct rpc_gss_cred	*gc;
373 	struct rpc_gss_init_res	 gr;
374 	int			 call_stat, offset;
375 
376 	log_debug("in svcauth_gss()");
377 
378 	/* Initialize reply. */
379 	rqst->rq_xprt->xp_verf = _null_auth;
380 
381 	/* Allocate and set up server auth handle. */
382 	if (rqst->rq_xprt->xp_auth == NULL ||
383 	    rqst->rq_xprt->xp_auth == &svc_auth_none) {
384 		if ((auth = calloc(sizeof(*auth), 1)) == NULL) {
385 			fprintf(stderr, "svcauth_gss: out_of_memory\n");
386 			return (AUTH_FAILED);
387 		}
388 		if ((gd = calloc(sizeof(*gd), 1)) == NULL) {
389 			fprintf(stderr, "svcauth_gss: out_of_memory\n");
390 			return (AUTH_FAILED);
391 		}
392 		auth->svc_ah_ops = &svc_auth_gss_ops;
393 		auth->svc_ah_private = (caddr_t) gd;
394 		rqst->rq_xprt->xp_auth = auth;
395 	}
396 	else gd = SVCAUTH_PRIVATE(rqst->rq_xprt->xp_auth);
397 
398 	/* Deserialize client credentials. */
399 	if (rqst->rq_cred.oa_length <= 0)
400 		return (AUTH_BADCRED);
401 
402 	gc = (struct rpc_gss_cred *)rqst->rq_clntcred;
403 	memset(gc, 0, sizeof(*gc));
404 
405 	xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
406 		      rqst->rq_cred.oa_length, XDR_DECODE);
407 
408 	if (!xdr_rpc_gss_cred(&xdrs, gc)) {
409 		XDR_DESTROY(&xdrs);
410 		return (AUTH_BADCRED);
411 	}
412 	XDR_DESTROY(&xdrs);
413 
414 	/* Check version. */
415 	if (gc->gc_v != RPCSEC_GSS_VERSION)
416 		return (AUTH_BADCRED);
417 
418 	/* Check RPCSEC_GSS service. */
419 	if (gc->gc_svc != RPCSEC_GSS_SVC_NONE &&
420 	    gc->gc_svc != RPCSEC_GSS_SVC_INTEGRITY &&
421 	    gc->gc_svc != RPCSEC_GSS_SVC_PRIVACY)
422 		return (AUTH_BADCRED);
423 
424 	/* Check sequence number. */
425 	if (gd->established) {
426 		if (gc->gc_seq > MAXSEQ)
427 			return (RPCSEC_GSS_CTXPROBLEM);
428 
429 		if ((offset = gd->seqlast - gc->gc_seq) < 0) {
430 			gd->seqlast = gc->gc_seq;
431 			offset = 0 - offset;
432 			gd->seqmask <<= offset;
433 			offset = 0;
434 		}
435 		else if (offset >= gd->win || (gd->seqmask & (1 << offset))) {
436 			*no_dispatch = 1;
437 			return (RPCSEC_GSS_CTXPROBLEM);
438 		}
439 		gd->seq = gc->gc_seq;
440 		gd->seqmask |= (1 << offset);
441 	}
442 
443 	if (gd->established) {
444 		rqst->rq_clntname = (char *)gd->client_name;
445 		rqst->rq_svcname = (char *)gd->ctx;
446 	}
447 
448 	/* Handle RPCSEC_GSS control procedure. */
449 	switch (gc->gc_proc) {
450 
451 	case RPCSEC_GSS_INIT:
452 	case RPCSEC_GSS_CONTINUE_INIT:
453 		if (rqst->rq_proc != NULLPROC)
454 			return (AUTH_FAILED);		/* XXX ? */
455 
456 		if (_svcauth_gss_name == NULL) {
457 			if (!svcauth_gss_import_name("nfs"))
458 				return (AUTH_FAILED);
459 		}
460 
461 		if (!svcauth_gss_acquire_cred())
462 			return (AUTH_FAILED);
463 
464 		if (!svcauth_gss_accept_sec_context(rqst, &gr))
465 			return (AUTH_REJECTEDCRED);
466 
467 		if (!svcauth_gss_nextverf(rqst, htonl(gr.gr_win)))
468 			return (AUTH_FAILED);
469 
470 		*no_dispatch = TRUE;
471 
472 		call_stat = svc_sendreply(rqst->rq_xprt,
473 			(xdrproc_t)xdr_rpc_gss_init_res, (caddr_t)&gr);
474 
475 		if (!call_stat)
476 			return (AUTH_FAILED);
477 
478 		if (gr.gr_major == GSS_S_COMPLETE)
479 			gd->established = TRUE;
480 
481 		break;
482 
483 	case RPCSEC_GSS_DATA:
484 		if (!svcauth_gss_validate(gd, msg))
485 			return (RPCSEC_GSS_CREDPROBLEM);
486 
487 		if (!svcauth_gss_nextverf(rqst, htonl(gc->gc_seq)))
488 			return (AUTH_FAILED);
489 		break;
490 
491 	case RPCSEC_GSS_DESTROY:
492 		if (rqst->rq_proc != NULLPROC)
493 			return (AUTH_FAILED);		/* XXX ? */
494 
495 		if (!svcauth_gss_validate(gd, msg))
496 			return (RPCSEC_GSS_CREDPROBLEM);
497 
498 		if (!svcauth_gss_nextverf(rqst, htonl(gc->gc_seq)))
499 			return (AUTH_FAILED);
500 
501 		if (!svcauth_gss_release_cred())
502 			return (AUTH_FAILED);
503 
504 		SVCAUTH_DESTROY(rqst->rq_xprt->xp_auth);
505 		rqst->rq_xprt->xp_auth = &svc_auth_none;
506 
507 		break;
508 
509 	default:
510 		return (AUTH_REJECTEDCRED);
511 		break;
512 	}
513 	return (AUTH_OK);
514 }
515 
516 bool_t
517 svcauth_gss_destroy(SVCAUTH *auth)
518 {
519 	struct svc_rpc_gss_data	*gd;
520 	OM_uint32		 min_stat;
521 
522 	log_debug("in svcauth_gss_destroy()");
523 
524 	gd = SVCAUTH_PRIVATE(auth);
525 
526 	gss_delete_sec_context(&min_stat, &gd->ctx, GSS_C_NO_BUFFER);
527 	gss_release_buffer(&min_stat, &gd->cname);
528 
529 	if (gd->client_name)
530 		gss_release_name(&min_stat, &gd->client_name);
531 
532 	mem_free(gd, sizeof(*gd));
533 	mem_free(auth, sizeof(*auth));
534 
535 	return (TRUE);
536 }
537 
538 bool_t
539 svcauth_gss_wrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
540 {
541 	struct svc_rpc_gss_data	*gd;
542 
543 	log_debug("in svcauth_gss_wrap()");
544 
545 	gd = SVCAUTH_PRIVATE(auth);
546 
547 	if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) {
548 		return ((*xdr_func)(xdrs, xdr_ptr));
549 	}
550 	return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr,
551 				 gd->ctx, gd->sec.qop,
552 				 gd->sec.svc, gd->seq));
553 }
554 
555 bool_t
556 svcauth_gss_unwrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
557 {
558 	struct svc_rpc_gss_data	*gd;
559 
560 	log_debug("in svcauth_gss_unwrap()");
561 
562 	gd = SVCAUTH_PRIVATE(auth);
563 
564 	if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) {
565 		return ((*xdr_func)(xdrs, xdr_ptr));
566 	}
567 	return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr,
568 				 gd->ctx, gd->sec.qop,
569 				 gd->sec.svc, gd->seq));
570 }
571 
572 char *
573 svcauth_gss_get_principal(SVCAUTH *auth)
574 {
575 	struct svc_rpc_gss_data *gd;
576 	char *pname;
577 
578 	gd = SVCAUTH_PRIVATE(auth);
579 
580 	if (gd->cname.length == 0)
581 		return (NULL);
582 
583 	if ((pname = malloc(gd->cname.length + 1)) == NULL)
584 		return (NULL);
585 
586 	memcpy(pname, gd->cname.value, gd->cname.length);
587 	pname[gd->cname.length] = '\0';
588 
589 	return (pname);
590 }
591