4 Copyright (c) 2000 The Regents of the University of Michigan.
7 Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
8 All rights reserved, all wrongs reversed.
10 Redistribution and use in source and binary forms, with or without
11 modification, are permitted provided that the following conditions
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.
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.
41 #include <gssapi/gssapi.h>
43 extern SVCAUTH svc_auth_none;
46 * from mit-krb5-1.2.1 mechglue/mglueP.h:
47 * Array of context IDs typed by mechanism OID
49 typedef struct gss_union_ctx_id_t {
51 gss_ctx_id_t internal_ctx_id;
52 } gss_union_ctx_id_desc, *gss_union_ctx_id_t;
56 static bool_t svcauth_gss_wrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t);
57 static bool_t svcauth_gss_unwrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t);
58 static bool_t svcauth_gss_destroy(SVCAUTH *);
60 static struct svc_auth_ops svc_auth_gss_ops = {
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 */
78 #define SVCAUTH_PRIVATE(auth) \
79 ((struct svc_rpc_gss_data *)(auth)->svc_ah_private)
81 /* Global server credentials. */
82 static gss_cred_id_t _svcauth_gss_creds;
83 static gss_name_t _svcauth_gss_name = NULL;
86 svcauth_gss_set_svc_name(gss_name_t name)
88 OM_uint32 maj_stat, min_stat;
90 gss_log_debug("in svcauth_gss_set_svc_name()");
92 if (_svcauth_gss_name != NULL) {
93 maj_stat = gss_release_name(&min_stat, &_svcauth_gss_name);
95 if (maj_stat != GSS_S_COMPLETE) {
96 gss_log_status("svcauth_gss_set_svc_name: gss_release_name",
100 _svcauth_gss_name = NULL;
102 maj_stat = gss_duplicate_name(&min_stat, name, &_svcauth_gss_name);
104 if (maj_stat != GSS_S_COMPLETE) {
105 gss_log_status("svcauth_gss_set_svc_name: gss_duplicate_name",
114 svcauth_gss_import_name(char *service)
117 gss_buffer_desc namebuf;
118 OM_uint32 maj_stat, min_stat;
120 gss_log_debug("in svcauth_gss_import_name()");
122 namebuf.value = service;
123 namebuf.length = strlen(service);
125 maj_stat = gss_import_name(&min_stat, &namebuf,
126 (gss_OID)GSS_C_NT_HOSTBASED_SERVICE, &name);
128 if (maj_stat != GSS_S_COMPLETE) {
129 gss_log_status("svcauth_gss_import_name: gss_import_name",
133 if (svcauth_gss_set_svc_name(name) != TRUE) {
134 gss_release_name(&min_stat, &name);
141 svcauth_gss_acquire_cred(void)
143 OM_uint32 maj_stat, min_stat;
145 gss_log_debug("in svcauth_gss_acquire_cred()");
147 maj_stat = gss_acquire_cred(&min_stat, _svcauth_gss_name, 0,
148 GSS_C_NULL_OID_SET, GSS_C_ACCEPT,
149 &_svcauth_gss_creds, NULL, NULL);
151 if (maj_stat != GSS_S_COMPLETE) {
152 gss_log_status("svcauth_gss_acquire_cred: gss_acquire_cred",
160 svcauth_gss_release_cred(void)
162 OM_uint32 maj_stat, min_stat;
164 gss_log_debug("in svcauth_gss_release_cred()");
166 maj_stat = gss_release_cred(&min_stat, &_svcauth_gss_creds);
168 if (maj_stat != GSS_S_COMPLETE) {
169 gss_log_status("svcauth_gss_release_cred: gss_release_cred",
174 _svcauth_gss_creds = NULL;
180 svcauth_gss_accept_sec_context(struct svc_req *rqst,
181 struct rpc_gss_init_res *gr)
183 struct svc_rpc_gss_data *gd;
184 struct rpc_gss_cred *gc;
185 gss_buffer_desc recv_tok, seqbuf, checksum;
187 OM_uint32 maj_stat = 0, min_stat = 0, ret_flags, seq;
189 gss_log_debug("in svcauth_gss_accept_context()");
191 gd = SVCAUTH_PRIVATE(rqst->rq_xprt->xp_auth);
192 gc = (struct rpc_gss_cred *)rqst->rq_clntcred;
193 memset(gr, 0, sizeof(*gr));
195 /* Deserialize arguments. */
196 memset(&recv_tok, 0, sizeof(recv_tok));
198 if (!svc_getargs(rqst->rq_xprt, (xdrproc_t)xdr_rpc_gss_init_args,
202 gr->gr_major = gss_accept_sec_context(&gr->gr_minor,
206 GSS_C_NO_CHANNEL_BINDINGS,
214 if (gr->gr_major != GSS_S_COMPLETE &&
215 gr->gr_major != GSS_S_CONTINUE_NEEDED) {
216 gss_log_status("svcauth_gss_accept_sec_context: accept_sec_context",
217 gr->gr_major, gr->gr_minor);
218 gd->ctx = GSS_C_NO_CONTEXT;
219 gss_release_buffer(&min_stat, &gr->gr_token);
222 /* ANDROS: krb5 mechglue returns ctx of size 8 - two pointers,
223 * one to the mechanism oid, one to the internal_ctx_id */
224 if ((gr->gr_ctx.value = mem_alloc(sizeof(gss_union_ctx_id_desc))) == NULL) {
225 fprintf(stderr, "svcauth_gss_accept_context: out of memory\n");
228 memcpy(gr->gr_ctx.value, gd->ctx, sizeof(gss_union_ctx_id_desc));
229 gr->gr_ctx.length = sizeof(gss_union_ctx_id_desc);
231 /* ANDROS: change for debugging linux kernel version...
232 gr->gr_win = sizeof(gd->seqmask) * 8;
234 gr->gr_win = 0x00000005;
236 /* Save client info. */
238 gd->sec.qop = GSS_C_QOP_DEFAULT;
239 gd->sec.svc = gc->gc_svc;
240 gd->seq = gc->gc_seq;
241 gd->win = gr->gr_win;
243 if (gr->gr_major == GSS_S_COMPLETE) {
244 maj_stat = gss_display_name(&min_stat, gd->client_name,
245 &gd->cname, &gd->sec.mech);
246 if (maj_stat != GSS_S_COMPLETE) {
247 gss_log_status("svcauth_gss_accept_sec_context: display_name",
253 gss_buffer_desc mechname;
255 gss_oid_to_str(&min_stat, mech, &mechname);
257 gss_log_debug("accepted context for %.*s with "
258 "<mech %.*s, qop %d, svc %d>",
259 gd->cname.length, (char *)gd->cname.value,
260 mechname.length, (char *)mechname.value,
261 gd->sec.qop, gd->sec.svc);
263 gss_release_buffer(&min_stat, &mechname);
266 gss_log_debug("accepted context for %.*s with "
267 "<mech {}, qop %d, svc %d>",
268 gd->cname.length, (char *)gd->cname.value,
269 gd->sec.qop, gd->sec.svc);
271 seq = htonl(gr->gr_win);
273 seqbuf.length = sizeof(seq);
275 maj_stat = gss_sign(&min_stat, gd->ctx, GSS_C_QOP_DEFAULT,
278 if (maj_stat != GSS_S_COMPLETE)
281 rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;
282 rqst->rq_xprt->xp_verf.oa_base = checksum.value;
283 rqst->rq_xprt->xp_verf.oa_length = checksum.length;
289 svcauth_gss_validate(struct svc_rpc_gss_data *gd, struct rpc_msg *msg)
291 struct opaque_auth *oa;
292 gss_buffer_desc rpcbuf, checksum;
293 OM_uint32 maj_stat, min_stat, qop_state;
297 gss_log_debug("in svcauth_gss_validate()");
299 /* XXX - Reconstruct RPC header for signing (from xdr_callmsg). */
300 oa = &msg->rm_call.cb_cred;
301 if (oa->oa_length > MAX_AUTH_BYTES)
304 rpchdr = (u_char *)calloc(((8 * BYTES_PER_XDR_UNIT) +
305 RNDUP(oa->oa_length)), 1);
309 buf = (int32_t *)rpchdr;
310 IXDR_PUT_LONG(buf, msg->rm_xid);
311 IXDR_PUT_ENUM(buf, msg->rm_direction);
312 IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
313 IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
314 IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
315 IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
316 IXDR_PUT_ENUM(buf, oa->oa_flavor);
317 IXDR_PUT_LONG(buf, oa->oa_length);
319 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
320 buf += RNDUP(oa->oa_length) / sizeof(int32_t);
322 rpcbuf.value = rpchdr;
323 rpcbuf.length = (u_char *)buf - rpchdr;
325 checksum.value = msg->rm_call.cb_verf.oa_base;
326 checksum.length = msg->rm_call.cb_verf.oa_length;
328 maj_stat = gss_verify_mic(&min_stat, gd->ctx, &rpcbuf, &checksum,
333 if (maj_stat != GSS_S_COMPLETE) {
334 gss_log_status("svcauth_gss_validate: gss_verify_mic",
342 svcauth_gss_nextverf(struct svc_req *rqst, u_int num)
344 struct svc_rpc_gss_data *gd;
345 gss_buffer_desc signbuf, checksum;
346 OM_uint32 maj_stat, min_stat;
348 gss_log_debug("in svcauth_gss_nextverf()");
350 if (rqst->rq_xprt->xp_auth == NULL)
353 gd = SVCAUTH_PRIVATE(rqst->rq_xprt->xp_auth);
355 signbuf.value = #
356 signbuf.length = sizeof(num);
358 maj_stat = gss_get_mic(&min_stat, gd->ctx, gd->sec.qop,
359 &signbuf, &checksum);
361 if (maj_stat != GSS_S_COMPLETE) {
362 gss_log_status("svcauth_gss_nextverf: gss_get_mic",
366 rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;
367 rqst->rq_xprt->xp_verf.oa_base = (caddr_t)checksum.value;
368 rqst->rq_xprt->xp_verf.oa_length = (u_int)checksum.length;
374 _svcauth_gss(struct svc_req *rqst, struct rpc_msg *msg, bool_t *no_dispatch)
378 struct svc_rpc_gss_data *gd;
379 struct rpc_gss_cred *gc;
380 struct rpc_gss_init_res gr;
381 int call_stat, offset;
383 gss_log_debug("in svcauth_gss()");
385 /* Initialize reply. */
386 rqst->rq_xprt->xp_verf = _null_auth;
388 /* Allocate and set up server auth handle. */
389 if (rqst->rq_xprt->xp_auth == NULL ||
390 rqst->rq_xprt->xp_auth == &svc_auth_none) {
391 if ((auth = calloc(sizeof(*auth), 1)) == NULL) {
392 fprintf(stderr, "svcauth_gss: out_of_memory\n");
393 return (AUTH_FAILED);
395 if ((gd = calloc(sizeof(*gd), 1)) == NULL) {
396 fprintf(stderr, "svcauth_gss: out_of_memory\n");
397 return (AUTH_FAILED);
399 auth->svc_ah_ops = &svc_auth_gss_ops;
400 auth->svc_ah_private = (caddr_t) gd;
401 rqst->rq_xprt->xp_auth = auth;
403 else gd = SVCAUTH_PRIVATE(rqst->rq_xprt->xp_auth);
405 /* Deserialize client credentials. */
406 if (rqst->rq_cred.oa_length <= 0)
407 return (AUTH_BADCRED);
409 gc = (struct rpc_gss_cred *)rqst->rq_clntcred;
410 memset(gc, 0, sizeof(*gc));
412 xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
413 rqst->rq_cred.oa_length, XDR_DECODE);
415 if (!xdr_rpc_gss_cred(&xdrs, gc)) {
417 return (AUTH_BADCRED);
422 if (gc->gc_v != RPCSEC_GSS_VERSION)
423 return (AUTH_BADCRED);
425 /* Check RPCSEC_GSS service. */
426 if (gc->gc_svc != RPCSEC_GSS_SVC_NONE &&
427 gc->gc_svc != RPCSEC_GSS_SVC_INTEGRITY &&
428 gc->gc_svc != RPCSEC_GSS_SVC_PRIVACY)
429 return (AUTH_BADCRED);
431 /* Check sequence number. */
432 if (gd->established) {
433 if (gc->gc_seq > MAXSEQ)
434 return (RPCSEC_GSS_CTXPROBLEM);
436 if ((offset = gd->seqlast - gc->gc_seq) < 0) {
437 gd->seqlast = gc->gc_seq;
439 gd->seqmask <<= offset;
442 else if (offset >= gd->win || (gd->seqmask & (1 << offset))) {
444 return (RPCSEC_GSS_CTXPROBLEM);
446 gd->seq = gc->gc_seq;
447 gd->seqmask |= (1 << offset);
450 if (gd->established) {
451 rqst->rq_clntname = (char *)gd->client_name;
452 rqst->rq_svcname = (char *)gd->ctx;
455 /* Handle RPCSEC_GSS control procedure. */
456 switch (gc->gc_proc) {
458 case RPCSEC_GSS_INIT:
459 case RPCSEC_GSS_CONTINUE_INIT:
460 if (rqst->rq_proc != NULLPROC)
461 return (AUTH_FAILED); /* XXX ? */
463 if (_svcauth_gss_name == NULL) {
464 if (!svcauth_gss_import_name("nfs"))
465 return (AUTH_FAILED);
468 if (!svcauth_gss_acquire_cred())
469 return (AUTH_FAILED);
471 if (!svcauth_gss_accept_sec_context(rqst, &gr))
472 return (AUTH_REJECTEDCRED);
474 if (!svcauth_gss_nextverf(rqst, htonl(gr.gr_win)))
475 return (AUTH_FAILED);
479 call_stat = svc_sendreply(rqst->rq_xprt,
480 (xdrproc_t)xdr_rpc_gss_init_res, (caddr_t)&gr);
483 return (AUTH_FAILED);
485 if (gr.gr_major == GSS_S_COMPLETE)
486 gd->established = TRUE;
490 case RPCSEC_GSS_DATA:
491 if (!svcauth_gss_validate(gd, msg))
492 return (RPCSEC_GSS_CREDPROBLEM);
494 if (!svcauth_gss_nextverf(rqst, htonl(gc->gc_seq)))
495 return (AUTH_FAILED);
498 case RPCSEC_GSS_DESTROY:
499 if (rqst->rq_proc != NULLPROC)
500 return (AUTH_FAILED); /* XXX ? */
502 if (!svcauth_gss_validate(gd, msg))
503 return (RPCSEC_GSS_CREDPROBLEM);
505 if (!svcauth_gss_nextverf(rqst, htonl(gc->gc_seq)))
506 return (AUTH_FAILED);
508 if (!svcauth_gss_release_cred())
509 return (AUTH_FAILED);
511 SVCAUTH_DESTROY(rqst->rq_xprt->xp_auth);
512 rqst->rq_xprt->xp_auth = &svc_auth_none;
517 return (AUTH_REJECTEDCRED);
524 svcauth_gss_destroy(SVCAUTH *auth)
526 struct svc_rpc_gss_data *gd;
529 gss_log_debug("in svcauth_gss_destroy()");
531 gd = SVCAUTH_PRIVATE(auth);
533 gss_delete_sec_context(&min_stat, &gd->ctx, GSS_C_NO_BUFFER);
534 gss_release_buffer(&min_stat, &gd->cname);
537 gss_release_name(&min_stat, &gd->client_name);
539 mem_free(gd, sizeof(*gd));
540 mem_free(auth, sizeof(*auth));
546 svcauth_gss_wrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
548 struct svc_rpc_gss_data *gd;
550 gss_log_debug("in svcauth_gss_wrap()");
552 gd = SVCAUTH_PRIVATE(auth);
554 if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) {
555 return ((*xdr_func)(xdrs, xdr_ptr));
557 return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr,
558 gd->ctx, gd->sec.qop,
559 gd->sec.svc, gd->seq));
563 svcauth_gss_unwrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
565 struct svc_rpc_gss_data *gd;
567 gss_log_debug("in svcauth_gss_unwrap()");
569 gd = SVCAUTH_PRIVATE(auth);
571 if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) {
572 return ((*xdr_func)(xdrs, xdr_ptr));
574 return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr,
575 gd->ctx, gd->sec.qop,
576 gd->sec.svc, gd->seq));
580 svcauth_gss_get_principal(SVCAUTH *auth)
582 struct svc_rpc_gss_data *gd;
585 gd = SVCAUTH_PRIVATE(auth);
587 if (gd->cname.length == 0)
590 if ((pname = malloc(gd->cname.length + 1)) == NULL)
593 memcpy(pname, gd->cname.value, gd->cname.length);
594 pname[gd->cname.length] = '\0';