]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - lib/librpcsec_gss/rpcsec_gss.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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   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 *
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
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
253 rpc_gss_nextverf(__unused AUTH *auth)
254 {
255
256         /* not used */
257 }
258
259 static bool_t
260 rpc_gss_marshal(__unused AUTH *auth, __unused XDR *xdrs)
261 {
262
263         /* not used */
264         return (FALSE);
265 }
266
267 static bool_t
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 = &num;
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
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
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
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
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
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
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
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 }