1 /*-
2 * Copyright (c) 2008 Doug Rabson
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
13 *
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
24 * SUCH DAMAGE.
25 */
26 /*
27 SPDX-License-Identifier: BSD-3-Clause
28
29 auth_gss.c
30
31 RPCSEC_GSS client routines.
32
33 Copyright (c) 2000 The Regents of the University of Michigan.
34 All rights reserved.
35
36 Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
37 All rights reserved, all wrongs reversed.
38
39 Redistribution and use in source and binary forms, with or without
40 modification, are permitted provided that the following conditions
41 are met:
42
43 1. Redistributions of source code must retain the above copyright
44 notice, this list of conditions and the following disclaimer.
45 2. Redistributions in binary form must reproduce the above copyright
46 notice, this list of conditions and the following disclaimer in the
47 documentation and/or other materials provided with the distribution.
48 3. Neither the name of the University nor the names of its
49 contributors may be used to endorse or promote products derived
50 from this software without specific prior written permission.
51
52 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
53 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
54 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
55 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
57 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
58 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
59 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
60 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
61 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
62 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
63
64 $Id: auth_gss.c,v 1.32 2002/01/15 15:43:00 andros Exp $
65 */
66
67 #include <stdio.h>
68 #include <stdlib.h>
69 #include <unistd.h>
70 #include <string.h>
71 #include <errno.h>
72 #include <netinet/in.h>
73 #include <rpc/rpc.h>
74 #include <rpc/rpcsec_gss.h>
75 #include "rpcsec_gss_int.h"
76
77 static void rpc_gss_nextverf(AUTH*);
78 static bool_t rpc_gss_marshal(AUTH *, XDR *);
79 static bool_t rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret);
80 static bool_t rpc_gss_refresh(AUTH *, void *);
81 static bool_t rpc_gss_validate(AUTH *, struct opaque_auth *);
82 static void rpc_gss_destroy(AUTH *);
83 static void rpc_gss_destroy_context(AUTH *, bool_t);
84
85 static struct auth_ops rpc_gss_ops = {
86 rpc_gss_nextverf,
87 rpc_gss_marshal,
88 rpc_gss_validate,
89 rpc_gss_refresh,
90 rpc_gss_destroy
91 };
92
93 enum rpcsec_gss_state {
94 RPCSEC_GSS_START,
95 RPCSEC_GSS_CONTEXT,
96 RPCSEC_GSS_ESTABLISHED
97 };
98
99 struct rpc_gss_data {
100 rpc_gss_options_req_t gd_options; /* GSS context options */
101 enum rpcsec_gss_state gd_state; /* connection state */
102 gss_buffer_desc gd_verf; /* save GSS_S_COMPLETE
103 * NULL RPC verfier to
104 * process at end of
105 * context negotiation */
106 CLIENT *gd_clnt; /* client handle */
107 gss_name_t gd_name; /* service name */
108 gss_OID gd_mech; /* mechanism to use */
109 gss_qop_t gd_qop; /* quality of protection */
110 gss_ctx_id_t gd_ctx; /* context id */
111 struct rpc_gss_cred gd_cred; /* client credentials */
112 u_int gd_win; /* sequence window */
113 };
114
115 #define AUTH_PRIVATE(auth) ((struct rpc_gss_data *)auth->ah_private)
116
117 static struct timeval AUTH_TIMEOUT = { 25, 0 };
118
119 AUTH *
rpc_gss_seccreate(CLIENT * clnt,const char * principal,const char * mechanism,rpc_gss_service_t service,const char * qop,rpc_gss_options_req_t * options_req,rpc_gss_options_ret_t * options_ret)120 rpc_gss_seccreate(CLIENT *clnt, const char *principal,
121 const char *mechanism, rpc_gss_service_t service, const char *qop,
122 rpc_gss_options_req_t *options_req, rpc_gss_options_ret_t *options_ret)
123 {
124 AUTH *auth, *save_auth;
125 rpc_gss_options_ret_t options;
126 gss_OID oid;
127 u_int qop_num;
128 struct rpc_gss_data *gd;
129 OM_uint32 maj_stat = 0, min_stat = 0;
130 gss_buffer_desc principal_desc;
131
132 /*
133 * Bail out now if we don't know this mechanism.
134 */
135 if (!rpc_gss_mech_to_oid(mechanism, &oid))
136 return (NULL);
137
138 if (qop) {
139 if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num))
140 return (NULL);
141 } else {
142 qop_num = GSS_C_QOP_DEFAULT;
143 }
144
145 /*
146 * If the caller doesn't want the options, point at local
147 * storage to simplify the code below.
148 */
149 if (!options_ret)
150 options_ret = &options;
151
152 /*
153 * Default service is integrity.
154 */
155 if (service == rpc_gss_svc_default)
156 service = rpc_gss_svc_integrity;
157
158 memset(options_ret, 0, sizeof(*options_ret));
159
160 log_debug("in rpc_gss_seccreate()");
161
162 memset(&rpc_createerr, 0, sizeof(rpc_createerr));
163
164 auth = mem_alloc(sizeof(*auth));
165 if (auth == NULL) {
166 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
167 rpc_createerr.cf_error.re_errno = ENOMEM;
168 return (NULL);
169 }
170 gd = mem_alloc(sizeof(*gd));
171 if (gd == NULL) {
172 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
173 rpc_createerr.cf_error.re_errno = ENOMEM;
174 free(auth);
175 return (NULL);
176 }
177
178 auth->ah_ops = &rpc_gss_ops;
179 auth->ah_private = (caddr_t) gd;
180 auth->ah_cred.oa_flavor = RPCSEC_GSS;
181
182 principal_desc.value = (void *)(intptr_t) principal;
183 principal_desc.length = strlen(principal);
184 maj_stat = gss_import_name(&min_stat, &principal_desc,
185 GSS_C_NT_HOSTBASED_SERVICE, &gd->gd_name);
186 if (maj_stat != GSS_S_COMPLETE) {
187 options_ret->major_status = maj_stat;
188 options_ret->minor_status = min_stat;
189 goto bad;
190 }
191
192 if (options_req) {
193 gd->gd_options = *options_req;
194 } else {
195 gd->gd_options.req_flags = GSS_C_MUTUAL_FLAG;
196 gd->gd_options.time_req = 0;
197 gd->gd_options.my_cred = GSS_C_NO_CREDENTIAL;
198 gd->gd_options.input_channel_bindings = NULL;
199 }
200 gd->gd_clnt = clnt;
201 gd->gd_ctx = GSS_C_NO_CONTEXT;
202 gd->gd_mech = oid;
203 gd->gd_qop = qop_num;
204
205 gd->gd_cred.gc_version = RPCSEC_GSS_VERSION;
206 gd->gd_cred.gc_proc = RPCSEC_GSS_INIT;
207 gd->gd_cred.gc_seq = 0;
208 gd->gd_cred.gc_svc = service;
209
210 save_auth = clnt->cl_auth;
211
212 clnt->cl_auth = auth;
213 if (!rpc_gss_init(auth, options_ret)) {
214 clnt->cl_auth = save_auth;
215 goto bad;
216 }
217
218 clnt->cl_auth = save_auth;
219
220 return (auth);
221
222 bad:
223 AUTH_DESTROY(auth);
224 return (NULL);
225 }
226
227 bool_t
rpc_gss_set_defaults(AUTH * auth,rpc_gss_service_t service,const char * qop)228 rpc_gss_set_defaults(AUTH *auth, rpc_gss_service_t service, const char *qop)
229 {
230 struct rpc_gss_data *gd;
231 u_int qop_num;
232 const char *mechanism;
233
234 gd = AUTH_PRIVATE(auth);
235 if (!rpc_gss_oid_to_mech(gd->gd_mech, &mechanism)) {
236 return (FALSE);
237 }
238
239 if (qop) {
240 if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num)) {
241 return (FALSE);
242 }
243 } else {
244 qop_num = GSS_C_QOP_DEFAULT;
245 }
246
247 gd->gd_cred.gc_svc = service;
248 gd->gd_qop = qop_num;
249 return (TRUE);
250 }
251
252 static void
rpc_gss_nextverf(__unused AUTH * auth)253 rpc_gss_nextverf(__unused AUTH *auth)
254 {
255
256 /* not used */
257 }
258
259 static bool_t
rpc_gss_marshal(__unused AUTH * auth,__unused XDR * xdrs)260 rpc_gss_marshal(__unused AUTH *auth, __unused XDR *xdrs)
261 {
262
263 /* not used */
264 return (FALSE);
265 }
266
267 static bool_t
rpc_gss_validate(AUTH * auth,struct opaque_auth * verf)268 rpc_gss_validate(AUTH *auth, struct opaque_auth *verf)
269 {
270 struct rpc_gss_data *gd;
271 gss_qop_t qop_state;
272 uint32_t num;
273 gss_buffer_desc signbuf, checksum;
274 OM_uint32 maj_stat, min_stat;
275
276 log_debug("in rpc_gss_validate()");
277
278 gd = AUTH_PRIVATE(auth);
279
280 if (gd->gd_state == RPCSEC_GSS_CONTEXT) {
281 /*
282 * Save the on the wire verifier to validate last INIT
283 * phase packet after decode if the major status is
284 * GSS_S_COMPLETE.
285 */
286 if (gd->gd_verf.value)
287 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
288 (char *) &gd->gd_verf);
289 gd->gd_verf.value = mem_alloc(verf->oa_length);
290 if (gd->gd_verf.value == NULL) {
291 fprintf(stderr, "gss_validate: out of memory\n");
292 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
293 return (FALSE);
294 }
295 memcpy(gd->gd_verf.value, verf->oa_base, verf->oa_length);
296 gd->gd_verf.length = verf->oa_length;
297 return (TRUE);
298 }
299
300 num = htonl(gd->gd_cred.gc_seq);
301 signbuf.value = #
302 signbuf.length = sizeof(num);
303
304 checksum.value = verf->oa_base;
305 checksum.length = verf->oa_length;
306
307 maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx, &signbuf,
308 &checksum, &qop_state);
309 if (maj_stat != GSS_S_COMPLETE || qop_state != gd->gd_qop) {
310 log_status("gss_verify_mic", gd->gd_mech, maj_stat, min_stat);
311 if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
312 rpc_gss_destroy_context(auth, TRUE);
313 }
314 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
315 return (FALSE);
316 }
317 return (TRUE);
318 }
319
320 static bool_t
rpc_gss_init(AUTH * auth,rpc_gss_options_ret_t * options_ret)321 rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret)
322 {
323 struct rpc_gss_data *gd;
324 struct rpc_gss_init_res gr;
325 gss_buffer_desc *recv_tokenp, recv_token, send_token;
326 OM_uint32 maj_stat, min_stat, call_stat;
327 const char *mech;
328
329 log_debug("in rpc_gss_refresh()");
330
331 gd = AUTH_PRIVATE(auth);
332
333 if (gd->gd_state != RPCSEC_GSS_START)
334 return (TRUE);
335
336 /* GSS context establishment loop. */
337 gd->gd_state = RPCSEC_GSS_CONTEXT;
338 gd->gd_cred.gc_proc = RPCSEC_GSS_INIT;
339 gd->gd_cred.gc_seq = 0;
340
341 memset(&recv_token, 0, sizeof(recv_token));
342 memset(&gr, 0, sizeof(gr));
343 recv_tokenp = GSS_C_NO_BUFFER;
344
345 for (;;) {
346 maj_stat = gss_init_sec_context(&min_stat,
347 gd->gd_options.my_cred,
348 &gd->gd_ctx,
349 gd->gd_name,
350 gd->gd_mech,
351 gd->gd_options.req_flags,
352 gd->gd_options.time_req,
353 gd->gd_options.input_channel_bindings,
354 recv_tokenp,
355 &gd->gd_mech, /* used mech */
356 &send_token,
357 &options_ret->ret_flags,
358 &options_ret->time_req);
359
360 /*
361 * Free the token which we got from the server (if
362 * any). Remember that this was allocated by XDR, not
363 * GSS-API.
364 */
365 if (recv_tokenp != GSS_C_NO_BUFFER) {
366 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
367 (char *) &recv_token);
368 recv_tokenp = GSS_C_NO_BUFFER;
369 }
370 if (maj_stat != GSS_S_COMPLETE &&
371 maj_stat != GSS_S_CONTINUE_NEEDED) {
372 log_status("gss_init_sec_context", gd->gd_mech,
373 maj_stat, min_stat);
374 options_ret->major_status = maj_stat;
375 options_ret->minor_status = min_stat;
376 break;
377 }
378 if (send_token.length != 0) {
379 memset(&gr, 0, sizeof(gr));
380
381 call_stat = clnt_call(gd->gd_clnt, NULLPROC,
382 (xdrproc_t)xdr_gss_buffer_desc,
383 &send_token,
384 (xdrproc_t)xdr_rpc_gss_init_res,
385 (caddr_t)&gr, AUTH_TIMEOUT);
386
387 gss_release_buffer(&min_stat, &send_token);
388
389 if (call_stat != RPC_SUCCESS)
390 break;
391
392 if (gr.gr_major != GSS_S_COMPLETE &&
393 gr.gr_major != GSS_S_CONTINUE_NEEDED) {
394 log_status("server reply", gd->gd_mech,
395 gr.gr_major, gr.gr_minor);
396 options_ret->major_status = gr.gr_major;
397 options_ret->minor_status = gr.gr_minor;
398 break;
399 }
400
401 /*
402 * Save the server's gr_handle value, freeing
403 * what we have already (remember that this
404 * was allocated by XDR, not GSS-API).
405 */
406 if (gr.gr_handle.length != 0) {
407 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
408 (char *) &gd->gd_cred.gc_handle);
409 gd->gd_cred.gc_handle = gr.gr_handle;
410 }
411
412 /*
413 * Save the server's token as well.
414 */
415 if (gr.gr_token.length != 0) {
416 recv_token = gr.gr_token;
417 recv_tokenp = &recv_token;
418 }
419
420 /*
421 * Since we have copied out all the bits of gr
422 * which XDR allocated for us, we don't need
423 * to free it.
424 */
425 gd->gd_cred.gc_proc = RPCSEC_GSS_CONTINUE_INIT;
426 }
427
428 if (maj_stat == GSS_S_COMPLETE) {
429 gss_buffer_desc bufin;
430 u_int seq, qop_state = 0;
431
432 /*
433 * gss header verifier,
434 * usually checked in gss_validate
435 */
436 seq = htonl(gr.gr_win);
437 bufin.value = (unsigned char *)&seq;
438 bufin.length = sizeof(seq);
439
440 maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx,
441 &bufin, &gd->gd_verf, &qop_state);
442
443 if (maj_stat != GSS_S_COMPLETE ||
444 qop_state != gd->gd_qop) {
445 log_status("gss_verify_mic", gd->gd_mech,
446 maj_stat, min_stat);
447 if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
448 rpc_gss_destroy_context(auth, TRUE);
449 }
450 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR,
451 EPERM);
452 options_ret->major_status = maj_stat;
453 options_ret->minor_status = min_stat;
454 return (FALSE);
455 }
456
457 options_ret->major_status = GSS_S_COMPLETE;
458 options_ret->minor_status = 0;
459 options_ret->rpcsec_version = gd->gd_cred.gc_version;
460 options_ret->gss_context = gd->gd_ctx;
461 if (rpc_gss_oid_to_mech(gd->gd_mech, &mech)) {
462 strlcpy(options_ret->actual_mechanism,
463 mech,
464 sizeof(options_ret->actual_mechanism));
465 }
466
467 gd->gd_state = RPCSEC_GSS_ESTABLISHED;
468 gd->gd_cred.gc_proc = RPCSEC_GSS_DATA;
469 gd->gd_cred.gc_seq = 0;
470 gd->gd_win = gr.gr_win;
471 break;
472 }
473 }
474 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
475 (char *) &gd->gd_verf);
476
477 /* End context negotiation loop. */
478 if (gd->gd_cred.gc_proc != RPCSEC_GSS_DATA) {
479 rpc_createerr.cf_stat = RPC_AUTHERROR;
480 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
481 return (FALSE);
482 }
483
484 return (TRUE);
485 }
486
487 static bool_t
rpc_gss_refresh(AUTH * auth,void * msg)488 rpc_gss_refresh(AUTH *auth, void *msg)
489 {
490 struct rpc_msg *reply = (struct rpc_msg *) msg;
491 rpc_gss_options_ret_t options;
492
493 /*
494 * If the error was RPCSEC_GSS_CREDPROBLEM of
495 * RPCSEC_GSS_CTXPROBLEM we start again from scratch. All
496 * other errors are fatal.
497 */
498 if (reply->rm_reply.rp_stat == MSG_DENIED
499 && reply->rm_reply.rp_rjct.rj_stat == AUTH_ERROR
500 && (reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CREDPROBLEM
501 || reply->rm_reply.rp_rjct.rj_why == RPCSEC_GSS_CTXPROBLEM)) {
502 rpc_gss_destroy_context(auth, FALSE);
503 memset(&options, 0, sizeof(options));
504 return (rpc_gss_init(auth, &options));
505 }
506
507 return (FALSE);
508 }
509
510 static void
rpc_gss_destroy_context(AUTH * auth,bool_t send_destroy)511 rpc_gss_destroy_context(AUTH *auth, bool_t send_destroy)
512 {
513 struct rpc_gss_data *gd;
514 OM_uint32 min_stat;
515
516 log_debug("in rpc_gss_destroy_context()");
517
518 gd = AUTH_PRIVATE(auth);
519
520 if (gd->gd_state == RPCSEC_GSS_ESTABLISHED && send_destroy) {
521 gd->gd_cred.gc_proc = RPCSEC_GSS_DESTROY;
522 clnt_call(gd->gd_clnt, NULLPROC,
523 (xdrproc_t)xdr_void, NULL,
524 (xdrproc_t)xdr_void, NULL, AUTH_TIMEOUT);
525 }
526
527 /*
528 * Free the context token. Remember that this was
529 * allocated by XDR, not GSS-API.
530 */
531 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
532 (char *) &gd->gd_cred.gc_handle);
533 gd->gd_cred.gc_handle.length = 0;
534
535 if (gd->gd_ctx != GSS_C_NO_CONTEXT)
536 gss_delete_sec_context(&min_stat, &gd->gd_ctx, NULL);
537
538 gd->gd_state = RPCSEC_GSS_START;
539 }
540
541 static void
rpc_gss_destroy(AUTH * auth)542 rpc_gss_destroy(AUTH *auth)
543 {
544 struct rpc_gss_data *gd;
545 OM_uint32 min_stat;
546
547 log_debug("in rpc_gss_destroy()");
548
549 gd = AUTH_PRIVATE(auth);
550
551 rpc_gss_destroy_context(auth, TRUE);
552
553 if (gd->gd_name != GSS_C_NO_NAME)
554 gss_release_name(&min_stat, &gd->gd_name);
555 if (gd->gd_verf.value)
556 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
557 (char *) &gd->gd_verf);
558
559 mem_free(gd, sizeof(*gd));
560 mem_free(auth, sizeof(*auth));
561 }
562
563 bool_t
__rpc_gss_wrap(AUTH * auth,void * header,size_t headerlen,XDR * xdrs,xdrproc_t xdr_args,void * args_ptr)564 __rpc_gss_wrap(AUTH *auth, void *header, size_t headerlen,
565 XDR* xdrs, xdrproc_t xdr_args, void *args_ptr)
566 {
567 XDR tmpxdrs;
568 char credbuf[MAX_AUTH_BYTES];
569 char tmpheader[MAX_AUTH_BYTES];
570 struct opaque_auth creds, verf;
571 struct rpc_gss_data *gd;
572 gss_buffer_desc rpcbuf, checksum;
573 OM_uint32 maj_stat, min_stat;
574 bool_t xdr_stat;
575
576 log_debug("in rpc_gss_wrap()");
577
578 gd = AUTH_PRIVATE(auth);
579
580 if (gd->gd_state == RPCSEC_GSS_ESTABLISHED)
581 gd->gd_cred.gc_seq++;
582
583 /*
584 * We need to encode our creds and then put the header and
585 * creds together in a buffer so that we can create a checksum
586 * for the verf.
587 */
588 xdrmem_create(&tmpxdrs, credbuf, sizeof(credbuf), XDR_ENCODE);
589 if (!xdr_rpc_gss_cred(&tmpxdrs, &gd->gd_cred)) {
590 XDR_DESTROY(&tmpxdrs);
591 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
592 return (FALSE);
593 }
594 creds.oa_flavor = RPCSEC_GSS;
595 creds.oa_base = credbuf;
596 creds.oa_length = XDR_GETPOS(&tmpxdrs);
597 XDR_DESTROY(&tmpxdrs);
598
599 xdrmem_create(&tmpxdrs, tmpheader, sizeof(tmpheader), XDR_ENCODE);
600 if (!XDR_PUTBYTES(&tmpxdrs, header, headerlen) ||
601 !xdr_opaque_auth(&tmpxdrs, &creds)) {
602 XDR_DESTROY(&tmpxdrs);
603 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
604 return (FALSE);
605 }
606 headerlen = XDR_GETPOS(&tmpxdrs);
607 XDR_DESTROY(&tmpxdrs);
608
609 if (!XDR_PUTBYTES(xdrs, tmpheader, headerlen)) {
610 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
611 return (FALSE);
612 }
613
614 if (gd->gd_cred.gc_proc == RPCSEC_GSS_INIT ||
615 gd->gd_cred.gc_proc == RPCSEC_GSS_CONTINUE_INIT) {
616 if (!xdr_opaque_auth(xdrs, &_null_auth)) {
617 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
618 return (FALSE);
619 }
620 } else {
621 /*
622 * Checksum serialized RPC header, up to and including
623 * credential.
624 */
625 rpcbuf.length = headerlen;
626 rpcbuf.value = tmpheader;
627
628 maj_stat = gss_get_mic(&min_stat, gd->gd_ctx, gd->gd_qop,
629 &rpcbuf, &checksum);
630
631 if (maj_stat != GSS_S_COMPLETE) {
632 log_status("gss_get_mic", gd->gd_mech,
633 maj_stat, min_stat);
634 if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
635 rpc_gss_destroy_context(auth, TRUE);
636 }
637 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
638 return (FALSE);
639 }
640
641 verf.oa_flavor = RPCSEC_GSS;
642 verf.oa_base = checksum.value;
643 verf.oa_length = checksum.length;
644
645 xdr_stat = xdr_opaque_auth(xdrs, &verf);
646 gss_release_buffer(&min_stat, &checksum);
647 if (!xdr_stat) {
648 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
649 return (FALSE);
650 }
651 }
652
653 if (gd->gd_state != RPCSEC_GSS_ESTABLISHED ||
654 gd->gd_cred.gc_svc == rpc_gss_svc_none) {
655 return (xdr_args(xdrs, args_ptr));
656 }
657 return (xdr_rpc_gss_wrap_data(xdrs, xdr_args, args_ptr,
658 gd->gd_ctx, gd->gd_qop, gd->gd_cred.gc_svc,
659 gd->gd_cred.gc_seq));
660 }
661
662 bool_t
__rpc_gss_unwrap(AUTH * auth,XDR * xdrs,xdrproc_t xdr_func,void * xdr_ptr)663 __rpc_gss_unwrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, void *xdr_ptr)
664 {
665 struct rpc_gss_data *gd;
666
667 log_debug("in rpc_gss_unwrap()");
668
669 gd = AUTH_PRIVATE(auth);
670
671 if (gd->gd_state != RPCSEC_GSS_ESTABLISHED ||
672 gd->gd_cred.gc_svc == rpc_gss_svc_none) {
673 return (xdr_func(xdrs, xdr_ptr));
674 }
675 return (xdr_rpc_gss_unwrap_data(xdrs, xdr_func, xdr_ptr,
676 gd->gd_ctx, gd->gd_qop, gd->gd_cred.gc_svc,
677 gd->gd_cred.gc_seq));
678 }
679
680 int
rpc_gss_max_data_length(AUTH * auth,int max_tp_unit_len)681 rpc_gss_max_data_length(AUTH *auth, int max_tp_unit_len)
682 {
683 struct rpc_gss_data *gd;
684 int want_conf;
685 OM_uint32 max;
686 OM_uint32 maj_stat, min_stat;
687 int result;
688
689 gd = AUTH_PRIVATE(auth);
690
691 switch (gd->gd_cred.gc_svc) {
692 case rpc_gss_svc_none:
693 return (max_tp_unit_len);
694 break;
695
696 case rpc_gss_svc_default:
697 case rpc_gss_svc_integrity:
698 want_conf = FALSE;
699 break;
700
701 case rpc_gss_svc_privacy:
702 want_conf = TRUE;
703 break;
704
705 default:
706 return (0);
707 }
708
709 maj_stat = gss_wrap_size_limit(&min_stat, gd->gd_ctx, want_conf,
710 gd->gd_qop, max_tp_unit_len, &max);
711
712 if (maj_stat == GSS_S_COMPLETE) {
713 result = (int) max;
714 if (result < 0)
715 result = 0;
716 return (result);
717 } else {
718 log_status("gss_wrap_size_limit", gd->gd_mech,
719 maj_stat, min_stat);
720 return (0);
721 }
722 }
723