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