Deleted Added
full compact
1
2/*
3 * Copyright (c) 1988 by Sun Microsystems, Inc.
4 */
5
6/*
7 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
8 * unrestricted use provided that this legend is included on all tape
9 * media and as a part of the software program in whole or part. Users
10 * may copy or modify Sun RPC without charge, but are not authorized
11 * to license or distribute it to anyone else except as part of a product or
12 * program developed by the user.
13 *
14 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
15 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
17 *
18 * Sun RPC is provided with no support and without any obligation on the
19 * part of Sun Microsystems, Inc. to assist in its use, correction,
20 * modification or enhancement.
21 *
22 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
23 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
24 * OR ANY PART THEREOF.
25 *
26 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
27 * or profits or other special, indirect and consequential damages, even if
28 * Sun has been advised of the possibility of such damages.
29 *
30 * Sun Microsystems, Inc.
31 * 2550 Garcia Avenue
32 * Mountain View, California 94043
33 */
34
35/*
36 * svcauth_des.c, server-side des authentication
37 *
38 * We insure for the service the following:
39 * (1) The timestamp microseconds do not exceed 1 million.
40 * (2) The timestamp plus the window is less than the current time.
41 * (3) The timestamp is not less than the one previously
42 * seen in the current session.
43 *
44 * It is up to the server to determine if the window size is
45 * too small .
46 *
47 */
48
49#include "reentrant.h"
49#include "namespace.h"
50#include "reentrant.h"
51#include <string.h>
52#include <stdlib.h>
53#include <stdio.h>
54#include <unistd.h>
55#include <rpc/des_crypt.h>
56#include <sys/param.h>
57#include <netinet/in.h>
58#include <rpc/types.h>
59#include <rpc/xdr.h>
60#include <rpc/auth.h>
61#include <rpc/auth_des.h>
62#include <rpc/svc.h>
63#include <rpc/rpc_msg.h>
64#include <rpc/svc_auth.h>
65#include "libc_private.h"
66
67#if defined(LIBC_SCCS) && !defined(lint)
68/* from: static char sccsid[] = "@(#)svcauth_des.c 2.3 89/07/11 4.0 RPCSRC; from 1.15 88/02/08 SMI"; */
69static const char rcsid[] = "$FreeBSD: head/lib/libc/rpc/svc_auth_des.c 74462 2001-03-19 12:50:13Z alfred $";
69static const char rcsid[] = "$FreeBSD: head/lib/libc/rpc/svc_auth_des.c 75094 2001-04-02 21:41:44Z iedowse $";
70#endif
71
72#define debug(msg) printf("svcauth_des: %s\n", msg)
73
74#define USEC_PER_SEC ((u_long) 1000000L)
75#define BEFORE(t1, t2) timercmp(t1, t2, <)
76
77/*
78 * LRU cache of conversation keys and some other useful items.
79 */
80#define AUTHDES_CACHESZ 64
81struct cache_entry {
82 des_block key; /* conversation key */
83 char *rname; /* client's name */
84 u_int window; /* credential lifetime window */
85 struct timeval laststamp; /* detect replays of creds */
86 char *localcred; /* generic local credential */
87};
88static struct cache_entry *authdes_cache/* [AUTHDES_CACHESZ] */;
89static short *authdes_lru/* [AUTHDES_CACHESZ] */;
90
91static void cache_init(); /* initialize the cache */
92static short cache_spot(); /* find an entry in the cache */
93static void cache_ref(/*short sid*/); /* note that sid was ref'd */
94
95static void invalidate(); /* invalidate entry in cache */
96
97/*
98 * cache statistics
99 */
100static struct {
101 u_long ncachehits; /* times cache hit, and is not replay */
102 u_long ncachereplays; /* times cache hit, and is replay */
103 u_long ncachemisses; /* times cache missed */
104} svcauthdes_stats;
105
106/*
107 * Service side authenticator for AUTH_DES
108 */
109enum auth_stat
110_svcauth_des(rqst, msg)
111 register struct svc_req *rqst;
112 register struct rpc_msg *msg;
113{
114
115 register long *ixdr;
116 des_block cryptbuf[2];
117 register struct authdes_cred *cred;
118 struct authdes_verf verf;
119 int status;
120 register struct cache_entry *entry;
121 short sid = 0;
122 des_block *sessionkey;
123 des_block ivec;
124 u_int window;
125 struct timeval timestamp;
126 u_long namelen;
127 struct area {
128 struct authdes_cred area_cred;
129 char area_netname[MAXNETNAMELEN+1];
130 } *area;
131
132 if (authdes_cache == NULL) {
133 cache_init();
134 }
135
136 area = (struct area *)rqst->rq_clntcred;
137 cred = (struct authdes_cred *)&area->area_cred;
138
139 /*
140 * Get the credential
141 */
142 ixdr = (long *)msg->rm_call.cb_cred.oa_base;
143 cred->adc_namekind = IXDR_GET_ENUM(ixdr, enum authdes_namekind);
144 switch (cred->adc_namekind) {
145 case ADN_FULLNAME:
146 namelen = IXDR_GET_U_LONG(ixdr);
147 if (namelen > MAXNETNAMELEN) {
148 return (AUTH_BADCRED);
149 }
150 cred->adc_fullname.name = area->area_netname;
151 bcopy((char *)ixdr, cred->adc_fullname.name,
152 (u_int)namelen);
153 cred->adc_fullname.name[namelen] = 0;
154 ixdr += (RNDUP(namelen) / BYTES_PER_XDR_UNIT);
155 cred->adc_fullname.key.key.high = (u_long)*ixdr++;
156 cred->adc_fullname.key.key.low = (u_long)*ixdr++;
157 cred->adc_fullname.window = (u_long)*ixdr++;
158 break;
159 case ADN_NICKNAME:
160 cred->adc_nickname = (u_long)*ixdr++;
161 break;
162 default:
163 return (AUTH_BADCRED);
164 }
165
166 /*
167 * Get the verifier
168 */
169 ixdr = (long *)msg->rm_call.cb_verf.oa_base;
170 verf.adv_xtimestamp.key.high = (u_long)*ixdr++;
171 verf.adv_xtimestamp.key.low = (u_long)*ixdr++;
172 verf.adv_int_u = (u_long)*ixdr++;
173
174
175 /*
176 * Get the conversation key
177 */
178 if (cred->adc_namekind == ADN_FULLNAME) {
179 netobj pkey;
180 char pkey_data[1024];
181
182 sessionkey = &cred->adc_fullname.key;
183 if (! getpublickey(cred->adc_fullname.name, pkey_data)) {
184 debug("getpublickey");
185 return(AUTH_BADCRED);
186 }
187 pkey.n_bytes = pkey_data;
188 pkey.n_len = strlen(pkey_data) + 1;
189 if (key_decryptsession_pk(cred->adc_fullname.name, &pkey,
190 sessionkey) < 0) {
191 debug("decryptsessionkey");
192 return (AUTH_BADCRED); /* key not found */
193 }
194 } else { /* ADN_NICKNAME */
195 sid = (short)cred->adc_nickname;
196 if (sid < 0 || sid >= AUTHDES_CACHESZ) {
197 debug("bad nickname");
198 return (AUTH_BADCRED); /* garbled credential */
199 }
200 sessionkey = &authdes_cache[sid].key;
201 }
202
203
204 /*
205 * Decrypt the timestamp
206 */
207 cryptbuf[0] = verf.adv_xtimestamp;
208 if (cred->adc_namekind == ADN_FULLNAME) {
209 cryptbuf[1].key.high = cred->adc_fullname.window;
210 cryptbuf[1].key.low = verf.adv_winverf;
211 ivec.key.high = ivec.key.low = 0;
212 status = cbc_crypt((char *)sessionkey, (char *)cryptbuf,
213 2*sizeof(des_block), DES_DECRYPT | DES_HW,
214 (char *)&ivec);
215 } else {
216 status = ecb_crypt((char *)sessionkey, (char *)cryptbuf,
217 sizeof(des_block), DES_DECRYPT | DES_HW);
218 }
219 if (DES_FAILED(status)) {
220 debug("decryption failure");
221 return (AUTH_FAILED); /* system error */
222 }
223
224 /*
225 * XDR the decrypted timestamp
226 */
227 ixdr = (long *)cryptbuf;
228 timestamp.tv_sec = IXDR_GET_LONG(ixdr);
229 timestamp.tv_usec = IXDR_GET_LONG(ixdr);
230
231 /*
232 * Check for valid credentials and verifiers.
233 * They could be invalid because the key was flushed
234 * out of the cache, and so a new session should begin.
235 * Be sure and send AUTH_REJECTED{CRED, VERF} if this is the case.
236 */
237 {
238 struct timeval current;
239 int nick;
240 int winverf;
241
242 if (cred->adc_namekind == ADN_FULLNAME) {
243 window = IXDR_GET_U_LONG(ixdr);
244 winverf = IXDR_GET_U_LONG(ixdr);
245 if (winverf != window - 1) {
246 debug("window verifier mismatch");
247 return (AUTH_BADCRED); /* garbled credential */
248 }
249 sid = cache_spot(sessionkey, cred->adc_fullname.name,
250 &timestamp);
251 if (sid < 0) {
252 debug("replayed credential");
253 return (AUTH_REJECTEDCRED); /* replay */
254 }
255 nick = 0;
256 } else { /* ADN_NICKNAME */
257 window = authdes_cache[sid].window;
258 nick = 1;
259 }
260
261 if ((u_long)timestamp.tv_usec >= USEC_PER_SEC) {
262 debug("invalid usecs");
263 /* cached out (bad key), or garbled verifier */
264 return (nick ? AUTH_REJECTEDVERF : AUTH_BADVERF);
265 }
266 if (nick && BEFORE(&timestamp,
267 &authdes_cache[sid].laststamp)) {
268 debug("timestamp before last seen");
269 return (AUTH_REJECTEDVERF); /* replay */
270 }
271 (void) gettimeofday(&current, (struct timezone *)NULL);
272 current.tv_sec -= window; /* allow for expiration */
273 if (!BEFORE(&current, &timestamp)) {
274 debug("timestamp expired");
275 /* replay, or garbled credential */
276 return (nick ? AUTH_REJECTEDVERF : AUTH_BADCRED);
277 }
278 }
279
280 /*
281 * Set up the reply verifier
282 */
283 verf.adv_nickname = (u_long)sid;
284
285 /*
286 * xdr the timestamp before encrypting
287 */
288 ixdr = (long *)cryptbuf;
289 IXDR_PUT_LONG(ixdr, timestamp.tv_sec - 1);
290 IXDR_PUT_LONG(ixdr, timestamp.tv_usec);
291
292 /*
293 * encrypt the timestamp
294 */
295 status = ecb_crypt((char *)sessionkey, (char *)cryptbuf,
296 sizeof(des_block), DES_ENCRYPT | DES_HW);
297 if (DES_FAILED(status)) {
298 debug("encryption failure");
299 return (AUTH_FAILED); /* system error */
300 }
301 verf.adv_xtimestamp = cryptbuf[0];
302
303 /*
304 * Serialize the reply verifier, and update rqst
305 */
306 ixdr = (long *)msg->rm_call.cb_verf.oa_base;
307 *ixdr++ = (long)verf.adv_xtimestamp.key.high;
308 *ixdr++ = (long)verf.adv_xtimestamp.key.low;
309 *ixdr++ = (long)verf.adv_int_u;
310
311 rqst->rq_xprt->xp_verf.oa_flavor = AUTH_DES;
312 rqst->rq_xprt->xp_verf.oa_base = msg->rm_call.cb_verf.oa_base;
313 rqst->rq_xprt->xp_verf.oa_length =
314 (char *)ixdr - msg->rm_call.cb_verf.oa_base;
315
316 /*
317 * We succeeded, commit the data to the cache now and
318 * finish cooking the credential.
319 */
320 entry = &authdes_cache[sid];
321 entry->laststamp = timestamp;
322 cache_ref(sid);
323 if (cred->adc_namekind == ADN_FULLNAME) {
324 cred->adc_fullname.window = window;
325 cred->adc_nickname = (u_long)sid; /* save nickname */
326 if (entry->rname != NULL) {
327 mem_free(entry->rname, strlen(entry->rname) + 1);
328 }
329 entry->rname = (char *)mem_alloc((u_int)strlen(cred->adc_fullname.name)
330 + 1);
331 if (entry->rname != NULL) {
332 (void) strcpy(entry->rname, cred->adc_fullname.name);
333 } else {
334 debug("out of memory");
335 }
336 entry->key = *sessionkey;
337 entry->window = window;
338 invalidate(entry->localcred); /* mark any cached cred invalid */
339 } else { /* ADN_NICKNAME */
340 /*
341 * nicknames are cooked into fullnames
342 */
343 cred->adc_namekind = ADN_FULLNAME;
344 cred->adc_fullname.name = entry->rname;
345 cred->adc_fullname.key = entry->key;
346 cred->adc_fullname.window = entry->window;
347 }
348 return (AUTH_OK); /* we made it!*/
349}
350
351
352/*
353 * Initialize the cache
354 */
355static void
356cache_init()
357{
358 register int i;
359
360 authdes_cache = (struct cache_entry *)
361 mem_alloc(sizeof(struct cache_entry) * AUTHDES_CACHESZ);
362 bzero((char *)authdes_cache,
363 sizeof(struct cache_entry) * AUTHDES_CACHESZ);
364
365 authdes_lru = (short *)mem_alloc(sizeof(short) * AUTHDES_CACHESZ);
366 /*
367 * Initialize the lru list
368 */
369 for (i = 0; i < AUTHDES_CACHESZ; i++) {
370 authdes_lru[i] = i;
371 }
372}
373
374
375/*
376 * Find the lru victim
377 */
378static short
379cache_victim()
380{
381 return (authdes_lru[AUTHDES_CACHESZ-1]);
382}
383
384/*
385 * Note that sid was referenced
386 */
387static void
388cache_ref(sid)
389 register short sid;
390{
391 register int i;
392 register short curr;
393 register short prev;
394
395 prev = authdes_lru[0];
396 authdes_lru[0] = sid;
397 for (i = 1; prev != sid; i++) {
398 curr = authdes_lru[i];
399 authdes_lru[i] = prev;
400 prev = curr;
401 }
402}
403
404
405/*
406 * Find a spot in the cache for a credential containing
407 * the items given. Return -1 if a replay is detected, otherwise
408 * return the spot in the cache.
409 */
410static short
411cache_spot(key, name, timestamp)
412 register des_block *key;
413 char *name;
414 struct timeval *timestamp;
415{
416 register struct cache_entry *cp;
417 register int i;
418 register u_long hi;
419
420 hi = key->key.high;
421 for (cp = authdes_cache, i = 0; i < AUTHDES_CACHESZ; i++, cp++) {
422 if (cp->key.key.high == hi &&
423 cp->key.key.low == key->key.low &&
424 cp->rname != NULL &&
425 bcmp(cp->rname, name, strlen(name) + 1) == 0) {
426 if (BEFORE(timestamp, &cp->laststamp)) {
427 svcauthdes_stats.ncachereplays++;
428 return (-1); /* replay */
429 }
430 svcauthdes_stats.ncachehits++;
431 return (i); /* refresh */
432 }
433 }
434 svcauthdes_stats.ncachemisses++;
435 return (cache_victim()); /* new credential */
436}
437
438
439#if (defined(sun) || defined(vax) || defined(__FreeBSD__))
440/*
441 * Local credential handling stuff.
442 * NOTE: bsd unix dependent.
443 * Other operating systems should put something else here.
444 */
445#define UNKNOWN -2 /* grouplen, if cached cred is unknown user */
446#define INVALID -1 /* grouplen, if cache entry is invalid */
447
448struct bsdcred {
449 short uid; /* cached uid */
450 short gid; /* cached gid */
451 short grouplen; /* length of cached groups */
452 short groups[NGROUPS]; /* cached groups */
453};
454
455/*
456 * Map a des credential into a unix cred.
457 * We cache the credential here so the application does
458 * not have to make an rpc call every time to interpret
459 * the credential.
460 */
461int
462authdes_getucred(adc, uid, gid, grouplen, groups)
463 struct authdes_cred *adc;
464 uid_t *uid;
465 gid_t *gid;
466 int *grouplen;
467 register gid_t *groups;
468{
469 unsigned sid;
470 register int i;
471 uid_t i_uid;
472 gid_t i_gid;
473 int i_grouplen;
474 struct bsdcred *cred;
475
476 sid = adc->adc_nickname;
477 if (sid >= AUTHDES_CACHESZ) {
478 debug("invalid nickname");
479 return (0);
480 }
481 cred = (struct bsdcred *)authdes_cache[sid].localcred;
482 if (cred == NULL) {
483 cred = (struct bsdcred *)mem_alloc(sizeof(struct bsdcred));
484 authdes_cache[sid].localcred = (char *)cred;
485 cred->grouplen = INVALID;
486 }
487 if (cred->grouplen == INVALID) {
488 /*
489 * not in cache: lookup
490 */
491 if (!netname2user(adc->adc_fullname.name, &i_uid, &i_gid,
492 &i_grouplen, groups))
493 {
494 debug("unknown netname");
495 cred->grouplen = UNKNOWN; /* mark as lookup up, but not found */
496 return (0);
497 }
498 debug("missed ucred cache");
499 *uid = cred->uid = i_uid;
500 *gid = cred->gid = i_gid;
501 *grouplen = cred->grouplen = i_grouplen;
502 for (i = i_grouplen - 1; i >= 0; i--) {
503 cred->groups[i] = groups[i]; /* int to short */
504 }
505 return (1);
506 } else if (cred->grouplen == UNKNOWN) {
507 /*
508 * Already lookup up, but no match found
509 */
510 return (0);
511 }
512
513 /*
514 * cached credentials
515 */
516 *uid = cred->uid;
517 *gid = cred->gid;
518 *grouplen = cred->grouplen;
519 for (i = cred->grouplen - 1; i >= 0; i--) {
520 groups[i] = cred->groups[i]; /* short to int */
521 }
522 return (1);
523}
524
525static void
526invalidate(cred)
527 char *cred;
528{
529 if (cred == NULL) {
530 return;
531 }
532 ((struct bsdcred *)cred)->grouplen = INVALID;
533}
534#endif
535