Imported Upstream version 0.2.5
[platform/upstream/libtirpc.git] / src / svc_auth_des.c
1
2 /*
3  * Copyright (c) 1988 by Sun Microsystems, Inc.
4  */
5
6 /*
7  * Copyright (c) 2009, Sun Microsystems, Inc.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions are met:
12  * - Redistributions of source code must retain the above copyright notice,
13  *   this list of conditions and the following disclaimer.
14  * - Redistributions in binary form must reproduce the above copyright notice,
15  *   this list of conditions and the following disclaimer in the documentation
16  *   and/or other materials provided with the distribution.
17  * - Neither the name of Sun Microsystems, Inc. nor the names of its
18  *   contributors may be used to endorse or promote products derived
19  *   from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 /*
35  * svcauth_des.c, server-side des authentication
36  *
37  * We insure for the service the following:
38  * (1) The timestamp microseconds do not exceed 1 million.
39  * (2) The timestamp plus the window is less than the current time.
40  * (3) The timestamp is not less than the one previously
41  *     seen in the current session.
42  *
43  * It is up to the server to determine if the window size is
44  * too small .
45  *
46  */
47 #include <pthread.h>
48 #include <reentrant.h>
49 #include <string.h>
50 #include <stdlib.h>
51 #include <stdio.h>
52 #include <unistd.h>
53 #include <rpc/des_crypt.h>
54 #include <sys/param.h>
55 #include <netinet/in.h>
56 #include <rpc/types.h>
57 #include <rpc/xdr.h>
58 #include <rpc/auth.h>
59 #include <rpc/auth_des.h>
60 #include <rpc/svc.h>
61 #include <rpc/rpc_msg.h>
62 #include <rpc/svc_auth.h>
63 #if defined(__FreeBSD__) || defined(__NetBSD__)
64 #include <libc_private.h>
65 #endif
66
67 #include "debug.h"
68
69 extern int key_decryptsession_pk(const char *, netobj *, des_block *);
70
71 #define USEC_PER_SEC ((u_long) 1000000L)
72 #define BEFORE(t1, t2) timercmp(t1, t2, <)
73
74 /*
75  * LRU cache of conversation keys and some other useful items.
76  */
77 #define AUTHDES_CACHESZ 64
78 struct cache_entry {
79         des_block key;          /* conversation key */
80         char *rname;            /* client's name */
81         u_int window;           /* credential lifetime window */
82         struct timeval laststamp;       /* detect replays of creds */
83         char *localcred;        /* generic local credential */
84 };
85 static struct cache_entry *authdes_cache/* [AUTHDES_CACHESZ] */;
86 static short *authdes_lru/* [AUTHDES_CACHESZ] */;
87
88 static void cache_init();       /* initialize the cache */
89 static short cache_spot();      /* find an entry in the cache */
90 static void cache_ref(/*short sid*/);   /* note that sid was ref'd */
91
92 static void invalidate();       /* invalidate entry in cache */
93
94 /*
95  * cache statistics 
96  */
97 static struct {
98         u_long ncachehits;      /* times cache hit, and is not replay */
99         u_long ncachereplays;   /* times cache hit, and is replay */
100         u_long ncachemisses;    /* times cache missed */
101 } svcauthdes_stats;
102
103 /*
104  * Service side authenticator for AUTH_DES
105  */
106 enum auth_stat
107 _svcauth_des(rqst, msg)
108         struct svc_req *rqst;
109         struct rpc_msg *msg;
110 {
111
112         long *ixdr;
113         des_block cryptbuf[2];
114         struct authdes_cred *cred;
115         struct authdes_verf verf;
116         int status;
117         struct cache_entry *entry;
118         short sid = 0;
119         des_block *sessionkey;
120         des_block ivec;
121         u_int window;
122         struct timeval timestamp;
123         u_long namelen;
124         struct area {
125                 struct authdes_cred area_cred;
126                 char area_netname[MAXNETNAMELEN+1];
127         } *area;
128
129         if (authdes_cache == NULL) {
130                 cache_init();
131         }
132
133         area = (struct area *)rqst->rq_clntcred;
134         cred = (struct authdes_cred *)&area->area_cred;
135
136         /*
137          * Get the credential
138          */
139         ixdr = (long *)msg->rm_call.cb_cred.oa_base;
140         cred->adc_namekind = IXDR_GET_ENUM(ixdr, enum authdes_namekind);
141         switch (cred->adc_namekind) {
142         case ADN_FULLNAME:
143                 namelen = IXDR_GET_U_LONG(ixdr);
144                 if (namelen > MAXNETNAMELEN) {
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                 netobj pkey;
177                 char pkey_data[1024];
178
179                 sessionkey = &cred->adc_fullname.key;
180                 if (! getpublickey(cred->adc_fullname.name, pkey_data)) {
181                         LIBTIRPC_DEBUG(1, ("_svcauth_des: getpublickey failed"));
182                         return(AUTH_BADCRED);
183                 }
184                 pkey.n_bytes = pkey_data;
185                 pkey.n_len = strlen(pkey_data) + 1;
186                 if (key_decryptsession_pk(cred->adc_fullname.name, &pkey,
187                                        sessionkey) < 0) {
188                         LIBTIRPC_DEBUG(1, ("_svcauth_des: key_decryptsessionkey failed"));
189                         return (AUTH_BADCRED); /* key not found */
190                 }
191         } else { /* ADN_NICKNAME */     
192                 sid = (short)cred->adc_nickname;
193                 if (sid < 0 || sid >= AUTHDES_CACHESZ) {
194                         LIBTIRPC_DEBUG(1, ("_svcauth_des: bad nickname"));
195                         return (AUTH_BADCRED);  /* garbled credential */
196                 }
197                 sessionkey = &authdes_cache[sid].key;
198         }
199
200
201         /*
202          * Decrypt the timestamp
203          */
204         cryptbuf[0] = verf.adv_xtimestamp; 
205         if (cred->adc_namekind == ADN_FULLNAME) {
206                 cryptbuf[1].key.high = cred->adc_fullname.window;
207                 cryptbuf[1].key.low = verf.adv_winverf;
208                 ivec.key.high = ivec.key.low = 0;       
209                 status = cbc_crypt((char *)sessionkey, (char *)cryptbuf,
210                         2*sizeof(des_block), DES_DECRYPT | DES_HW, 
211                         (char *)&ivec);
212         } else {
213                 status = ecb_crypt((char *)sessionkey, (char *)cryptbuf,
214                         sizeof(des_block), DES_DECRYPT | DES_HW);
215         }
216         if (DES_FAILED(status)) {
217                 LIBTIRPC_DEBUG(1, ("_svcauth_des: 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                 int winverf;
238
239                 if (cred->adc_namekind == ADN_FULLNAME) {
240                         window = IXDR_GET_U_LONG(ixdr);
241                         winverf = IXDR_GET_U_LONG(ixdr);
242                         if (winverf != window - 1) {
243                                 LIBTIRPC_DEBUG(1, ("_svcauth_des: window verifier mismatch"));
244                                 return (AUTH_BADCRED);  /* garbled credential */
245                         }
246                         sid = cache_spot(sessionkey, cred->adc_fullname.name, 
247                             &timestamp);
248                         if (sid < 0) {
249                                 LIBTIRPC_DEBUG(1, ("_svcauth_des: replayed credential"));
250                                 return (AUTH_REJECTEDCRED);     /* replay */
251                         }
252                         nick = 0;
253                 } else {        /* ADN_NICKNAME */
254                         window = authdes_cache[sid].window;
255                         nick = 1;
256                 }
257
258                 if ((u_long)timestamp.tv_usec >= USEC_PER_SEC) {
259                         LIBTIRPC_DEBUG(1, ("_svcauth_des: invalid usecs"));
260                         /* cached out (bad key), or garbled verifier */
261                         return (nick ? AUTH_REJECTEDVERF : AUTH_BADVERF);
262                 }
263                 if (nick && BEFORE(&timestamp, 
264                                    &authdes_cache[sid].laststamp)) {
265                         LIBTIRPC_DEBUG(1, ("_svcauth_des: timestamp before last seen"));
266                         return (AUTH_REJECTEDVERF);     /* replay */
267                 }
268                 (void) gettimeofday(&current, (struct timezone *)NULL);
269                 current.tv_sec -= window;       /* allow for expiration */
270                 if (!BEFORE(&current, &timestamp)) {
271                         LIBTIRPC_DEBUG(1, ("_svcauth_des: timestamp expired"));
272                         /* replay, or garbled credential */
273                         return (nick ? AUTH_REJECTEDVERF : AUTH_BADCRED);
274                 }
275         }
276
277         /*
278          * Set up the reply verifier
279          */
280         verf.adv_nickname = (u_long)sid;
281
282         /*
283          * xdr the timestamp before encrypting
284          */
285         ixdr = (long *)cryptbuf;
286         IXDR_PUT_LONG(ixdr, timestamp.tv_sec - 1);
287         IXDR_PUT_LONG(ixdr, timestamp.tv_usec);
288
289         /*       
290          * encrypt the timestamp
291          */
292         status = ecb_crypt((char *)sessionkey, (char *)cryptbuf,
293             sizeof(des_block), DES_ENCRYPT | DES_HW);
294         if (DES_FAILED(status)) {
295                 LIBTIRPC_DEBUG(1, ("_svcauth_des: encryption failure"));
296                 return (AUTH_FAILED);   /* system error */
297         }
298         verf.adv_xtimestamp = cryptbuf[0];
299
300         /*
301          * Serialize the reply verifier, and update rqst
302          */
303         ixdr = (long *)msg->rm_call.cb_verf.oa_base;
304         *ixdr++ = (long)verf.adv_xtimestamp.key.high;
305         *ixdr++ = (long)verf.adv_xtimestamp.key.low;
306         *ixdr++ = (long)verf.adv_int_u;
307
308         rqst->rq_xprt->xp_verf.oa_flavor = AUTH_DES;
309         rqst->rq_xprt->xp_verf.oa_base = msg->rm_call.cb_verf.oa_base;
310         rqst->rq_xprt->xp_verf.oa_length = 
311                 (char *)ixdr - msg->rm_call.cb_verf.oa_base;
312
313         /*
314          * We succeeded, commit the data to the cache now and
315          * finish cooking the credential.
316          */
317         entry = &authdes_cache[sid];
318         entry->laststamp = timestamp;
319         cache_ref(sid);
320         if (cred->adc_namekind == ADN_FULLNAME) {
321                 cred->adc_fullname.window = window;
322                 cred->adc_nickname = (u_long)sid;       /* save nickname */
323                 if (entry->rname != NULL) {
324                         mem_free(entry->rname, strlen(entry->rname) + 1);
325                 }
326                 entry->rname = (char *)mem_alloc((u_int)strlen(cred->adc_fullname.name)
327                                          + 1);
328                 if (entry->rname != NULL) {
329                         (void) strcpy(entry->rname, cred->adc_fullname.name);
330                 } else {
331                         LIBTIRPC_DEBUG(1, ("_svcauth_des: out of memory"));
332                 }
333                 entry->key = *sessionkey;
334                 entry->window = window;
335                 invalidate(entry->localcred); /* mark any cached cred invalid */
336         } else { /* ADN_NICKNAME */
337                 /*
338                  * nicknames are cooked into fullnames
339                  */     
340                 cred->adc_namekind = ADN_FULLNAME;
341                 cred->adc_fullname.name = entry->rname;
342                 cred->adc_fullname.key = entry->key;
343                 cred->adc_fullname.window = entry->window;
344         }
345         return (AUTH_OK);       /* we made it!*/
346 }
347
348
349 /*
350  * Initialize the cache
351  */
352 static void
353 cache_init()
354 {
355         int i;
356
357         authdes_cache = (struct cache_entry *)
358                 mem_alloc(sizeof(struct cache_entry) * AUTHDES_CACHESZ);        
359         bzero((char *)authdes_cache, 
360                 sizeof(struct cache_entry) * AUTHDES_CACHESZ);
361
362         authdes_lru = (short *)mem_alloc(sizeof(short) * AUTHDES_CACHESZ);
363         /*
364          * Initialize the lru list
365          */
366         for (i = 0; i < AUTHDES_CACHESZ; i++) {
367                 authdes_lru[i] = i;
368         }
369 }
370
371
372 /*
373  * Find the lru victim
374  */
375 static short
376 cache_victim()
377 {
378         return (authdes_lru[AUTHDES_CACHESZ-1]);
379 }
380
381 /*
382  * Note that sid was referenced
383  */
384 static void
385 cache_ref(sid)
386         short sid;
387 {
388         int i;
389         short curr;
390         short prev;
391
392         prev = authdes_lru[0];
393         authdes_lru[0] = sid;
394         for (i = 1; prev != sid; i++) {
395                 curr = authdes_lru[i];
396                 authdes_lru[i] = prev;
397                 prev = curr;
398         }
399 }
400
401
402 /*
403  * Find a spot in the cache for a credential containing
404  * the items given.  Return -1 if a replay is detected, otherwise
405  * return the spot in the cache.
406  */
407 static short
408 cache_spot(key, name, timestamp)
409         des_block *key;
410         char *name;
411         struct timeval *timestamp;
412 {
413         struct cache_entry *cp;
414         int i;
415         u_long hi;
416
417         hi = key->key.high;
418         for (cp = authdes_cache, i = 0; i < AUTHDES_CACHESZ; i++, cp++) {
419                 if (cp->key.key.high == hi && 
420                     cp->key.key.low == key->key.low &&
421                     cp->rname != NULL &&
422                     bcmp(cp->rname, name, strlen(name) + 1) == 0) {
423                         if (BEFORE(timestamp, &cp->laststamp)) {
424                                 svcauthdes_stats.ncachereplays++;
425                                 return (-1); /* replay */
426                         }
427                         svcauthdes_stats.ncachehits++;
428                         return (i);     /* refresh */
429                 }
430         }
431         svcauthdes_stats.ncachemisses++;
432         return (cache_victim());        /* new credential */
433 }
434
435
436 #if (defined(sun) || defined(vax) || defined(__FreeBSD__))
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                 LIBTIRPC_DEBUG(1, ("authdes_getucred: 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                         LIBTIRPC_DEBUG(1, ("authdes_getucred: unknown netname"));
492                         cred->grouplen = UNKNOWN;       /* mark as lookup up, but not found */
493                         return (0);
494                 }
495                 LIBTIRPC_DEBUG(1, ("authdes_getucred: 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