source: rtems/cpukit/librpc/src/rpc/svc_auth_des.c @ df49c60

4.104.114.84.9
Last change on this file since df49c60 was df49c60, checked in by Joel Sherrill <joel.sherrill@…>, on Jun 12, 2000 at 3:00:15 PM

Merged from 4.5.0-beta3a

  • Property mode set to 100644
File size: 13.5 KB
Line 
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"; */
65static 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
77struct 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};
84static struct cache_entry *authdes_cache/* [AUTHDES_CACHESZ] */;
85static short *authdes_lru/* [AUTHDES_CACHESZ] */;
86
87static void cache_init();       /* initialize the cache */
88static short cache_spot();      /* find an entry in the cache */
89static void cache_ref(/*short sid*/);   /* note that sid was ref'd */
90
91static void invalidate();       /* invalidate entry in cache */
92
93/*
94 * cache statistics
95 */
96static 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 */
105enum 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 */
351static void
352cache_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 */
374static short
375cache_victim()
376{
377        return (authdes_lru[AUTHDES_CACHESZ-1]);
378}
379
380/*
381 * Note that sid was referenced
382 */
383static void
384cache_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 */
406static short
407cache_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
444struct 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 */
457int
458authdes_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
521static void
522invalidate(cred)
523        char *cred;
524{
525        if (cred == NULL) {
526                return;
527        }
528        ((struct bsdcred *)cred)->grouplen = INVALID;
529}
530#endif
531
Note: See TracBrowser for help on using the repository browser.