2 * Copyright (c) 2008 Doug Rabson
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 SPDX-License-Identifier: BSD-3-Clause
33 RPCSEC_GSS client routines.
35 Copyright (c) 2000 The Regents of the University of Michigan.
38 Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
39 All rights reserved, all wrongs reversed.
41 Redistribution and use in source and binary forms, with or without
42 modification, are permitted provided that the following conditions
45 1. Redistributions of source code must retain the above copyright
46 notice, this list of conditions and the following disclaimer.
47 2. Redistributions in binary form must reproduce the above copyright
48 notice, this list of conditions and the following disclaimer in the
49 documentation and/or other materials provided with the distribution.
50 3. Neither the name of the University nor the names of its
51 contributors may be used to endorse or promote products derived
52 from this software without specific prior written permission.
54 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
55 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
56 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
57 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
58 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
59 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
60 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
61 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
62 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
63 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
64 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
66 $Id: auth_gss.c,v 1.32 2002/01/15 15:43:00 andros Exp $
74 #include <netinet/in.h>
76 #include <rpc/rpcsec_gss.h>
77 #include "rpcsec_gss_int.h"
79 static void rpc_gss_nextverf(AUTH*);
80 static bool_t rpc_gss_marshal(AUTH *, XDR *);
81 static bool_t rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret);
82 static bool_t rpc_gss_refresh(AUTH *, void *);
83 static bool_t rpc_gss_validate(AUTH *, struct opaque_auth *);
84 static void rpc_gss_destroy(AUTH *);
85 static void rpc_gss_destroy_context(AUTH *, bool_t);
87 static struct auth_ops rpc_gss_ops = {
95 enum rpcsec_gss_state {
98 RPCSEC_GSS_ESTABLISHED
101 struct rpc_gss_data {
102 rpc_gss_options_req_t gd_options; /* GSS context options */
103 enum rpcsec_gss_state gd_state; /* connection state */
104 gss_buffer_desc gd_verf; /* save GSS_S_COMPLETE
105 * NULL RPC verfier to
107 * context negotiation */
108 CLIENT *gd_clnt; /* client handle */
109 gss_name_t gd_name; /* service name */
110 gss_OID gd_mech; /* mechanism to use */
111 gss_qop_t gd_qop; /* quality of protection */
112 gss_ctx_id_t gd_ctx; /* context id */
113 struct rpc_gss_cred gd_cred; /* client credentials */
114 u_int gd_win; /* sequence window */
117 #define AUTH_PRIVATE(auth) ((struct rpc_gss_data *)auth->ah_private)
119 static struct timeval AUTH_TIMEOUT = { 25, 0 };
122 rpc_gss_seccreate(CLIENT *clnt, const char *principal,
123 const char *mechanism, rpc_gss_service_t service, const char *qop,
124 rpc_gss_options_req_t *options_req, rpc_gss_options_ret_t *options_ret)
126 AUTH *auth, *save_auth;
127 rpc_gss_options_ret_t options;
130 struct rpc_gss_data *gd;
131 OM_uint32 maj_stat = 0, min_stat = 0;
132 gss_buffer_desc principal_desc;
135 * Bail out now if we don't know this mechanism.
137 if (!rpc_gss_mech_to_oid(mechanism, &oid))
141 if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num))
144 qop_num = GSS_C_QOP_DEFAULT;
148 * If the caller doesn't want the options, point at local
149 * storage to simplify the code below.
152 options_ret = &options;
155 * Default service is integrity.
157 if (service == rpc_gss_svc_default)
158 service = rpc_gss_svc_integrity;
160 memset(options_ret, 0, sizeof(*options_ret));
162 log_debug("in rpc_gss_seccreate()");
164 memset(&rpc_createerr, 0, sizeof(rpc_createerr));
166 auth = mem_alloc(sizeof(*auth));
168 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
169 rpc_createerr.cf_error.re_errno = ENOMEM;
172 gd = mem_alloc(sizeof(*gd));
174 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
175 rpc_createerr.cf_error.re_errno = ENOMEM;
180 auth->ah_ops = &rpc_gss_ops;
181 auth->ah_private = (caddr_t) gd;
182 auth->ah_cred.oa_flavor = RPCSEC_GSS;
184 principal_desc.value = (void *)(intptr_t) principal;
185 principal_desc.length = strlen(principal);
186 maj_stat = gss_import_name(&min_stat, &principal_desc,
187 GSS_C_NT_HOSTBASED_SERVICE, &gd->gd_name);
188 if (maj_stat != GSS_S_COMPLETE) {
189 options_ret->major_status = maj_stat;
190 options_ret->minor_status = min_stat;
195 gd->gd_options = *options_req;
197 gd->gd_options.req_flags = GSS_C_MUTUAL_FLAG;
198 gd->gd_options.time_req = 0;
199 gd->gd_options.my_cred = GSS_C_NO_CREDENTIAL;
200 gd->gd_options.input_channel_bindings = NULL;
203 gd->gd_ctx = GSS_C_NO_CONTEXT;
205 gd->gd_qop = qop_num;
207 gd->gd_cred.gc_version = RPCSEC_GSS_VERSION;
208 gd->gd_cred.gc_proc = RPCSEC_GSS_INIT;
209 gd->gd_cred.gc_seq = 0;
210 gd->gd_cred.gc_svc = service;
212 save_auth = clnt->cl_auth;
214 clnt->cl_auth = auth;
215 if (!rpc_gss_init(auth, options_ret)) {
216 clnt->cl_auth = save_auth;
220 clnt->cl_auth = save_auth;
230 rpc_gss_set_defaults(AUTH *auth, rpc_gss_service_t service, const char *qop)
232 struct rpc_gss_data *gd;
234 const char *mechanism;
236 gd = AUTH_PRIVATE(auth);
237 if (!rpc_gss_oid_to_mech(gd->gd_mech, &mechanism)) {
242 if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num)) {
246 qop_num = GSS_C_QOP_DEFAULT;
249 gd->gd_cred.gc_svc = service;
250 gd->gd_qop = qop_num;
255 rpc_gss_nextverf(__unused AUTH *auth)
262 rpc_gss_marshal(__unused AUTH *auth, __unused XDR *xdrs)
270 rpc_gss_validate(AUTH *auth, struct opaque_auth *verf)
272 struct rpc_gss_data *gd;
275 gss_buffer_desc signbuf, checksum;
276 OM_uint32 maj_stat, min_stat;
278 log_debug("in rpc_gss_validate()");
280 gd = AUTH_PRIVATE(auth);
282 if (gd->gd_state == RPCSEC_GSS_CONTEXT) {
284 * Save the on the wire verifier to validate last INIT
285 * phase packet after decode if the major status is
288 if (gd->gd_verf.value)
289 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
290 (char *) &gd->gd_verf);
291 gd->gd_verf.value = mem_alloc(verf->oa_length);
292 if (gd->gd_verf.value == NULL) {
293 fprintf(stderr, "gss_validate: out of memory\n");
294 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
297 memcpy(gd->gd_verf.value, verf->oa_base, verf->oa_length);
298 gd->gd_verf.length = verf->oa_length;
302 num = htonl(gd->gd_cred.gc_seq);
303 signbuf.value = #
304 signbuf.length = sizeof(num);
306 checksum.value = verf->oa_base;
307 checksum.length = verf->oa_length;
309 maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx, &signbuf,
310 &checksum, &qop_state);
311 if (maj_stat != GSS_S_COMPLETE || qop_state != gd->gd_qop) {
312 log_status("gss_verify_mic", gd->gd_mech, maj_stat, min_stat);
313 if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
314 rpc_gss_destroy_context(auth, TRUE);
316 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
323 rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret)
325 struct rpc_gss_data *gd;
326 struct rpc_gss_init_res gr;
327 gss_buffer_desc *recv_tokenp, recv_token, send_token;
328 OM_uint32 maj_stat, min_stat, call_stat;
331 log_debug("in rpc_gss_refresh()");
333 gd = AUTH_PRIVATE(auth);
335 if (gd->gd_state != RPCSEC_GSS_START)
338 /* GSS context establishment loop. */
339 gd->gd_state = RPCSEC_GSS_CONTEXT;
340 gd->gd_cred.gc_proc = RPCSEC_GSS_INIT;
341 gd->gd_cred.gc_seq = 0;
343 memset(&recv_token, 0, sizeof(recv_token));
344 memset(&gr, 0, sizeof(gr));
345 recv_tokenp = GSS_C_NO_BUFFER;
348 maj_stat = gss_init_sec_context(&min_stat,
349 gd->gd_options.my_cred,
353 gd->gd_options.req_flags,
354 gd->gd_options.time_req,
355 gd->gd_options.input_channel_bindings,
357 &gd->gd_mech, /* used mech */
359 &options_ret->ret_flags,
360 &options_ret->time_req);
363 * Free the token which we got from the server (if
364 * any). Remember that this was allocated by XDR, not
367 if (recv_tokenp != GSS_C_NO_BUFFER) {
368 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
369 (char *) &recv_token);
370 recv_tokenp = GSS_C_NO_BUFFER;
372 if (maj_stat != GSS_S_COMPLETE &&
373 maj_stat != GSS_S_CONTINUE_NEEDED) {
374 log_status("gss_init_sec_context", gd->gd_mech,
376 options_ret->major_status = maj_stat;
377 options_ret->minor_status = min_stat;
380 if (send_token.length != 0) {
381 memset(&gr, 0, sizeof(gr));
383 call_stat = clnt_call(gd->gd_clnt, NULLPROC,
384 (xdrproc_t)xdr_gss_buffer_desc,
386 (xdrproc_t)xdr_rpc_gss_init_res,
387 (caddr_t)&gr, AUTH_TIMEOUT);
389 gss_release_buffer(&min_stat, &send_token);
391 if (call_stat != RPC_SUCCESS)
394 if (gr.gr_major != GSS_S_COMPLETE &&
395 gr.gr_major != GSS_S_CONTINUE_NEEDED) {
396 log_status("server reply", gd->gd_mech,
397 gr.gr_major, gr.gr_minor);
398 options_ret->major_status = gr.gr_major;
399 options_ret->minor_status = gr.gr_minor;
404 * Save the server's gr_handle value, freeing
405 * what we have already (remember that this
406 * was allocated by XDR, not GSS-API).
408 if (gr.gr_handle.length != 0) {
409 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
410 (char *) &gd->gd_cred.gc_handle);
411 gd->gd_cred.gc_handle = gr.gr_handle;
415 * Save the server's token as well.
417 if (gr.gr_token.length != 0) {
418 recv_token = gr.gr_token;
419 recv_tokenp = &recv_token;
423 * Since we have copied out all the bits of gr
424 * which XDR allocated for us, we don't need
427 gd->gd_cred.gc_proc = RPCSEC_GSS_CONTINUE_INIT;
430 if (maj_stat == GSS_S_COMPLETE) {
431 gss_buffer_desc bufin;
432 u_int seq, qop_state = 0;
435 * gss header verifier,
436 * usually checked in gss_validate
438 seq = htonl(gr.gr_win);
439 bufin.value = (unsigned char *)&seq;
440 bufin.length = sizeof(seq);
442 maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx,
443 &bufin, &gd->gd_verf, &qop_state);
445 if (maj_stat != GSS_S_COMPLETE ||
446 qop_state != gd->gd_qop) {
447 log_status("gss_verify_mic", gd->gd_mech,
449 if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
450 rpc_gss_destroy_context(auth, TRUE);
452 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR,
454 options_ret->major_status = maj_stat;
455 options_ret->minor_status = min_stat;
459 options_ret->major_status = GSS_S_COMPLETE;
460 options_ret->minor_status = 0;
461 options_ret->rpcsec_version = gd->gd_cred.gc_version;
462 options_ret->gss_context = gd->gd_ctx;
463 if (rpc_gss_oid_to_mech(gd->gd_mech, &mech)) {
464 strlcpy(options_ret->actual_mechanism,
466 sizeof(options_ret->actual_mechanism));
469 gd->gd_state = RPCSEC_GSS_ESTABLISHED;
470 gd->gd_cred.gc_proc = RPCSEC_GSS_DATA;
471 gd->gd_cred.gc_seq = 0;
472 gd->gd_win = gr.gr_win;
476 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
477 (char *) &gd->gd_verf);
479 /* End context negotiation loop. */
480 if (gd->gd_cred.gc_proc != RPCSEC_GSS_DATA) {
481 rpc_createerr.cf_stat = RPC_AUTHERROR;
482 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
490 rpc_gss_refresh(AUTH *auth, void *msg)
492 struct rpc_msg *reply = (struct rpc_msg *) msg;
493 rpc_gss_options_ret_t options;
496 * If the error was RPCSEC_GSS_CREDPROBLEM of
497 * RPCSEC_GSS_CTXPROBLEM we start again from scratch. All
498 * other errors are fatal.
500 if (reply->rm_reply.rp_stat == MSG_DENIED
501 && reply->rm_reply.rp_rjct.rj_stat == AUTH_ERROR
502 && (reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CREDPROBLEM
503 || reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CTXPROBLEM)) {
504 rpc_gss_destroy_context(auth, FALSE);
505 memset(&options, 0, sizeof(options));
506 return (rpc_gss_init(auth, &options));
513 rpc_gss_destroy_context(AUTH *auth, bool_t send_destroy)
515 struct rpc_gss_data *gd;
518 log_debug("in rpc_gss_destroy_context()");
520 gd = AUTH_PRIVATE(auth);
522 if (gd->gd_state == RPCSEC_GSS_ESTABLISHED && send_destroy) {
523 gd->gd_cred.gc_proc = RPCSEC_GSS_DESTROY;
524 clnt_call(gd->gd_clnt, NULLPROC,
525 (xdrproc_t)xdr_void, NULL,
526 (xdrproc_t)xdr_void, NULL, AUTH_TIMEOUT);
530 * Free the context token. Remember that this was
531 * allocated by XDR, not GSS-API.
533 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
534 (char *) &gd->gd_cred.gc_handle);
535 gd->gd_cred.gc_handle.length = 0;
537 if (gd->gd_ctx != GSS_C_NO_CONTEXT)
538 gss_delete_sec_context(&min_stat, &gd->gd_ctx, NULL);
540 gd->gd_state = RPCSEC_GSS_START;
544 rpc_gss_destroy(AUTH *auth)
546 struct rpc_gss_data *gd;
549 log_debug("in rpc_gss_destroy()");
551 gd = AUTH_PRIVATE(auth);
553 rpc_gss_destroy_context(auth, TRUE);
555 if (gd->gd_name != GSS_C_NO_NAME)
556 gss_release_name(&min_stat, &gd->gd_name);
557 if (gd->gd_verf.value)
558 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
559 (char *) &gd->gd_verf);
561 mem_free(gd, sizeof(*gd));
562 mem_free(auth, sizeof(*auth));
566 __rpc_gss_wrap(AUTH *auth, void *header, size_t headerlen,
567 XDR* xdrs, xdrproc_t xdr_args, void *args_ptr)
570 char credbuf[MAX_AUTH_BYTES];
571 char tmpheader[MAX_AUTH_BYTES];
572 struct opaque_auth creds, verf;
573 struct rpc_gss_data *gd;
574 gss_buffer_desc rpcbuf, checksum;
575 OM_uint32 maj_stat, min_stat;
578 log_debug("in rpc_gss_wrap()");
580 gd = AUTH_PRIVATE(auth);
582 if (gd->gd_state == RPCSEC_GSS_ESTABLISHED)
583 gd->gd_cred.gc_seq++;
586 * We need to encode our creds and then put the header and
587 * creds together in a buffer so that we can create a checksum
590 xdrmem_create(&tmpxdrs, credbuf, sizeof(credbuf), XDR_ENCODE);
591 if (!xdr_rpc_gss_cred(&tmpxdrs, &gd->gd_cred)) {
592 XDR_DESTROY(&tmpxdrs);
593 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
596 creds.oa_flavor = RPCSEC_GSS;
597 creds.oa_base = credbuf;
598 creds.oa_length = XDR_GETPOS(&tmpxdrs);
599 XDR_DESTROY(&tmpxdrs);
601 xdrmem_create(&tmpxdrs, tmpheader, sizeof(tmpheader), XDR_ENCODE);
602 if (!XDR_PUTBYTES(&tmpxdrs, header, headerlen) ||
603 !xdr_opaque_auth(&tmpxdrs, &creds)) {
604 XDR_DESTROY(&tmpxdrs);
605 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
608 headerlen = XDR_GETPOS(&tmpxdrs);
609 XDR_DESTROY(&tmpxdrs);
611 if (!XDR_PUTBYTES(xdrs, tmpheader, headerlen)) {
612 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
616 if (gd->gd_cred.gc_proc == RPCSEC_GSS_INIT ||
617 gd->gd_cred.gc_proc == RPCSEC_GSS_CONTINUE_INIT) {
618 if (!xdr_opaque_auth(xdrs, &_null_auth)) {
619 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
624 * Checksum serialized RPC header, up to and including
627 rpcbuf.length = headerlen;
628 rpcbuf.value = tmpheader;
630 maj_stat = gss_get_mic(&min_stat, gd->gd_ctx, gd->gd_qop,
633 if (maj_stat != GSS_S_COMPLETE) {
634 log_status("gss_get_mic", gd->gd_mech,
636 if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
637 rpc_gss_destroy_context(auth, TRUE);
639 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
643 verf.oa_flavor = RPCSEC_GSS;
644 verf.oa_base = checksum.value;
645 verf.oa_length = checksum.length;
647 xdr_stat = xdr_opaque_auth(xdrs, &verf);
648 gss_release_buffer(&min_stat, &checksum);
650 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
655 if (gd->gd_state != RPCSEC_GSS_ESTABLISHED ||
656 gd->gd_cred.gc_svc == rpc_gss_svc_none) {
657 return (xdr_args(xdrs, args_ptr));
659 return (xdr_rpc_gss_wrap_data(xdrs, xdr_args, args_ptr,
660 gd->gd_ctx, gd->gd_qop, gd->gd_cred.gc_svc,
661 gd->gd_cred.gc_seq));
665 __rpc_gss_unwrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, void *xdr_ptr)
667 struct rpc_gss_data *gd;
669 log_debug("in rpc_gss_unwrap()");
671 gd = AUTH_PRIVATE(auth);
673 if (gd->gd_state != RPCSEC_GSS_ESTABLISHED ||
674 gd->gd_cred.gc_svc == rpc_gss_svc_none) {
675 return (xdr_func(xdrs, xdr_ptr));
677 return (xdr_rpc_gss_unwrap_data(xdrs, xdr_func, xdr_ptr,
678 gd->gd_ctx, gd->gd_qop, gd->gd_cred.gc_svc,
679 gd->gd_cred.gc_seq));
683 rpc_gss_max_data_length(AUTH *auth, int max_tp_unit_len)
685 struct rpc_gss_data *gd;
688 OM_uint32 maj_stat, min_stat;
691 gd = AUTH_PRIVATE(auth);
693 switch (gd->gd_cred.gc_svc) {
694 case rpc_gss_svc_none:
695 return (max_tp_unit_len);
698 case rpc_gss_svc_default:
699 case rpc_gss_svc_integrity:
703 case rpc_gss_svc_privacy:
711 maj_stat = gss_wrap_size_limit(&min_stat, gd->gd_ctx, want_conf,
712 gd->gd_qop, max_tp_unit_len, &max);
714 if (maj_stat == GSS_S_COMPLETE) {
720 log_status("gss_wrap_size_limit", gd->gd_mech,