svc_auth_des.c revision 75094
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 "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 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
536