b1d699fe57e1916756756bdf50d105fb96e99e04
[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.3 1999/08/28 00:00:48 peter Exp $
7  * $DragonFly: src/lib/libc/rpc/svc_auth_des.c,v 1.5 2005/01/31 22:29:38 dillon Exp $
8  */
9
10 /*
11  * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
12  * unrestricted use provided that this legend is included on all tape
13  * media and as a part of the software program in whole or part.  Users
14  * may copy or modify Sun RPC without charge, but are not authorized
15  * to license or distribute it to anyone else except as part of a product or
16  * program developed by the user.
17  * 
18  * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
19  * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
21  * 
22  * Sun RPC is provided with no support and without any obligation on the
23  * part of Sun Microsystems, Inc. to assist in its use, correction,
24  * modification or enhancement.
25  *
26  * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
27  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
28  * OR ANY PART THEREOF.
29  *
30  * In no event will Sun Microsystems, Inc. be liable for any lost revenue
31  * or profits or other special, indirect and consequential damages, even if
32  * Sun has been advised of the possibility of such damages.
33  *
34  * Sun Microsystems, Inc.
35  * 2550 Garcia Avenue
36  * Mountain View, California  94043
37  */
38
39 /*
40  * svcauth_des.c, server-side des authentication
41  *
42  * We insure for the service the following:
43  * (1) The timestamp microseconds do not exceed 1 million.
44  * (2) The timestamp plus the window is less than the current time.
45  * (3) The timestamp is not less than the one previously
46  *     seen in the current session.
47  *
48  * It is up to the server to determine if the window size is
49  * too small .
50  *
51  */
52
53 #include <string.h>
54 #include <stdlib.h>
55 #include <stdio.h>
56 #include <unistd.h>
57 #include <rpc/des_crypt.h>
58 #include <sys/param.h>
59 #include <netinet/in.h>
60 #include <rpc/types.h>
61 #include <rpc/xdr.h>
62 #include <rpc/auth.h>
63 #include <rpc/auth_des.h>
64 #include <rpc/svc.h>
65 #include <rpc/rpc_msg.h>
66 #include <rpc/svc_auth.h>
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         struct svc_req *rqst;
108         struct rpc_msg *msg;
109 {
110
111         long *ixdr;
112         des_block cryptbuf[2];
113         struct authdes_cred *cred;
114         struct authdes_verf verf;
115         int status;
116         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         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         short sid;
386 {
387         int i;
388         short curr;
389         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         des_block *key;
409         char *name;
410         struct timeval *timestamp;
411 {
412         struct cache_entry *cp;
413         int i;
414         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) || \
436     defined(__FreeBSD__) || defined(__DragonFly__)
437 /*
438  * Local credential handling stuff.
439  * NOTE: bsd unix dependent.
440  * Other operating systems should put something else here.
441  */
442 #define UNKNOWN         -2      /* grouplen, if cached cred is unknown user */
443 #define INVALID         -1      /* grouplen, if cache entry is invalid */
444
445 struct bsdcred {
446         short uid;              /* cached uid */
447         short gid;              /* cached gid */
448         short grouplen; /* length of cached groups */
449         short groups[NGROUPS];  /* cached groups */
450 };
451
452 /*
453  * Map a des credential into a unix cred.
454  * We cache the credential here so the application does
455  * not have to make an rpc call every time to interpret
456  * the credential.
457  */
458 int
459 authdes_getucred(adc, uid, gid, grouplen, groups)
460         struct authdes_cred *adc;
461         uid_t *uid;
462         gid_t *gid;
463         int *grouplen;
464         gid_t *groups;
465 {
466         unsigned sid;
467         int i;
468         uid_t i_uid;    
469         gid_t i_gid;
470         int i_grouplen;
471         struct bsdcred *cred;
472
473         sid = adc->adc_nickname;
474         if (sid >= AUTHDES_CACHESZ) {
475                 debug("invalid nickname");
476                 return (0);
477         }
478         cred = (struct bsdcred *)authdes_cache[sid].localcred;
479         if (cred == NULL) {
480                 cred = (struct bsdcred *)mem_alloc(sizeof(struct bsdcred));
481                 authdes_cache[sid].localcred = (char *)cred;
482                 cred->grouplen = INVALID;
483         }
484         if (cred->grouplen == INVALID) {
485                 /*
486                  * not in cache: lookup
487                  */
488                 if (!netname2user(adc->adc_fullname.name, &i_uid, &i_gid, 
489                         &i_grouplen, groups))
490                 {
491                         debug("unknown netname");
492                         cred->grouplen = UNKNOWN;       /* mark as lookup up, but not found */
493                         return (0);
494                 }
495                 debug("missed ucred cache");
496                 *uid = cred->uid = i_uid;
497                 *gid = cred->gid = i_gid;
498                 *grouplen = cred->grouplen = i_grouplen;
499                 for (i = i_grouplen - 1; i >= 0; i--) {
500                         cred->groups[i] = groups[i]; /* int to short */
501                 }
502                 return (1);
503         } else if (cred->grouplen == UNKNOWN) {
504                 /*
505                  * Already lookup up, but no match found
506                  */     
507                 return (0);
508         }
509
510         /*
511          * cached credentials
512          */
513         *uid = cred->uid;
514         *gid = cred->gid;
515         *grouplen = cred->grouplen;
516         for (i = cred->grouplen - 1; i >= 0; i--) {
517                 groups[i] = cred->groups[i];    /* short to int */
518         }
519         return (1);
520 }
521
522 static void
523 invalidate(cred)
524         char *cred;
525 {
526         if (cred == NULL) {
527                 return;
528         }
529         ((struct bsdcred *)cred)->grouplen = INVALID;
530 }
531 #endif
532