]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/librpcsec_gss/svc_rpcsec_gss.c
Import tzdata 2017c
[FreeBSD/FreeBSD.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                                 client->cl_sname = sname;
635                                 break;
636                         }
637                 }
638                 if (!sname) {
639                         xdr_free((xdrproc_t) xdr_gss_buffer_desc,
640                             (char *) &recv_tok);
641                         return (FALSE);
642                 }
643         } else {
644                 gr->gr_major = gss_accept_sec_context(
645                         &gr->gr_minor,
646                         &client->cl_ctx,
647                         client->cl_sname->sn_cred,
648                         &recv_tok,
649                         GSS_C_NO_CHANNEL_BINDINGS,
650                         &client->cl_cname,
651                         &mech,
652                         &gr->gr_token,
653                         &ret_flags,
654                         &cred_lifetime,
655                         NULL);
656         }
657         
658         xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
659
660         /*
661          * If we get an error from gss_accept_sec_context, send the
662          * reply anyway so that the client gets a chance to see what
663          * is wrong.
664          */
665         if (gr->gr_major != GSS_S_COMPLETE &&
666             gr->gr_major != GSS_S_CONTINUE_NEEDED) {
667                 log_status("accept_sec_context", client->cl_mech,
668                     gr->gr_major, gr->gr_minor);
669                 client->cl_state = CLIENT_STALE;
670                 return (TRUE);
671         }
672
673         gr->gr_handle.value = &client->cl_id;
674         gr->gr_handle.length = sizeof(client->cl_id);
675         gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
676         
677         /* Save client info. */
678         client->cl_mech = mech;
679         client->cl_qop = GSS_C_QOP_DEFAULT;
680         client->cl_seq = gc->gc_seq;
681         client->cl_win = gr->gr_win;
682         client->cl_done_callback = FALSE;
683
684         if (gr->gr_major == GSS_S_COMPLETE) {
685                 gss_buffer_desc export_name;
686
687                 /*
688                  * Change client expiration time to be near when the
689                  * client creds expire (or 24 hours if we can't figure
690                  * that out).
691                  */
692                 if (cred_lifetime == GSS_C_INDEFINITE)
693                         cred_lifetime = time(0) + 24*60*60;
694
695                 client->cl_expiration = time(0) + cred_lifetime;
696
697                 /*
698                  * Fill in cred details in the rawcred structure.
699                  */
700                 client->cl_rawcred.version = RPCSEC_GSS_VERSION;
701                 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
702                 maj_stat = gss_export_name(&min_stat, client->cl_cname,
703                     &export_name);
704                 if (maj_stat != GSS_S_COMPLETE) {
705                         log_status("gss_export_name", client->cl_mech,
706                             maj_stat, min_stat);
707                         return (FALSE);
708                 }
709                 client->cl_rawcred.client_principal =
710                         mem_alloc(sizeof(*client->cl_rawcred.client_principal)
711                             + export_name.length);
712                 client->cl_rawcred.client_principal->len = export_name.length;
713                 memcpy(client->cl_rawcred.client_principal->name,
714                     export_name.value, export_name.length);
715                 gss_release_buffer(&min_stat, &export_name);
716                 client->cl_rawcred.svc_principal =
717                         client->cl_sname->sn_principal;
718                 client->cl_rawcred.service = gc->gc_svc;
719
720                 /*
721                  * Use gss_pname_to_uid to map to unix creds. For
722                  * kerberos5, this uses krb5_aname_to_localname.
723                  */
724                 svc_rpc_gss_build_ucred(client, client->cl_cname);
725                 gss_release_name(&min_stat, &client->cl_cname);
726
727 #ifdef DEBUG
728                 {
729                         gss_buffer_desc mechname;
730
731                         gss_oid_to_str(&min_stat, mech, &mechname);
732                         
733                         log_debug("accepted context for %s with "
734                             "<mech %.*s, qop %d, svc %d>",
735                             client->cl_rawcred.client_principal->name,
736                             mechname.length, (char *)mechname.value,
737                             client->cl_qop, client->rawcred.service);
738
739                         gss_release_buffer(&min_stat, &mechname);
740                 }
741 #endif /* DEBUG */
742         }
743         return (TRUE);
744 }
745
746 static bool_t
747 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
748         gss_qop_t *qop)
749 {
750         struct opaque_auth      *oa;
751         gss_buffer_desc          rpcbuf, checksum;
752         OM_uint32                maj_stat, min_stat;
753         gss_qop_t                qop_state;
754         int32_t                  rpchdr[128 / sizeof(int32_t)];
755         int32_t                 *buf;
756
757         log_debug("in svc_rpc_gss_validate()");
758         
759         memset(rpchdr, 0, sizeof(rpchdr));
760
761         /* Reconstruct RPC header for signing (from xdr_callmsg). */
762         buf = rpchdr;
763         IXDR_PUT_LONG(buf, msg->rm_xid);
764         IXDR_PUT_ENUM(buf, msg->rm_direction);
765         IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
766         IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
767         IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
768         IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
769         oa = &msg->rm_call.cb_cred;
770         IXDR_PUT_ENUM(buf, oa->oa_flavor);
771         IXDR_PUT_LONG(buf, oa->oa_length);
772         if (oa->oa_length) {
773                 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
774                 buf += RNDUP(oa->oa_length) / sizeof(int32_t);
775         }
776         rpcbuf.value = rpchdr;
777         rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
778
779         checksum.value = msg->rm_call.cb_verf.oa_base;
780         checksum.length = msg->rm_call.cb_verf.oa_length;
781         
782         maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
783                                   &qop_state);
784         
785         if (maj_stat != GSS_S_COMPLETE) {
786                 log_status("gss_verify_mic", client->cl_mech,
787                     maj_stat, min_stat);
788                 client->cl_state = CLIENT_STALE;
789                 return (FALSE);
790         }
791         *qop = qop_state;
792         return (TRUE);
793 }
794
795 static bool_t
796 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
797     struct svc_req *rqst, u_int seq)
798 {
799         gss_buffer_desc         signbuf;
800         OM_uint32               maj_stat, min_stat;
801         uint32_t                nseq;       
802
803         log_debug("in svc_rpc_gss_nextverf()");
804
805         nseq = htonl(seq);
806         signbuf.value = &nseq;
807         signbuf.length = sizeof(nseq);
808
809         if (client->cl_verf.value)
810                 gss_release_buffer(&min_stat, &client->cl_verf);
811
812         maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
813             &signbuf, &client->cl_verf);
814
815         if (maj_stat != GSS_S_COMPLETE) {
816                 log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
817                 client->cl_state = CLIENT_STALE;
818                 return (FALSE);
819         }
820         rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;
821         rqst->rq_xprt->xp_verf.oa_base = (caddr_t)client->cl_verf.value;
822         rqst->rq_xprt->xp_verf.oa_length = (u_int)client->cl_verf.length;
823         
824         return (TRUE);
825 }
826
827 static bool_t
828 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
829 {
830         struct svc_rpc_gss_callback *scb;
831         rpc_gss_lock_t  lock;
832         void            *cookie;
833         bool_t          cb_res;
834         bool_t          result;
835
836         /*
837          * See if we have a callback for this guy.
838          */
839         result = TRUE;
840         SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
841                 if (scb->cb_callback.program == rqst->rq_prog
842                     && scb->cb_callback.version == rqst->rq_vers) {
843                         /*
844                          * This one matches. Call the callback and see
845                          * if it wants to veto or something.
846                          */
847                         lock.locked = FALSE;
848                         lock.raw_cred = &client->cl_rawcred;
849                         cb_res = scb->cb_callback.callback(rqst,
850                             client->cl_creds,
851                             client->cl_ctx,
852                             &lock,
853                             &cookie);
854
855                         if (!cb_res) {
856                                 client->cl_state = CLIENT_STALE;
857                                 result = FALSE;
858                                 break;
859                         }
860
861                         /*
862                          * The callback accepted the connection - it
863                          * is responsible for freeing client->cl_creds
864                          * now.
865                          */
866                         client->cl_creds = GSS_C_NO_CREDENTIAL;
867                         client->cl_locked = lock.locked;
868                         client->cl_cookie = cookie;
869                         return (TRUE);
870                 }
871         }
872
873         /*
874          * Either no callback exists for this program/version or one
875          * of the callbacks rejected the connection. We just need to
876          * clean up the delegated client creds, if any.
877          */
878         if (client->cl_creds) {
879                 OM_uint32 min_ver;
880                 gss_release_cred(&min_ver, &client->cl_creds);
881         }
882         return (result);
883 }
884
885 static bool_t
886 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
887 {
888         u_int32_t offset;
889         int word, bit;
890
891         if (seq <= client->cl_seqlast) {
892                 /*
893                  * The request sequence number is less than
894                  * the largest we have seen so far. If it is
895                  * outside the window or if we have seen a
896                  * request with this sequence before, silently
897                  * discard it.
898                  */
899                 offset = client->cl_seqlast - seq;
900                 if (offset >= SVC_RPC_GSS_SEQWINDOW)
901                         return (FALSE);
902                 word = offset / 32;
903                 bit = offset % 32;
904                 if (client->cl_seqmask[word] & (1 << bit))
905                         return (FALSE);
906         }
907
908         return (TRUE);
909 }
910
911 static void
912 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
913 {
914         int offset, i, word, bit;
915         uint32_t carry, newcarry;
916         uint32_t* maskp;
917
918         maskp = client->cl_seqmask;
919         if (seq > client->cl_seqlast) {
920                 /*
921                  * This request has a sequence number greater
922                  * than any we have seen so far. Advance the
923                  * seq window and set bit zero of the window
924                  * (which corresponds to the new sequence
925                  * number)
926                  */
927                 offset = seq - client->cl_seqlast;
928                 while (offset >= 32) {
929                         for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
930                              i > 0; i--) {
931                                 maskp[i] = maskp[i-1];
932                         }
933                         maskp[0] = 0;
934                         offset -= 32;
935                 }
936                 if (offset > 0) {
937                         carry = 0;
938                         for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
939                                 newcarry = maskp[i] >> (32 - offset);
940                                 maskp[i] = (maskp[i] << offset) | carry;
941                                 carry = newcarry;
942                         }
943                 }
944                 maskp[0] |= 1;
945                 client->cl_seqlast = seq;
946         } else {
947                 offset = client->cl_seqlast - seq;
948                 word = offset / 32;
949                 bit = offset % 32;
950                 maskp[word] |= (1 << bit);
951         }
952
953 }
954
955 enum auth_stat
956 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
957
958 {
959         OM_uint32                min_stat;
960         XDR                      xdrs;
961         struct svc_rpc_gss_client *client;
962         struct rpc_gss_cred      gc;
963         struct rpc_gss_init_res  gr;
964         gss_qop_t                qop;
965         int                      call_stat;
966         enum auth_stat           result;
967         
968         log_debug("in svc_rpc_gss()");
969         
970         /* Garbage collect old clients. */
971         svc_rpc_gss_timeout_clients();
972
973         /* Initialize reply. */
974         rqst->rq_xprt->xp_verf = _null_auth;
975
976         /* Deserialize client credentials. */
977         if (rqst->rq_cred.oa_length <= 0)
978                 return (AUTH_BADCRED);
979         
980         memset(&gc, 0, sizeof(gc));
981         
982         xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
983             rqst->rq_cred.oa_length, XDR_DECODE);
984         
985         if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
986                 XDR_DESTROY(&xdrs);
987                 return (AUTH_BADCRED);
988         }
989         XDR_DESTROY(&xdrs);
990
991         /* Check version. */
992         if (gc.gc_version != RPCSEC_GSS_VERSION) {
993                 result = AUTH_BADCRED;
994                 goto out;
995         }
996
997         /* Check the proc and find the client (or create it) */
998         if (gc.gc_proc == RPCSEC_GSS_INIT) {
999                 if (gc.gc_handle.length != 0) {
1000                         result = AUTH_BADCRED;
1001                         goto out;
1002                 }
1003                 client = svc_rpc_gss_create_client();
1004         } else {
1005                 if (gc.gc_handle.length != sizeof(uint32_t)) {
1006                         result = AUTH_BADCRED;
1007                         goto out;
1008                 }
1009                 uint32_t *p = gc.gc_handle.value;
1010                 client = svc_rpc_gss_find_client(*p);
1011                 if (!client) {
1012                         /*
1013                          * Can't find the client - we may have
1014                          * destroyed it - tell the other side to
1015                          * re-authenticate.
1016                          */
1017                         result = RPCSEC_GSS_CREDPROBLEM;
1018                         goto out;
1019                 }
1020         }
1021         rqst->rq_clntcred = client;
1022
1023         /*
1024          * The service and sequence number must be ignored for
1025          * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1026          */
1027         if (gc.gc_proc != RPCSEC_GSS_INIT
1028             && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1029                 /*
1030                  * Check for sequence number overflow.
1031                  */
1032                 if (gc.gc_seq >= MAXSEQ) {
1033                         result = RPCSEC_GSS_CTXPROBLEM;
1034                         goto out;
1035                 }
1036                 client->cl_seq = gc.gc_seq;
1037
1038                 /*
1039                  * Check for valid service.
1040                  */
1041                 if (gc.gc_svc != rpc_gss_svc_none &&
1042                     gc.gc_svc != rpc_gss_svc_integrity &&
1043                     gc.gc_svc != rpc_gss_svc_privacy) {
1044                         result = AUTH_BADCRED;
1045                         goto out;
1046                 }
1047         }
1048
1049         /* Handle RPCSEC_GSS control procedure. */
1050         switch (gc.gc_proc) {
1051
1052         case RPCSEC_GSS_INIT:
1053         case RPCSEC_GSS_CONTINUE_INIT:
1054                 if (rqst->rq_proc != NULLPROC) {
1055                         result = AUTH_REJECTEDCRED;
1056                         break;
1057                 }
1058
1059                 memset(&gr, 0, sizeof(gr));
1060                 if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1061                         result = AUTH_REJECTEDCRED;
1062                         break;
1063                 }
1064
1065                 if (gr.gr_major == GSS_S_COMPLETE) {
1066                         if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1067                                 result = AUTH_REJECTEDCRED;
1068                                 break;
1069                         }
1070                 } else {
1071                         rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL;
1072                         rqst->rq_xprt->xp_verf.oa_length = 0;
1073                 }
1074                 
1075                 call_stat = svc_sendreply(rqst->rq_xprt,
1076                     (xdrproc_t) xdr_rpc_gss_init_res,
1077                     (caddr_t) &gr);
1078
1079                 gss_release_buffer(&min_stat, &gr.gr_token);
1080
1081                 if (!call_stat) {
1082                         result = AUTH_FAILED;
1083                         break;
1084                 }
1085
1086                 if (gr.gr_major == GSS_S_COMPLETE)
1087                         client->cl_state = CLIENT_ESTABLISHED;
1088
1089                 result = RPCSEC_GSS_NODISPATCH;
1090                 break;
1091                 
1092         case RPCSEC_GSS_DATA:
1093         case RPCSEC_GSS_DESTROY:
1094                 if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1095                         result = RPCSEC_GSS_NODISPATCH;
1096                         break;
1097                 }
1098
1099                 if (!svc_rpc_gss_validate(client, msg, &qop)) {
1100                         result = RPCSEC_GSS_CREDPROBLEM;
1101                         break;
1102                 }
1103                 
1104                 if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1105                         result = RPCSEC_GSS_CTXPROBLEM;
1106                         break;
1107                 }
1108
1109                 svc_rpc_gss_update_seq(client, gc.gc_seq);
1110
1111                 /*
1112                  * Change the SVCAUTH ops on the transport to point at
1113                  * our own code so that we can unwrap the arguments
1114                  * and wrap the result. The caller will re-set this on
1115                  * every request to point to a set of null wrap/unwrap
1116                  * methods.
1117                  */
1118                 SVC_AUTH(rqst->rq_xprt).svc_ah_ops = &svc_auth_gss_ops;
1119                 SVC_AUTH(rqst->rq_xprt).svc_ah_private = client;
1120
1121                 if (gc.gc_proc == RPCSEC_GSS_DATA) {
1122                         /*
1123                          * We might be ready to do a callback to the server to
1124                          * see if it wants to accept/reject the connection.
1125                          */
1126                         if (!client->cl_done_callback) {
1127                                 client->cl_done_callback = TRUE;
1128                                 client->cl_qop = qop;
1129                                 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1130                                         client->cl_rawcred.mechanism, qop);
1131                                 if (!svc_rpc_gss_callback(client, rqst)) {
1132                                         result = AUTH_REJECTEDCRED;
1133                                         break;
1134                                 }
1135                         }
1136
1137                         /*
1138                          * If the server has locked this client to a
1139                          * particular service+qop pair, enforce that
1140                          * restriction now.
1141                          */
1142                         if (client->cl_locked) {
1143                                 if (client->cl_rawcred.service != gc.gc_svc) {
1144                                         result = AUTH_FAILED;
1145                                         break;
1146                                 } else if (client->cl_qop != qop) {
1147                                         result = AUTH_BADVERF;
1148                                         break;
1149                                 }
1150                         }
1151
1152                         /*
1153                          * If the qop changed, look up the new qop
1154                          * name for rawcred.
1155                          */
1156                         if (client->cl_qop != qop) {
1157                                 client->cl_qop = qop;
1158                                 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1159                                         client->cl_rawcred.mechanism, qop);
1160                         }
1161
1162                         /*
1163                          * Make sure we use the right service value
1164                          * for unwrap/wrap.
1165                          */
1166                         client->cl_rawcred.service = gc.gc_svc;
1167
1168                         result = AUTH_OK;
1169                 } else {
1170                         if (rqst->rq_proc != NULLPROC) {
1171                                 result = AUTH_REJECTEDCRED;
1172                                 break;
1173                         }
1174
1175                         call_stat = svc_sendreply(rqst->rq_xprt,
1176                             (xdrproc_t) xdr_void, (caddr_t) NULL);
1177
1178                         if (!call_stat) {
1179                                 result = AUTH_FAILED;
1180                                 break;
1181                         }
1182
1183                         svc_rpc_gss_destroy_client(client);
1184
1185                         result = RPCSEC_GSS_NODISPATCH;
1186                         break;
1187                 }
1188                 break;
1189
1190         default:
1191                 result = AUTH_BADCRED;
1192                 break;
1193         }
1194 out:
1195         xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1196         return (result);
1197 }
1198
1199 bool_t
1200 svc_rpc_gss_wrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
1201 {
1202         struct svc_rpc_gss_client *client;
1203         
1204         log_debug("in svc_rpc_gss_wrap()");
1205
1206         client = (struct svc_rpc_gss_client *) auth->svc_ah_private;
1207         if (client->cl_state != CLIENT_ESTABLISHED
1208             || client->cl_rawcred.service == rpc_gss_svc_none) {
1209                 return xdr_func(xdrs, xdr_ptr);
1210         }
1211         return (xdr_rpc_gss_wrap_data(xdrs, xdr_func, xdr_ptr,
1212                 client->cl_ctx, client->cl_qop,
1213                 client->cl_rawcred.service, client->cl_seq));
1214 }
1215
1216 bool_t
1217 svc_rpc_gss_unwrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
1218 {
1219         struct svc_rpc_gss_client *client;
1220
1221         log_debug("in svc_rpc_gss_unwrap()");
1222         
1223         client = (struct svc_rpc_gss_client *) auth->svc_ah_private;
1224         if (client->cl_state != CLIENT_ESTABLISHED
1225             || client->cl_rawcred.service == rpc_gss_svc_none) {
1226                 return xdr_func(xdrs, xdr_ptr);
1227         }
1228         return (xdr_rpc_gss_unwrap_data(xdrs, xdr_func, xdr_ptr,
1229                 client->cl_ctx, client->cl_qop,
1230                 client->cl_rawcred.service, client->cl_seq));
1231 }