]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/librpcsec_gss/svc_rpcsec_gss.c
MFV of r183861:
[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 struct svc_rpc_gss_client_list svc_rpc_gss_client_hash[CLIENT_HASH_SIZE];
144 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 = malloc(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 = malloc(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         free(buf.value);
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 = malloc(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                 free(client->cl_rawcred.client_principal);
447
448         if (client->cl_verf.value)
449                 gss_release_buffer(&min_stat, &client->cl_verf);
450
451         list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE];
452         TAILQ_REMOVE(list, client, cl_link);
453         TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
454         svc_rpc_gss_client_count--;
455         mem_free(client, sizeof(*client));
456 }
457
458 static void
459 svc_rpc_gss_timeout_clients(void)
460 {
461         struct svc_rpc_gss_client *client;
462         struct svc_rpc_gss_client *nclient;
463         time_t now = time(0);
464
465         log_debug("in svc_rpc_gss_timeout_clients()");
466         /*
467          * First enforce the max client limit. We keep
468          * svc_rpc_gss_clients in LRU order.
469          */
470         while (svc_rpc_gss_client_count > CLIENT_MAX)
471                 svc_rpc_gss_destroy_client(TAILQ_LAST(&svc_rpc_gss_clients,
472                             svc_rpc_gss_client_list));
473         TAILQ_FOREACH_SAFE(client, &svc_rpc_gss_clients, cl_alllink, nclient) {
474                 if (client->cl_state == CLIENT_STALE
475                     || now > client->cl_expiration) {
476                         log_debug("expiring client %p", client);
477                         svc_rpc_gss_destroy_client(client);
478                 }
479         }
480 }
481
482 #ifdef DEBUG
483 /*
484  * OID<->string routines.  These are uuuuugly.
485  */
486 static OM_uint32
487 gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
488 {
489         char            numstr[128];
490         unsigned long   number;
491         int             numshift;
492         size_t          string_length;
493         size_t          i;
494         unsigned char   *cp;
495         char            *bp;
496
497         /* Decoded according to krb5/gssapi_krb5.c */
498
499         /* First determine the size of the string */
500         string_length = 0;
501         number = 0;
502         numshift = 0;
503         cp = (unsigned char *) oid->elements;
504         number = (unsigned long) cp[0];
505         sprintf(numstr, "%ld ", number/40);
506         string_length += strlen(numstr);
507         sprintf(numstr, "%ld ", number%40);
508         string_length += strlen(numstr);
509         for (i=1; i<oid->length; i++) {
510                 if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
511                         number = (number << 7) | (cp[i] & 0x7f);
512                         numshift += 7;
513                 }
514                 else {
515                         *minor_status = 0;
516                         return(GSS_S_FAILURE);
517                 }
518                 if ((cp[i] & 0x80) == 0) {
519                         sprintf(numstr, "%ld ", number);
520                         string_length += strlen(numstr);
521                         number = 0;
522                         numshift = 0;
523                 }
524         }
525         /*
526          * If we get here, we've calculated the length of "n n n ... n ".  Add 4
527          * here for "{ " and "}\0".
528          */
529         string_length += 4;
530         if ((bp = (char *) malloc(string_length))) {
531                 strcpy(bp, "{ ");
532                 number = (unsigned long) cp[0];
533                 sprintf(numstr, "%ld ", number/40);
534                 strcat(bp, numstr);
535                 sprintf(numstr, "%ld ", number%40);
536                 strcat(bp, numstr);
537                 number = 0;
538                 cp = (unsigned char *) oid->elements;
539                 for (i=1; i<oid->length; i++) {
540                         number = (number << 7) | (cp[i] & 0x7f);
541                         if ((cp[i] & 0x80) == 0) {
542                                 sprintf(numstr, "%ld ", number);
543                                 strcat(bp, numstr);
544                                 number = 0;
545                         }
546                 }
547                 strcat(bp, "}");
548                 oid_str->length = strlen(bp)+1;
549                 oid_str->value = (void *) bp;
550                 *minor_status = 0;
551                 return(GSS_S_COMPLETE);
552         }
553         *minor_status = 0;
554         return(GSS_S_FAILURE);
555 }
556 #endif
557
558 static void
559 svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
560     const gss_name_t name)
561 {
562         OM_uint32               maj_stat, min_stat;
563         char                    buf[128];
564         uid_t                   uid;
565         struct passwd           pwd, *pw;
566         rpc_gss_ucred_t         *uc = &client->cl_ucred;
567
568         uc->uid = 65534;
569         uc->gid = 65534;
570         uc->gidlen = 0;
571         uc->gidlist = client->cl_gid_storage;
572
573         maj_stat = gss_pname_to_uid(&min_stat, name, client->cl_mech, &uid);
574         if (maj_stat != GSS_S_COMPLETE)
575                 return;
576
577         getpwuid_r(uid, &pwd, buf, sizeof(buf), &pw);
578         if (pw) {
579                 int len = NGRPS;
580                 uc->uid = pw->pw_uid;
581                 uc->gid = pw->pw_gid;
582                 uc->gidlist = client->cl_gid_storage;
583                 getgrouplist(pw->pw_name, pw->pw_gid, uc->gidlist, &len);
584                 uc->gidlen = len;
585         }
586 }
587
588 static bool_t
589 svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
590                                struct svc_req *rqst,
591                                struct rpc_gss_init_res *gr,
592                                struct rpc_gss_cred *gc)
593 {
594         gss_buffer_desc         recv_tok;
595         gss_OID                 mech;
596         OM_uint32               maj_stat = 0, min_stat = 0, ret_flags;
597         OM_uint32               cred_lifetime;
598         struct svc_rpc_gss_svc_name *sname;
599
600         log_debug("in svc_rpc_gss_accept_context()");
601         
602         /* Deserialize arguments. */
603         memset(&recv_tok, 0, sizeof(recv_tok));
604         
605         if (!svc_getargs(rqst->rq_xprt,
606                 (xdrproc_t) xdr_gss_buffer_desc,
607                 (caddr_t) &recv_tok)) {
608                 client->cl_state = CLIENT_STALE;
609                 return (FALSE);
610         }
611
612         /*
613          * First time round, try all the server names we have until
614          * one matches. Afterwards, stick with that one.
615          */
616         if (!client->cl_sname) {
617                 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
618                         if (sname->sn_program == rqst->rq_prog
619                             && sname->sn_version == rqst->rq_vers) {
620                                 gr->gr_major = gss_accept_sec_context(
621                                         &gr->gr_minor,
622                                         &client->cl_ctx,
623                                         sname->sn_cred,
624                                         &recv_tok,
625                                         GSS_C_NO_CHANNEL_BINDINGS,
626                                         &client->cl_cname,
627                                         &mech,
628                                         &gr->gr_token,
629                                         &ret_flags,
630                                         &cred_lifetime,
631                                         &client->cl_creds);
632                                 if (gr->gr_major == GSS_S_COMPLETE
633                                     || gr->gr_major == GSS_S_CONTINUE_NEEDED) {
634                                         client->cl_sname = sname;
635                                         break;
636                                 }
637                         }
638                 }
639         } else {
640                 gr->gr_major = gss_accept_sec_context(
641                         &gr->gr_minor,
642                         &client->cl_ctx,
643                         client->cl_sname->sn_cred,
644                         &recv_tok,
645                         GSS_C_NO_CHANNEL_BINDINGS,
646                         &client->cl_cname,
647                         &mech,
648                         &gr->gr_token,
649                         &ret_flags,
650                         &cred_lifetime,
651                         NULL);
652         }
653         
654         xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
655
656         /*
657          * If we get an error from gss_accept_sec_context, send the
658          * reply anyway so that the client gets a chance to see what
659          * is wrong.
660          */
661         if (gr->gr_major != GSS_S_COMPLETE &&
662             gr->gr_major != GSS_S_CONTINUE_NEEDED) {
663                 log_status("accept_sec_context", client->cl_mech,
664                     gr->gr_major, gr->gr_minor);
665                 client->cl_state = CLIENT_STALE;
666                 return (FALSE);
667         }
668
669         gr->gr_handle.value = &client->cl_id;
670         gr->gr_handle.length = sizeof(uint32_t);
671         gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
672         
673         /* Save client info. */
674         client->cl_mech = mech;
675         client->cl_qop = GSS_C_QOP_DEFAULT;
676         client->cl_seq = gc->gc_seq;
677         client->cl_win = gr->gr_win;
678         client->cl_done_callback = FALSE;
679
680         if (gr->gr_major == GSS_S_COMPLETE) {
681                 gss_buffer_desc export_name;
682
683                 /*
684                  * Change client expiration time to be near when the
685                  * client creds expire (or 24 hours if we can't figure
686                  * that out).
687                  */
688                 if (cred_lifetime == GSS_C_INDEFINITE)
689                         cred_lifetime = time(0) + 24*60*60;
690
691                 client->cl_expiration = time(0) + cred_lifetime;
692
693                 /*
694                  * Fill in cred details in the rawcred structure.
695                  */
696                 client->cl_rawcred.version = RPCSEC_GSS_VERSION;
697                 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
698                 maj_stat = gss_export_name(&min_stat, client->cl_cname,
699                     &export_name);
700                 if (maj_stat != GSS_S_COMPLETE) {
701                         log_status("gss_export_name", client->cl_mech,
702                             maj_stat, min_stat);
703                         return (FALSE);
704                 }
705                 client->cl_rawcred.client_principal =
706                         malloc(sizeof(*client->cl_rawcred.client_principal)
707                             + export_name.length);
708                 client->cl_rawcred.client_principal->len = export_name.length;
709                 memcpy(client->cl_rawcred.client_principal->name,
710                     export_name.value, export_name.length);
711                 gss_release_buffer(&min_stat, &export_name);
712                 client->cl_rawcred.svc_principal =
713                         client->cl_sname->sn_principal;
714                 client->cl_rawcred.service = gc->gc_svc;
715
716                 /*
717                  * Use gss_pname_to_uid to map to unix creds. For
718                  * kerberos5, this uses krb5_aname_to_localname.
719                  */
720                 svc_rpc_gss_build_ucred(client, client->cl_cname);
721
722 #ifdef DEBUG
723                 {
724                         gss_buffer_desc mechname;
725
726                         gss_oid_to_str(&min_stat, mech, &mechname);
727                         
728                         log_debug("accepted context for %s with "
729                             "<mech %.*s, qop %d, svc %d>",
730                             client->cl_rawcred.client_principal->name,
731                             mechname.length, (char *)mechname.value,
732                             client->cl_qop, client->rawcred.service);
733
734                         gss_release_buffer(&min_stat, &mechname);
735                 }
736 #endif /* DEBUG */
737         }
738         return (TRUE);
739 }
740
741 static bool_t
742 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
743         gss_qop_t *qop)
744 {
745         struct opaque_auth      *oa;
746         gss_buffer_desc          rpcbuf, checksum;
747         OM_uint32                maj_stat, min_stat;
748         gss_qop_t                qop_state;
749         int32_t                  rpchdr[128 / sizeof(int32_t)];
750         int32_t                 *buf;
751
752         log_debug("in svc_rpc_gss_validate()");
753         
754         memset(rpchdr, 0, sizeof(rpchdr));
755
756         /* Reconstruct RPC header for signing (from xdr_callmsg). */
757         buf = rpchdr;
758         IXDR_PUT_LONG(buf, msg->rm_xid);
759         IXDR_PUT_ENUM(buf, msg->rm_direction);
760         IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
761         IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
762         IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
763         IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
764         oa = &msg->rm_call.cb_cred;
765         IXDR_PUT_ENUM(buf, oa->oa_flavor);
766         IXDR_PUT_LONG(buf, oa->oa_length);
767         if (oa->oa_length) {
768                 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
769                 buf += RNDUP(oa->oa_length) / sizeof(int32_t);
770         }
771         rpcbuf.value = rpchdr;
772         rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
773
774         checksum.value = msg->rm_call.cb_verf.oa_base;
775         checksum.length = msg->rm_call.cb_verf.oa_length;
776         
777         maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
778                                   &qop_state);
779         
780         if (maj_stat != GSS_S_COMPLETE) {
781                 log_status("gss_verify_mic", client->cl_mech,
782                     maj_stat, min_stat);
783                 client->cl_state = CLIENT_STALE;
784                 return (FALSE);
785         }
786         *qop = qop_state;
787         return (TRUE);
788 }
789
790 static bool_t
791 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
792     struct svc_req *rqst, u_int seq)
793 {
794         gss_buffer_desc         signbuf;
795         OM_uint32               maj_stat, min_stat;
796         uint32_t                nseq;       
797
798         log_debug("in svc_rpc_gss_nextverf()");
799
800         nseq = htonl(seq);
801         signbuf.value = &nseq;
802         signbuf.length = sizeof(nseq);
803
804         if (client->cl_verf.value)
805                 gss_release_buffer(&min_stat, &client->cl_verf);
806
807         maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
808             &signbuf, &client->cl_verf);
809
810         if (maj_stat != GSS_S_COMPLETE) {
811                 log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
812                 client->cl_state = CLIENT_STALE;
813                 return (FALSE);
814         }
815         rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;
816         rqst->rq_xprt->xp_verf.oa_base = (caddr_t)client->cl_verf.value;
817         rqst->rq_xprt->xp_verf.oa_length = (u_int)client->cl_verf.length;
818         
819         return (TRUE);
820 }
821
822 static bool_t
823 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
824 {
825         struct svc_rpc_gss_callback *scb;
826         rpc_gss_lock_t  lock;
827         void            *cookie;
828         bool_t          cb_res;
829         bool_t          result;
830
831         /*
832          * See if we have a callback for this guy.
833          */
834         result = TRUE;
835         SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
836                 if (scb->cb_callback.program == rqst->rq_prog
837                     && scb->cb_callback.version == rqst->rq_vers) {
838                         /*
839                          * This one matches. Call the callback and see
840                          * if it wants to veto or something.
841                          */
842                         lock.locked = FALSE;
843                         lock.raw_cred = &client->cl_rawcred;
844                         cb_res = scb->cb_callback.callback(rqst,
845                             client->cl_creds,
846                             client->cl_ctx,
847                             &lock,
848                             &cookie);
849
850                         if (!cb_res) {
851                                 client->cl_state = CLIENT_STALE;
852                                 result = FALSE;
853                                 break;
854                         }
855
856                         /*
857                          * The callback accepted the connection - it
858                          * is responsible for freeing client->cl_creds
859                          * now.
860                          */
861                         client->cl_creds = GSS_C_NO_CREDENTIAL;
862                         client->cl_locked = lock.locked;
863                         client->cl_cookie = cookie;
864                         return (TRUE);
865                 }
866         }
867
868         /*
869          * Either no callback exists for this program/version or one
870          * of the callbacks rejected the connection. We just need to
871          * clean up the delegated client creds, if any.
872          */
873         if (client->cl_creds) {
874                 OM_uint32 min_ver;
875                 gss_release_cred(&min_ver, &client->cl_creds);
876         }
877         return (result);
878 }
879
880 static bool_t
881 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
882 {
883         u_int32_t offset;
884         int word, bit;
885
886         if (seq <= client->cl_seqlast) {
887                 /*
888                  * The request sequence number is less than
889                  * the largest we have seen so far. If it is
890                  * outside the window or if we have seen a
891                  * request with this sequence before, silently
892                  * discard it.
893                  */
894                 offset = client->cl_seqlast - seq;
895                 if (offset >= client->cl_win)
896                         return (FALSE);
897                 word = offset / 32;
898                 bit = offset % 32;
899                 if (client->cl_seqmask[word] & (1 << bit))
900                         return (FALSE);
901                 client->cl_seqmask[word] |= (1 << bit);
902         }
903
904         return (TRUE);
905 }
906
907 static void
908 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
909 {
910         int offset, i;
911         uint32_t carry, newcarry;
912
913         if (seq > client->cl_seqlast) {
914                 /*
915                  * This request has a sequence number greater
916                  * than any we have seen so far. Advance the
917                  * seq window and set bit zero of the window
918                  * (which corresponds to the new sequence
919                  * number)
920                  */
921                 offset = seq - client->cl_seqlast;
922                 while (offset > 32) {
923                         for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
924                              i > 0; i--) {
925                                 client->cl_seqmask[i] = client->cl_seqmask[i-1];
926                         }
927                         client->cl_seqmask[0] = 0;
928                         offset -= 32;
929                 }
930                 carry = 0;
931                 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
932                         newcarry = client->cl_seqmask[i] >> (32 - offset);
933                         client->cl_seqmask[i] =
934                                 (client->cl_seqmask[i] << offset) | carry;
935                         carry = newcarry;
936                 }
937                 client->cl_seqmask[0] |= 1;
938                 client->cl_seqlast = seq;
939         }
940 }
941
942 enum auth_stat
943 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg)
944
945 {
946         OM_uint32                min_stat;
947         XDR                      xdrs;
948         struct svc_rpc_gss_client *client;
949         struct rpc_gss_cred      gc;
950         struct rpc_gss_init_res  gr;
951         gss_qop_t                qop;
952         int                      call_stat;
953         enum auth_stat           result;
954         
955         log_debug("in svc_rpc_gss()");
956         
957         /* Garbage collect old clients. */
958         svc_rpc_gss_timeout_clients();
959
960         /* Initialize reply. */
961         rqst->rq_xprt->xp_verf = _null_auth;
962
963         /* Deserialize client credentials. */
964         if (rqst->rq_cred.oa_length <= 0)
965                 return (AUTH_BADCRED);
966         
967         memset(&gc, 0, sizeof(gc));
968         
969         xdrmem_create(&xdrs, rqst->rq_cred.oa_base,
970             rqst->rq_cred.oa_length, XDR_DECODE);
971         
972         if (!xdr_rpc_gss_cred(&xdrs, &gc)) {
973                 XDR_DESTROY(&xdrs);
974                 return (AUTH_BADCRED);
975         }
976         XDR_DESTROY(&xdrs);
977
978         /* Check version. */
979         if (gc.gc_version != RPCSEC_GSS_VERSION) {
980                 result = AUTH_BADCRED;
981                 goto out;
982         }
983
984         /* Check the proc and find the client (or create it) */
985         if (gc.gc_proc == RPCSEC_GSS_INIT) {
986                 client = svc_rpc_gss_create_client();
987         } else {
988                 if (gc.gc_handle.length != sizeof(uint32_t)) {
989                         result = AUTH_BADCRED;
990                         goto out;
991                 }
992                 uint32_t *p = gc.gc_handle.value;
993                 client = svc_rpc_gss_find_client(*p);
994                 if (!client) {
995                         /*
996                          * Can't find the client - we may have
997                          * destroyed it - tell the other side to
998                          * re-authenticate.
999                          */
1000                         result = RPCSEC_GSS_CREDPROBLEM;
1001                         goto out;
1002                 }
1003         }
1004         rqst->rq_clntcred = client;
1005
1006         /*
1007          * The service and sequence number must be ignored for
1008          * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT.
1009          */
1010         if (gc.gc_proc != RPCSEC_GSS_INIT
1011             && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) {
1012                 /*
1013                  * Check for sequence number overflow.
1014                  */
1015                 if (gc.gc_seq >= MAXSEQ) {
1016                         result = RPCSEC_GSS_CTXPROBLEM;
1017                         goto out;
1018                 }
1019                 client->cl_seq = gc.gc_seq;
1020
1021                 /*
1022                  * Check for valid service.
1023                  */
1024                 if (gc.gc_svc != rpc_gss_svc_none &&
1025                     gc.gc_svc != rpc_gss_svc_integrity &&
1026                     gc.gc_svc != rpc_gss_svc_privacy) {
1027                         result = AUTH_BADCRED;
1028                         goto out;
1029                 }
1030         }
1031
1032         /* Handle RPCSEC_GSS control procedure. */
1033         switch (gc.gc_proc) {
1034
1035         case RPCSEC_GSS_INIT:
1036         case RPCSEC_GSS_CONTINUE_INIT:
1037                 if (rqst->rq_proc != NULLPROC) {
1038                         result = AUTH_REJECTEDCRED;
1039                         break;
1040                 }
1041
1042                 memset(&gr, 0, sizeof(gr));
1043                 if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) {
1044                         result = AUTH_REJECTEDCRED;
1045                         break;
1046                 }
1047
1048                 if (gr.gr_major == GSS_S_COMPLETE) {
1049                         if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) {
1050                                 result = AUTH_REJECTEDCRED;
1051                                 break;
1052                         }
1053                 } else {
1054                         rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL;
1055                         rqst->rq_xprt->xp_verf.oa_length = 0;
1056                 }
1057                 
1058                 call_stat = svc_sendreply(rqst->rq_xprt,
1059                     (xdrproc_t) xdr_rpc_gss_init_res,
1060                     (caddr_t) &gr);
1061
1062                 gss_release_buffer(&min_stat, &gr.gr_token);
1063
1064                 if (!call_stat) {
1065                         result = AUTH_FAILED;
1066                         break;
1067                 }
1068
1069                 if (gr.gr_major == GSS_S_COMPLETE)
1070                         client->cl_state = CLIENT_ESTABLISHED;
1071
1072                 result = RPCSEC_GSS_NODISPATCH;
1073                 break;
1074                 
1075         case RPCSEC_GSS_DATA:
1076         case RPCSEC_GSS_DESTROY:
1077                 if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) {
1078                         result = RPCSEC_GSS_NODISPATCH;
1079                         break;
1080                 }
1081
1082                 if (!svc_rpc_gss_validate(client, msg, &qop)) {
1083                         result = RPCSEC_GSS_CREDPROBLEM;
1084                         break;
1085                 }
1086                 
1087                 if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) {
1088                         result = RPCSEC_GSS_CTXPROBLEM;
1089                         break;
1090                 }
1091
1092                 svc_rpc_gss_update_seq(client, gc.gc_seq);
1093
1094                 /*
1095                  * Change the SVCAUTH ops on the transport to point at
1096                  * our own code so that we can unwrap the arguments
1097                  * and wrap the result. The caller will re-set this on
1098                  * every request to point to a set of null wrap/unwrap
1099                  * methods.
1100                  */
1101                 SVC_AUTH(rqst->rq_xprt).svc_ah_ops = &svc_auth_gss_ops;
1102                 SVC_AUTH(rqst->rq_xprt).svc_ah_private = client;
1103
1104                 if (gc.gc_proc == RPCSEC_GSS_DATA) {
1105                         /*
1106                          * We might be ready to do a callback to the server to
1107                          * see if it wants to accept/reject the connection.
1108                          */
1109                         if (!client->cl_done_callback) {
1110                                 client->cl_done_callback = TRUE;
1111                                 client->cl_qop = qop;
1112                                 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1113                                         client->cl_rawcred.mechanism, qop);
1114                                 if (!svc_rpc_gss_callback(client, rqst)) {
1115                                         result = AUTH_REJECTEDCRED;
1116                                         break;
1117                                 }
1118                         }
1119
1120                         /*
1121                          * If the server has locked this client to a
1122                          * particular service+qop pair, enforce that
1123                          * restriction now.
1124                          */
1125                         if (client->cl_locked) {
1126                                 if (client->cl_rawcred.service != gc.gc_svc) {
1127                                         result = AUTH_FAILED;
1128                                         break;
1129                                 } else if (client->cl_qop != qop) {
1130                                         result = AUTH_BADVERF;
1131                                         break;
1132                                 }
1133                         }
1134
1135                         /*
1136                          * If the qop changed, look up the new qop
1137                          * name for rawcred.
1138                          */
1139                         if (client->cl_qop != qop) {
1140                                 client->cl_qop = qop;
1141                                 client->cl_rawcred.qop = _rpc_gss_num_to_qop(
1142                                         client->cl_rawcred.mechanism, qop);
1143                         }
1144
1145                         /*
1146                          * Make sure we use the right service value
1147                          * for unwrap/wrap.
1148                          */
1149                         client->cl_rawcred.service = gc.gc_svc;
1150
1151                         result = AUTH_OK;
1152                 } else {
1153                         if (rqst->rq_proc != NULLPROC) {
1154                                 result = AUTH_REJECTEDCRED;
1155                                 break;
1156                         }
1157
1158                         call_stat = svc_sendreply(rqst->rq_xprt,
1159                             (xdrproc_t) xdr_void, (caddr_t) NULL);
1160
1161                         if (!call_stat) {
1162                                 result = AUTH_FAILED;
1163                                 break;
1164                         }
1165
1166                         svc_rpc_gss_destroy_client(client);
1167
1168                         result = RPCSEC_GSS_NODISPATCH;
1169                         break;
1170                 }
1171                 break;
1172
1173         default:
1174                 result = AUTH_BADCRED;
1175                 break;
1176         }
1177 out:
1178         xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1179         return (result);
1180 }
1181
1182 bool_t
1183 svc_rpc_gss_wrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr)
1184 {
1185         struct svc_rpc_gss_client *client;
1186         
1187         log_debug("in svc_rpc_gss_wrap()");
1188
1189         client = (struct svc_rpc_gss_client *) auth->svc_ah_private;
1190         if (client->cl_state != CLIENT_ESTABLISHED
1191             || client->cl_rawcred.service == rpc_gss_svc_none) {
1192                 return xdr_func(xdrs, xdr_ptr);
1193         }
1194         return (xdr_rpc_gss_wrap_data(xdrs, xdr_func, xdr_ptr,
1195                 client->cl_ctx, client->cl_qop,
1196                 client->cl_rawcred.service, client->cl_seq));
1197 }
1198
1199 bool_t
1200 svc_rpc_gss_unwrap(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_unwrap()");
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_unwrap_data(xdrs, xdr_func, xdr_ptr,
1212                 client->cl_ctx, client->cl_qop,
1213                 client->cl_rawcred.service, client->cl_seq));
1214 }