Imported Upstream version 0.2.5
[platform/upstream/libtirpc.git] / src / svc_auth_gss.c
1 /*
2   svc_auth_gss.c
3
4   Copyright (c) 2000 The Regents of the University of Michigan.
5   All rights reserved.
6
7   Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
8   All rights reserved, all wrongs reversed.
9
10   Redistribution and use in source and binary forms, with or without
11   modification, are permitted provided that the following conditions
12   are met:
13
14   1. Redistributions of source code must retain the above copyright
15      notice, this list of conditions and the following disclaimer.
16   2. Redistributions in binary form must reproduce the above copyright
17      notice, this list of conditions and the following disclaimer in the
18      documentation and/or other materials provided with the distribution.
19   3. Neither the name of the University nor the names of its
20      contributors may be used to endorse or promote products derived
21      from this software without specific prior written permission.
22
23   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
24   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26   DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
30   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
35  */
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <rpc/rpc.h>
41 #include <gssapi/gssapi.h>
42
43 extern SVCAUTH svc_auth_none;
44
45 /*
46  * from mit-krb5-1.2.1 mechglue/mglueP.h:
47  * Array of context IDs typed by mechanism OID
48  */
49 typedef struct gss_union_ctx_id_t {
50   gss_OID     mech_type;
51   gss_ctx_id_t    internal_ctx_id;
52 } gss_union_ctx_id_desc, *gss_union_ctx_id_t;
53
54
55
56 static bool_t   svcauth_gss_wrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t);
57 static bool_t   svcauth_gss_unwrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t);
58 static bool_t   svcauth_gss_destroy(SVCAUTH *);
59
60 static struct svc_auth_ops svc_auth_gss_ops = {
61         svcauth_gss_wrap,
62         svcauth_gss_unwrap,
63         svcauth_gss_destroy
64 };
65
66 struct svc_rpc_gss_data {
67         bool_t                  established;    /* context established */
68         gss_ctx_id_t            ctx;            /* context id */
69         struct rpc_gss_sec      sec;            /* security triple */
70         gss_buffer_desc         cname;          /* GSS client name */
71         u_int                   seq;            /* sequence number */
72         u_int                   win;            /* sequence window */
73         u_int                   seqlast;        /* last sequence number */
74         u_int32_t               seqmask;        /* bitmask of seqnums */
75         gss_name_t              client_name;    /* unparsed name string */
76 };
77
78 #define SVCAUTH_PRIVATE(auth) \
79         ((struct svc_rpc_gss_data *)(auth)->svc_ah_private)
80
81 /* Global server credentials. */
82 static gss_cred_id_t    _svcauth_gss_creds;
83 static gss_name_t       _svcauth_gss_name = NULL;
84
85 bool_t
86 svcauth_gss_set_svc_name(gss_name_t name)
87 {
88         OM_uint32       maj_stat, min_stat;
89
90         gss_log_debug("in svcauth_gss_set_svc_name()");
91
92         if (_svcauth_gss_name != NULL) {
93                 maj_stat = gss_release_name(&min_stat, &_svcauth_gss_name);
94
95                 if (maj_stat != GSS_S_COMPLETE) {
96                         gss_log_status("svcauth_gss_set_svc_name: gss_release_name", 
97                                 maj_stat, min_stat);
98                         return (FALSE);
99                 }
100                 _svcauth_gss_name = NULL;
101         }
102         maj_stat = gss_duplicate_name(&min_stat, name, &_svcauth_gss_name);
103
104         if (maj_stat != GSS_S_COMPLETE) {
105                 gss_log_status("svcauth_gss_set_svc_name: gss_duplicate_name", 
106                         maj_stat, min_stat);
107                 return (FALSE);
108         }
109
110         return (TRUE);
111 }
112
113 static bool_t
114 svcauth_gss_import_name(char *service)
115 {
116         gss_name_t      name;
117         gss_buffer_desc namebuf;
118         OM_uint32       maj_stat, min_stat;
119
120         gss_log_debug("in svcauth_gss_import_name()");
121
122         namebuf.value = service;
123         namebuf.length = strlen(service);
124
125         maj_stat = gss_import_name(&min_stat, &namebuf,
126                                    (gss_OID)GSS_C_NT_HOSTBASED_SERVICE, &name);
127
128         if (maj_stat != GSS_S_COMPLETE) {
129                 gss_log_status("svcauth_gss_import_name: gss_import_name", 
130                         maj_stat, min_stat);
131                 return (FALSE);
132         }
133         if (svcauth_gss_set_svc_name(name) != TRUE) {
134                 gss_release_name(&min_stat, &name);
135                 return (FALSE);
136         }
137         return (TRUE);
138 }
139
140 static bool_t
141 svcauth_gss_acquire_cred(void)
142 {
143         OM_uint32       maj_stat, min_stat;
144
145         gss_log_debug("in svcauth_gss_acquire_cred()");
146
147         maj_stat = gss_acquire_cred(&min_stat, _svcauth_gss_name, 0,
148                                     GSS_C_NULL_OID_SET, GSS_C_ACCEPT,
149                                     &_svcauth_gss_creds, NULL, NULL);
150
151         if (maj_stat != GSS_S_COMPLETE) {
152                 gss_log_status("svcauth_gss_acquire_cred: gss_acquire_cred", 
153                         maj_stat, min_stat);
154                 return (FALSE);
155         }
156         return (TRUE);
157 }
158
159 static bool_t
160 svcauth_gss_release_cred(void)
161 {
162         OM_uint32       maj_stat, min_stat;
163
164         gss_log_debug("in svcauth_gss_release_cred()");
165
166         maj_stat = gss_release_cred(&min_stat, &_svcauth_gss_creds);
167
168         if (maj_stat != GSS_S_COMPLETE) {
169                 gss_log_status("svcauth_gss_release_cred: gss_release_cred", 
170                         maj_stat, min_stat);
171                 return (FALSE);
172         }
173
174         _svcauth_gss_creds = NULL;
175
176         return (TRUE);
177 }
178
179 static bool_t
180 svcauth_gss_accept_sec_context(struct svc_req *rqst,
181                                struct rpc_gss_init_res *gr)
182 {
183         struct svc_rpc_gss_data *gd;
184         struct rpc_gss_cred     *gc;
185         gss_buffer_desc          recv_tok, seqbuf, checksum;
186         gss_OID                  mech;
187         OM_uint32                maj_stat = 0, min_stat = 0, ret_flags, seq;
188
189         gss_log_debug("in svcauth_gss_accept_context()");
190
191         gd = SVCAUTH_PRIVATE(rqst->rq_xprt->xp_auth);
192         gc = (struct rpc_gss_cred *)rqst->rq_clntcred;
193         memset(gr, 0, sizeof(*gr));
194
195         /* Deserialize arguments. */
196         memset(&recv_tok, 0, sizeof(recv_tok));
197
198         if (!svc_getargs(rqst->rq_xprt, (xdrproc_t)xdr_rpc_gss_init_args,
199                          (caddr_t)&recv_tok))
200                 return (FALSE);
201
202         gr->gr_major = gss_accept_sec_context(&gr->gr_minor,
203                                               &gd->ctx,
204                                               _svcauth_gss_creds,
205                                               &recv_tok,
206                                               GSS_C_NO_CHANNEL_BINDINGS,
207                                               &gd->client_name,
208                                               &mech,
209                                               &gr->gr_token,
210                                               &ret_flags,
211                                               NULL,
212                                               NULL);
213
214         if (gr->gr_major != GSS_S_COMPLETE &&
215             gr->gr_major != GSS_S_CONTINUE_NEEDED) {
216                 gss_log_status("svcauth_gss_accept_sec_context: accept_sec_context",
217                         gr->gr_major, gr->gr_minor);
218                 gd->ctx = GSS_C_NO_CONTEXT;
219                 gss_release_buffer(&min_stat, &gr->gr_token);
220                 return (FALSE);
221         }
222         /* ANDROS: krb5 mechglue returns ctx of size 8 - two pointers,
223          * one to the mechanism oid, one to the internal_ctx_id */
224         if ((gr->gr_ctx.value = mem_alloc(sizeof(gss_union_ctx_id_desc))) == NULL) {
225                 fprintf(stderr, "svcauth_gss_accept_context: out of memory\n");
226                 return (FALSE);
227         }
228         memcpy(gr->gr_ctx.value, gd->ctx, sizeof(gss_union_ctx_id_desc));
229         gr->gr_ctx.length = sizeof(gss_union_ctx_id_desc);
230
231         /* ANDROS: change for debugging linux kernel version...
232         gr->gr_win = sizeof(gd->seqmask) * 8;
233         */
234         gr->gr_win = 0x00000005;
235
236         /* Save client info. */
237         gd->sec.mech = mech;
238         gd->sec.qop = GSS_C_QOP_DEFAULT;
239         gd->sec.svc = gc->gc_svc;
240         gd->seq = gc->gc_seq;
241         gd->win = gr->gr_win;
242
243         if (gr->gr_major == GSS_S_COMPLETE) {
244                 maj_stat = gss_display_name(&min_stat, gd->client_name,
245                                             &gd->cname, &gd->sec.mech);
246                 if (maj_stat != GSS_S_COMPLETE) {
247                         gss_log_status("svcauth_gss_accept_sec_context: display_name", 
248                                 maj_stat, min_stat);
249                         return (FALSE);
250                 }
251 #ifdef HAVE_KRB5
252                 {
253                         gss_buffer_desc mechname;
254
255                         gss_oid_to_str(&min_stat, mech, &mechname);
256
257                         gss_log_debug("accepted context for %.*s with "
258                                       "<mech %.*s, qop %d, svc %d>",
259                                       gd->cname.length, (char *)gd->cname.value,
260                                       mechname.length, (char *)mechname.value,
261                                       gd->sec.qop, gd->sec.svc);
262
263                         gss_release_buffer(&min_stat, &mechname);
264                 }
265 #elif HAVE_HEIMDAL
266                 gss_log_debug("accepted context for %.*s with "
267                               "<mech {}, qop %d, svc %d>",
268                               gd->cname.length, (char *)gd->cname.value,
269                               gd->sec.qop, gd->sec.svc);
270 #endif
271                 seq = htonl(gr->gr_win);
272                 seqbuf.value = &seq;
273                 seqbuf.length = sizeof(seq);
274
275                 maj_stat = gss_sign(&min_stat, gd->ctx, GSS_C_QOP_DEFAULT,
276                                     &seqbuf, &checksum);
277
278                 if (maj_stat != GSS_S_COMPLETE)
279                         return (FALSE);
280
281                 rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;
282                 rqst->rq_xprt->xp_verf.oa_base = checksum.value;
283                 rqst->rq_xprt->xp_verf.oa_length = checksum.length;
284         }
285         return (TRUE);
286 }
287
288 static bool_t
289 svcauth_gss_validate(struct svc_rpc_gss_data *gd, struct rpc_msg *msg)
290 {
291         struct opaque_auth      *oa;
292         gss_buffer_desc          rpcbuf, checksum;
293         OM_uint32                maj_stat, min_stat, qop_state;
294         u_char                   *rpchdr;
295         int32_t                 *buf;
296
297         gss_log_debug("in svcauth_gss_validate()");
298
299         /* XXX - Reconstruct RPC header for signing (from xdr_callmsg). */
300         oa = &msg->rm_call.cb_cred;
301         if (oa->oa_length > MAX_AUTH_BYTES)
302                 return (FALSE);
303
304         rpchdr = (u_char *)calloc(((8 * BYTES_PER_XDR_UNIT) + 
305                         RNDUP(oa->oa_length)), 1);
306         if (rpchdr == NULL)
307                 return (FALSE);
308
309         buf = (int32_t *)rpchdr;
310         IXDR_PUT_LONG(buf, msg->rm_xid);
311         IXDR_PUT_ENUM(buf, msg->rm_direction);
312         IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
313         IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
314         IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
315         IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
316         IXDR_PUT_ENUM(buf, oa->oa_flavor);
317         IXDR_PUT_LONG(buf, oa->oa_length);
318         if (oa->oa_length) {
319                 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
320                 buf += RNDUP(oa->oa_length) / sizeof(int32_t);
321         }
322         rpcbuf.value = rpchdr;
323         rpcbuf.length = (u_char *)buf - rpchdr;
324
325         checksum.value = msg->rm_call.cb_verf.oa_base;
326         checksum.length = msg->rm_call.cb_verf.oa_length;
327
328         maj_stat = gss_verify_mic(&min_stat, gd->ctx, &rpcbuf, &checksum,
329                                   &qop_state);
330
331         free(rpchdr);
332
333         if (maj_stat != GSS_S_COMPLETE) {
334                 gss_log_status("svcauth_gss_validate: gss_verify_mic", 
335                         maj_stat, min_stat);
336                 return (FALSE);
337         }
338         return (TRUE);
339 }
340
341 static bool_t
342 svcauth_gss_nextverf(struct svc_req *rqst, u_int num)
343 {
344         struct svc_rpc_gss_data *gd;
345         gss_buffer_desc          signbuf, checksum;
346         OM_uint32                maj_stat, min_stat;
347
348         gss_log_debug("in svcauth_gss_nextverf()");
349
350         if (rqst->rq_xprt->xp_auth == NULL)
351                 return (FALSE);
352
353         gd = SVCAUTH_PRIVATE(rqst->rq_xprt->xp_auth);
354
355         signbuf.value = &num;
356         signbuf.length = sizeof(num);
357
358         maj_stat = gss_get_mic(&min_stat, gd->ctx, gd->sec.qop,
359                                &signbuf, &checksum);
360
361         if (maj_stat != GSS_S_COMPLETE) {
362                 gss_log_status("svcauth_gss_nextverf: gss_get_mic", 
363                         maj_stat, min_stat);
364                 return (FALSE);
365         }
366         rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;
367         rqst->rq_xprt->xp_verf.oa_base = (caddr_t)checksum.value;
368         rqst->rq_xprt->xp_verf.oa_length = (u_int)checksum.length;
369
370         return (TRUE);
371 }
372
373 enum auth_stat
374 _svcauth_gss(struct svc_req *rqst, struct rpc_msg *msg, bool_t *no_dispatch)
375 {
376         XDR                      xdrs;
377         SVCAUTH                 *auth;
378         struct svc_rpc_gss_data *gd;
379         struct rpc_gss_cred     *gc;
380         struct rpc_gss_init_res  gr;
381         int                      call_stat, offset;
382
383         gss_log_debug("in svcauth_gss()");
384
385         /* Initialize reply. */
386         rqst->rq_xprt->xp_verf = _null_auth;
387
388         /* Allocate and set up server auth handle. */
389         if (rqst->rq_xprt->xp_auth == NULL ||
390             rqst->rq_xprt->xp_auth == &svc_auth_none) {
391                 if ((auth = calloc(sizeof(*auth), 1)) == NULL) {
392                         fprintf(stderr, "svcauth_gss: out_of_memory\n");
393                         return (AUTH_FAILED);
394                 }
395                 if ((gd = calloc(sizeof(*gd), 1)) == NULL) {
396                         fprintf(stderr, "svcauth_gss: out_of_memory\n");
397                         return (AUTH_FAILED);
398                 }
399                 auth->svc_ah_ops = &svc_auth_gss_ops;
400                 auth->svc_ah_private = (caddr_t) gd;
401                 rqst->rq_xprt->xp_auth = auth;
402         }
403         else gd = SVCAUTH_PRIVATE(rqst->rq_xprt->xp_auth);
404
405         /* Deserialize client credentials. */
406         if (rqst->rq_cred.oa_length <= 0)
407                 return (AUTH_BADCRED);
408
409         gc = (struct rpc_gss_cred *)rqst->rq_clntcred;
410         memset(gc, 0, sizeof(*gc));
411
412         xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
413                       rqst->rq_cred.oa_length, XDR_DECODE);
414
415         if (!xdr_rpc_gss_cred(&xdrs, gc)) {
416                 XDR_DESTROY(&xdrs);
417                 return (AUTH_BADCRED);
418         }
419         XDR_DESTROY(&xdrs);
420
421         /* Check version. */
422         if (gc->gc_v != RPCSEC_GSS_VERSION)
423                 return (AUTH_BADCRED);
424
425         /* Check RPCSEC_GSS service. */
426         if (gc->gc_svc != RPCSEC_GSS_SVC_NONE &&
427             gc->gc_svc != RPCSEC_GSS_SVC_INTEGRITY &&
428             gc->gc_svc != RPCSEC_GSS_SVC_PRIVACY)
429                 return (AUTH_BADCRED);
430
431         /* Check sequence number. */
432         if (gd->established) {
433                 if (gc->gc_seq > MAXSEQ)
434                         return (RPCSEC_GSS_CTXPROBLEM);
435
436                 if ((offset = gd->seqlast - gc->gc_seq) < 0) {
437                         gd->seqlast = gc->gc_seq;
438                         offset = 0 - offset;
439                         gd->seqmask <<= offset;
440                         offset = 0;
441                 }
442                 else if (offset >= gd->win || (gd->seqmask & (1 << offset))) {
443                         *no_dispatch = 1;
444                         return (RPCSEC_GSS_CTXPROBLEM);
445                 }
446                 gd->seq = gc->gc_seq;
447                 gd->seqmask |= (1 << offset);
448         }
449
450         if (gd->established) {
451                 rqst->rq_clntname = (char *)gd->client_name;
452                 rqst->rq_svcname = (char *)gd->ctx;
453         }
454
455         /* Handle RPCSEC_GSS control procedure. */
456         switch (gc->gc_proc) {
457
458         case RPCSEC_GSS_INIT:
459         case RPCSEC_GSS_CONTINUE_INIT:
460                 if (rqst->rq_proc != NULLPROC)
461                         return (AUTH_FAILED);           /* XXX ? */
462
463                 if (_svcauth_gss_name == NULL) {
464                         if (!svcauth_gss_import_name("nfs"))
465                                 return (AUTH_FAILED);
466                 }
467
468                 if (!svcauth_gss_acquire_cred())
469                         return (AUTH_FAILED);
470
471                 if (!svcauth_gss_accept_sec_context(rqst, &gr))
472                         return (AUTH_REJECTEDCRED);
473
474                 if (!svcauth_gss_nextverf(rqst, htonl(gr.gr_win)))
475                         return (AUTH_FAILED);
476
477                 *no_dispatch = TRUE;
478
479                 call_stat = svc_sendreply(rqst->rq_xprt, 
480                         (xdrproc_t)xdr_rpc_gss_init_res, (caddr_t)&gr);
481
482                 if (!call_stat)
483                         return (AUTH_FAILED);
484
485                 if (gr.gr_major == GSS_S_COMPLETE)
486                         gd->established = TRUE;
487
488                 break;
489
490         case RPCSEC_GSS_DATA:
491                 if (!svcauth_gss_validate(gd, msg))
492                         return (RPCSEC_GSS_CREDPROBLEM);
493
494                 if (!svcauth_gss_nextverf(rqst, htonl(gc->gc_seq)))
495                         return (AUTH_FAILED);
496                 break;
497
498         case RPCSEC_GSS_DESTROY:
499                 if (rqst->rq_proc != NULLPROC)
500                         return (AUTH_FAILED);           /* XXX ? */
501
502                 if (!svcauth_gss_validate(gd, msg))
503                         return (RPCSEC_GSS_CREDPROBLEM);
504
505                 if (!svcauth_gss_nextverf(rqst, htonl(gc->gc_seq)))
506                         return (AUTH_FAILED);
507
508                 if (!svcauth_gss_release_cred())
509                         return (AUTH_FAILED);
510
511                 SVCAUTH_DESTROY(rqst->rq_xprt->xp_auth);
512                 rqst->rq_xprt->xp_auth = &svc_auth_none;
513
514                 break;
515
516         default:
517                 return (AUTH_REJECTEDCRED);
518                 break;
519         }
520         return (AUTH_OK);
521 }
522
523 static bool_t
524 svcauth_gss_destroy(SVCAUTH *auth)
525 {
526         struct svc_rpc_gss_data *gd;
527         OM_uint32                min_stat;
528
529         gss_log_debug("in svcauth_gss_destroy()");
530
531         gd = SVCAUTH_PRIVATE(auth);
532
533         gss_delete_sec_context(&min_stat, &gd->ctx, GSS_C_NO_BUFFER);
534         gss_release_buffer(&min_stat, &gd->cname);
535
536         if (gd->client_name)
537                 gss_release_name(&min_stat, &gd->client_name);
538
539         mem_free(gd, sizeof(*gd));
540         mem_free(auth, sizeof(*auth));
541
542         return (TRUE);
543 }
544
545 static bool_t
546 svcauth_gss_wrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
547 {
548         struct svc_rpc_gss_data *gd;
549
550         gss_log_debug("in svcauth_gss_wrap()");
551
552         gd = SVCAUTH_PRIVATE(auth);
553
554         if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) {
555                 return ((*xdr_func)(xdrs, xdr_ptr));
556         }
557         return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr,
558                                  gd->ctx, gd->sec.qop,
559                                  gd->sec.svc, gd->seq));
560 }
561
562 static bool_t
563 svcauth_gss_unwrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
564 {
565         struct svc_rpc_gss_data *gd;
566
567         gss_log_debug("in svcauth_gss_unwrap()");
568
569         gd = SVCAUTH_PRIVATE(auth);
570
571         if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) {
572                 return ((*xdr_func)(xdrs, xdr_ptr));
573         }
574         return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr,
575                                  gd->ctx, gd->sec.qop,
576                                  gd->sec.svc, gd->seq));
577 }
578
579 char *
580 svcauth_gss_get_principal(SVCAUTH *auth)
581 {
582         struct svc_rpc_gss_data *gd;
583         char *pname;
584
585         gd = SVCAUTH_PRIVATE(auth);
586
587         if (gd->cname.length == 0)
588                 return (NULL);
589
590         if ((pname = malloc(gd->cname.length + 1)) == NULL)
591                 return (NULL);
592
593         memcpy(pname, gd->cname.value, gd->cname.length);
594         pname[gd->cname.length] = '\0';
595
596         return (pname);
597 }