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