Deleted Added
full compact
svc_rpcsec_gss.c (201145) svc_rpcsec_gss.c (241720)
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 *
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: head/lib/librpcsec_gss/svc_rpcsec_gss.c 201145 2009-12-28 22:56:30Z antoine $
26 * $FreeBSD: head/lib/librpcsec_gss/svc_rpcsec_gss.c 241720 2012-10-19 05:43:38Z ed $
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
77static bool_t svc_rpc_gss_initialised = FALSE;
78
79static bool_t svc_rpc_gss_wrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t);
80static bool_t svc_rpc_gss_unwrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t);
81static enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *);
82
83static struct svc_auth_ops svc_auth_gss_ops = {
84 svc_rpc_gss_wrap,
85 svc_rpc_gss_unwrap,
86};
87
88struct svc_rpc_gss_callback {
89 SLIST_ENTRY(svc_rpc_gss_callback) cb_link;
90 rpc_gss_callback_t cb_callback;
91};
92static SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback)
93 svc_rpc_gss_callbacks = SLIST_HEAD_INITIALIZER(svc_rpc_gss_callbacks);
94
95struct 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};
104static 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
107enum 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
115struct 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};
139TAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client);
140
141#define CLIENT_HASH_SIZE 256
142#define CLIENT_MAX 128
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
77static bool_t svc_rpc_gss_initialised = FALSE;
78
79static bool_t svc_rpc_gss_wrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t);
80static bool_t svc_rpc_gss_unwrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t);
81static enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *);
82
83static struct svc_auth_ops svc_auth_gss_ops = {
84 svc_rpc_gss_wrap,
85 svc_rpc_gss_unwrap,
86};
87
88struct svc_rpc_gss_callback {
89 SLIST_ENTRY(svc_rpc_gss_callback) cb_link;
90 rpc_gss_callback_t cb_callback;
91};
92static SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback)
93 svc_rpc_gss_callbacks = SLIST_HEAD_INITIALIZER(svc_rpc_gss_callbacks);
94
95struct 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};
104static 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
107enum 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
115struct 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};
139TAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client);
140
141#define CLIENT_HASH_SIZE 256
142#define CLIENT_MAX 128
143struct svc_rpc_gss_client_list svc_rpc_gss_client_hash[CLIENT_HASH_SIZE];
144struct svc_rpc_gss_client_list svc_rpc_gss_clients;
143static struct svc_rpc_gss_client_list svc_rpc_gss_client_hash[CLIENT_HASH_SIZE];
144static struct svc_rpc_gss_client_list svc_rpc_gss_clients;
145static size_t svc_rpc_gss_client_count;
146static uint32_t svc_rpc_gss_next_clientid = 1;
147
148#ifdef __GNUC__
149static void svc_rpc_gss_init(void) __attribute__ ((constructor));
150#endif
151
152static void
153svc_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
166bool_t
167rpc_gss_set_callback(rpc_gss_callback_t *cb)
168{
169 struct svc_rpc_gss_callback *scb;
170
171 scb = mem_alloc(sizeof(struct svc_rpc_gss_callback));
172 if (!scb) {
173 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
174 return (FALSE);
175 }
176 scb->cb_callback = *cb;
177 SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link);
178
179 return (TRUE);
180}
181
182bool_t
183rpc_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
230bool_t
231rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
232 const char *mech, const char *name, const char *node, const char *domain)
233{
234 OM_uint32 maj_stat, min_stat;
235 gss_OID mech_oid;
236 size_t namelen;
237 gss_buffer_desc buf;
238 gss_name_t gss_name, gss_mech_name;
239 rpc_gss_principal_t result;
240
241 svc_rpc_gss_init();
242
243 if (!rpc_gss_mech_to_oid(mech, &mech_oid))
244 return (FALSE);
245
246 /*
247 * Construct a gss_buffer containing the full name formatted
248 * as "name/node@domain" where node and domain are optional.
249 */
250 namelen = strlen(name);
251 if (node) {
252 namelen += strlen(node) + 1;
253 }
254 if (domain) {
255 namelen += strlen(domain) + 1;
256 }
257
258 buf.value = mem_alloc(namelen);
259 buf.length = namelen;
260 strcpy((char *) buf.value, name);
261 if (node) {
262 strcat((char *) buf.value, "/");
263 strcat((char *) buf.value, node);
264 }
265 if (domain) {
266 strcat((char *) buf.value, "@");
267 strcat((char *) buf.value, domain);
268 }
269
270 /*
271 * Convert that to a gss_name_t and then convert that to a
272 * mechanism name in the selected mechanism.
273 */
274 maj_stat = gss_import_name(&min_stat, &buf,
275 GSS_C_NT_USER_NAME, &gss_name);
276 mem_free(buf.value, buf.length);
277 if (maj_stat != GSS_S_COMPLETE) {
278 log_status("gss_import_name", mech_oid, maj_stat, min_stat);
279 return (FALSE);
280 }
281 maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid,
282 &gss_mech_name);
283 if (maj_stat != GSS_S_COMPLETE) {
284 log_status("gss_canonicalize_name", mech_oid, maj_stat,
285 min_stat);
286 gss_release_name(&min_stat, &gss_name);
287 return (FALSE);
288 }
289 gss_release_name(&min_stat, &gss_name);
290
291 /*
292 * Export the mechanism name and use that to construct the
293 * rpc_gss_principal_t result.
294 */
295 maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf);
296 if (maj_stat != GSS_S_COMPLETE) {
297 log_status("gss_export_name", mech_oid, maj_stat, min_stat);
298 gss_release_name(&min_stat, &gss_mech_name);
299 return (FALSE);
300 }
301 gss_release_name(&min_stat, &gss_mech_name);
302
303 result = mem_alloc(sizeof(int) + buf.length);
304 if (!result) {
305 gss_release_buffer(&min_stat, &buf);
306 return (FALSE);
307 }
308 result->len = buf.length;
309 memcpy(result->name, buf.value, buf.length);
310 gss_release_buffer(&min_stat, &buf);
311
312 *principal = result;
313 return (TRUE);
314}
315
316bool_t
317rpc_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
335int
336rpc_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
377static struct svc_rpc_gss_client *
378svc_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
403static struct svc_rpc_gss_client *
404svc_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
430static void
431svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
432{
433 struct svc_rpc_gss_client_list *list;
434 OM_uint32 min_stat;
435
436 log_debug("in svc_rpc_gss_destroy_client()");
437
438 if (client->cl_ctx)
439 gss_delete_sec_context(&min_stat,
440 &client->cl_ctx, GSS_C_NO_BUFFER);
441
442 if (client->cl_cname)
443 gss_release_name(&min_stat, &client->cl_cname);
444
445 if (client->cl_rawcred.client_principal)
446 mem_free(client->cl_rawcred.client_principal,
447 sizeof(*client->cl_rawcred.client_principal)
448 + client->cl_rawcred.client_principal->len);
449
450 if (client->cl_verf.value)
451 gss_release_buffer(&min_stat, &client->cl_verf);
452
453 list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE];
454 TAILQ_REMOVE(list, client, cl_link);
455 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
456 svc_rpc_gss_client_count--;
457 mem_free(client, sizeof(*client));
458}
459
460static void
461svc_rpc_gss_timeout_clients(void)
462{
463 struct svc_rpc_gss_client *client;
464 struct svc_rpc_gss_client *nclient;
465 time_t now = time(0);
466
467 log_debug("in svc_rpc_gss_timeout_clients()");
468 /*
469 * First enforce the max client limit. We keep
470 * svc_rpc_gss_clients in LRU order.
471 */
472 while (svc_rpc_gss_client_count > CLIENT_MAX)
473 svc_rpc_gss_destroy_client(TAILQ_LAST(&svc_rpc_gss_clients,
474 svc_rpc_gss_client_list));
475 TAILQ_FOREACH_SAFE(client, &svc_rpc_gss_clients, cl_alllink, nclient) {
476 if (client->cl_state == CLIENT_STALE
477 || now > client->cl_expiration) {
478 log_debug("expiring client %p", client);
479 svc_rpc_gss_destroy_client(client);
480 }
481 }
482}
483
484#ifdef DEBUG
485/*
486 * OID<->string routines. These are uuuuugly.
487 */
488static OM_uint32
489gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
490{
491 char numstr[128];
492 unsigned long number;
493 int numshift;
494 size_t string_length;
495 size_t i;
496 unsigned char *cp;
497 char *bp;
498
499 /* Decoded according to krb5/gssapi_krb5.c */
500
501 /* First determine the size of the string */
502 string_length = 0;
503 number = 0;
504 numshift = 0;
505 cp = (unsigned char *) oid->elements;
506 number = (unsigned long) cp[0];
507 sprintf(numstr, "%ld ", number/40);
508 string_length += strlen(numstr);
509 sprintf(numstr, "%ld ", number%40);
510 string_length += strlen(numstr);
511 for (i=1; i<oid->length; i++) {
512 if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
513 number = (number << 7) | (cp[i] & 0x7f);
514 numshift += 7;
515 }
516 else {
517 *minor_status = 0;
518 return(GSS_S_FAILURE);
519 }
520 if ((cp[i] & 0x80) == 0) {
521 sprintf(numstr, "%ld ", number);
522 string_length += strlen(numstr);
523 number = 0;
524 numshift = 0;
525 }
526 }
527 /*
528 * If we get here, we've calculated the length of "n n n ... n ". Add 4
529 * here for "{ " and "}\0".
530 */
531 string_length += 4;
532 if ((bp = (char *) mem_alloc(string_length))) {
533 strcpy(bp, "{ ");
534 number = (unsigned long) cp[0];
535 sprintf(numstr, "%ld ", number/40);
536 strcat(bp, numstr);
537 sprintf(numstr, "%ld ", number%40);
538 strcat(bp, numstr);
539 number = 0;
540 cp = (unsigned char *) oid->elements;
541 for (i=1; i<oid->length; i++) {
542 number = (number << 7) | (cp[i] & 0x7f);
543 if ((cp[i] & 0x80) == 0) {
544 sprintf(numstr, "%ld ", number);
545 strcat(bp, numstr);
546 number = 0;
547 }
548 }
549 strcat(bp, "}");
550 oid_str->length = strlen(bp)+1;
551 oid_str->value = (void *) bp;
552 *minor_status = 0;
553 return(GSS_S_COMPLETE);
554 }
555 *minor_status = 0;
556 return(GSS_S_FAILURE);
557}
558#endif
559
560static void
561svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
562 const gss_name_t name)
563{
564 OM_uint32 maj_stat, min_stat;
565 char buf[128];
566 uid_t uid;
567 struct passwd pwd, *pw;
568 rpc_gss_ucred_t *uc = &client->cl_ucred;
569
570 uc->uid = 65534;
571 uc->gid = 65534;
572 uc->gidlen = 0;
573 uc->gidlist = client->cl_gid_storage;
574
575 maj_stat = gss_pname_to_uid(&min_stat, name, client->cl_mech, &uid);
576 if (maj_stat != GSS_S_COMPLETE)
577 return;
578
579 getpwuid_r(uid, &pwd, buf, sizeof(buf), &pw);
580 if (pw) {
581 int len = NGRPS;
582 uc->uid = pw->pw_uid;
583 uc->gid = pw->pw_gid;
584 uc->gidlist = client->cl_gid_storage;
585 getgrouplist(pw->pw_name, pw->pw_gid, uc->gidlist, &len);
586 uc->gidlen = len;
587 }
588}
589
590static bool_t
591svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
592 struct svc_req *rqst,
593 struct rpc_gss_init_res *gr,
594 struct rpc_gss_cred *gc)
595{
596 gss_buffer_desc recv_tok;
597 gss_OID mech;
598 OM_uint32 maj_stat = 0, min_stat = 0, ret_flags;
599 OM_uint32 cred_lifetime;
600 struct svc_rpc_gss_svc_name *sname;
601
602 log_debug("in svc_rpc_gss_accept_context()");
603
604 /* Deserialize arguments. */
605 memset(&recv_tok, 0, sizeof(recv_tok));
606
607 if (!svc_getargs(rqst->rq_xprt,
608 (xdrproc_t) xdr_gss_buffer_desc,
609 (caddr_t) &recv_tok)) {
610 client->cl_state = CLIENT_STALE;
611 return (FALSE);
612 }
613
614 /*
615 * First time round, try all the server names we have until
616 * one matches. Afterwards, stick with that one.
617 */
618 if (!client->cl_sname) {
619 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
620 if (sname->sn_program == rqst->rq_prog
621 && sname->sn_version == rqst->rq_vers) {
622 gr->gr_major = gss_accept_sec_context(
623 &gr->gr_minor,
624 &client->cl_ctx,
625 sname->sn_cred,
626 &recv_tok,
627 GSS_C_NO_CHANNEL_BINDINGS,
628 &client->cl_cname,
629 &mech,
630 &gr->gr_token,
631 &ret_flags,
632 &cred_lifetime,
633 &client->cl_creds);
634 if (gr->gr_major == GSS_S_COMPLETE
635 || gr->gr_major == GSS_S_CONTINUE_NEEDED) {
636 client->cl_sname = sname;
637 break;
638 }
639 client->cl_sname = sname;
640 break;
641 }
642 }
643 if (!sname) {
644 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
645 (char *) &recv_tok);
646 return (FALSE);
647 }
648 } else {
649 gr->gr_major = gss_accept_sec_context(
650 &gr->gr_minor,
651 &client->cl_ctx,
652 client->cl_sname->sn_cred,
653 &recv_tok,
654 GSS_C_NO_CHANNEL_BINDINGS,
655 &client->cl_cname,
656 &mech,
657 &gr->gr_token,
658 &ret_flags,
659 &cred_lifetime,
660 NULL);
661 }
662
663 xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
664
665 /*
666 * If we get an error from gss_accept_sec_context, send the
667 * reply anyway so that the client gets a chance to see what
668 * is wrong.
669 */
670 if (gr->gr_major != GSS_S_COMPLETE &&
671 gr->gr_major != GSS_S_CONTINUE_NEEDED) {
672 log_status("accept_sec_context", client->cl_mech,
673 gr->gr_major, gr->gr_minor);
674 client->cl_state = CLIENT_STALE;
675 return (TRUE);
676 }
677
678 gr->gr_handle.value = &client->cl_id;
679 gr->gr_handle.length = sizeof(client->cl_id);
680 gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
681
682 /* Save client info. */
683 client->cl_mech = mech;
684 client->cl_qop = GSS_C_QOP_DEFAULT;
685 client->cl_seq = gc->gc_seq;
686 client->cl_win = gr->gr_win;
687 client->cl_done_callback = FALSE;
688
689 if (gr->gr_major == GSS_S_COMPLETE) {
690 gss_buffer_desc export_name;
691
692 /*
693 * Change client expiration time to be near when the
694 * client creds expire (or 24 hours if we can't figure
695 * that out).
696 */
697 if (cred_lifetime == GSS_C_INDEFINITE)
698 cred_lifetime = time(0) + 24*60*60;
699
700 client->cl_expiration = time(0) + cred_lifetime;
701
702 /*
703 * Fill in cred details in the rawcred structure.
704 */
705 client->cl_rawcred.version = RPCSEC_GSS_VERSION;
706 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
707 maj_stat = gss_export_name(&min_stat, client->cl_cname,
708 &export_name);
709 if (maj_stat != GSS_S_COMPLETE) {
710 log_status("gss_export_name", client->cl_mech,
711 maj_stat, min_stat);
712 return (FALSE);
713 }
714 client->cl_rawcred.client_principal =
715 mem_alloc(sizeof(*client->cl_rawcred.client_principal)
716 + export_name.length);
717 client->cl_rawcred.client_principal->len = export_name.length;
718 memcpy(client->cl_rawcred.client_principal->name,
719 export_name.value, export_name.length);
720 gss_release_buffer(&min_stat, &export_name);
721 client->cl_rawcred.svc_principal =
722 client->cl_sname->sn_principal;
723 client->cl_rawcred.service = gc->gc_svc;
724
725 /*
726 * Use gss_pname_to_uid to map to unix creds. For
727 * kerberos5, this uses krb5_aname_to_localname.
728 */
729 svc_rpc_gss_build_ucred(client, client->cl_cname);
730 gss_release_name(&min_stat, &client->cl_cname);
731
732#ifdef DEBUG
733 {
734 gss_buffer_desc mechname;
735
736 gss_oid_to_str(&min_stat, mech, &mechname);
737
738 log_debug("accepted context for %s with "
739 "<mech %.*s, qop %d, svc %d>",
740 client->cl_rawcred.client_principal->name,
741 mechname.length, (char *)mechname.value,
742 client->cl_qop, client->rawcred.service);
743
744 gss_release_buffer(&min_stat, &mechname);
745 }
746#endif /* DEBUG */
747 }
748 return (TRUE);
749}
750
751static bool_t
752svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
753 gss_qop_t *qop)
754{
755 struct opaque_auth *oa;
756 gss_buffer_desc rpcbuf, checksum;
757 OM_uint32 maj_stat, min_stat;
758 gss_qop_t qop_state;
759 int32_t rpchdr[128 / sizeof(int32_t)];
760 int32_t *buf;
761
762 log_debug("in svc_rpc_gss_validate()");
763
764 memset(rpchdr, 0, sizeof(rpchdr));
765
766 /* Reconstruct RPC header for signing (from xdr_callmsg). */
767 buf = rpchdr;
768 IXDR_PUT_LONG(buf, msg->rm_xid);
769 IXDR_PUT_ENUM(buf, msg->rm_direction);
770 IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
771 IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
772 IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
773 IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
774 oa = &msg->rm_call.cb_cred;
775 IXDR_PUT_ENUM(buf, oa->oa_flavor);
776 IXDR_PUT_LONG(buf, oa->oa_length);
777 if (oa->oa_length) {
778 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
779 buf += RNDUP(oa->oa_length) / sizeof(int32_t);
780 }
781 rpcbuf.value = rpchdr;
782 rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
783
784 checksum.value = msg->rm_call.cb_verf.oa_base;
785 checksum.length = msg->rm_call.cb_verf.oa_length;
786
787 maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
788 &qop_state);
789
790 if (maj_stat != GSS_S_COMPLETE) {
791 log_status("gss_verify_mic", client->cl_mech,
792 maj_stat, min_stat);
793 client->cl_state = CLIENT_STALE;
794 return (FALSE);
795 }
796 *qop = qop_state;
797 return (TRUE);
798}
799
800static bool_t
801svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
802 struct svc_req *rqst, u_int seq)
803{
804 gss_buffer_desc signbuf;
805 OM_uint32 maj_stat, min_stat;
806 uint32_t nseq;
807
808 log_debug("in svc_rpc_gss_nextverf()");
809
810 nseq = htonl(seq);
811 signbuf.value = &nseq;
812 signbuf.length = sizeof(nseq);
813
814 if (client->cl_verf.value)
815 gss_release_buffer(&min_stat, &client->cl_verf);
816
817 maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
818 &signbuf, &client->cl_verf);
819
820 if (maj_stat != GSS_S_COMPLETE) {
821 log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
822 client->cl_state = CLIENT_STALE;
823 return (FALSE);
824 }
825 rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;
826 rqst->rq_xprt->xp_verf.oa_base = (caddr_t)client->cl_verf.value;
827 rqst->rq_xprt->xp_verf.oa_length = (u_int)client->cl_verf.length;
828
829 return (TRUE);
830}
831
832static bool_t
833svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
834{
835 struct svc_rpc_gss_callback *scb;
836 rpc_gss_lock_t lock;
837 void *cookie;
838 bool_t cb_res;
839 bool_t result;
840
841 /*
842 * See if we have a callback for this guy.
843 */
844 result = TRUE;
845 SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
846 if (scb->cb_callback.program == rqst->rq_prog
847 && scb->cb_callback.version == rqst->rq_vers) {
848 /*
849 * This one matches. Call the callback and see
850 * if it wants to veto or something.
851 */
852 lock.locked = FALSE;
853 lock.raw_cred = &client->cl_rawcred;
854 cb_res = scb->cb_callback.callback(rqst,
855 client->cl_creds,
856 client->cl_ctx,
857 &lock,
858 &cookie);
859
860 if (!cb_res) {
861 client->cl_state = CLIENT_STALE;
862 result = FALSE;
863 break;
864 }
865
866 /*
867 * The callback accepted the connection - it
868 * is responsible for freeing client->cl_creds
869 * now.
870 */
871 client->cl_creds = GSS_C_NO_CREDENTIAL;
872 client->cl_locked = lock.locked;
873 client->cl_cookie = cookie;
874 return (TRUE);
875 }
876 }
877
878 /*
879 * Either no callback exists for this program/version or one
880 * of the callbacks rejected the connection. We just need to
881 * clean up the delegated client creds, if any.
882 */
883 if (client->cl_creds) {
884 OM_uint32 min_ver;
885 gss_release_cred(&min_ver, &client->cl_creds);
886 }
887 return (result);
888}
889
890static bool_t
891svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
892{
893 u_int32_t offset;
894 int word, bit;
895
896 if (seq <= client->cl_seqlast) {
897 /*
898 * The request sequence number is less than
899 * the largest we have seen so far. If it is
900 * outside the window or if we have seen a
901 * request with this sequence before, silently
902 * discard it.
903 */
904 offset = client->cl_seqlast - seq;
905 if (offset >= SVC_RPC_GSS_SEQWINDOW)
906 return (FALSE);
907 word = offset / 32;
908 bit = offset % 32;
909 if (client->cl_seqmask[word] & (1 << bit))
910 return (FALSE);
911 }
912
913 return (TRUE);
914}
915
916static void
917svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
918{
919 int offset, i, word, bit;
920 uint32_t carry, newcarry;
921
922 if (seq > client->cl_seqlast) {
923 /*
924 * This request has a sequence number greater
925 * than any we have seen so far. Advance the
926 * seq window and set bit zero of the window
927 * (which corresponds to the new sequence
928 * number)
929 */
930 offset = seq - client->cl_seqlast;
931 while (offset > 32) {
932 for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
933 i > 0; i--) {
934 client->cl_seqmask[i] = client->cl_seqmask[i-1];
935 }
936 client->cl_seqmask[0] = 0;
937 offset -= 32;
938 }
939 carry = 0;
940 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
941 newcarry = client->cl_seqmask[i] >> (32 - offset);
942 client->cl_seqmask[i] =
943 (client->cl_seqmask[i] << offset) | carry;
944 carry = newcarry;
945 }
946 client->cl_seqmask[0] |= 1;
947 client->cl_seqlast = seq;
948 } else {
949 offset = client->cl_seqlast - seq;
950 word = offset / 32;
951 bit = offset % 32;
952 client->cl_seqmask[word] |= (1 << bit);
953 }
954
955}
956
957enum auth_stat
958svc_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 }
1196out:
1197 xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1198 return (result);
1199}
1200
1201bool_t
1202svc_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
1218bool_t
1219svc_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}
145static size_t svc_rpc_gss_client_count;
146static uint32_t svc_rpc_gss_next_clientid = 1;
147
148#ifdef __GNUC__
149static void svc_rpc_gss_init(void) __attribute__ ((constructor));
150#endif
151
152static void
153svc_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
166bool_t
167rpc_gss_set_callback(rpc_gss_callback_t *cb)
168{
169 struct svc_rpc_gss_callback *scb;
170
171 scb = mem_alloc(sizeof(struct svc_rpc_gss_callback));
172 if (!scb) {
173 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM);
174 return (FALSE);
175 }
176 scb->cb_callback = *cb;
177 SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link);
178
179 return (TRUE);
180}
181
182bool_t
183rpc_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
230bool_t
231rpc_gss_get_principal_name(rpc_gss_principal_t *principal,
232 const char *mech, const char *name, const char *node, const char *domain)
233{
234 OM_uint32 maj_stat, min_stat;
235 gss_OID mech_oid;
236 size_t namelen;
237 gss_buffer_desc buf;
238 gss_name_t gss_name, gss_mech_name;
239 rpc_gss_principal_t result;
240
241 svc_rpc_gss_init();
242
243 if (!rpc_gss_mech_to_oid(mech, &mech_oid))
244 return (FALSE);
245
246 /*
247 * Construct a gss_buffer containing the full name formatted
248 * as "name/node@domain" where node and domain are optional.
249 */
250 namelen = strlen(name);
251 if (node) {
252 namelen += strlen(node) + 1;
253 }
254 if (domain) {
255 namelen += strlen(domain) + 1;
256 }
257
258 buf.value = mem_alloc(namelen);
259 buf.length = namelen;
260 strcpy((char *) buf.value, name);
261 if (node) {
262 strcat((char *) buf.value, "/");
263 strcat((char *) buf.value, node);
264 }
265 if (domain) {
266 strcat((char *) buf.value, "@");
267 strcat((char *) buf.value, domain);
268 }
269
270 /*
271 * Convert that to a gss_name_t and then convert that to a
272 * mechanism name in the selected mechanism.
273 */
274 maj_stat = gss_import_name(&min_stat, &buf,
275 GSS_C_NT_USER_NAME, &gss_name);
276 mem_free(buf.value, buf.length);
277 if (maj_stat != GSS_S_COMPLETE) {
278 log_status("gss_import_name", mech_oid, maj_stat, min_stat);
279 return (FALSE);
280 }
281 maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid,
282 &gss_mech_name);
283 if (maj_stat != GSS_S_COMPLETE) {
284 log_status("gss_canonicalize_name", mech_oid, maj_stat,
285 min_stat);
286 gss_release_name(&min_stat, &gss_name);
287 return (FALSE);
288 }
289 gss_release_name(&min_stat, &gss_name);
290
291 /*
292 * Export the mechanism name and use that to construct the
293 * rpc_gss_principal_t result.
294 */
295 maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf);
296 if (maj_stat != GSS_S_COMPLETE) {
297 log_status("gss_export_name", mech_oid, maj_stat, min_stat);
298 gss_release_name(&min_stat, &gss_mech_name);
299 return (FALSE);
300 }
301 gss_release_name(&min_stat, &gss_mech_name);
302
303 result = mem_alloc(sizeof(int) + buf.length);
304 if (!result) {
305 gss_release_buffer(&min_stat, &buf);
306 return (FALSE);
307 }
308 result->len = buf.length;
309 memcpy(result->name, buf.value, buf.length);
310 gss_release_buffer(&min_stat, &buf);
311
312 *principal = result;
313 return (TRUE);
314}
315
316bool_t
317rpc_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
335int
336rpc_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
377static struct svc_rpc_gss_client *
378svc_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
403static struct svc_rpc_gss_client *
404svc_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
430static void
431svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client)
432{
433 struct svc_rpc_gss_client_list *list;
434 OM_uint32 min_stat;
435
436 log_debug("in svc_rpc_gss_destroy_client()");
437
438 if (client->cl_ctx)
439 gss_delete_sec_context(&min_stat,
440 &client->cl_ctx, GSS_C_NO_BUFFER);
441
442 if (client->cl_cname)
443 gss_release_name(&min_stat, &client->cl_cname);
444
445 if (client->cl_rawcred.client_principal)
446 mem_free(client->cl_rawcred.client_principal,
447 sizeof(*client->cl_rawcred.client_principal)
448 + client->cl_rawcred.client_principal->len);
449
450 if (client->cl_verf.value)
451 gss_release_buffer(&min_stat, &client->cl_verf);
452
453 list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE];
454 TAILQ_REMOVE(list, client, cl_link);
455 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink);
456 svc_rpc_gss_client_count--;
457 mem_free(client, sizeof(*client));
458}
459
460static void
461svc_rpc_gss_timeout_clients(void)
462{
463 struct svc_rpc_gss_client *client;
464 struct svc_rpc_gss_client *nclient;
465 time_t now = time(0);
466
467 log_debug("in svc_rpc_gss_timeout_clients()");
468 /*
469 * First enforce the max client limit. We keep
470 * svc_rpc_gss_clients in LRU order.
471 */
472 while (svc_rpc_gss_client_count > CLIENT_MAX)
473 svc_rpc_gss_destroy_client(TAILQ_LAST(&svc_rpc_gss_clients,
474 svc_rpc_gss_client_list));
475 TAILQ_FOREACH_SAFE(client, &svc_rpc_gss_clients, cl_alllink, nclient) {
476 if (client->cl_state == CLIENT_STALE
477 || now > client->cl_expiration) {
478 log_debug("expiring client %p", client);
479 svc_rpc_gss_destroy_client(client);
480 }
481 }
482}
483
484#ifdef DEBUG
485/*
486 * OID<->string routines. These are uuuuugly.
487 */
488static OM_uint32
489gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
490{
491 char numstr[128];
492 unsigned long number;
493 int numshift;
494 size_t string_length;
495 size_t i;
496 unsigned char *cp;
497 char *bp;
498
499 /* Decoded according to krb5/gssapi_krb5.c */
500
501 /* First determine the size of the string */
502 string_length = 0;
503 number = 0;
504 numshift = 0;
505 cp = (unsigned char *) oid->elements;
506 number = (unsigned long) cp[0];
507 sprintf(numstr, "%ld ", number/40);
508 string_length += strlen(numstr);
509 sprintf(numstr, "%ld ", number%40);
510 string_length += strlen(numstr);
511 for (i=1; i<oid->length; i++) {
512 if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) {
513 number = (number << 7) | (cp[i] & 0x7f);
514 numshift += 7;
515 }
516 else {
517 *minor_status = 0;
518 return(GSS_S_FAILURE);
519 }
520 if ((cp[i] & 0x80) == 0) {
521 sprintf(numstr, "%ld ", number);
522 string_length += strlen(numstr);
523 number = 0;
524 numshift = 0;
525 }
526 }
527 /*
528 * If we get here, we've calculated the length of "n n n ... n ". Add 4
529 * here for "{ " and "}\0".
530 */
531 string_length += 4;
532 if ((bp = (char *) mem_alloc(string_length))) {
533 strcpy(bp, "{ ");
534 number = (unsigned long) cp[0];
535 sprintf(numstr, "%ld ", number/40);
536 strcat(bp, numstr);
537 sprintf(numstr, "%ld ", number%40);
538 strcat(bp, numstr);
539 number = 0;
540 cp = (unsigned char *) oid->elements;
541 for (i=1; i<oid->length; i++) {
542 number = (number << 7) | (cp[i] & 0x7f);
543 if ((cp[i] & 0x80) == 0) {
544 sprintf(numstr, "%ld ", number);
545 strcat(bp, numstr);
546 number = 0;
547 }
548 }
549 strcat(bp, "}");
550 oid_str->length = strlen(bp)+1;
551 oid_str->value = (void *) bp;
552 *minor_status = 0;
553 return(GSS_S_COMPLETE);
554 }
555 *minor_status = 0;
556 return(GSS_S_FAILURE);
557}
558#endif
559
560static void
561svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client,
562 const gss_name_t name)
563{
564 OM_uint32 maj_stat, min_stat;
565 char buf[128];
566 uid_t uid;
567 struct passwd pwd, *pw;
568 rpc_gss_ucred_t *uc = &client->cl_ucred;
569
570 uc->uid = 65534;
571 uc->gid = 65534;
572 uc->gidlen = 0;
573 uc->gidlist = client->cl_gid_storage;
574
575 maj_stat = gss_pname_to_uid(&min_stat, name, client->cl_mech, &uid);
576 if (maj_stat != GSS_S_COMPLETE)
577 return;
578
579 getpwuid_r(uid, &pwd, buf, sizeof(buf), &pw);
580 if (pw) {
581 int len = NGRPS;
582 uc->uid = pw->pw_uid;
583 uc->gid = pw->pw_gid;
584 uc->gidlist = client->cl_gid_storage;
585 getgrouplist(pw->pw_name, pw->pw_gid, uc->gidlist, &len);
586 uc->gidlen = len;
587 }
588}
589
590static bool_t
591svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client,
592 struct svc_req *rqst,
593 struct rpc_gss_init_res *gr,
594 struct rpc_gss_cred *gc)
595{
596 gss_buffer_desc recv_tok;
597 gss_OID mech;
598 OM_uint32 maj_stat = 0, min_stat = 0, ret_flags;
599 OM_uint32 cred_lifetime;
600 struct svc_rpc_gss_svc_name *sname;
601
602 log_debug("in svc_rpc_gss_accept_context()");
603
604 /* Deserialize arguments. */
605 memset(&recv_tok, 0, sizeof(recv_tok));
606
607 if (!svc_getargs(rqst->rq_xprt,
608 (xdrproc_t) xdr_gss_buffer_desc,
609 (caddr_t) &recv_tok)) {
610 client->cl_state = CLIENT_STALE;
611 return (FALSE);
612 }
613
614 /*
615 * First time round, try all the server names we have until
616 * one matches. Afterwards, stick with that one.
617 */
618 if (!client->cl_sname) {
619 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) {
620 if (sname->sn_program == rqst->rq_prog
621 && sname->sn_version == rqst->rq_vers) {
622 gr->gr_major = gss_accept_sec_context(
623 &gr->gr_minor,
624 &client->cl_ctx,
625 sname->sn_cred,
626 &recv_tok,
627 GSS_C_NO_CHANNEL_BINDINGS,
628 &client->cl_cname,
629 &mech,
630 &gr->gr_token,
631 &ret_flags,
632 &cred_lifetime,
633 &client->cl_creds);
634 if (gr->gr_major == GSS_S_COMPLETE
635 || gr->gr_major == GSS_S_CONTINUE_NEEDED) {
636 client->cl_sname = sname;
637 break;
638 }
639 client->cl_sname = sname;
640 break;
641 }
642 }
643 if (!sname) {
644 xdr_free((xdrproc_t) xdr_gss_buffer_desc,
645 (char *) &recv_tok);
646 return (FALSE);
647 }
648 } else {
649 gr->gr_major = gss_accept_sec_context(
650 &gr->gr_minor,
651 &client->cl_ctx,
652 client->cl_sname->sn_cred,
653 &recv_tok,
654 GSS_C_NO_CHANNEL_BINDINGS,
655 &client->cl_cname,
656 &mech,
657 &gr->gr_token,
658 &ret_flags,
659 &cred_lifetime,
660 NULL);
661 }
662
663 xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok);
664
665 /*
666 * If we get an error from gss_accept_sec_context, send the
667 * reply anyway so that the client gets a chance to see what
668 * is wrong.
669 */
670 if (gr->gr_major != GSS_S_COMPLETE &&
671 gr->gr_major != GSS_S_CONTINUE_NEEDED) {
672 log_status("accept_sec_context", client->cl_mech,
673 gr->gr_major, gr->gr_minor);
674 client->cl_state = CLIENT_STALE;
675 return (TRUE);
676 }
677
678 gr->gr_handle.value = &client->cl_id;
679 gr->gr_handle.length = sizeof(client->cl_id);
680 gr->gr_win = SVC_RPC_GSS_SEQWINDOW;
681
682 /* Save client info. */
683 client->cl_mech = mech;
684 client->cl_qop = GSS_C_QOP_DEFAULT;
685 client->cl_seq = gc->gc_seq;
686 client->cl_win = gr->gr_win;
687 client->cl_done_callback = FALSE;
688
689 if (gr->gr_major == GSS_S_COMPLETE) {
690 gss_buffer_desc export_name;
691
692 /*
693 * Change client expiration time to be near when the
694 * client creds expire (or 24 hours if we can't figure
695 * that out).
696 */
697 if (cred_lifetime == GSS_C_INDEFINITE)
698 cred_lifetime = time(0) + 24*60*60;
699
700 client->cl_expiration = time(0) + cred_lifetime;
701
702 /*
703 * Fill in cred details in the rawcred structure.
704 */
705 client->cl_rawcred.version = RPCSEC_GSS_VERSION;
706 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism);
707 maj_stat = gss_export_name(&min_stat, client->cl_cname,
708 &export_name);
709 if (maj_stat != GSS_S_COMPLETE) {
710 log_status("gss_export_name", client->cl_mech,
711 maj_stat, min_stat);
712 return (FALSE);
713 }
714 client->cl_rawcred.client_principal =
715 mem_alloc(sizeof(*client->cl_rawcred.client_principal)
716 + export_name.length);
717 client->cl_rawcred.client_principal->len = export_name.length;
718 memcpy(client->cl_rawcred.client_principal->name,
719 export_name.value, export_name.length);
720 gss_release_buffer(&min_stat, &export_name);
721 client->cl_rawcred.svc_principal =
722 client->cl_sname->sn_principal;
723 client->cl_rawcred.service = gc->gc_svc;
724
725 /*
726 * Use gss_pname_to_uid to map to unix creds. For
727 * kerberos5, this uses krb5_aname_to_localname.
728 */
729 svc_rpc_gss_build_ucred(client, client->cl_cname);
730 gss_release_name(&min_stat, &client->cl_cname);
731
732#ifdef DEBUG
733 {
734 gss_buffer_desc mechname;
735
736 gss_oid_to_str(&min_stat, mech, &mechname);
737
738 log_debug("accepted context for %s with "
739 "<mech %.*s, qop %d, svc %d>",
740 client->cl_rawcred.client_principal->name,
741 mechname.length, (char *)mechname.value,
742 client->cl_qop, client->rawcred.service);
743
744 gss_release_buffer(&min_stat, &mechname);
745 }
746#endif /* DEBUG */
747 }
748 return (TRUE);
749}
750
751static bool_t
752svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg,
753 gss_qop_t *qop)
754{
755 struct opaque_auth *oa;
756 gss_buffer_desc rpcbuf, checksum;
757 OM_uint32 maj_stat, min_stat;
758 gss_qop_t qop_state;
759 int32_t rpchdr[128 / sizeof(int32_t)];
760 int32_t *buf;
761
762 log_debug("in svc_rpc_gss_validate()");
763
764 memset(rpchdr, 0, sizeof(rpchdr));
765
766 /* Reconstruct RPC header for signing (from xdr_callmsg). */
767 buf = rpchdr;
768 IXDR_PUT_LONG(buf, msg->rm_xid);
769 IXDR_PUT_ENUM(buf, msg->rm_direction);
770 IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers);
771 IXDR_PUT_LONG(buf, msg->rm_call.cb_prog);
772 IXDR_PUT_LONG(buf, msg->rm_call.cb_vers);
773 IXDR_PUT_LONG(buf, msg->rm_call.cb_proc);
774 oa = &msg->rm_call.cb_cred;
775 IXDR_PUT_ENUM(buf, oa->oa_flavor);
776 IXDR_PUT_LONG(buf, oa->oa_length);
777 if (oa->oa_length) {
778 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
779 buf += RNDUP(oa->oa_length) / sizeof(int32_t);
780 }
781 rpcbuf.value = rpchdr;
782 rpcbuf.length = (u_char *)buf - (u_char *)rpchdr;
783
784 checksum.value = msg->rm_call.cb_verf.oa_base;
785 checksum.length = msg->rm_call.cb_verf.oa_length;
786
787 maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum,
788 &qop_state);
789
790 if (maj_stat != GSS_S_COMPLETE) {
791 log_status("gss_verify_mic", client->cl_mech,
792 maj_stat, min_stat);
793 client->cl_state = CLIENT_STALE;
794 return (FALSE);
795 }
796 *qop = qop_state;
797 return (TRUE);
798}
799
800static bool_t
801svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client,
802 struct svc_req *rqst, u_int seq)
803{
804 gss_buffer_desc signbuf;
805 OM_uint32 maj_stat, min_stat;
806 uint32_t nseq;
807
808 log_debug("in svc_rpc_gss_nextverf()");
809
810 nseq = htonl(seq);
811 signbuf.value = &nseq;
812 signbuf.length = sizeof(nseq);
813
814 if (client->cl_verf.value)
815 gss_release_buffer(&min_stat, &client->cl_verf);
816
817 maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop,
818 &signbuf, &client->cl_verf);
819
820 if (maj_stat != GSS_S_COMPLETE) {
821 log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat);
822 client->cl_state = CLIENT_STALE;
823 return (FALSE);
824 }
825 rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS;
826 rqst->rq_xprt->xp_verf.oa_base = (caddr_t)client->cl_verf.value;
827 rqst->rq_xprt->xp_verf.oa_length = (u_int)client->cl_verf.length;
828
829 return (TRUE);
830}
831
832static bool_t
833svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst)
834{
835 struct svc_rpc_gss_callback *scb;
836 rpc_gss_lock_t lock;
837 void *cookie;
838 bool_t cb_res;
839 bool_t result;
840
841 /*
842 * See if we have a callback for this guy.
843 */
844 result = TRUE;
845 SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) {
846 if (scb->cb_callback.program == rqst->rq_prog
847 && scb->cb_callback.version == rqst->rq_vers) {
848 /*
849 * This one matches. Call the callback and see
850 * if it wants to veto or something.
851 */
852 lock.locked = FALSE;
853 lock.raw_cred = &client->cl_rawcred;
854 cb_res = scb->cb_callback.callback(rqst,
855 client->cl_creds,
856 client->cl_ctx,
857 &lock,
858 &cookie);
859
860 if (!cb_res) {
861 client->cl_state = CLIENT_STALE;
862 result = FALSE;
863 break;
864 }
865
866 /*
867 * The callback accepted the connection - it
868 * is responsible for freeing client->cl_creds
869 * now.
870 */
871 client->cl_creds = GSS_C_NO_CREDENTIAL;
872 client->cl_locked = lock.locked;
873 client->cl_cookie = cookie;
874 return (TRUE);
875 }
876 }
877
878 /*
879 * Either no callback exists for this program/version or one
880 * of the callbacks rejected the connection. We just need to
881 * clean up the delegated client creds, if any.
882 */
883 if (client->cl_creds) {
884 OM_uint32 min_ver;
885 gss_release_cred(&min_ver, &client->cl_creds);
886 }
887 return (result);
888}
889
890static bool_t
891svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq)
892{
893 u_int32_t offset;
894 int word, bit;
895
896 if (seq <= client->cl_seqlast) {
897 /*
898 * The request sequence number is less than
899 * the largest we have seen so far. If it is
900 * outside the window or if we have seen a
901 * request with this sequence before, silently
902 * discard it.
903 */
904 offset = client->cl_seqlast - seq;
905 if (offset >= SVC_RPC_GSS_SEQWINDOW)
906 return (FALSE);
907 word = offset / 32;
908 bit = offset % 32;
909 if (client->cl_seqmask[word] & (1 << bit))
910 return (FALSE);
911 }
912
913 return (TRUE);
914}
915
916static void
917svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq)
918{
919 int offset, i, word, bit;
920 uint32_t carry, newcarry;
921
922 if (seq > client->cl_seqlast) {
923 /*
924 * This request has a sequence number greater
925 * than any we have seen so far. Advance the
926 * seq window and set bit zero of the window
927 * (which corresponds to the new sequence
928 * number)
929 */
930 offset = seq - client->cl_seqlast;
931 while (offset > 32) {
932 for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1;
933 i > 0; i--) {
934 client->cl_seqmask[i] = client->cl_seqmask[i-1];
935 }
936 client->cl_seqmask[0] = 0;
937 offset -= 32;
938 }
939 carry = 0;
940 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) {
941 newcarry = client->cl_seqmask[i] >> (32 - offset);
942 client->cl_seqmask[i] =
943 (client->cl_seqmask[i] << offset) | carry;
944 carry = newcarry;
945 }
946 client->cl_seqmask[0] |= 1;
947 client->cl_seqlast = seq;
948 } else {
949 offset = client->cl_seqlast - seq;
950 word = offset / 32;
951 bit = offset % 32;
952 client->cl_seqmask[word] |= (1 << bit);
953 }
954
955}
956
957enum auth_stat
958svc_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 }
1196out:
1197 xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc);
1198 return (result);
1199}
1200
1201bool_t
1202svc_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
1218bool_t
1219svc_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}