]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/librpcsec_gss/rpcsec_gss.c
Add two missing eventhandler.h headers
[FreeBSD/FreeBSD.git] / lib / librpcsec_gss / rpcsec_gss.c
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  *      $FreeBSD$
27  */
28 /*
29   SPDX-License-Identifier: BSD-3-Clause
30
31   auth_gss.c
32
33   RPCSEC_GSS client routines.
34   
35   Copyright (c) 2000 The Regents of the University of Michigan.
36   All rights reserved.
37
38   Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
39   All rights reserved, all wrongs reversed.
40
41   Redistribution and use in source and binary forms, with or without
42   modification, are permitted provided that the following conditions
43   are met:
44
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.
53
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.
65
66   $Id: auth_gss.c,v 1.32 2002/01/15 15:43:00 andros Exp $
67 */
68
69 #include <stdio.h>
70 #include <stdlib.h>
71 #include <unistd.h>
72 #include <string.h>
73 #include <errno.h>
74 #include <netinet/in.h>
75 #include <rpc/rpc.h>
76 #include <rpc/rpcsec_gss.h>
77 #include "rpcsec_gss_int.h"
78
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);
86
87 static struct auth_ops rpc_gss_ops = {
88         rpc_gss_nextverf,
89         rpc_gss_marshal,
90         rpc_gss_validate,
91         rpc_gss_refresh,
92         rpc_gss_destroy
93 };
94
95 enum rpcsec_gss_state {
96         RPCSEC_GSS_START,
97         RPCSEC_GSS_CONTEXT,
98         RPCSEC_GSS_ESTABLISHED
99 };
100
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
106                                                  * process at end of
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 */
115 };
116
117 #define AUTH_PRIVATE(auth)      ((struct rpc_gss_data *)auth->ah_private)
118
119 static struct timeval AUTH_TIMEOUT = { 25, 0 };
120
121 AUTH *
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)
125 {
126         AUTH                    *auth, *save_auth;
127         rpc_gss_options_ret_t   options;
128         gss_OID                 oid;
129         u_int                   qop_num;
130         struct rpc_gss_data     *gd;
131         OM_uint32               maj_stat = 0, min_stat = 0;
132         gss_buffer_desc         principal_desc;
133
134         /*
135          * Bail out now if we don't know this mechanism.
136          */
137         if (!rpc_gss_mech_to_oid(mechanism, &oid))
138                 return (NULL);
139
140         if (qop) {
141                 if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num))
142                         return (NULL);
143         } else {
144                 qop_num = GSS_C_QOP_DEFAULT;
145         }
146
147         /*
148          * If the caller doesn't want the options, point at local
149          * storage to simplify the code below.
150          */
151         if (!options_ret)
152                 options_ret = &options;
153
154         /*
155          * Default service is integrity.
156          */
157         if (service == rpc_gss_svc_default)
158                 service = rpc_gss_svc_integrity;
159
160         memset(options_ret, 0, sizeof(*options_ret));
161
162         log_debug("in rpc_gss_seccreate()");
163         
164         memset(&rpc_createerr, 0, sizeof(rpc_createerr));
165         
166         auth = mem_alloc(sizeof(*auth));
167         if (auth == NULL) {
168                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
169                 rpc_createerr.cf_error.re_errno = ENOMEM;
170                 return (NULL);
171         }
172         gd = mem_alloc(sizeof(*gd));
173         if (gd == NULL) {
174                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
175                 rpc_createerr.cf_error.re_errno = ENOMEM;
176                 free(auth);
177                 return (NULL);
178         }
179
180         auth->ah_ops = &rpc_gss_ops;
181         auth->ah_private = (caddr_t) gd;
182         auth->ah_cred.oa_flavor = RPCSEC_GSS;
183         
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;
191                 goto bad;
192         }
193
194         if (options_req) {
195                 gd->gd_options = *options_req;
196         } else {
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;
201         }
202         gd->gd_clnt = clnt;
203         gd->gd_ctx = GSS_C_NO_CONTEXT;
204         gd->gd_mech = oid;
205         gd->gd_qop = qop_num;
206
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;
211         
212         save_auth = clnt->cl_auth;
213
214         clnt->cl_auth = auth;
215         if (!rpc_gss_init(auth, options_ret)) {
216                 clnt->cl_auth = save_auth;
217                 goto bad;
218         }
219         
220         clnt->cl_auth = save_auth;
221         
222         return (auth);
223
224  bad:
225         AUTH_DESTROY(auth);
226         return (NULL);
227 }
228
229 bool_t
230 rpc_gss_set_defaults(AUTH *auth, rpc_gss_service_t service, const char *qop)
231 {
232         struct rpc_gss_data     *gd;
233         u_int                   qop_num;
234         const char              *mechanism;
235
236         gd = AUTH_PRIVATE(auth);
237         if (!rpc_gss_oid_to_mech(gd->gd_mech, &mechanism)) {
238                 return (FALSE);
239         }
240
241         if (qop) {
242                 if (!rpc_gss_qop_to_num(qop, mechanism, &qop_num)) {
243                         return (FALSE);
244                 }
245         } else {
246                 qop_num = GSS_C_QOP_DEFAULT;
247         }
248
249         gd->gd_cred.gc_svc = service;
250         gd->gd_qop = qop_num;
251         return (TRUE);
252 }
253
254 static void
255 rpc_gss_nextverf(__unused AUTH *auth)
256 {
257
258         /* not used */
259 }
260
261 static bool_t
262 rpc_gss_marshal(__unused AUTH *auth, __unused XDR *xdrs)
263 {
264
265         /* not used */
266         return (FALSE);
267 }
268
269 static bool_t
270 rpc_gss_validate(AUTH *auth, struct opaque_auth *verf)
271 {
272         struct rpc_gss_data     *gd;
273         gss_qop_t               qop_state;
274         uint32_t                num;
275         gss_buffer_desc         signbuf, checksum;
276         OM_uint32               maj_stat, min_stat;
277
278         log_debug("in rpc_gss_validate()");
279         
280         gd = AUTH_PRIVATE(auth);
281
282         if (gd->gd_state == RPCSEC_GSS_CONTEXT) {
283                 /*
284                  * Save the on the wire verifier to validate last INIT
285                  * phase packet after decode if the major status is
286                  * GSS_S_COMPLETE.
287                  */
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);
295                         return (FALSE);
296                 }
297                 memcpy(gd->gd_verf.value, verf->oa_base, verf->oa_length);
298                 gd->gd_verf.length = verf->oa_length;
299                 return (TRUE);
300         }
301
302         num = htonl(gd->gd_cred.gc_seq);
303         signbuf.value = &num;
304         signbuf.length = sizeof(num);
305         
306         checksum.value = verf->oa_base;
307         checksum.length = verf->oa_length;
308         
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);
315                 }
316                 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
317                 return (FALSE);
318         }
319         return (TRUE);
320 }
321
322 static bool_t
323 rpc_gss_init(AUTH *auth, rpc_gss_options_ret_t *options_ret)
324 {
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;
329         const char              *mech;
330
331         log_debug("in rpc_gss_refresh()");
332         
333         gd = AUTH_PRIVATE(auth);
334         
335         if (gd->gd_state != RPCSEC_GSS_START)
336                 return (TRUE);
337         
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;
342
343         memset(&recv_token, 0, sizeof(recv_token));
344         memset(&gr, 0, sizeof(gr));
345         recv_tokenp = GSS_C_NO_BUFFER;
346         
347         for (;;) {
348                 maj_stat = gss_init_sec_context(&min_stat,
349                     gd->gd_options.my_cred,
350                     &gd->gd_ctx,
351                     gd->gd_name,
352                     gd->gd_mech,
353                     gd->gd_options.req_flags,
354                     gd->gd_options.time_req,
355                     gd->gd_options.input_channel_bindings,
356                     recv_tokenp,
357                     &gd->gd_mech,       /* used mech */
358                     &send_token,
359                     &options_ret->ret_flags,
360                     &options_ret->time_req);
361                 
362                 /*
363                  * Free the token which we got from the server (if
364                  * any).  Remember that this was allocated by XDR, not
365                  * GSS-API.
366                  */
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;
371                 }
372                 if (maj_stat != GSS_S_COMPLETE &&
373                     maj_stat != GSS_S_CONTINUE_NEEDED) {
374                         log_status("gss_init_sec_context", gd->gd_mech,
375                             maj_stat, min_stat);
376                         options_ret->major_status = maj_stat;
377                         options_ret->minor_status = min_stat;
378                         break;
379                 }
380                 if (send_token.length != 0) {
381                         memset(&gr, 0, sizeof(gr));
382                         
383                         call_stat = clnt_call(gd->gd_clnt, NULLPROC,
384                             (xdrproc_t)xdr_gss_buffer_desc,
385                             &send_token,
386                             (xdrproc_t)xdr_rpc_gss_init_res,
387                             (caddr_t)&gr, AUTH_TIMEOUT);
388                         
389                         gss_release_buffer(&min_stat, &send_token);
390                         
391                         if (call_stat != RPC_SUCCESS)
392                                 break;
393
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;
400                                 break;
401                         }
402                         
403                         /*
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).
407                          */
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;
412                         }
413
414                         /*
415                          * Save the server's token as well.
416                          */
417                         if (gr.gr_token.length != 0) {
418                                 recv_token = gr.gr_token;
419                                 recv_tokenp = &recv_token;
420                         }
421
422                         /*
423                          * Since we have copied out all the bits of gr
424                          * which XDR allocated for us, we don't need
425                          * to free it.
426                          */
427                         gd->gd_cred.gc_proc = RPCSEC_GSS_CONTINUE_INIT;
428                 }
429
430                 if (maj_stat == GSS_S_COMPLETE) {
431                         gss_buffer_desc   bufin;
432                         u_int seq, qop_state = 0;
433
434                         /* 
435                          * gss header verifier,
436                          * usually checked in gss_validate
437                          */
438                         seq = htonl(gr.gr_win);
439                         bufin.value = (unsigned char *)&seq;
440                         bufin.length = sizeof(seq);
441
442                         maj_stat = gss_verify_mic(&min_stat, gd->gd_ctx,
443                             &bufin, &gd->gd_verf, &qop_state);
444
445                         if (maj_stat != GSS_S_COMPLETE ||
446                             qop_state != gd->gd_qop) {
447                                 log_status("gss_verify_mic", gd->gd_mech,
448                                     maj_stat, min_stat);
449                                 if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
450                                         rpc_gss_destroy_context(auth, TRUE);
451                                 }
452                                 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR,
453                                     EPERM);
454                                 options_ret->major_status = maj_stat;
455                                 options_ret->minor_status = min_stat;
456                                 return (FALSE);
457                         }
458
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,
465                                     mech,
466                                     sizeof(options_ret->actual_mechanism));
467                         }
468
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;
473                         break;
474                 }
475         }
476         xdr_free((xdrproc_t) xdr_gss_buffer_desc,
477             (char *) &gd->gd_verf);
478
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);
483                 return (FALSE);
484         }
485         
486         return (TRUE);
487 }
488
489 static bool_t
490 rpc_gss_refresh(AUTH *auth, void *msg)
491 {
492         struct rpc_msg *reply = (struct rpc_msg *) msg;
493         rpc_gss_options_ret_t options;
494
495         /*
496          * If the error was RPCSEC_GSS_CREDPROBLEM of
497          * RPCSEC_GSS_CTXPROBLEM we start again from scratch. All
498          * other errors are fatal.
499          */
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));
507         }
508
509         return (FALSE);
510 }
511
512 static void
513 rpc_gss_destroy_context(AUTH *auth, bool_t send_destroy)
514 {
515         struct rpc_gss_data     *gd;
516         OM_uint32                min_stat;
517
518         log_debug("in rpc_gss_destroy_context()");
519         
520         gd = AUTH_PRIVATE(auth);
521         
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);
527         }
528
529         /*
530          * Free the context token. Remember that this was
531          * allocated by XDR, not GSS-API.
532          */
533         xdr_free((xdrproc_t) xdr_gss_buffer_desc,
534             (char *) &gd->gd_cred.gc_handle);
535         gd->gd_cred.gc_handle.length = 0;
536
537         if (gd->gd_ctx != GSS_C_NO_CONTEXT)
538                 gss_delete_sec_context(&min_stat, &gd->gd_ctx, NULL);
539
540         gd->gd_state = RPCSEC_GSS_START;
541 }
542
543 static void
544 rpc_gss_destroy(AUTH *auth)
545 {
546         struct rpc_gss_data     *gd;
547         OM_uint32                min_stat;
548         
549         log_debug("in rpc_gss_destroy()");
550         
551         gd = AUTH_PRIVATE(auth);
552         
553         rpc_gss_destroy_context(auth, TRUE);
554         
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);
560
561         mem_free(gd, sizeof(*gd));
562         mem_free(auth, sizeof(*auth));
563 }
564
565 bool_t
566 __rpc_gss_wrap(AUTH *auth, void *header, size_t headerlen,
567     XDR* xdrs, xdrproc_t xdr_args, void *args_ptr)
568 {
569         XDR                      tmpxdrs;
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;
576         bool_t                   xdr_stat;
577         
578         log_debug("in rpc_gss_wrap()");
579         
580         gd = AUTH_PRIVATE(auth);
581
582         if (gd->gd_state == RPCSEC_GSS_ESTABLISHED)
583                 gd->gd_cred.gc_seq++;
584         
585         /*
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
588          * for the verf.
589          */
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);
594                 return (FALSE);
595         }
596         creds.oa_flavor = RPCSEC_GSS;
597         creds.oa_base = credbuf;
598         creds.oa_length = XDR_GETPOS(&tmpxdrs);
599         XDR_DESTROY(&tmpxdrs);
600         
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);
606                 return (FALSE);
607         }
608         headerlen = XDR_GETPOS(&tmpxdrs);
609         XDR_DESTROY(&tmpxdrs);
610                 
611         if (!XDR_PUTBYTES(xdrs, tmpheader, headerlen)) {
612                 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
613                 return (FALSE);
614         }
615         
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);
620                         return (FALSE);
621                 }
622         } else {
623                 /*
624                  * Checksum serialized RPC header, up to and including
625                  * credential.
626                  */
627                 rpcbuf.length = headerlen;
628                 rpcbuf.value = tmpheader;
629         
630                 maj_stat = gss_get_mic(&min_stat, gd->gd_ctx, gd->gd_qop,
631                     &rpcbuf, &checksum);
632
633                 if (maj_stat != GSS_S_COMPLETE) {
634                         log_status("gss_get_mic", gd->gd_mech,
635                             maj_stat, min_stat);
636                         if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
637                                 rpc_gss_destroy_context(auth, TRUE);
638                         }
639                         _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, EPERM);
640                         return (FALSE);
641                 }
642
643                 verf.oa_flavor = RPCSEC_GSS;
644                 verf.oa_base = checksum.value;
645                 verf.oa_length = checksum.length;
646
647                 xdr_stat = xdr_opaque_auth(xdrs, &verf);
648                 gss_release_buffer(&min_stat, &checksum);
649                 if (!xdr_stat) {
650                         _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
651                         return (FALSE);
652                 }
653         }
654         
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));
658         }
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));
662 }
663
664 bool_t
665 __rpc_gss_unwrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, void *xdr_ptr)
666 {
667         struct rpc_gss_data     *gd;
668
669         log_debug("in rpc_gss_unwrap()");
670         
671         gd = AUTH_PRIVATE(auth);
672         
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));
676         }
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));
680 }
681
682 int
683 rpc_gss_max_data_length(AUTH *auth, int max_tp_unit_len)
684 {
685         struct rpc_gss_data     *gd;
686         int                     want_conf;
687         OM_uint32               max;
688         OM_uint32               maj_stat, min_stat;
689         int                     result;
690
691         gd = AUTH_PRIVATE(auth);
692
693         switch (gd->gd_cred.gc_svc) {
694         case rpc_gss_svc_none:
695                 return (max_tp_unit_len);
696                 break;
697
698         case rpc_gss_svc_default:
699         case rpc_gss_svc_integrity:
700                 want_conf = FALSE;
701                 break;
702
703         case rpc_gss_svc_privacy:
704                 want_conf = TRUE;
705                 break;
706
707         default:
708                 return (0);
709         }
710
711         maj_stat = gss_wrap_size_limit(&min_stat, gd->gd_ctx, want_conf,
712             gd->gd_qop, max_tp_unit_len, &max);
713
714         if (maj_stat == GSS_S_COMPLETE) {
715                 result = (int) max;
716                 if (result < 0)
717                         result = 0;
718                 return (result);
719         } else {
720                 log_status("gss_wrap_size_limit", gd->gd_mech,
721                     maj_stat, min_stat);
722                 return (0);
723         }
724 }