]> git.decadent.org.uk Git - nfs-utils.git/blob - support/rpc/auth_gss.c
Don't use cast as lvalue
[nfs-utils.git] / support / rpc / auth_gss.c
1 /*
2   auth_gss.c
3
4   RPCSEC_GSS client routines.
5
6   Copyright (c) 2000 The Regents of the University of Michigan.
7   All rights reserved.
8
9   Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
10   All rights reserved, all wrongs reversed.
11
12   Redistribution and use in source and binary forms, with or without
13   modification, are permitted provided that the following conditions
14   are met:
15
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.
24
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.
36
37 */
38
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <unistd.h>
42 #include <string.h>
43 #include <errno.h>
44 #include <rpc/types.h>
45 #include <rpc/xdr.h>
46 #include <rpc/auth.h>
47 #include <rpc/auth_gss.h>
48 #include <rpc/clnt.h>
49 #include <netinet/in.h>
50 #include <gssapi/gssapi.h>
51
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();
60
61
62 /*
63  * from mit-krb5-1.2.1 mechglue/mglueP.h:
64  * Array of context IDs typed by mechanism OID
65  */
66 typedef struct gss_union_ctx_id_t {
67         gss_OID     mech_type;
68         gss_ctx_id_t    internal_ctx_id;
69 } gss_union_ctx_id_desc, *gss_union_ctx_id_t;
70
71 static struct auth_ops authgss_ops = {
72         authgss_nextverf,
73         authgss_marshal,
74         authgss_validate,
75         authgss_refresh,
76         authgss_destroy,
77         authgss_wrap,
78         authgss_unwrap
79 };
80
81 #ifdef DEBUG
82
83 /* useful as i add more mechanisms */
84 void
85 print_rpc_gss_sec(struct rpc_gss_sec *ptr)
86 {
87 int i;
88 char *p;
89
90         log_debug("rpc_gss_sec:");
91         if(ptr->mech == NULL)
92                 log_debug("NULL gss_OID mech");
93         else {
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 */
98                         if (i == 0) {
99                                 int first, second;
100                                 if (*p < 40) {
101                                         first = 0;
102                                         second = *p;
103                                 }
104                                 else if (40 <= *p && *p < 80) {
105                                         first = 1;
106                                         second = *p - 40;
107                                 }
108                                 else if (80 <= *p && *p < 127) {
109                                         first = 2;
110                                         second = *p - 80;
111                                 }
112                                 else {
113                                         /* Invalid value! */
114                                         first = -1;
115                                         second = -1;
116                                 }
117                                 fprintf(stderr, " %u %u", first, second);
118                                 p++;
119                         }
120                         else {
121                                 fprintf(stderr, " %u", (unsigned char)*p++);
122                         }
123                 fprintf(stderr, " }\n");
124         }
125         fprintf(stderr, "     qop: %d\n", ptr->qop);
126         fprintf(stderr, "     service: %d\n", ptr->svc);
127         fprintf(stderr, "     cred: %p\n", ptr->cred);
128 }
129 #endif /*DEBUG*/
130
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 */
141 };
142
143 #define AUTH_PRIVATE(auth)      ((struct rpc_gss_data *)auth->ah_private)
144
145 static struct timeval AUTH_TIMEOUT = { 25, 0 };
146
147 AUTH *
148 authgss_create(CLIENT *clnt, gss_name_t name, struct rpc_gss_sec *sec)
149 {
150         AUTH                    *auth, *save_auth;
151         struct rpc_gss_data     *gd;
152         OM_uint32               min_stat = 0;
153
154         log_debug("in authgss_create()");
155
156         memset(&rpc_createerr, 0, sizeof(rpc_createerr));
157
158         if ((auth = calloc(sizeof(*auth), 1)) == NULL) {
159                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
160                 rpc_createerr.cf_error.re_errno = ENOMEM;
161                 return (NULL);
162         }
163         if ((gd = calloc(sizeof(*gd), 1)) == NULL) {
164                 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
165                 rpc_createerr.cf_error.re_errno = ENOMEM;
166                 free(auth);
167                 return (NULL);
168         }
169 #ifdef DEBUG
170         fprintf(stderr, "authgss_create: name is %p\n", name);
171 #endif
172         if (name != GSS_C_NO_NAME) {
173                 if (gss_duplicate_name(&min_stat, name, &gd->name)
174                                                 != GSS_S_COMPLETE) {
175                         rpc_createerr.cf_stat = RPC_SYSTEMERROR;
176                         rpc_createerr.cf_error.re_errno = ENOMEM;
177                         free(auth);
178                         return (NULL);
179                 }
180         }
181         else
182                 gd->name = name;
183
184 #ifdef DEBUG
185         fprintf(stderr, "authgss_create: gd->name is %p\n", gd->name);
186 #endif
187         gd->clnt = clnt;
188         gd->ctx = GSS_C_NO_CONTEXT;
189         gd->sec = *sec;
190
191         gd->gc.gc_v = RPCSEC_GSS_VERSION;
192         gd->gc.gc_proc = RPCSEC_GSS_INIT;
193         gd->gc.gc_svc = gd->sec.svc;
194
195         auth->ah_ops = &authgss_ops;
196         auth->ah_private = (caddr_t)gd;
197
198         save_auth = clnt->cl_auth;
199         clnt->cl_auth = auth;
200
201         if (!authgss_refresh(auth))
202                 auth = NULL;
203
204         clnt->cl_auth = save_auth;
205
206         return (auth);
207 }
208
209 AUTH *
210 authgss_create_default(CLIENT *clnt, char *service, struct rpc_gss_sec *sec)
211 {
212         AUTH                    *auth;
213         OM_uint32                maj_stat = 0, min_stat = 0;
214         gss_buffer_desc          sname;
215         gss_name_t               name = GSS_C_NO_NAME;
216
217         log_debug("in authgss_create_default()");
218
219
220         sname.value = service;
221         sname.length = strlen(service);
222
223         maj_stat = gss_import_name(&min_stat, &sname,
224                 GSS_C_NT_HOSTBASED_SERVICE,
225                 &name);
226
227         if (maj_stat != GSS_S_COMPLETE) {
228                 log_status("gss_import_name", maj_stat, min_stat);
229                 rpc_createerr.cf_stat = RPC_AUTHERROR;
230                 return (NULL);
231         }
232
233         auth = authgss_create(clnt, name, sec);
234
235         if (name != GSS_C_NO_NAME) {
236 #ifdef DEBUG
237         fprintf(stderr, "authgss_create_default: freeing name %p\n", name);
238 #endif
239                 gss_release_name(&min_stat, &name);
240         }
241
242         return (auth);
243 }
244
245 bool_t
246 authgss_get_private_data(AUTH *auth, struct authgss_private_data *pd)
247 {
248         struct rpc_gss_data     *gd;
249
250         log_debug("in authgss_get_private_data()");
251
252         if (!auth || !pd)
253                 return (FALSE);
254
255         gd = AUTH_PRIVATE(auth);
256
257         if (!gd || !gd->established)
258                 return (FALSE);
259
260         pd->pd_ctx = gd->ctx;
261         pd->pd_ctx_hndl = gd->gc.gc_ctx;
262         pd->pd_seq_win = gd->win;
263
264         return (TRUE);
265 }
266
267 static void
268 authgss_nextverf(AUTH *auth)
269 {
270         log_debug("in authgss_nextverf()");
271         /* no action necessary */
272 }
273
274 static bool_t
275 authgss_marshal(AUTH *auth, XDR *xdrs)
276 {
277         XDR                      tmpxdrs;
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;
282         bool_t                   xdr_stat;
283
284         log_debug("in authgss_marshal()");
285
286         gd = AUTH_PRIVATE(auth);
287
288         if (gd->established)
289                 gd->gc.gc_seq++;
290
291         xdrmem_create(&tmpxdrs, tmp, sizeof(tmp), XDR_ENCODE);
292
293         if (!xdr_rpc_gss_cred(&tmpxdrs, &gd->gc)) {
294                 XDR_DESTROY(&tmpxdrs);
295                 return (FALSE);
296         }
297         auth->ah_cred.oa_flavor = RPCSEC_GSS;
298         auth->ah_cred.oa_base = tmp;
299         auth->ah_cred.oa_length = XDR_GETPOS(&tmpxdrs);
300
301         XDR_DESTROY(&tmpxdrs);
302
303         if (!xdr_opaque_auth(xdrs, &auth->ah_cred))
304                 return (FALSE);
305
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));
309         }
310         /* Checksum serialized RPC header, up to and including credential. */
311         rpcbuf.length = XDR_GETPOS(xdrs);
312         XDR_SETPOS(xdrs, 0);
313         rpcbuf.value = XDR_INLINE(xdrs, rpcbuf.length);
314
315         maj_stat = gss_get_mic(&min_stat, gd->ctx, gd->sec.qop,
316                             &rpcbuf, &checksum);
317
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);
323                 }
324                 return (FALSE);
325         }
326         auth->ah_verf.oa_flavor = RPCSEC_GSS;
327         auth->ah_verf.oa_base = checksum.value;
328         auth->ah_verf.oa_length = checksum.length;
329
330         xdr_stat = xdr_opaque_auth(xdrs, &auth->ah_verf);
331         gss_release_buffer(&min_stat, &checksum);
332
333         return (xdr_stat);
334 }
335
336 static bool_t
337 authgss_validate(AUTH *auth, struct opaque_auth *verf)
338 {
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;
343
344         log_debug("in authgss_validate()");
345
346         gd = AUTH_PRIVATE(auth);
347
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
354                  */
355                 if ((gd->gc_wire_verf.value =
356                                 mem_alloc(verf->oa_length)) == NULL) {
357                         fprintf(stderr, "gss_validate: out of memory\n");
358                         return (FALSE);
359                 }
360                 memcpy(gd->gc_wire_verf.value, verf->oa_base, verf->oa_length);
361                 gd->gc_wire_verf.length = verf->oa_length;
362                 return (TRUE);
363         }
364
365         if (gd->gc.gc_proc == RPCSEC_GSS_INIT ||
366             gd->gc.gc_proc == RPCSEC_GSS_CONTINUE_INIT) {
367                 num = htonl(gd->win);
368         }
369         else num = htonl(gd->gc.gc_seq);
370
371         signbuf.value = &num;
372         signbuf.length = sizeof(num);
373
374         checksum.value = verf->oa_base;
375         checksum.length = verf->oa_length;
376
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);
384                 }
385                 return (FALSE);
386         }
387         return (TRUE);
388 }
389
390 static bool_t
391 authgss_refresh(AUTH *auth)
392 {
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;
397         OM_uint32                req_flags=0;
398
399         log_debug("in authgss_refresh()");
400
401         gd = AUTH_PRIVATE(auth);
402
403         if (gd->established)
404                 return (TRUE);
405
406         /* GSS context establishment loop. */
407         memset(&gr, 0, sizeof(gr));
408         recv_tokenp = GSS_C_NO_BUFFER;
409
410 #ifdef DEBUG
411         print_rpc_gss_sec(&gd->sec);
412 #endif /*DEBUG*/
413
414         for (;;) {
415 #ifdef DEBUG
416                 /* print the token we just received */
417                 if (recv_tokenp != GSS_C_NO_BUFFER) {
418                         log_debug("The token we just received (length %d):",
419                                   recv_tokenp->length);
420                         log_hexdump(recv_tokenp->value, recv_tokenp->length, 0);
421                 }
422 #endif
423                 maj_stat = gss_init_sec_context(&min_stat,
424                                                 gd->sec.cred,
425                                                 &gd->ctx,
426                                                 gd->name,
427                                                 gd->sec.mech,
428                                                 gd->sec.req_flags,
429                                                 0,              /* time req */
430                                                 NULL,           /* channel */
431                                                 recv_tokenp,
432                                                 NULL,           /* used mech */
433                                                 &send_token,
434                                                 &ret_flags,
435                                                 NULL);          /* time rec */
436
437                 if (recv_tokenp != GSS_C_NO_BUFFER) {
438                         gss_release_buffer(&min_stat, &gr.gr_token);
439                         recv_tokenp = GSS_C_NO_BUFFER;
440                 }
441                 if (maj_stat != GSS_S_COMPLETE &&
442                     maj_stat != GSS_S_CONTINUE_NEEDED) {
443                         log_status("gss_init_sec_context", maj_stat, min_stat);
444                         break;
445                 }
446                 if (send_token.length != 0) {
447                         memset(&gr, 0, sizeof(gr));
448
449 #ifdef DEBUG
450                         /* print the token we are about to send */
451                         log_debug("The token being sent (length %d):",
452                                   send_token.length);
453                         log_hexdump(send_token.value, send_token.length, 0);
454 #endif
455
456                         call_stat = clnt_call(gd->clnt, NULLPROC,
457                                               xdr_rpc_gss_init_args,
458                                               &send_token,
459                                               xdr_rpc_gss_init_res,
460                                               (caddr_t)&gr, AUTH_TIMEOUT);
461
462                         gss_release_buffer(&min_stat, &send_token);
463
464                         if (call_stat != RPC_SUCCESS ||
465                             (gr.gr_major != GSS_S_COMPLETE &&
466                              gr.gr_major != GSS_S_CONTINUE_NEEDED))
467                                 return FALSE;
468
469                         if (gr.gr_ctx.length != 0) {
470                                 if (gd->gc.gc_ctx.value)
471                                         gss_release_buffer(&min_stat,
472                                                            &gd->gc.gc_ctx);
473                                 gd->gc.gc_ctx = gr.gr_ctx;
474                         }
475                         if (gr.gr_token.length != 0) {
476                                 if (maj_stat != GSS_S_CONTINUE_NEEDED)
477                                         break;
478                                 recv_tokenp = &gr.gr_token;
479                         }
480                         gd->gc.gc_proc = RPCSEC_GSS_CONTINUE_INIT;
481                 }
482
483                 /* GSS_S_COMPLETE => check gss header verifier,
484                  * usually checked in gss_validate
485                  */
486                 if (maj_stat == GSS_S_COMPLETE) {
487                         gss_buffer_desc   bufin;
488                         gss_buffer_desc   bufout;
489                         u_int seq, qop_state = 0;
490
491                         seq = htonl(gr.gr_win);
492                         bufin.value = (unsigned char *)&seq;
493                         bufin.length = sizeof(seq);
494                         bufout.value = (unsigned char *)gd->gc_wire_verf.value;
495                         bufout.length = gd->gc_wire_verf.length;
496
497                         maj_stat = gss_verify_mic(&min_stat, gd->ctx,
498                                 &bufin, &bufout, &qop_state);
499
500                         if (maj_stat != GSS_S_COMPLETE
501                                         || qop_state != gd->sec.qop) {
502                                 log_status("gss_verify_mic", maj_stat, min_stat);
503                                 if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
504                                         gd->established = FALSE;
505                                         authgss_destroy_context(auth);
506                                 }
507                                 return (FALSE);
508                         }
509                         gd->established = TRUE;
510                         gd->gc.gc_proc = RPCSEC_GSS_DATA;
511                         gd->gc.gc_seq = 0;
512                         gd->win = gr.gr_win;
513                         break;
514                 }
515         }
516         /* End context negotiation loop. */
517         if (gd->gc.gc_proc != RPCSEC_GSS_DATA) {
518                 if (gr.gr_token.length != 0)
519                         gss_release_buffer(&min_stat, &gr.gr_token);
520
521                 authgss_destroy(auth);
522                 auth = NULL;
523                 rpc_createerr.cf_stat = RPC_AUTHERROR;
524
525                 return (FALSE);
526         }
527         return (TRUE);
528 }
529
530 bool_t
531 authgss_service(AUTH *auth, int svc)
532 {
533         struct rpc_gss_data     *gd;
534
535         log_debug("in authgss_service()");
536
537         if (!auth)
538                 return(FALSE);
539         gd = AUTH_PRIVATE(auth);
540         if (!gd || !gd->established)
541                 return (FALSE);
542         gd->sec.svc = svc;
543         gd->gc.gc_svc = svc;
544         return (TRUE);
545 }
546
547 static void
548 authgss_destroy_context(AUTH *auth)
549 {
550         struct rpc_gss_data     *gd;
551         OM_uint32                min_stat;
552
553         log_debug("in authgss_destroy_context()");
554
555         gd = AUTH_PRIVATE(auth);
556
557         if (gd->gc.gc_ctx.length != 0) {
558                 if (gd->established) {
559                         gd->gc.gc_proc = RPCSEC_GSS_DESTROY;
560                         clnt_call(gd->clnt, NULLPROC, xdr_void, NULL,
561                                   xdr_void, NULL, AUTH_TIMEOUT);
562                 }
563                 gss_release_buffer(&min_stat, &gd->gc.gc_ctx);
564                 /* XXX ANDROS check size of context  - should be 8 */
565                 memset(&gd->gc.gc_ctx, 0, sizeof(gd->gc.gc_ctx));
566         }
567         if (gd->ctx != GSS_C_NO_CONTEXT) {
568                 gss_delete_sec_context(&min_stat, &gd->ctx, NULL);
569                 gd->ctx = GSS_C_NO_CONTEXT;
570         }
571         gd->established = FALSE;
572 }
573
574 static void
575 authgss_destroy(AUTH *auth)
576 {
577         struct rpc_gss_data     *gd;
578         OM_uint32                min_stat;
579
580         log_debug("in authgss_destroy()");
581
582         gd = AUTH_PRIVATE(auth);
583
584         authgss_destroy_context(auth);
585
586 #ifdef DEBUG
587         fprintf(stderr, "authgss_destroy: freeing name %p\n", gd->name);
588 #endif
589         if (gd->name != GSS_C_NO_NAME)
590                 gss_release_name(&min_stat, &gd->name);
591
592         free(gd);
593         free(auth);
594 }
595
596 bool_t
597 authgss_wrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
598 {
599         struct rpc_gss_data     *gd;
600
601         log_debug("in authgss_wrap()");
602
603         gd = AUTH_PRIVATE(auth);
604
605         if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) {
606                 return ((*xdr_func)(xdrs, xdr_ptr));
607         }
608         return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr,
609                                  gd->ctx, gd->sec.qop,
610                                  gd->sec.svc, gd->gc.gc_seq));
611 }
612
613 bool_t
614 authgss_unwrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
615 {
616         struct rpc_gss_data     *gd;
617
618         log_debug("in authgss_unwrap()");
619
620         gd = AUTH_PRIVATE(auth);
621
622         if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) {
623                 return ((*xdr_func)(xdrs, xdr_ptr));
624         }
625         return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr,
626                                  gd->ctx, gd->sec.qop,
627                                  gd->sec.svc, gd->gc.gc_seq));
628 }