Initial import from FreeBSD RELENG_4:
[dragonfly.git] / lib / libcr / rpc / svc_auth_des.c
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 <string.h>
50 #include <stdlib.h>
51 #include <unistd.h>
52 #include <rpc/des_crypt.h>
53 #include <sys/param.h>
54 #include <netinet/in.h>
55 #include <rpc/types.h>
56 #include <rpc/xdr.h>
57 #include <rpc/auth.h>
58 #include <rpc/auth_des.h>
59 #include <rpc/svc.h>
60 #include <rpc/rpc_msg.h>
61 #include <rpc/svc_auth.h>
62
63 #if defined(LIBC_SCCS) && !defined(lint)
64 /* from: static char sccsid[] =         "@(#)svcauth_des.c      2.3 89/07/11 4.0 RPCSRC; from 1.15 88/02/08 SMI"; */
65 static const char rcsid[] = "$FreeBSD: src/lib/libc/rpc/svc_auth_des.c,v 1.3 1999/08/28 00:00:48 peter Exp $";
66 #endif
67
68 #define debug(msg)       printf("svcauth_des: %s\n", msg) 
69
70 #define USEC_PER_SEC ((u_long) 1000000L)
71 #define BEFORE(t1, t2) timercmp(t1, t2, <)
72
73 /*
74  * LRU cache of conversation keys and some other useful items.
75  */
76 #define AUTHDES_CACHESZ 64
77 struct cache_entry {
78         des_block key;          /* conversation key */
79         char *rname;            /* client's name */
80         u_int window;           /* credential lifetime window */
81         struct timeval laststamp;       /* detect replays of creds */
82         char *localcred;        /* generic local credential */
83 };
84 static struct cache_entry *authdes_cache/* [AUTHDES_CACHESZ] */;
85 static short *authdes_lru/* [AUTHDES_CACHESZ] */;
86
87 static void cache_init();       /* initialize the cache */
88 static short cache_spot();      /* find an entry in the cache */
89 static void cache_ref(/*short sid*/);   /* note that sid was ref'd */
90
91 static void invalidate();       /* invalidate entry in cache */
92
93 /*
94  * cache statistics 
95  */
96 static struct {
97         u_long ncachehits;      /* times cache hit, and is not replay */
98         u_long ncachereplays;   /* times cache hit, and is replay */
99         u_long ncachemisses;    /* times cache missed */
100 } svcauthdes_stats;
101
102 /*
103  * Service side authenticator for AUTH_DES
104  */
105 enum auth_stat
106 _svcauth_des(rqst, msg)
107         register struct svc_req *rqst;
108         register struct rpc_msg *msg;
109 {
110
111         register long *ixdr;
112         des_block cryptbuf[2];
113         register struct authdes_cred *cred;
114         struct authdes_verf verf;
115         int status;
116         register struct cache_entry *entry;
117         short sid = 0;
118         des_block *sessionkey;
119         des_block ivec;
120         u_int window;
121         struct timeval timestamp;
122         u_long namelen;
123         struct area {
124                 struct authdes_cred area_cred;
125                 char area_netname[MAXNETNAMELEN+1];
126         } *area;
127
128         if (authdes_cache == NULL) {
129                 cache_init();
130         }
131
132         area = (struct area *)rqst->rq_clntcred;
133         cred = (struct authdes_cred *)&area->area_cred;
134
135         /*
136          * Get the credential
137          */
138         ixdr = (long *)msg->rm_call.cb_cred.oa_base;
139         cred->adc_namekind = IXDR_GET_ENUM(ixdr, enum authdes_namekind);
140         switch (cred->adc_namekind) {
141         case ADN_FULLNAME:
142                 namelen = IXDR_GET_U_LONG(ixdr);
143                 if (namelen > MAXNETNAMELEN) {
144                         return (AUTH_BADCRED);
145                 }
146                 cred->adc_fullname.name = area->area_netname;
147                 bcopy((char *)ixdr, cred->adc_fullname.name, 
148                         (u_int)namelen);
149                 cred->adc_fullname.name[namelen] = 0;
150                 ixdr += (RNDUP(namelen) / BYTES_PER_XDR_UNIT);
151                 cred->adc_fullname.key.key.high = (u_long)*ixdr++;
152                 cred->adc_fullname.key.key.low = (u_long)*ixdr++;
153                 cred->adc_fullname.window = (u_long)*ixdr++;
154                 break;
155         case ADN_NICKNAME:
156                 cred->adc_nickname = (u_long)*ixdr++;
157                 break;
158         default:
159                 return (AUTH_BADCRED);  
160         }
161
162         /*
163          * Get the verifier
164          */
165         ixdr = (long *)msg->rm_call.cb_verf.oa_base;
166         verf.adv_xtimestamp.key.high = (u_long)*ixdr++;
167         verf.adv_xtimestamp.key.low =  (u_long)*ixdr++;
168         verf.adv_int_u = (u_long)*ixdr++;
169
170
171         /*
172          * Get the conversation key
173          */
174         if (cred->adc_namekind == ADN_FULLNAME) {
175                 netobj pkey;
176                 char pkey_data[1024];
177
178                 sessionkey = &cred->adc_fullname.key;
179                 if (! getpublickey(cred->adc_fullname.name, pkey_data)) {
180                         debug("getpublickey");
181                         return(AUTH_BADCRED);
182                 }
183                 pkey.n_bytes = pkey_data;
184                 pkey.n_len = strlen(pkey_data) + 1;
185                 if (key_decryptsession_pk(cred->adc_fullname.name, &pkey,
186                                        sessionkey) < 0) {
187                         debug("decryptsessionkey");
188                         return (AUTH_BADCRED); /* key not found */
189                 }
190         } else { /* ADN_NICKNAME */     
191                 sid = (short)cred->adc_nickname;
192                 if (sid >= AUTHDES_CACHESZ) {
193                         debug("bad nickname");
194                         return (AUTH_BADCRED);  /* garbled credential */
195                 }
196                 sessionkey = &authdes_cache[sid].key;
197         }
198
199
200         /*
201          * Decrypt the timestamp
202          */
203         cryptbuf[0] = verf.adv_xtimestamp; 
204         if (cred->adc_namekind == ADN_FULLNAME) {
205                 cryptbuf[1].key.high = cred->adc_fullname.window;
206                 cryptbuf[1].key.low = verf.adv_winverf;
207                 ivec.key.high = ivec.key.low = 0;       
208                 status = cbc_crypt((char *)sessionkey, (char *)cryptbuf,
209                         2*sizeof(des_block), DES_DECRYPT | DES_HW, 
210                         (char *)&ivec);
211         } else {
212                 status = ecb_crypt((char *)sessionkey, (char *)cryptbuf,
213                         sizeof(des_block), DES_DECRYPT | DES_HW);
214         }
215         if (DES_FAILED(status)) {
216                 debug("decryption failure");
217                 return (AUTH_FAILED);   /* system error */
218         }
219
220         /*
221          * XDR the decrypted timestamp
222          */
223         ixdr = (long *)cryptbuf;
224         timestamp.tv_sec = IXDR_GET_LONG(ixdr);
225         timestamp.tv_usec = IXDR_GET_LONG(ixdr);
226
227         /*
228          * Check for valid credentials and verifiers.
229          * They could be invalid because the key was flushed
230          * out of the cache, and so a new session should begin.
231          * Be sure and send AUTH_REJECTED{CRED, VERF} if this is the case.
232          */
233         {
234                 struct timeval current;
235                 int nick;
236                 int winverf;
237
238                 if (cred->adc_namekind == ADN_FULLNAME) {
239                         window = IXDR_GET_U_LONG(ixdr);
240                         winverf = IXDR_GET_U_LONG(ixdr);
241                         if (winverf != window - 1) {
242                                 debug("window verifier mismatch");
243                                 return (AUTH_BADCRED);  /* garbled credential */
244                         }
245                         sid = cache_spot(sessionkey, cred->adc_fullname.name, 
246                             &timestamp);
247                         if (sid < 0) {
248                                 debug("replayed credential");
249                                 return (AUTH_REJECTEDCRED);     /* replay */
250                         }
251                         nick = 0;
252                 } else {        /* ADN_NICKNAME */
253                         window = authdes_cache[sid].window;
254                         nick = 1;
255                 }
256
257                 if ((u_long)timestamp.tv_usec >= USEC_PER_SEC) {
258                         debug("invalid usecs");
259                         /* cached out (bad key), or garbled verifier */
260                         return (nick ? AUTH_REJECTEDVERF : AUTH_BADVERF);
261                 }
262                 if (nick && BEFORE(&timestamp, 
263                                    &authdes_cache[sid].laststamp)) {
264                         debug("timestamp before last seen");
265                         return (AUTH_REJECTEDVERF);     /* replay */
266                 }
267                 (void) gettimeofday(&current, (struct timezone *)NULL);
268                 current.tv_sec -= window;       /* allow for expiration */
269                 if (!BEFORE(&current, &timestamp)) {
270                         debug("timestamp expired");
271                         /* replay, or garbled credential */
272                         return (nick ? AUTH_REJECTEDVERF : AUTH_BADCRED);
273                 }
274         }
275
276         /*
277          * Set up the reply verifier
278          */
279         verf.adv_nickname = (u_long)sid;
280
281         /*
282          * xdr the timestamp before encrypting
283          */
284         ixdr = (long *)cryptbuf;
285         IXDR_PUT_LONG(ixdr, timestamp.tv_sec - 1);
286         IXDR_PUT_LONG(ixdr, timestamp.tv_usec);
287
288         /*       
289          * encrypt the timestamp
290          */
291         status = ecb_crypt((char *)sessionkey, (char *)cryptbuf,
292             sizeof(des_block), DES_ENCRYPT | DES_HW);
293         if (DES_FAILED(status)) {
294                 debug("encryption failure");
295                 return (AUTH_FAILED);   /* system error */
296         }
297         verf.adv_xtimestamp = cryptbuf[0];
298
299         /*
300          * Serialize the reply verifier, and update rqst
301          */
302         ixdr = (long *)msg->rm_call.cb_verf.oa_base;
303         *ixdr++ = (long)verf.adv_xtimestamp.key.high;
304         *ixdr++ = (long)verf.adv_xtimestamp.key.low;
305         *ixdr++ = (long)verf.adv_int_u;
306
307         rqst->rq_xprt->xp_verf.oa_flavor = AUTH_DES;
308         rqst->rq_xprt->xp_verf.oa_base = msg->rm_call.cb_verf.oa_base;
309         rqst->rq_xprt->xp_verf.oa_length = 
310                 (char *)ixdr - msg->rm_call.cb_verf.oa_base;
311
312         /*
313          * We succeeded, commit the data to the cache now and
314          * finish cooking the credential.
315          */
316         entry = &authdes_cache[sid];
317         entry->laststamp = timestamp;
318         cache_ref(sid);
319         if (cred->adc_namekind == ADN_FULLNAME) {
320                 cred->adc_fullname.window = window;
321                 cred->adc_nickname = (u_long)sid;       /* save nickname */
322                 if (entry->rname != NULL) {
323                         mem_free(entry->rname, strlen(entry->rname) + 1);
324                 }
325                 entry->rname = (char *)mem_alloc((u_int)strlen(cred->adc_fullname.name)
326                                          + 1);
327                 if (entry->rname != NULL) {
328                         (void) strcpy(entry->rname, cred->adc_fullname.name);
329                 } else {
330                         debug("out of memory");
331                 }
332                 entry->key = *sessionkey;
333                 entry->window = window;
334                 invalidate(entry->localcred); /* mark any cached cred invalid */
335         } else { /* ADN_NICKNAME */
336                 /*
337                  * nicknames are cooked into fullnames
338                  */     
339                 cred->adc_namekind = ADN_FULLNAME;
340                 cred->adc_fullname.name = entry->rname;
341                 cred->adc_fullname.key = entry->key;
342                 cred->adc_fullname.window = entry->window;
343         }
344         return (AUTH_OK);       /* we made it!*/
345 }
346
347
348 /*
349  * Initialize the cache
350  */
351 static void
352 cache_init()
353 {
354         register int i;
355
356         authdes_cache = (struct cache_entry *)
357                 mem_alloc(sizeof(struct cache_entry) * AUTHDES_CACHESZ);        
358         bzero((char *)authdes_cache, 
359                 sizeof(struct cache_entry) * AUTHDES_CACHESZ);
360
361         authdes_lru = (short *)mem_alloc(sizeof(short) * AUTHDES_CACHESZ);
362         /*
363          * Initialize the lru list
364          */
365         for (i = 0; i < AUTHDES_CACHESZ; i++) {
366                 authdes_lru[i] = i;
367         }
368 }
369
370
371 /*
372  * Find the lru victim
373  */
374 static short
375 cache_victim()
376 {
377         return (authdes_lru[AUTHDES_CACHESZ-1]);
378 }
379
380 /*
381  * Note that sid was referenced
382  */
383 static void
384 cache_ref(sid)
385         register short sid;
386 {
387         register int i;
388         register short curr;
389         register short prev;
390
391         prev = authdes_lru[0];
392         authdes_lru[0] = sid;
393         for (i = 1; prev != sid; i++) {
394                 curr = authdes_lru[i];
395                 authdes_lru[i] = prev;
396                 prev = curr;
397         }
398 }
399
400
401 /*
402  * Find a spot in the cache for a credential containing
403  * the items given.  Return -1 if a replay is detected, otherwise
404  * return the spot in the cache.
405  */
406 static short
407 cache_spot(key, name, timestamp)
408         register des_block *key;
409         char *name;
410         struct timeval *timestamp;
411 {
412         register struct cache_entry *cp;
413         register int i;
414         register u_long hi;
415
416         hi = key->key.high;
417         for (cp = authdes_cache, i = 0; i < AUTHDES_CACHESZ; i++, cp++) {
418                 if (cp->key.key.high == hi && 
419                     cp->key.key.low == key->key.low &&
420                     cp->rname != NULL &&
421                     bcmp(cp->rname, name, strlen(name) + 1) == 0) {
422                         if (BEFORE(timestamp, &cp->laststamp)) {
423                                 svcauthdes_stats.ncachereplays++;
424                                 return (-1); /* replay */
425                         }
426                         svcauthdes_stats.ncachehits++;
427                         return (i);     /* refresh */
428                 }
429         }
430         svcauthdes_stats.ncachemisses++;
431         return (cache_victim());        /* new credential */
432 }
433
434
435 #if (defined(sun) || defined(vax) || defined(__FreeBSD__))
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(adc, uid, gid, grouplen, groups)
459         struct authdes_cred *adc;
460         uid_t *uid;
461         gid_t *gid;
462         int *grouplen;
463         register gid_t *groups;
464 {
465         unsigned sid;
466         register int i;
467         uid_t i_uid;    
468         gid_t i_gid;
469         int i_grouplen;
470         struct bsdcred *cred;
471
472         sid = adc->adc_nickname;
473         if (sid >= AUTHDES_CACHESZ) {
474                 debug("invalid nickname");
475                 return (0);
476         }
477         cred = (struct bsdcred *)authdes_cache[sid].localcred;
478         if (cred == NULL) {
479                 cred = (struct bsdcred *)mem_alloc(sizeof(struct bsdcred));
480                 authdes_cache[sid].localcred = (char *)cred;
481                 cred->grouplen = INVALID;
482         }
483         if (cred->grouplen == INVALID) {
484                 /*
485                  * not in cache: lookup
486                  */
487                 if (!netname2user(adc->adc_fullname.name, &i_uid, &i_gid, 
488                         &i_grouplen, groups))
489                 {
490                         debug("unknown netname");
491                         cred->grouplen = UNKNOWN;       /* mark as lookup up, but not found */
492                         return (0);
493                 }
494                 debug("missed ucred cache");
495                 *uid = cred->uid = i_uid;
496                 *gid = cred->gid = i_gid;
497                 *grouplen = cred->grouplen = i_grouplen;
498                 for (i = i_grouplen - 1; i >= 0; i--) {
499                         cred->groups[i] = groups[i]; /* int to short */
500                 }
501                 return (1);
502         } else if (cred->grouplen == UNKNOWN) {
503                 /*
504                  * Already lookup up, but no match found
505                  */     
506                 return (0);
507         }
508
509         /*
510          * cached credentials
511          */
512         *uid = cred->uid;
513         *gid = cred->gid;
514         *grouplen = cred->grouplen;
515         for (i = cred->grouplen - 1; i >= 0; i--) {
516                 groups[i] = cred->groups[i];    /* short to int */
517         }
518         return (1);
519 }
520
521 static void
522 invalidate(cred)
523         char *cred;
524 {
525         if (cred == NULL) {
526                 return;
527         }
528         ((struct bsdcred *)cred)->grouplen = INVALID;
529 }
530 #endif
531