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