Imported Upstream version 0.2.2
[platform/upstream/libtirpc.git] / src / 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_OID)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
398         log_debug("in authgss_refresh()");
399
400         gd = AUTH_PRIVATE(auth);
401
402         if (gd->established)
403                 return (TRUE);
404
405         /* GSS context establishment loop. */
406         memset(&gr, 0, sizeof(gr));
407         recv_tokenp = GSS_C_NO_BUFFER;
408
409 #ifdef DEBUG
410         print_rpc_gss_sec(&gd->sec);
411 #endif /*DEBUG*/
412
413         for (;;) {
414 #ifdef DEBUG
415                 /* print the token we just received */
416                 if (recv_tokenp != GSS_C_NO_BUFFER) {
417                         log_debug("The token we just received (length %d):",
418                                   recv_tokenp->length);
419                         log_hexdump(recv_tokenp->value, recv_tokenp->length, 0);
420                 }
421 #endif
422                 maj_stat = gss_init_sec_context(&min_stat,
423                                                 gd->sec.cred,
424                                                 &gd->ctx,
425                                                 gd->name,
426                                                 gd->sec.mech,
427                                                 gd->sec.req_flags,
428                                                 0,              /* time req */
429                                                 NULL,           /* channel */
430                                                 recv_tokenp,
431                                                 NULL,           /* used mech */
432                                                 &send_token,
433                                                 &ret_flags,
434                                                 NULL);          /* time rec */
435
436                 if (recv_tokenp != GSS_C_NO_BUFFER) {
437                         gss_release_buffer(&min_stat, &gr.gr_token);
438                         recv_tokenp = GSS_C_NO_BUFFER;
439                 }
440                 if (maj_stat != GSS_S_COMPLETE &&
441                     maj_stat != GSS_S_CONTINUE_NEEDED) {
442                         log_status("gss_init_sec_context", maj_stat, min_stat);
443                         break;
444                 }
445                 if (send_token.length != 0) {
446                         memset(&gr, 0, sizeof(gr));
447
448 #ifdef DEBUG
449                         /* print the token we are about to send */
450                         log_debug("The token being sent (length %d):",
451                                   send_token.length);
452                         log_hexdump(send_token.value, send_token.length, 0);
453 #endif
454
455                         call_stat = clnt_call(gd->clnt, NULLPROC,
456                                               (xdrproc_t)xdr_rpc_gss_init_args,
457                                               &send_token,
458                                               (xdrproc_t)xdr_rpc_gss_init_res,
459                                               (caddr_t)&gr, AUTH_TIMEOUT);
460
461                         gss_release_buffer(&min_stat, &send_token);
462
463                         if (call_stat != RPC_SUCCESS ||
464                             (gr.gr_major != GSS_S_COMPLETE &&
465                              gr.gr_major != GSS_S_CONTINUE_NEEDED))
466                                 return FALSE;
467
468                         if (gr.gr_ctx.length != 0) {
469                                 if (gd->gc.gc_ctx.value)
470                                         gss_release_buffer(&min_stat,
471                                                            &gd->gc.gc_ctx);
472                                 gd->gc.gc_ctx = gr.gr_ctx;
473                         }
474                         if (gr.gr_token.length != 0) {
475                                 if (maj_stat != GSS_S_CONTINUE_NEEDED)
476                                         break;
477                                 recv_tokenp = &gr.gr_token;
478                         }
479                         gd->gc.gc_proc = RPCSEC_GSS_CONTINUE_INIT;
480                 }
481
482                 /* GSS_S_COMPLETE => check gss header verifier,
483                  * usually checked in gss_validate
484                  */
485                 if (maj_stat == GSS_S_COMPLETE) {
486                         gss_buffer_desc   bufin;
487                         gss_buffer_desc   bufout;
488                         u_int seq, qop_state = 0;
489
490                         seq = htonl(gr.gr_win);
491                         bufin.value = (unsigned char *)&seq;
492                         bufin.length = sizeof(seq);
493                         bufout.value = (unsigned char *)gd->gc_wire_verf.value;
494                         bufout.length = gd->gc_wire_verf.length;
495
496                         maj_stat = gss_verify_mic(&min_stat, gd->ctx,
497                                 &bufin, &bufout, &qop_state);
498
499                         if (maj_stat != GSS_S_COMPLETE
500                                         || qop_state != gd->sec.qop) {
501                                 log_status("gss_verify_mic", maj_stat, min_stat);
502                                 if (maj_stat == GSS_S_CONTEXT_EXPIRED) {
503                                         gd->established = FALSE;
504                                         authgss_destroy_context(auth);
505                                 }
506                                 return (FALSE);
507                         }
508                         gd->established = TRUE;
509                         gd->gc.gc_proc = RPCSEC_GSS_DATA;
510                         gd->gc.gc_seq = 0;
511                         gd->win = gr.gr_win;
512                         break;
513                 }
514         }
515         /* End context negotiation loop. */
516         if (gd->gc.gc_proc != RPCSEC_GSS_DATA) {
517                 if (gr.gr_token.length != 0)
518                         gss_release_buffer(&min_stat, &gr.gr_token);
519
520                 authgss_destroy(auth);
521                 auth = NULL;
522                 rpc_createerr.cf_stat = RPC_AUTHERROR;
523
524                 return (FALSE);
525         }
526         return (TRUE);
527 }
528
529 bool_t
530 authgss_service(AUTH *auth, int svc)
531 {
532         struct rpc_gss_data     *gd;
533
534         log_debug("in authgss_service()");
535
536         if (!auth)
537                 return(FALSE);
538         gd = AUTH_PRIVATE(auth);
539         if (!gd || !gd->established)
540                 return (FALSE);
541         gd->sec.svc = svc;
542         gd->gc.gc_svc = svc;
543         return (TRUE);
544 }
545
546 static void
547 authgss_destroy_context(AUTH *auth)
548 {
549         struct rpc_gss_data     *gd;
550         OM_uint32                min_stat;
551
552         log_debug("in authgss_destroy_context()");
553
554         gd = AUTH_PRIVATE(auth);
555
556         if (gd->gc.gc_ctx.length != 0) {
557                 if (gd->established) {
558                         gd->gc.gc_proc = RPCSEC_GSS_DESTROY;
559                         clnt_call(gd->clnt, NULLPROC, (xdrproc_t)xdr_void, NULL,
560                                   (xdrproc_t)xdr_void, NULL, AUTH_TIMEOUT);
561                 }
562                 gss_release_buffer(&min_stat, &gd->gc.gc_ctx);
563                 /* XXX ANDROS check size of context  - should be 8 */
564                 memset(&gd->gc.gc_ctx, 0, sizeof(gd->gc.gc_ctx));
565         }
566         if (gd->ctx != GSS_C_NO_CONTEXT) {
567                 gss_delete_sec_context(&min_stat, &gd->ctx, NULL);
568                 gd->ctx = GSS_C_NO_CONTEXT;
569         }
570
571         /* free saved wire verifier (if any) */
572         mem_free(gd->gc_wire_verf.value, gd->gc_wire_verf.length);
573         gd->gc_wire_verf.value = NULL;
574         gd->gc_wire_verf.length = 0;
575
576         gd->established = FALSE;
577 }
578
579 static void
580 authgss_destroy(AUTH *auth)
581 {
582         struct rpc_gss_data     *gd;
583         OM_uint32                min_stat;
584
585         log_debug("in authgss_destroy()");
586
587         gd = AUTH_PRIVATE(auth);
588
589         authgss_destroy_context(auth);
590
591 #ifdef DEBUG
592         fprintf(stderr, "authgss_destroy: freeing name %p\n", gd->name);
593 #endif
594         if (gd->name != GSS_C_NO_NAME)
595                 gss_release_name(&min_stat, &gd->name);
596
597         free(gd);
598         free(auth);
599 }
600
601 bool_t
602 authgss_wrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
603 {
604         struct rpc_gss_data     *gd;
605
606         log_debug("in authgss_wrap()");
607
608         gd = AUTH_PRIVATE(auth);
609
610         if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) {
611                 return ((*xdr_func)(xdrs, xdr_ptr));
612         }
613         return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr,
614                                  gd->ctx, gd->sec.qop,
615                                  gd->sec.svc, gd->gc.gc_seq));
616 }
617
618 bool_t
619 authgss_unwrap(AUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
620 {
621         struct rpc_gss_data     *gd;
622
623         log_debug("in authgss_unwrap()");
624
625         gd = AUTH_PRIVATE(auth);
626
627         if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) {
628                 return ((*xdr_func)(xdrs, xdr_ptr));
629         }
630         return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr,
631                                  gd->ctx, gd->sec.qop,
632                                  gd->sec.svc, gd->gc.gc_seq));
633 }