]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - lib/librpcsec_gss/svc_rpcsec_gss.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / lib / librpcsec_gss / svc_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   svc_rpcsec_gss.c
30   
31   Copyright (c) 2000 The Regents of the University of Michigan.
32   All rights reserved.
33
34   Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>.
35   All rights reserved, all wrongs reversed.
36
37   Redistribution and use in source and binary forms, with or without
38   modification, are permitted provided that the following conditions
39   are met:
40
41   1. Redistributions of source code must retain the above copyright
42      notice, this list of conditions and the following disclaimer.
43   2. Redistributions in binary form must reproduce the above copyright
44      notice, this list of conditions and the following disclaimer in the
45      documentation and/or other materials provided with the distribution.
46   3. Neither the name of the University nor the names of its
47      contributors may be used to endorse or promote products derived
48      from this software without specific prior written permission.
49
50   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
51   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
52   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
53   DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
54   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
55   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
56   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
57   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
58   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
59   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
60   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
61
62   $Id: svc_auth_gss.c,v 1.27 2002/01/15 15:43:00 andros Exp $
63  */
64
65 #include <stdio.h>
66 #include <stdlib.h>
67 #include <string.h>
68 #include <pwd.h>
69 #include <grp.h>
70 #include <errno.h>
71 #include <unistd.h>
72 #include <sys/queue.h>
73 #include <rpc/rpc.h>
74 #include <rpc/rpcsec_gss.h>
75 #include "rpcsec_gss_int.h"
76
77 static bool_t   svc_rpc_gss_initialised = FALSE;
78
79 static bool_t   svc_rpc_gss_wrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t);
80 static bool_t   svc_rpc_gss_unwrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t);
81 static enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *);
82
83 static struct svc_auth_ops svc_auth_gss_ops = {
84         svc_rpc_gss_wrap,
85         svc_rpc_gss_unwrap,
86 };
87
88 struct svc_rpc_gss_callback {
89         SLIST_ENTRY(svc_rpc_gss_callback) cb_link;
90         rpc_gss_callback_t      cb_callback;
91 };
92 static SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback)
93         svc_rpc_gss_callbacks = SLIST_HEAD_INITIALIZER(svc_rpc_gss_callbacks);
94
95 struct svc_rpc_gss_svc_name {
96         SLIST_ENTRY(svc_rpc_gss_svc_name) sn_link;
97         char                    *sn_principal;
98         gss_OID                 sn_mech;
99         u_int                   sn_req_time;
100         gss_cred_id_t           sn_cred;
101         u_int                   sn_program;
102         u_int                   sn_version;
103 };
104 static SLIST_HEAD(svc_rpc_gss_svc_name_list, svc_rpc_gss_svc_name)
105         svc_rpc_gss_svc_names = SLIST_HEAD_INITIALIZER(svc_rpc_gss_svc_names);
106
107 enum svc_rpc_gss_client_state {
108         CLIENT_NEW,                             /* still authenticating */
109         CLIENT_ESTABLISHED,                     /* context established */
110         CLIENT_STALE                            /* garbage to collect */
111 };
112
113 #define SVC_RPC_GSS_SEQWINDOW   128
114
115 struct svc_rpc_gss_client {
116         TAILQ_ENTRY(svc_rpc_gss_client) cl_link;
117         TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink;
118         uint32_t                cl_id;
119         time_t                  cl_expiration;  /* when to gc */
120         enum svc_rpc_gss_client_state cl_state; /* client state */
121         bool_t                  cl_locked;      /* fixed service+qop */
122         gss_ctx_id_t            cl_ctx;         /* context id */
123         gss_cred_id_t           cl_creds;       /* delegated creds */
124         gss_name_t              cl_cname;       /* client name */
125         struct svc_rpc_gss_svc_name *cl_sname;  /* server name used */
126         rpc_gss_rawcred_t       cl_rawcred;     /* raw credentials */
127         rpc_gss_ucred_t         cl_ucred;       /* unix-style credentials */
128         bool_t                  cl_done_callback; /* TRUE after call */
129         void                    *cl_cookie;     /* user cookie from callback */
130         gid_t                   cl_gid_storage[NGRPS];
131         gss_OID                 cl_mech;        /* mechanism */
132         gss_qop_t               cl_qop;         /* quality of protection */
133         u_int                   cl_seq;         /* current sequence number */
134         u_int                   cl_win;         /* sequence window size */
135         u_int                   cl_seqlast;     /* sequence window origin */
136         uint32_t                cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */
137         gss_buffer_desc         cl_verf;        /* buffer for verf checksum */
138 };
139 TAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client);
140
141 #define CLIENT_HASH_SIZE        256
142 #define CLIENT_MAX              128
143 static struct svc_rpc_gss_client_list svc_rpc_gss_client_hash[CLIENT_HASH_SIZE];
144 static struct svc_rpc_gss_client_list svc_rpc_gss_clients;
145 static size_t svc_rpc_gss_client_count;
146 static uint32_t svc_rpc_gss_next_clientid = 1;
147
148 #ifdef __GNUC__
149 static void svc_rpc_gss_init(void) __attribute__ ((constructor));
150 #endif
151
152 static void
153 svc_rpc_gss_init(void)
154 {
155         int i;
156
157         if (!svc_rpc_gss_initialised) {
158                 for (i = 0; i < CLIENT_HASH_SIZE; i++)
159                         TAILQ_INIT(&svc_rpc_gss_client_hash[i]);
160                 TAILQ_INIT(&svc_rpc_gss_clients);
161                 svc_auth_reg(RPCSEC_GSS, svc_rpc_gss);
162                 svc_rpc_gss_initialised = TRUE;
163         }
164 }
165
166 bool_t
167 rpc_gss_set_callback(rpc_gss_callback_t *cb)
168 {
169         struct svc_rpc_gss_callback *scb;
170
171         scb = mem_alloc(sizeof(struct svc_rpc_gss_callback));
172         if (!scb) {
173                 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
174                 return (FALSE);
175         }
176         scb->cb_callback = *cb;
177         SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link);
178
179         return (TRUE);
180 }
181
182 bool_t
183 rpc_gss_set_svc_name(const char *principal, const char *mechanism,
184     u_int req_time, u_int program, u_int version)
185 {
186         OM_uint32               maj_stat, min_stat;
187         struct svc_rpc_gss_svc_name *sname;
188         gss_buffer_desc         namebuf;
189         gss_name_t              name;
190         gss_OID                 mech_oid;
191         gss_OID_set_desc        oid_set;
192         gss_cred_id_t           cred;
193
194         svc_rpc_gss_init();
195
196         if (!rpc_gss_mech_to_oid(mechanism, &mech_oid))
197                 return (FALSE);
198         oid_set.count = 1;
199         oid_set.elements = mech_oid;
200
201         namebuf.value = (void *)(intptr_t) principal;
202         namebuf.length = strlen(principal);
203
204         maj_stat = gss_import_name(&min_stat, &namebuf,
205                                    GSS_C_NT_HOSTBASED_SERVICE, &name);
206         if (maj_stat != GSS_S_COMPLETE)
207                 return (FALSE);
208
209         maj_stat = gss_acquire_cred(&min_stat, name,
210             req_time, &oid_set, GSS_C_ACCEPT, &cred, NULL, NULL);
211         if (maj_stat != GSS_S_COMPLETE)
212                 return (FALSE);
213
214         gss_release_name(&min_stat, &name);
215
216         sname = malloc(sizeof(struct svc_rpc_gss_svc_name));
217         if (!sname)
218                 return (FALSE);
219         sname->sn_principal = strdup(principal);
220         sname->sn_mech = mech_oid;
221         sname->sn_req_time = req_time;
222         sname->sn_cred = cred;
223         sname->sn_program = program;
224         sname->sn_version = version;
225         SLIST_INSERT_HEAD(&svc_rpc_gss_svc_names, sname, sn_link);
226
227         return (TRUE);
228 }
229
230 bool_t
231 rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
232     const char *mech, const char *name, const char *node, const char *domain)
233 {
234         OM_uint32               maj_stat, min_stat;
235         gss_OID                 mech_oid;
236         size_t                  namelen;
237         gss_buffer_desc         buf;
238         gss_name_t              gss_name, gss_mech_name;
239         rpc_gss_principal_t     result;
240
241         svc_rpc_gss_init();
242
243         if (!rpc_gss_mech_to_oid(mech, &mech_oid))
244                 return (FALSE);
245
246         /*
247          * Construct a gss_buffer containing the full name formatted
248          * as "name/node@domain" where node and domain are optional.
249          */
250         namelen = strlen(name);
251         if (node) {
252                 namelen += strlen(node) + 1;
253         }
254         if (domain) {
255                 namelen += strlen(domain) + 1;
256         }
257
258         buf.value = mem_alloc(namelen);
259         buf.length = namelen;
260         strcpy((char *) buf.value, name);
261         if (node) {
262                 strcat((char *) buf.value, "/");
263                 strcat((char *) buf.value, node);
264         }
265         if (domain) {
266                 strcat((char *) buf.value, "@");
267                 strcat((char *) buf.value, domain);
268         }
269
270         /*
271          * Convert that to a gss_name_t and then convert that to a
272          * mechanism name in the selected mechanism.
273          */
274         maj_stat = gss_import_name(&min_stat, &buf,
275             GSS_C_NT_USER_NAME, &gss_name);
276         mem_free(buf.value, buf.length);
277         if (maj_stat != GSS_S_COMPLETE) {
278                 log_status("gss_import_name", mech_oid, maj_stat, min_stat);
279                 return (FALSE);
280         }
281         maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid,
282             &gss_mech_name);
283         if (maj_stat != GSS_S_COMPLETE) {
284                 log_status("gss_canonicalize_name", mech_oid, maj_stat,
285                     min_stat);
286                 gss_release_name(&min_stat, &gss_name);
287                 return (FALSE);
288         }
289         gss_release_name(&min_stat, &gss_name);
290
291         /*
292          * Export the mechanism name and use that to construct the
293          * rpc_gss_principal_t result.
294          */
295         maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf);
296         if (maj_stat != GSS_S_COMPLETE) {
297                 log_status("gss_export_name", mech_oid, maj_stat, min_stat);
298                 gss_release_name(&min_stat, &gss_mech_name);
299                 return (FALSE);
300         }
301         gss_release_name(&min_stat, &gss_mech_name);
302
303         result = mem_alloc(sizeof(int) + buf.length);
304         if (!result) {
305                 gss_release_buffer(&min_stat, &buf);
306                 return (FALSE);
307         }
308         result->len = buf.length;
309         memcpy(result->name, buf.value, buf.length);
310         gss_release_buffer(&min_stat, &buf);
311
312         *principal = result;
313         return (TRUE);
314 }
315
316 bool_t
317 rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
318     rpc_gss_ucred_t **ucred, void **cookie)
319 {
320         struct svc_rpc_gss_client *client;
321
322         if (req->rq_cred.oa_flavor != RPCSEC_GSS)
323                 return (FALSE);
324
325         client = req->rq_clntcred;
326         if (rcred)
327                 *rcred = &client->cl_rawcred;
328         if (ucred)
329                 *ucred = &client->cl_ucred;
330         if (cookie)
331                 *cookie = client->cl_cookie;
332         return (TRUE);
333 }
334
335 int
336 rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
337 {
338         struct svc_rpc_gss_client *client = req->rq_clntcred;
339         int                     want_conf;
340         OM_uint32               max;
341         OM_uint32               maj_stat, min_stat;
342         int                     result;
343
344         switch (client->cl_rawcred.service) {
345         case rpc_gss_svc_none:
346                 return (max_tp_unit_len);
347                 break;
348
349         case rpc_gss_svc_default:
350         case rpc_gss_svc_integrity:
351                 want_conf = FALSE;
352                 break;
353
354         case rpc_gss_svc_privacy:
355                 want_conf = TRUE;
356                 break;
357
358         default:
359                 return (0);
360         }
361
362         maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf,
363             client->cl_qop, max_tp_unit_len, &max);
364
365         if (maj_stat == GSS_S_COMPLETE) {
366                 result = (int) max;
367                 if (result < 0)
368                         result = 0;
369                 return (result);
370         } else {
371                 log_status("gss_wrap_size_limit", client->cl_mech,
372                     maj_stat, min_stat);
373                 return (0);
374         }
375 }
376
377 static struct svc_rpc_gss_client *
378 svc_rpc_gss_find_client(uint32_t clientid)
379 {
380         struct svc_rpc_gss_client *client;
381         struct svc_rpc_gss_client_list *list;
382
383
384         log_debug("in svc_rpc_gss_find_client(%d)", clientid);
385
386         list = &svc_rpc_gss_client_hash[clientid % CLIENT_HASH_SIZE];
387         TAILQ_FOREACH(client, list, cl_link) {
388                 if (client->cl_id == clientid) {
389                         /*
390                          * Move this client to the front of the LRU
391                          * list.
392                          */
393                         TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
394                         TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client,
395                             cl_alllink);
396                         return client;
397                 }
398         }
399
400         return (NULL);
401 }
402
403 static struct svc_rpc_gss_client *
404 svc_rpc_gss_create_client(void)
405 {
406         struct svc_rpc_gss_client *client;
407         struct svc_rpc_gss_client_list *list;
408
409         log_debug("in svc_rpc_gss_create_client()");
410
411         client = mem_alloc(sizeof(struct svc_rpc_gss_client));
412         memset(client, 0, sizeof(struct svc_rpc_gss_client));
413         client->cl_id = svc_rpc_gss_next_clientid++;
414         list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE];
415         TAILQ_INSERT_HEAD(list, client, cl_link);
416         TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink);
417
418         /*
419          * Start the client off with a short expiration time. We will
420          * try to get a saner value from the client creds later.
421          */
422         client->cl_state = CLIENT_NEW;
423         client->cl_locked = FALSE;
424         client->cl_expiration = time(0) + 5*60;
425         svc_rpc_gss_client_count++;
426
427         return (client);
428 }
429
430 static void
431 svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
432 {
433         struct svc_rpc_gss_client_list *list;
434         OM_uint32 min_stat;
435
436         log_debug("in svc_rpc_gss_destroy_client()");
437
438         if (client->cl_ctx)
439                 gss_delete_sec_context(&min_stat,
440                     &client->cl_ctx, GSS_C_NO_BUFFER);
441
442         if (client->cl_cname)
443                 gss_release_name(&min_stat, &client->cl_cname);
444
445         if (client->cl_rawcred.client_principal)
446                 mem_free(client->cl_rawcred.client_principal,
447                     sizeof(*client->cl_rawcred.client_principal)
448                     + client->cl_rawcred.client_principal->len);
449
450         if (client->cl_verf.value)
451                 gss_release_buffer(&min_stat, &client->cl_verf);
452
453         list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE];
454         TAILQ_REMOVE(list, client, cl_link);
455         TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
456         svc_rpc_gss_client_count--;
457         mem_free(client, sizeof(*client));
458 }
459
460 static void
461 svc_rpc_gss_timeout_clients(void)
462 {
463         struct svc_rpc_gss_client *client;
464         struct svc_rpc_gss_client *nclient;
465         time_t now = time(0);
466
467         log_debug("in svc_rpc_gss_timeout_clients()");
468         /*
469          * First enforce the max client limit. We keep
470          * svc_rpc_gss_clients in LRU order.
471          */
472         while (svc_rpc_gss_client_count > CLIENT_MAX)
473                 svc_rpc_gss_destroy_client(TAILQ_LAST(&svc_rpc_gss_clients,
474                             svc_rpc_gss_client_list));
475         TAILQ_FOREACH_SAFE(client, &svc_rpc_gss_clients, cl_alllink, nclient) {
476                 if (client->cl_state == CLIENT_STALE
477                     || now > client->cl_expiration) {
478                         log_debug("expiring client %p", client);
479                         svc_rpc_gss_destroy_client(client);
480                 }
481         }
482 }
483
484 #ifdef DEBUG
485 /*
486  * OID<->string routines.  These are uuuuugly.
487  */
488 static OM_uint32
489 gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
490 {
491         char            numstr[128];
492         unsigned long   number;
493         int             numshift;
494         size_t          string_length;
495         size_t          i;
496         unsigned char   *cp;
497         char            *bp;
498
499         /* Decoded according to krb5/gssapi_krb5.c */
500
501         /* First determine the size of the string */
502         string_length = 0;
503         number = 0;
504         numshift = 0;
505         cp = (unsigned char *) oid->elements;
506         number = (unsigned long) cp[0];
507         sprintf(numstr, "%ld ", number/40);
508         string_length += strlen(numstr);
509         sprintf(numstr, "%ld ", number%40);
510         string_length += strlen(numstr);
511         for (i=1; i<oid->length; i++) {
512                 if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
513                         number = (number << 7) | (cp[i] & 0x7f);
514                         numshift += 7;
515                 }
516                 else {
517                         *minor_status = 0;
518                         return(GSS_S_FAILURE);
519                 }
520                 if ((cp[i] & 0x80) == 0) {
521                         sprintf(numstr, "%ld ", number);
522                         string_length += strlen(numstr);
523                         number = 0;
524                         numshift = 0;
525                 }
526         }
527         /*
528          * If we get here, we've calculated the length of "n n n ... n ".  Add 4
529          * here for "{ " and "}\0".
530          */
531         string_length += 4;
532         if ((bp = (char *) mem_alloc(string_length))) {
533                 strcpy(bp, "{ ");
534                 number = (unsigned long) cp[0];
535                 sprintf(numstr, "%ld ", number/40);
536                 strcat(bp, numstr);
537                 sprintf(numstr, "%ld ", number%40);
538                 strcat(bp, numstr);
539                 number = 0;
540                 cp = (unsigned char *) oid->elements;
541                 for (i=1; i<oid->length; i++) {
542                         number = (number << 7) | (cp[i] & 0x7f);
543                         if ((cp[i] & 0x80) == 0) {
544                                 sprintf(numstr, "%ld ", number);
545                                 strcat(bp, numstr);
546                                 number = 0;
547                         }
548                 }
549                 strcat(bp, "}");
550                 oid_str->length = strlen(bp)+1;
551                 oid_str->value = (void *) bp;
552                 *minor_status = 0;
553                 return(GSS_S_COMPLETE);
554         }
555         *minor_status = 0;
556         return(GSS_S_FAILURE);
557 }
558 #endif
559
560 static void
561 svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
562     const gss_name_t name)
563 {
564         OM_uint32               maj_stat, min_stat;
565         char                    buf[128];
566         uid_t                   uid;
567         struct passwd           pwd, *pw;
568         rpc_gss_ucred_t         *uc = &client->cl_ucred;
569
570         uc->uid = 65534;
571         uc->gid = 65534;
572         uc->gidlen = 0;
573         uc->gidlist = client->cl_gid_storage;
574
575         maj_stat = gss_pname_to_uid(&min_stat, name, client->cl_mech, &uid);
576         if (maj_stat != GSS_S_COMPLETE)
577                 return;
578
579         getpwuid_r(uid, &pwd, buf, sizeof(buf), &pw);
580         if (pw) {
581                 int len = NGRPS;
582                 uc->uid = pw->pw_uid;
583                 uc->gid = pw->pw_gid;
584                 uc->gidlist = client->cl_gid_storage;
585                 getgrouplist(pw->pw_name, pw->pw_gid, uc->gidlist, &len);
586                 uc->gidlen = len;
587         }
588 }
589
590 static bool_t
591 svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
592                                struct svc_req *rqst,
593                                struct rpc_gss_init_res *gr,
594                                struct rpc_gss_cred *gc)
595 {
596         gss_buffer_desc         recv_tok;
597         gss_OID                 mech;
598         OM_uint32               maj_stat = 0, min_stat = 0, ret_flags;
599         OM_uint32               cred_lifetime;
600         struct svc_rpc_gss_svc_name *sname;
601
602         log_debug("in svc_rpc_gss_accept_context()");
603         
604         /* Deserialize arguments. */
605         memset(&recv_tok, 0, sizeof(recv_tok));
606         
607         if (!svc_getargs(rqst->rq_xprt,
608                 (xdrproc_t) xdr_gss_buffer_desc,
609                 (caddr_t) &recv_tok)) {
610                 client->cl_state = CLIENT_STALE;
611                 return (FALSE);
612         }
613
614         /*
615          * First time round, try all the server names we have until
616          * one matches. Afterwards, stick with that one.
617          */
618         if (!client->cl_sname) {
619                 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
620                         if (sname->sn_program == rqst->rq_prog
621                             && sname->sn_version == rqst->rq_vers) {
622                                 gr->gr_major = gss_accept_sec_context(
623                                         &gr->gr_minor,
624                                         &client->cl_ctx,
625                                         sname->sn_cred,
626                                         &recv_tok,
627                                         GSS_C_NO_CHANNEL_BINDINGS,
628                                         &client->cl_cname,
629                                         &mech,
630                                         &gr->gr_token,
631                                         &ret_flags,
632                                         &cred_lifetime,
633                                         &client->cl_creds);
634                                 if (gr->gr_major == GSS_S_COMPLETE
635                                     || gr->gr_major == GSS_S_CONTINUE_NEEDED) {
636                                         client->cl_sname = sname;
637                                         break;
638                                 }
639                                 client->cl_sname = sname;
640                                 break;
641                         }
642                 }
643                 if (!sname) {
644                         xdr_free((xdrproc_t) xdr_gss_buffer_desc,
645                             (char *) &recv_tok);
646                         return (FALSE);
647                 }
648         } else {
649                 gr->gr_major = gss_accept_sec_context(
650                         &gr->gr_minor,
651                         &client->cl_ctx,
652                         client->cl_sname->sn_cred,
653                         &recv_tok,
654                         GSS_C_NO_CHANNEL_BINDINGS,
655                         &client->cl_cname,
656                         &mech,
657                         &gr->gr_token,
658                         &ret_flags,
659                         &cred_lifetime,
660                         NULL);
661         }
662         
663         xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
664
665         /*
666          * If we get an error from gss_accept_sec_context, send the
667          * reply anyway so that the client gets a chance to see what
668          * is wrong.
669          */
670         if (gr->gr_major != GSS_S_COMPLETE &&
671             gr->gr_major != GSS_S_CONTINUE_NEEDED) {
672                 log_status("accept_sec_context", client->cl_mech,
673                     gr->gr_major, gr->gr_minor);
674                 client->cl_state = CLIENT_STALE;
675                 return (TRUE);
676         }
677
678         gr->gr_handle.value = &client->cl_id;
679         gr->gr_handle.length = sizeof(client->cl_id);
680         gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
681         
682         /* Save client info. */
683         client->cl_mech = mech;
684         client->cl_qop = GSS_C_QOP_DEFAULT;
685         client->cl_seq = gc->gc_seq;
686         client->cl_win = gr->gr_win;
687         client->cl_done_callback = FALSE;
688
689         if (gr->gr_major == GSS_S_COMPLETE) {
690                 gss_buffer_desc export_name;
691
692                 /*
693                  * Change client expiration time to be near when the
694                  * client creds expire (or 24 hours if we can't figure
695                  * that out).
696                  */
697                 if (cred_lifetime == GSS_C_INDEFINITE)
698                         cred_lifetime = time(0) + 24*60*60;
699
700                 client->cl_expiration = time(0) + cred_lifetime;
701
702                 /*
703                  * Fill in cred details in the rawcred structure.
704                  */
705                 client->cl_rawcred.version = RPCSEC_GSS_VERSION;
706                 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
707                 maj_stat = gss_export_name(&min_stat, client->cl_cname,
708                     &export_name);
709                 if (maj_stat != GSS_S_COMPLETE) {
710                         log_status("gss_export_name", client->cl_mech,
711                             maj_stat, min_stat);
712                         return (FALSE);
713                 }
714                 client->cl_rawcred.client_principal =
715                         mem_alloc(sizeof(*client->cl_rawcred.client_principal)
716                             + export_name.length);
717                 client->cl_rawcred.client_principal->len = export_name.length;
718                 memcpy(client->cl_rawcred.client_principal->name,
719                     export_name.value, export_name.length);
720                 gss_release_buffer(&min_stat, &export_name);
721                 client->cl_rawcred.svc_principal =
722                         client->cl_sname->sn_principal;
723                 client->cl_rawcred.service = gc->gc_svc;
724
725                 /*
726                  * Use gss_pname_to_uid to map to unix creds. For
727                  * kerberos5, this uses krb5_aname_to_localname.
728                  */
729                 svc_rpc_gss_build_ucred(client, client->cl_cname);
730                 gss_release_name(&min_stat, &client->cl_cname);
731
732 #ifdef DEBUG
733                 {
734                         gss_buffer_desc mechname;
735
736                         gss_oid_to_str(&min_stat, mech, &mechname);
737                         
738                         log_debug("accepted context for %s with "
739                             "<mech %.*s, qop %d, svc %d>",
740                             client->cl_rawcred.client_principal->name,
741                             mechname.length, (char *)mechname.value,
742                             client->cl_qop, client->rawcred.service);
743
744                         gss_release_buffer(&min_stat, &mechname);
745                 }
746 #endif /* DEBUG */
747         }
748         return (TRUE);
749 }
750
751 static bool_t
752 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
753         gss_qop_t *qop)
754 {
755         struct opaque_auth      *oa;
756         gss_buffer_desc          rpcbuf, checksum;
757         OM_uint32                maj_stat, min_stat;
758         gss_qop_t                qop_state;
759         int32_t                  rpchdr[128 / sizeof(int32_t)];
760         int32_t                 *buf;
761
762         log_debug("in svc_rpc_gss_validate()");
763         
764         memset(rpchdr, 0, sizeof(rpchdr));
765
766         /* Reconstruct RPC header for signing (from xdr_callmsg). */
767         buf = rpchdr;
768         IXDR_PUT_LONG(buf, msg->rm_xid);
769         IXDR_PUT_ENUM(buf, msg->rm_direction);
770         IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
771         IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
772         IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
773         IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
774         oa = &msg->rm_call.cb_cred;
775         IXDR_PUT_ENUM(buf, oa->oa_flavor);
776         IXDR_PUT_LONG(buf, oa->oa_length);
777         if (oa->oa_length) {
778                 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
779                 buf += RNDUP(oa->oa_length) / sizeof(int32_t);
780         }
781         rpcbuf.value = rpchdr;
782         rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
783
784         checksum.value = msg->rm_call.cb_verf.oa_base;
785         checksum.length = msg->rm_call.cb_verf.oa_length;
786         
787         maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
788                                   &qop_state);
789         
790         if (maj_stat != GSS_S_COMPLETE) {
791                 log_status("gss_verify_mic", client->cl_mech,
792                     maj_stat, min_stat);
793                 client->cl_state = CLIENT_STALE;
794                 return (FALSE);
795         }
796         *qop = qop_state;
797         return (TRUE);
798 }
799
800 static bool_t
801 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
802     struct svc_req *rqst, u_int seq)
803 {
804         gss_buffer_desc         signbuf;
805         OM_uint32               maj_stat, min_stat;
806         uint32_t                nseq;       
807
808         log_debug("in svc_rpc_gss_nextverf()");
809
810         nseq = htonl(seq);
811         signbuf.value = &nseq;
812         signbuf.length = sizeof(nseq);
813
814         if (client->cl_verf.value)
815                 gss_release_buffer(&min_stat, &client->cl_verf);
816
817         maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
818             &signbuf, &client->cl_verf);
819
820         if (maj_stat != GSS_S_COMPLETE) {
821                 log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
822                 client->cl_state = CLIENT_STALE;
823                 return (FALSE);
824         }
825         rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;
826         rqst->rq_xprt->xp_verf.oa_base = (caddr_t)client->cl_verf.value;
827         rqst->rq_xprt->xp_verf.oa_length = (u_int)client->cl_verf.length;
828         
829         return (TRUE);
830 }
831
832 static bool_t
833 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
834 {
835         struct svc_rpc_gss_callback *scb;
836         rpc_gss_lock_t  lock;
837         void            *cookie;
838         bool_t          cb_res;
839         bool_t          result;
840
841         /*
842          * See if we have a callback for this guy.
843          */
844         result = TRUE;
845         SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
846                 if (scb->cb_callback.program == rqst->rq_prog
847                     && scb->cb_callback.version == rqst->rq_vers) {
848                         /*
849                          * This one matches. Call the callback and see
850                          * if it wants to veto or something.
851                          */
852                         lock.locked = FALSE;
853                         lock.raw_cred = &client->cl_rawcred;
854                         cb_res = scb->cb_callback.callback(rqst,
855                             client->cl_creds,
856                             client->cl_ctx,
857                             &lock,
858                             &cookie);
859
860                         if (!cb_res) {
861                                 client->cl_state = CLIENT_STALE;
862                                 result = FALSE;
863                                 break;
864                         }
865
866                         /*
867                          * The callback accepted the connection - it
868                          * is responsible for freeing client->cl_creds
869                          * now.
870                          */
871                         client->cl_creds = GSS_C_NO_CREDENTIAL;
872                         client->cl_locked = lock.locked;
873                         client->cl_cookie = cookie;
874                         return (TRUE);
875                 }
876         }
877
878         /*
879          * Either no callback exists for this program/version or one
880          * of the callbacks rejected the connection. We just need to
881          * clean up the delegated client creds, if any.
882          */
883         if (client->cl_creds) {
884                 OM_uint32 min_ver;
885                 gss_release_cred(&min_ver, &client->cl_creds);
886         }
887         return (result);
888 }
889
890 static bool_t
891 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
892 {
893         u_int32_t offset;
894         int word, bit;
895
896         if (seq <= client->cl_seqlast) {
897                 /*
898                  * The request sequence number is less than
899                  * the largest we have seen so far. If it is
900                  * outside the window or if we have seen a
901                  * request with this sequence before, silently
902                  * discard it.
903                  */
904                 offset = client->cl_seqlast - seq;
905                 if (offset >= SVC_RPC_GSS_SEQWINDOW)
906                         return (FALSE);
907                 word = offset / 32;
908                 bit = offset % 32;
909                 if (client->cl_seqmask[word] & (1 << bit))
910                         return (FALSE);
911         }
912
913         return (TRUE);
914 }
915
916 static void
917 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
918 {
919         int offset, i, word, bit;
920         uint32_t carry, newcarry;
921
922         if (seq > client->cl_seqlast) {
923                 /*
924                  * This request has a sequence number greater
925                  * than any we have seen so far. Advance the
926                  * seq window and set bit zero of the window
927                  * (which corresponds to the new sequence
928                  * number)
929                  */
930                 offset = seq - client->cl_seqlast;
931                 while (offset > 32) {
932                         for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
933                              i > 0; i--) {
934                                 client->cl_seqmask[i] = client->cl_seqmask[i-1];
935                         }
936                         client->cl_seqmask[0] = 0;
937                         offset -= 32;
938                 }
939                 carry = 0;
940                 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
941                         newcarry = client->cl_seqmask[i] >> (32 - offset);
942                         client->cl_seqmask[i] =
943                                 (client->cl_seqmask[i] << offset) | carry;
944                         carry = newcarry;
945                 }
946                 client->cl_seqmask[0] |= 1;
947                 client->cl_seqlast = seq;
948         } else {
949                 offset = client->cl_seqlast - seq;
950                 word = offset / 32;
951                 bit = offset % 32;
952                 client->cl_seqmask[word] |= (1 << bit);
953         }
954
955 }
956
957 enum auth_stat
958 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
959
960 {
961         OM_uint32                min_stat;
962         XDR                      xdrs;
963         struct svc_rpc_gss_client *client;
964         struct rpc_gss_cred      gc;
965         struct rpc_gss_init_res  gr;
966         gss_qop_t                qop;
967         int                      call_stat;
968         enum auth_stat           result;
969         
970         log_debug("in svc_rpc_gss()");
971         
972         /* Garbage collect old clients. */
973         svc_rpc_gss_timeout_clients();
974
975         /* Initialize reply. */
976         rqst->rq_xprt->xp_verf = _null_auth;
977
978         /* Deserialize client credentials. */
979         if (rqst->rq_cred.oa_length <= 0)
980                 return (AUTH_BADCRED);
981         
982         memset(&gc, 0, sizeof(gc));
983         
984         xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
985             rqst->rq_cred.oa_length, XDR_DECODE);
986         
987         if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
988                 XDR_DESTROY(&xdrs);
989                 return (AUTH_BADCRED);
990         }
991         XDR_DESTROY(&xdrs);
992
993         /* Check version. */
994         if (gc.gc_version != RPCSEC_GSS_VERSION) {
995                 result = AUTH_BADCRED;
996                 goto out;
997         }
998
999         /* Check the proc and find the client (or create it) */
1000         if (gc.gc_proc == RPCSEC_GSS_INIT) {
1001                 if (gc.gc_handle.length != 0) {
1002                         result = AUTH_BADCRED;
1003                         goto out;
1004                 }
1005                 client = svc_rpc_gss_create_client();
1006         } else {
1007                 if (gc.gc_handle.length != sizeof(uint32_t)) {
1008                         result = AUTH_BADCRED;
1009                         goto out;
1010                 }
1011                 uint32_t *p = gc.gc_handle.value;
1012                 client = svc_rpc_gss_find_client(*p);
1013                 if (!client) {
1014                         /*
1015                          * Can't find the client - we may have
1016                          * destroyed it - tell the other side to
1017                          * re-authenticate.
1018                          */
1019                         result = RPCSEC_GSS_CREDPROBLEM;
1020                         goto out;
1021                 }
1022         }
1023         rqst->rq_clntcred = client;
1024
1025         /*
1026          * The service and sequence number must be ignored for
1027          * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1028          */
1029         if (gc.gc_proc != RPCSEC_GSS_INIT
1030             && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1031                 /*
1032                  * Check for sequence number overflow.
1033                  */
1034                 if (gc.gc_seq >= MAXSEQ) {
1035                         result = RPCSEC_GSS_CTXPROBLEM;
1036                         goto out;
1037                 }
1038                 client->cl_seq = gc.gc_seq;
1039
1040                 /*
1041                  * Check for valid service.
1042                  */
1043                 if (gc.gc_svc != rpc_gss_svc_none &&
1044                     gc.gc_svc != rpc_gss_svc_integrity &&
1045                     gc.gc_svc != rpc_gss_svc_privacy) {
1046                         result = AUTH_BADCRED;
1047                         goto out;
1048                 }
1049         }
1050
1051         /* Handle RPCSEC_GSS control procedure. */
1052         switch (gc.gc_proc) {
1053
1054         case RPCSEC_GSS_INIT:
1055         case RPCSEC_GSS_CONTINUE_INIT:
1056                 if (rqst->rq_proc != NULLPROC) {
1057                         result = AUTH_REJECTEDCRED;
1058                         break;
1059                 }
1060
1061                 memset(&gr, 0, sizeof(gr));
1062                 if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1063                         result = AUTH_REJECTEDCRED;
1064                         break;
1065                 }
1066
1067                 if (gr.gr_major == GSS_S_COMPLETE) {
1068                         if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1069                                 result = AUTH_REJECTEDCRED;
1070                                 break;
1071                         }
1072                 } else {
1073                         rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL;
1074                         rqst->rq_xprt->xp_verf.oa_length = 0;
1075                 }
1076                 
1077                 call_stat = svc_sendreply(rqst->rq_xprt,
1078                     (xdrproc_t) xdr_rpc_gss_init_res,
1079                     (caddr_t) &gr);
1080
1081                 gss_release_buffer(&min_stat, &gr.gr_token);
1082
1083                 if (!call_stat) {
1084                         result = AUTH_FAILED;
1085                         break;
1086                 }
1087
1088                 if (gr.gr_major == GSS_S_COMPLETE)
1089                         client->cl_state = CLIENT_ESTABLISHED;
1090
1091                 result = RPCSEC_GSS_NODISPATCH;
1092                 break;
1093                 
1094         case RPCSEC_GSS_DATA:
1095         case RPCSEC_GSS_DESTROY:
1096                 if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1097                         result = RPCSEC_GSS_NODISPATCH;
1098                         break;
1099                 }
1100
1101                 if (!svc_rpc_gss_validate(client, msg, &qop)) {
1102                         result = RPCSEC_GSS_CREDPROBLEM;
1103                         break;
1104                 }
1105                 
1106                 if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1107                         result = RPCSEC_GSS_CTXPROBLEM;
1108                         break;
1109                 }
1110
1111                 svc_rpc_gss_update_seq(client, gc.gc_seq);
1112
1113                 /*
1114                  * Change the SVCAUTH ops on the transport to point at
1115                  * our own code so that we can unwrap the arguments
1116                  * and wrap the result. The caller will re-set this on
1117                  * every request to point to a set of null wrap/unwrap
1118                  * methods.
1119                  */
1120                 SVC_AUTH(rqst->rq_xprt).svc_ah_ops = &svc_auth_gss_ops;
1121                 SVC_AUTH(rqst->rq_xprt).svc_ah_private = client;
1122
1123                 if (gc.gc_proc == RPCSEC_GSS_DATA) {
1124                         /*
1125                          * We might be ready to do a callback to the server to
1126                          * see if it wants to accept/reject the connection.
1127                          */
1128                         if (!client->cl_done_callback) {
1129                                 client->cl_done_callback = TRUE;
1130                                 client->cl_qop = qop;
1131                                 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1132                                         client->cl_rawcred.mechanism, qop);
1133                                 if (!svc_rpc_gss_callback(client, rqst)) {
1134                                         result = AUTH_REJECTEDCRED;
1135                                         break;
1136                                 }
1137                         }
1138
1139                         /*
1140                          * If the server has locked this client to a
1141                          * particular service+qop pair, enforce that
1142                          * restriction now.
1143                          */
1144                         if (client->cl_locked) {
1145                                 if (client->cl_rawcred.service != gc.gc_svc) {
1146                                         result = AUTH_FAILED;
1147                                         break;
1148                                 } else if (client->cl_qop != qop) {
1149                                         result = AUTH_BADVERF;
1150                                         break;
1151                                 }
1152                         }
1153
1154                         /*
1155                          * If the qop changed, look up the new qop
1156                          * name for rawcred.
1157                          */
1158                         if (client->cl_qop != qop) {
1159                                 client->cl_qop = qop;
1160                                 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1161                                         client->cl_rawcred.mechanism, qop);
1162                         }
1163
1164                         /*
1165                          * Make sure we use the right service value
1166                          * for unwrap/wrap.
1167                          */
1168                         client->cl_rawcred.service = gc.gc_svc;
1169
1170                         result = AUTH_OK;
1171                 } else {
1172                         if (rqst->rq_proc != NULLPROC) {
1173                                 result = AUTH_REJECTEDCRED;
1174                                 break;
1175                         }
1176
1177                         call_stat = svc_sendreply(rqst->rq_xprt,
1178                             (xdrproc_t) xdr_void, (caddr_t) NULL);
1179
1180                         if (!call_stat) {
1181                                 result = AUTH_FAILED;
1182                                 break;
1183                         }
1184
1185                         svc_rpc_gss_destroy_client(client);
1186
1187                         result = RPCSEC_GSS_NODISPATCH;
1188                         break;
1189                 }
1190                 break;
1191
1192         default:
1193                 result = AUTH_BADCRED;
1194                 break;
1195         }
1196 out:
1197         xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1198         return (result);
1199 }
1200
1201 bool_t
1202 svc_rpc_gss_wrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
1203 {
1204         struct svc_rpc_gss_client *client;
1205         
1206         log_debug("in svc_rpc_gss_wrap()");
1207
1208         client = (struct svc_rpc_gss_client *) auth->svc_ah_private;
1209         if (client->cl_state != CLIENT_ESTABLISHED
1210             || client->cl_rawcred.service == rpc_gss_svc_none) {
1211                 return xdr_func(xdrs, xdr_ptr);
1212         }
1213         return (xdr_rpc_gss_wrap_data(xdrs, xdr_func, xdr_ptr,
1214                 client->cl_ctx, client->cl_qop,
1215                 client->cl_rawcred.service, client->cl_seq));
1216 }
1217
1218 bool_t
1219 svc_rpc_gss_unwrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
1220 {
1221         struct svc_rpc_gss_client *client;
1222
1223         log_debug("in svc_rpc_gss_unwrap()");
1224         
1225         client = (struct svc_rpc_gss_client *) auth->svc_ah_private;
1226         if (client->cl_state != CLIENT_ESTABLISHED
1227             || client->cl_rawcred.service == rpc_gss_svc_none) {
1228                 return xdr_func(xdrs, xdr_ptr);
1229         }
1230         return (xdr_rpc_gss_unwrap_data(xdrs, xdr_func, xdr_ptr,
1231                 client->cl_ctx, client->cl_qop,
1232                 client->cl_rawcred.service, client->cl_seq));
1233 }