Fix typo.
[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       cred->adc_fullname.window = window;
332       cred->adc_nickname = (u_long) sid;        /* save nickname */
333       if (entry->rname != NULL)
334         {
335           mem_free (entry->rname, strlen (entry->rname) + 1);
336         }
337       entry->rname = mem_alloc ((u_int) strlen (cred->adc_fullname.name)
338                                 + 1);
339       if (entry->rname != NULL)
340         {
341           strcpy (entry->rname, cred->adc_fullname.name);
342         }
343       else
344         {
345           debug ("out of memory");
346         }
347       entry->key = *sessionkey;
348       entry->window = window;
349       invalidate (entry->localcred);    /* mark any cached cred invalid */
350     }
351   else
352     {                           /* ADN_NICKNAME */
353       /*
354        * nicknames are cooked into fullnames
355        */
356       cred->adc_namekind = ADN_FULLNAME;
357       cred->adc_fullname.name = entry->rname;
358       cred->adc_fullname.key = entry->key;
359       cred->adc_fullname.window = entry->window;
360     }
361   return AUTH_OK;               /* we made it! */
362 }
363
364
365 /*
366  * Initialize the cache
367  */
368 static void
369 internal_function
370 cache_init (void)
371 {
372   register int i;
373
374   authdes_cache = (struct cache_entry *)
375     mem_alloc (sizeof (struct cache_entry) * AUTHDES_CACHESZ);
376   bzero ((char *) authdes_cache,
377          sizeof (struct cache_entry) * AUTHDES_CACHESZ);
378
379   authdes_lru = (short *) mem_alloc (sizeof (short) * AUTHDES_CACHESZ);
380   /*
381    * Initialize the lru list
382    */
383   for (i = 0; i < AUTHDES_CACHESZ; i++)
384     {
385       authdes_lru[i] = i;
386     }
387 }
388
389
390 /*
391  * Find the lru victim
392  */
393 static short
394 cache_victim (void)
395 {
396   return (authdes_lru[AUTHDES_CACHESZ - 1]);
397 }
398
399 /*
400  * Note that sid was referenced
401  */
402 static void
403 internal_function
404 cache_ref (register short sid)
405 {
406   register int i;
407   register short curr;
408   register short prev;
409
410   prev = authdes_lru[0];
411   authdes_lru[0] = sid;
412   for (i = 1; prev != sid; i++)
413     {
414       curr = authdes_lru[i];
415       authdes_lru[i] = prev;
416       prev = curr;
417     }
418 }
419
420
421 /*
422  * Find a spot in the cache for a credential containing
423  * the items given.  Return -1 if a replay is detected, otherwise
424  * return the spot in the cache.
425  */
426 static short
427 internal_function
428 cache_spot (register des_block * key, char *name, struct timeval *timestamp)
429 {
430   register struct cache_entry *cp;
431   register int i;
432   register u_long hi;
433
434   hi = key->key.high;
435   for (cp = authdes_cache, i = 0; i < AUTHDES_CACHESZ; i++, cp++)
436     {
437       if (cp->key.key.high == hi &&
438           cp->key.key.low == key->key.low &&
439           cp->rname != NULL &&
440           bcmp (cp->rname, name, strlen (name) + 1) == 0)
441         {
442           if (BEFORE (timestamp, &cp->laststamp))
443             {
444               svcauthdes_stats.ncachereplays++;
445               return -1;        /* replay */
446             }
447           svcauthdes_stats.ncachehits++;
448           return i;             /* refresh */
449         }
450     }
451   svcauthdes_stats.ncachemisses++;
452   return cache_victim ();       /* new credential */
453 }
454
455
456 /*
457  * Local credential handling stuff.
458  * NOTE: bsd unix dependent.
459  * Other operating systems should put something else here.
460  */
461 #define UNKNOWN         -2      /* grouplen, if cached cred is unknown user */
462 #define INVALID         -1      /* grouplen, if cache entry is invalid */
463
464 struct bsdcred
465 {
466   uid_t uid;                    /* cached uid */
467   gid_t gid;                    /* cached gid */
468   short grouplen;               /* length of cached groups */
469   gid_t groups[NGROUPS];        /* cached groups */
470 };
471
472 /*
473  * Map a des credential into a unix cred.
474  * We cache the credential here so the application does
475  * not have to make an rpc call every time to interpret
476  * the credential.
477  */
478 int
479 authdes_getucred (const struct authdes_cred *adc, uid_t * uid, gid_t * gid,
480                   short *grouplen, gid_t * groups)
481 {
482   unsigned sid;
483   register int i;
484   uid_t i_uid;
485   gid_t i_gid;
486   int i_grouplen;
487   struct bsdcred *cred;
488
489   sid = adc->adc_nickname;
490   if (sid >= AUTHDES_CACHESZ)
491     {
492       debug ("invalid nickname");
493       return 0;
494     }
495   cred = (struct bsdcred *) authdes_cache[sid].localcred;
496   if (cred == NULL)
497     {
498       cred = (struct bsdcred *) mem_alloc (sizeof (struct bsdcred));
499       authdes_cache[sid].localcred = (char *) cred;
500       cred->grouplen = INVALID;
501     }
502   if (cred->grouplen == INVALID)
503     {
504       /*
505        * not in cache: lookup
506        */
507       if (!netname2user (adc->adc_fullname.name, &i_uid, &i_gid,
508                          &i_grouplen, groups))
509         {
510           debug ("unknown netname");
511           cred->grouplen = UNKNOWN;     /* mark as lookup up, but not found */
512           return 0;
513         }
514       debug ("missed ucred cache");
515       *uid = cred->uid = i_uid;
516       *gid = cred->gid = i_gid;
517       *grouplen = cred->grouplen = i_grouplen;
518       for (i = i_grouplen - 1; i >= 0; i--)
519         {
520           cred->groups[i] = groups[i];  /* int to short */
521         }
522       return 1;
523     }
524   else if (cred->grouplen == UNKNOWN)
525     {
526       /*
527        * Already lookup up, but no match found
528        */
529       return 0;
530     }
531
532   /*
533    * cached credentials
534    */
535   *uid = cred->uid;
536   *gid = cred->gid;
537   *grouplen = cred->grouplen;
538   for (i = cred->grouplen - 1; i >= 0; i--)
539     {
540       groups[i] = cred->groups[i];      /* short to int */
541     }
542   return 1;
543 }
544
545 static void
546 internal_function
547 invalidate (char *cred)
548 {
549   if (cred == NULL)
550     {
551       return;
552     }
553   ((struct bsdcred *) cred)->grouplen = INVALID;
554 }