4 RPCSEC_GSS client routines.
6 Copyright (c) 2000 The Regents of the University of Michigan.
9 Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
10 All rights reserved, all wrongs reversed.
12 Redistribution and use in source and binary forms, with or without
13 modification, are permitted provided that the following conditions
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.
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.
44 #include <rpc/types.h>
47 #include <rpc/auth_gss.h>
49 #include <netinet/in.h>
50 #include <gssapi/gssapi.h>
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();
63 * from mit-krb5-1.2.1 mechglue/mglueP.h:
64 * Array of context IDs typed by mechanism OID
66 typedef struct gss_union_ctx_id_t
{
68 gss_ctx_id_t internal_ctx_id
;
69 } gss_union_ctx_id_desc
, *gss_union_ctx_id_t
;
71 static struct auth_ops authgss_ops
= {
83 /* useful as i add more mechanisms */
85 print_rpc_gss_sec(struct rpc_gss_sec
*ptr
)
90 log_debug("rpc_gss_sec:");
92 log_debug("NULL gss_OID mech");
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 */
104 else if (40 <= *p
&& *p
< 80) {
108 else if (80 <= *p
&& *p
< 127) {
117 fprintf(stderr
, " %u %u", first
, second
);
121 fprintf(stderr
, " %u", (unsigned char)*p
++);
123 fprintf(stderr
, " }\n");
125 fprintf(stderr
, " qop: %d\n", ptr
->qop
);
126 fprintf(stderr
, " service: %d\n", ptr
->svc
);
127 fprintf(stderr
, " cred: %p\n", ptr
->cred
);
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 */
143 #define AUTH_PRIVATE(auth) ((struct rpc_gss_data *)auth->ah_private)
145 static struct timeval AUTH_TIMEOUT
= { 25, 0 };
148 authgss_create(CLIENT
*clnt
, gss_name_t name
, struct rpc_gss_sec
*sec
)
150 AUTH
*auth
, *save_auth
;
151 struct rpc_gss_data
*gd
;
152 OM_uint32 min_stat
= 0;
154 log_debug("in authgss_create()");
156 memset(&rpc_createerr
, 0, sizeof(rpc_createerr
));
158 if ((auth
= calloc(sizeof(*auth
), 1)) == NULL
) {
159 rpc_createerr
.cf_stat
= RPC_SYSTEMERROR
;
160 rpc_createerr
.cf_error
.re_errno
= ENOMEM
;
163 if ((gd
= calloc(sizeof(*gd
), 1)) == NULL
) {
164 rpc_createerr
.cf_stat
= RPC_SYSTEMERROR
;
165 rpc_createerr
.cf_error
.re_errno
= ENOMEM
;
170 fprintf(stderr
, "authgss_create: name is %p\n", name
);
172 if (name
!= GSS_C_NO_NAME
) {
173 if (gss_duplicate_name(&min_stat
, name
, &gd
->name
)
175 rpc_createerr
.cf_stat
= RPC_SYSTEMERROR
;
176 rpc_createerr
.cf_error
.re_errno
= ENOMEM
;
185 fprintf(stderr
, "authgss_create: gd->name is %p\n", gd
->name
);
188 gd
->ctx
= GSS_C_NO_CONTEXT
;
191 gd
->gc
.gc_v
= RPCSEC_GSS_VERSION
;
192 gd
->gc
.gc_proc
= RPCSEC_GSS_INIT
;
193 gd
->gc
.gc_svc
= gd
->sec
.svc
;
195 auth
->ah_ops
= &authgss_ops
;
196 auth
->ah_private
= (caddr_t
)gd
;
198 save_auth
= clnt
->cl_auth
;
199 clnt
->cl_auth
= auth
;
201 if (!authgss_refresh(auth
))
204 clnt
->cl_auth
= save_auth
;
210 authgss_create_default(CLIENT
*clnt
, char *service
, struct rpc_gss_sec
*sec
)
213 OM_uint32 maj_stat
= 0, min_stat
= 0;
214 gss_buffer_desc sname
;
215 gss_name_t name
= GSS_C_NO_NAME
;
217 log_debug("in authgss_create_default()");
220 sname
.value
= service
;
221 sname
.length
= strlen(service
);
223 maj_stat
= gss_import_name(&min_stat
, &sname
,
224 (gss_OID
)GSS_C_NT_HOSTBASED_SERVICE
,
227 if (maj_stat
!= GSS_S_COMPLETE
) {
228 log_status("gss_import_name", maj_stat
, min_stat
);
229 rpc_createerr
.cf_stat
= RPC_AUTHERROR
;
233 auth
= authgss_create(clnt
, name
, sec
);
235 if (name
!= GSS_C_NO_NAME
) {
237 fprintf(stderr
, "authgss_create_default: freeing name %p\n", name
);
239 gss_release_name(&min_stat
, &name
);
246 authgss_get_private_data(AUTH
*auth
, struct authgss_private_data
*pd
)
248 struct rpc_gss_data
*gd
;
250 log_debug("in authgss_get_private_data()");
255 gd
= AUTH_PRIVATE(auth
);
257 if (!gd
|| !gd
->established
)
260 pd
->pd_ctx
= gd
->ctx
;
261 pd
->pd_ctx_hndl
= gd
->gc
.gc_ctx
;
262 pd
->pd_seq_win
= gd
->win
;
268 authgss_nextverf(AUTH
*auth
)
270 log_debug("in authgss_nextverf()");
271 /* no action necessary */
275 authgss_marshal(AUTH
*auth
, XDR
*xdrs
)
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
;
284 log_debug("in authgss_marshal()");
286 gd
= AUTH_PRIVATE(auth
);
291 xdrmem_create(&tmpxdrs
, tmp
, sizeof(tmp
), XDR_ENCODE
);
293 if (!xdr_rpc_gss_cred(&tmpxdrs
, &gd
->gc
)) {
294 XDR_DESTROY(&tmpxdrs
);
297 auth
->ah_cred
.oa_flavor
= RPCSEC_GSS
;
298 auth
->ah_cred
.oa_base
= tmp
;
299 auth
->ah_cred
.oa_length
= XDR_GETPOS(&tmpxdrs
);
301 XDR_DESTROY(&tmpxdrs
);
303 if (!xdr_opaque_auth(xdrs
, &auth
->ah_cred
))
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
));
310 /* Checksum serialized RPC header, up to and including credential. */
311 rpcbuf
.length
= XDR_GETPOS(xdrs
);
313 rpcbuf
.value
= XDR_INLINE(xdrs
, rpcbuf
.length
);
315 maj_stat
= gss_get_mic(&min_stat
, gd
->ctx
, gd
->sec
.qop
,
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
);
326 auth
->ah_verf
.oa_flavor
= RPCSEC_GSS
;
327 auth
->ah_verf
.oa_base
= checksum
.value
;
328 auth
->ah_verf
.oa_length
= checksum
.length
;
330 xdr_stat
= xdr_opaque_auth(xdrs
, &auth
->ah_verf
);
331 gss_release_buffer(&min_stat
, &checksum
);
337 authgss_validate(AUTH
*auth
, struct opaque_auth
*verf
)
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
;
344 log_debug("in authgss_validate()");
346 gd
= AUTH_PRIVATE(auth
);
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
355 if ((gd
->gc_wire_verf
.value
=
356 mem_alloc(verf
->oa_length
)) == NULL
) {
357 fprintf(stderr
, "gss_validate: out of memory\n");
360 memcpy(gd
->gc_wire_verf
.value
, verf
->oa_base
, verf
->oa_length
);
361 gd
->gc_wire_verf
.length
= verf
->oa_length
;
365 if (gd
->gc
.gc_proc
== RPCSEC_GSS_INIT
||
366 gd
->gc
.gc_proc
== RPCSEC_GSS_CONTINUE_INIT
) {
367 num
= htonl(gd
->win
);
369 else num
= htonl(gd
->gc
.gc_seq
);
371 signbuf
.value
= &num
;
372 signbuf
.length
= sizeof(num
);
374 checksum
.value
= verf
->oa_base
;
375 checksum
.length
= verf
->oa_length
;
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
);
391 authgss_refresh(AUTH
*auth
)
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
;
398 log_debug("in authgss_refresh()");
400 gd
= AUTH_PRIVATE(auth
);
405 /* GSS context establishment loop. */
406 memset(&gr
, 0, sizeof(gr
));
407 recv_tokenp
= GSS_C_NO_BUFFER
;
410 print_rpc_gss_sec(&gd
->sec
);
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);
422 maj_stat
= gss_init_sec_context(&min_stat
,
431 NULL
, /* used mech */
434 NULL
); /* time rec */
436 if (recv_tokenp
!= GSS_C_NO_BUFFER
) {
437 gss_release_buffer(&min_stat
, &gr
.gr_token
);
438 recv_tokenp
= GSS_C_NO_BUFFER
;
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
);
445 if (send_token
.length
!= 0) {
446 memset(&gr
, 0, sizeof(gr
));
449 /* print the token we are about to send */
450 log_debug("The token being sent (length %d):",
452 log_hexdump(send_token
.value
, send_token
.length
, 0);
455 call_stat
= clnt_call(gd
->clnt
, NULLPROC
,
456 (xdrproc_t
)xdr_rpc_gss_init_args
,
458 (xdrproc_t
)xdr_rpc_gss_init_res
,
459 (caddr_t
)&gr
, AUTH_TIMEOUT
);
461 gss_release_buffer(&min_stat
, &send_token
);
463 if (call_stat
!= RPC_SUCCESS
||
464 (gr
.gr_major
!= GSS_S_COMPLETE
&&
465 gr
.gr_major
!= GSS_S_CONTINUE_NEEDED
))
468 if (gr
.gr_ctx
.length
!= 0) {
469 if (gd
->gc
.gc_ctx
.value
)
470 gss_release_buffer(&min_stat
,
472 gd
->gc
.gc_ctx
= gr
.gr_ctx
;
474 if (gr
.gr_token
.length
!= 0) {
475 if (maj_stat
!= GSS_S_CONTINUE_NEEDED
)
477 recv_tokenp
= &gr
.gr_token
;
479 gd
->gc
.gc_proc
= RPCSEC_GSS_CONTINUE_INIT
;
482 /* GSS_S_COMPLETE => check gss header verifier,
483 * usually checked in gss_validate
485 if (maj_stat
== GSS_S_COMPLETE
) {
486 gss_buffer_desc bufin
;
487 gss_buffer_desc bufout
;
488 u_int seq
, qop_state
= 0;
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
;
496 maj_stat
= gss_verify_mic(&min_stat
, gd
->ctx
,
497 &bufin
, &bufout
, &qop_state
);
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
);
508 gd
->established
= TRUE
;
509 gd
->gc
.gc_proc
= RPCSEC_GSS_DATA
;
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
);
520 authgss_destroy(auth
);
522 rpc_createerr
.cf_stat
= RPC_AUTHERROR
;
530 authgss_service(AUTH
*auth
, int svc
)
532 struct rpc_gss_data
*gd
;
534 log_debug("in authgss_service()");
538 gd
= AUTH_PRIVATE(auth
);
539 if (!gd
|| !gd
->established
)
547 authgss_destroy_context(AUTH
*auth
)
549 struct rpc_gss_data
*gd
;
552 log_debug("in authgss_destroy_context()");
554 gd
= AUTH_PRIVATE(auth
);
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
);
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
));
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
;
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;
576 gd
->established
= FALSE
;
580 authgss_destroy(AUTH
*auth
)
582 struct rpc_gss_data
*gd
;
585 log_debug("in authgss_destroy()");
587 gd
= AUTH_PRIVATE(auth
);
589 authgss_destroy_context(auth
);
592 fprintf(stderr
, "authgss_destroy: freeing name %p\n", gd
->name
);
594 if (gd
->name
!= GSS_C_NO_NAME
)
595 gss_release_name(&min_stat
, &gd
->name
);
602 authgss_wrap(AUTH
*auth
, XDR
*xdrs
, xdrproc_t xdr_func
, caddr_t xdr_ptr
)
604 struct rpc_gss_data
*gd
;
606 log_debug("in authgss_wrap()");
608 gd
= AUTH_PRIVATE(auth
);
610 if (!gd
->established
|| gd
->sec
.svc
== RPCSEC_GSS_SVC_NONE
) {
611 return ((*xdr_func
)(xdrs
, xdr_ptr
));
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
));
619 authgss_unwrap(AUTH
*auth
, XDR
*xdrs
, xdrproc_t xdr_func
, caddr_t xdr_ptr
)
621 struct rpc_gss_data
*gd
;
623 log_debug("in authgss_unwrap()");
625 gd
= AUTH_PRIVATE(auth
);
627 if (!gd
->established
|| gd
->sec
.svc
== RPCSEC_GSS_SVC_NONE
) {
628 return ((*xdr_func
)(xdrs
, xdr_ptr
));
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
));