source: rtems-libbsd/freebsd/lib/libc/rpc/svc_auth_des.c @ f41a394

55-freebsd-126-freebsd-12freebsd-9.3
Last change on this file since f41a394 was 60b1d40, checked in by Sebastian Huber <sebastian.huber@…>, on 06/09/16 at 08:23:57

RPC(3): Import from FreeBSD

  • Property mode set to 100644
File size: 13.9 KB
Line 
1#include <machine/rtems-bsd-user-space.h>
2
3
4/*
5 * Copyright (c) 1988 by Sun Microsystems, Inc.
6 */
7
8/*-
9 * Copyright (c) 2009, Sun Microsystems, Inc.
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions are met:
14 * - Redistributions of source code must retain the above copyright notice,
15 *   this list of conditions and the following disclaimer.
16 * - Redistributions in binary form must reproduce the above copyright notice,
17 *   this list of conditions and the following disclaimer in the documentation
18 *   and/or other materials provided with the distribution.
19 * - Neither the name of Sun Microsystems, Inc. nor the names of its
20 *   contributors may be used to endorse or promote products derived
21 *   from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 */
35
36/*
37 * svcauth_des.c, server-side des authentication
38 *
39 * We insure for the service the following:
40 * (1) The timestamp microseconds do not exceed 1 million.
41 * (2) The timestamp plus the window is less than the current time.
42 * (3) The timestamp is not less than the one previously
43 *     seen in the current session.
44 *
45 * It is up to the server to determine if the window size is
46 * too small .
47 *
48 */
49
50#include "namespace.h"
51#include "reentrant.h"
52#include <string.h>
53#include <stdlib.h>
54#include <stdio.h>
55#include <unistd.h>
56#include <rpc/des_crypt.h>
57#include <rtems/bsd/sys/param.h>
58#include <netinet/in.h>
59#include <rpc/types.h>
60#include <rpc/xdr.h>
61#include <rpc/auth.h>
62#include <rpc/auth_des.h>
63#include <rpc/svc.h>
64#include <rpc/rpc_msg.h>
65#include <rpc/svc_auth.h>
66#include "libc_private.h"
67
68#if defined(LIBC_SCCS) && !defined(lint)
69static char sccsid[] =  "@(#)svcauth_des.c      2.3 89/07/11 4.0 RPCSRC; from 1.15 88/02/08 SMI";
70#endif
71#include <sys/cdefs.h>
72__FBSDID("$FreeBSD$");
73
74extern int key_decryptsession_pk(const char *, netobj *, des_block *);
75
76#define debug(msg)       printf("svcauth_des: %s\n", msg)
77
78#define USEC_PER_SEC ((u_long) 1000000L)
79#define BEFORE(t1, t2) timercmp(t1, t2, <)
80
81/*
82 * LRU cache of conversation keys and some other useful items.
83 */
84#define AUTHDES_CACHESZ 64
85struct cache_entry {
86        des_block key;          /* conversation key */
87        char *rname;            /* client's name */
88        u_int window;           /* credential lifetime window */
89        struct timeval laststamp;       /* detect replays of creds */
90        char *localcred;        /* generic local credential */
91};
92static struct cache_entry *authdes_cache/* [AUTHDES_CACHESZ] */;
93static short *authdes_lru/* [AUTHDES_CACHESZ] */;
94
95static void cache_init();       /* initialize the cache */
96static short cache_spot();      /* find an entry in the cache */
97static void cache_ref(/*short sid*/);   /* note that sid was ref'd */
98
99static void invalidate();       /* invalidate entry in cache */
100
101/*
102 * cache statistics
103 */
104static struct {
105        u_long ncachehits;      /* times cache hit, and is not replay */
106        u_long ncachereplays;   /* times cache hit, and is replay */
107        u_long ncachemisses;    /* times cache missed */
108} svcauthdes_stats;
109
110/*
111 * Service side authenticator for AUTH_DES
112 */
113enum auth_stat
114_svcauth_des(rqst, msg)
115        struct svc_req *rqst;
116        struct rpc_msg *msg;
117{
118
119        long *ixdr;
120        des_block cryptbuf[2];
121        struct authdes_cred *cred;
122        struct authdes_verf verf;
123        int status;
124        struct cache_entry *entry;
125        short sid = 0;
126        des_block *sessionkey;
127        des_block ivec;
128        u_int window;
129        struct timeval timestamp;
130        u_long namelen;
131        struct area {
132                struct authdes_cred area_cred;
133                char area_netname[MAXNETNAMELEN+1];
134        } *area;
135
136        if (authdes_cache == NULL) {
137                cache_init();
138        }
139
140        area = (struct area *)rqst->rq_clntcred;
141        cred = (struct authdes_cred *)&area->area_cred;
142
143        /*
144         * Get the credential
145         */
146        ixdr = (long *)msg->rm_call.cb_cred.oa_base;
147        cred->adc_namekind = IXDR_GET_ENUM(ixdr, enum authdes_namekind);
148        switch (cred->adc_namekind) {
149        case ADN_FULLNAME:
150                namelen = IXDR_GET_U_LONG(ixdr);
151                if (namelen > MAXNETNAMELEN) {
152                        return (AUTH_BADCRED);
153                }
154                cred->adc_fullname.name = area->area_netname;
155                bcopy((char *)ixdr, cred->adc_fullname.name,
156                        (u_int)namelen);
157                cred->adc_fullname.name[namelen] = 0;
158                ixdr += (RNDUP(namelen) / BYTES_PER_XDR_UNIT);
159                cred->adc_fullname.key.key.high = (u_long)*ixdr++;
160                cred->adc_fullname.key.key.low = (u_long)*ixdr++;
161                cred->adc_fullname.window = (u_long)*ixdr++;
162                break;
163        case ADN_NICKNAME:
164                cred->adc_nickname = (u_long)*ixdr++;
165                break;
166        default:
167                return (AUTH_BADCRED); 
168        }
169
170        /*
171         * Get the verifier
172         */
173        ixdr = (long *)msg->rm_call.cb_verf.oa_base;
174        verf.adv_xtimestamp.key.high = (u_long)*ixdr++;
175        verf.adv_xtimestamp.key.low =  (u_long)*ixdr++;
176        verf.adv_int_u = (u_long)*ixdr++;
177
178
179        /*
180         * Get the conversation key
181         */
182        if (cred->adc_namekind == ADN_FULLNAME) {
183                netobj pkey;
184                char pkey_data[1024];
185
186                sessionkey = &cred->adc_fullname.key;
187                if (! getpublickey(cred->adc_fullname.name, pkey_data)) {
188                        debug("getpublickey");
189                        return(AUTH_BADCRED);
190                }
191                pkey.n_bytes = pkey_data;
192                pkey.n_len = strlen(pkey_data) + 1;
193                if (key_decryptsession_pk(cred->adc_fullname.name, &pkey,
194                                       sessionkey) < 0) {
195                        debug("decryptsessionkey");
196                        return (AUTH_BADCRED); /* key not found */
197                }
198        } else { /* ADN_NICKNAME */     
199                sid = (short)cred->adc_nickname;
200                if (sid < 0 || sid >= AUTHDES_CACHESZ) {
201                        debug("bad nickname");
202                        return (AUTH_BADCRED);  /* garbled credential */
203                }
204                sessionkey = &authdes_cache[sid].key;
205        }
206
207
208        /*
209         * Decrypt the timestamp
210         */
211        cryptbuf[0] = verf.adv_xtimestamp;
212        if (cred->adc_namekind == ADN_FULLNAME) {
213                cryptbuf[1].key.high = cred->adc_fullname.window;
214                cryptbuf[1].key.low = verf.adv_winverf;
215                ivec.key.high = ivec.key.low = 0;       
216                status = cbc_crypt((char *)sessionkey, (char *)cryptbuf,
217                        2*sizeof(des_block), DES_DECRYPT | DES_HW,
218                        (char *)&ivec);
219        } else {
220                status = ecb_crypt((char *)sessionkey, (char *)cryptbuf,
221                        sizeof(des_block), DES_DECRYPT | DES_HW);
222        }
223        if (DES_FAILED(status)) {
224                debug("decryption failure");
225                return (AUTH_FAILED);   /* system error */
226        }
227
228        /*
229         * XDR the decrypted timestamp
230         */
231        ixdr = (long *)cryptbuf;
232        timestamp.tv_sec = IXDR_GET_LONG(ixdr);
233        timestamp.tv_usec = IXDR_GET_LONG(ixdr);
234
235        /*
236         * Check for valid credentials and verifiers.
237         * They could be invalid because the key was flushed
238         * out of the cache, and so a new session should begin.
239         * Be sure and send AUTH_REJECTED{CRED, VERF} if this is the case.
240         */
241        {
242                struct timeval current;
243                int nick;
244                int winverf;
245
246                if (cred->adc_namekind == ADN_FULLNAME) {
247                        window = IXDR_GET_U_LONG(ixdr);
248                        winverf = IXDR_GET_U_LONG(ixdr);
249                        if (winverf != window - 1) {
250                                debug("window verifier mismatch");
251                                return (AUTH_BADCRED);  /* garbled credential */
252                        }
253                        sid = cache_spot(sessionkey, cred->adc_fullname.name,
254                            &timestamp);
255                        if (sid < 0) {
256                                debug("replayed credential");
257                                return (AUTH_REJECTEDCRED);     /* replay */
258                        }
259                        nick = 0;
260                } else {        /* ADN_NICKNAME */
261                        window = authdes_cache[sid].window;
262                        nick = 1;
263                }
264
265                if ((u_long)timestamp.tv_usec >= USEC_PER_SEC) {
266                        debug("invalid usecs");
267                        /* cached out (bad key), or garbled verifier */
268                        return (nick ? AUTH_REJECTEDVERF : AUTH_BADVERF);
269                }
270                if (nick && BEFORE(&timestamp,
271                                   &authdes_cache[sid].laststamp)) {
272                        debug("timestamp before last seen");
273                        return (AUTH_REJECTEDVERF);     /* replay */
274                }
275                (void) gettimeofday(&current, (struct timezone *)NULL);
276                current.tv_sec -= window;       /* allow for expiration */
277                if (!BEFORE(&current, &timestamp)) {
278                        debug("timestamp expired");
279                        /* replay, or garbled credential */
280                        return (nick ? AUTH_REJECTEDVERF : AUTH_BADCRED);
281                }
282        }
283
284        /*
285         * Set up the reply verifier
286         */
287        verf.adv_nickname = (u_long)sid;
288
289        /*
290         * xdr the timestamp before encrypting
291         */
292        ixdr = (long *)cryptbuf;
293        IXDR_PUT_LONG(ixdr, timestamp.tv_sec - 1);
294        IXDR_PUT_LONG(ixdr, timestamp.tv_usec);
295
296        /*       
297         * encrypt the timestamp
298         */
299        status = ecb_crypt((char *)sessionkey, (char *)cryptbuf,
300            sizeof(des_block), DES_ENCRYPT | DES_HW);
301        if (DES_FAILED(status)) {
302                debug("encryption failure");
303                return (AUTH_FAILED);   /* system error */
304        }
305        verf.adv_xtimestamp = cryptbuf[0];
306
307        /*
308         * Serialize the reply verifier, and update rqst
309         */
310        ixdr = (long *)msg->rm_call.cb_verf.oa_base;
311        *ixdr++ = (long)verf.adv_xtimestamp.key.high;
312        *ixdr++ = (long)verf.adv_xtimestamp.key.low;
313        *ixdr++ = (long)verf.adv_int_u;
314
315        rqst->rq_xprt->xp_verf.oa_flavor = AUTH_DES;
316        rqst->rq_xprt->xp_verf.oa_base = msg->rm_call.cb_verf.oa_base;
317        rqst->rq_xprt->xp_verf.oa_length =
318                (char *)ixdr - msg->rm_call.cb_verf.oa_base;
319
320        /*
321         * We succeeded, commit the data to the cache now and
322         * finish cooking the credential.
323         */
324        entry = &authdes_cache[sid];
325        entry->laststamp = timestamp;
326        cache_ref(sid);
327        if (cred->adc_namekind == ADN_FULLNAME) {
328                cred->adc_fullname.window = window;
329                cred->adc_nickname = (u_long)sid;       /* save nickname */
330                if (entry->rname != NULL) {
331                        mem_free(entry->rname, strlen(entry->rname) + 1);
332                }
333                entry->rname = (char *)mem_alloc((u_int)strlen(cred->adc_fullname.name)
334                                         + 1);
335                if (entry->rname != NULL) {
336                        (void) strcpy(entry->rname, cred->adc_fullname.name);
337                } else {
338                        debug("out of memory");
339                }
340                entry->key = *sessionkey;
341                entry->window = window;
342                invalidate(entry->localcred); /* mark any cached cred invalid */
343        } else { /* ADN_NICKNAME */
344                /*
345                 * nicknames are cooked into fullnames
346                 */     
347                cred->adc_namekind = ADN_FULLNAME;
348                cred->adc_fullname.name = entry->rname;
349                cred->adc_fullname.key = entry->key;
350                cred->adc_fullname.window = entry->window;
351        }
352        return (AUTH_OK);       /* we made it!*/
353}
354
355
356/*
357 * Initialize the cache
358 */
359static void
360cache_init()
361{
362        int i;
363
364        authdes_cache = (struct cache_entry *)
365                mem_alloc(sizeof(struct cache_entry) * AUTHDES_CACHESZ);       
366        bzero((char *)authdes_cache,
367                sizeof(struct cache_entry) * AUTHDES_CACHESZ);
368
369        authdes_lru = (short *)mem_alloc(sizeof(short) * AUTHDES_CACHESZ);
370        /*
371         * Initialize the lru list
372         */
373        for (i = 0; i < AUTHDES_CACHESZ; i++) {
374                authdes_lru[i] = i;
375        }
376}
377
378
379/*
380 * Find the lru victim
381 */
382static short
383cache_victim()
384{
385        return (authdes_lru[AUTHDES_CACHESZ-1]);
386}
387
388/*
389 * Note that sid was referenced
390 */
391static void
392cache_ref(sid)
393        short sid;
394{
395        int i;
396        short curr;
397        short prev;
398
399        prev = authdes_lru[0];
400        authdes_lru[0] = sid;
401        for (i = 1; prev != sid; i++) {
402                curr = authdes_lru[i];
403                authdes_lru[i] = prev;
404                prev = curr;
405        }
406}
407
408
409/*
410 * Find a spot in the cache for a credential containing
411 * the items given.  Return -1 if a replay is detected, otherwise
412 * return the spot in the cache.
413 */
414static short
415cache_spot(key, name, timestamp)
416        des_block *key;
417        char *name;
418        struct timeval *timestamp;
419{
420        struct cache_entry *cp;
421        int i;
422        u_long hi;
423
424        hi = key->key.high;
425        for (cp = authdes_cache, i = 0; i < AUTHDES_CACHESZ; i++, cp++) {
426                if (cp->key.key.high == hi &&
427                    cp->key.key.low == key->key.low &&
428                    cp->rname != NULL &&
429                    bcmp(cp->rname, name, strlen(name) + 1) == 0) {
430                        if (BEFORE(timestamp, &cp->laststamp)) {
431                                svcauthdes_stats.ncachereplays++;
432                                return (-1); /* replay */
433                        }
434                        svcauthdes_stats.ncachehits++;
435                        return (i);     /* refresh */
436                }
437        }
438        svcauthdes_stats.ncachemisses++;
439        return (cache_victim());        /* new credential */
440}
441
442
443#if (defined(sun) || defined(vax) || defined(__FreeBSD__))
444/*
445 * Local credential handling stuff.
446 * NOTE: bsd unix dependent.
447 * Other operating systems should put something else here.
448 */
449#define UNKNOWN         -2      /* grouplen, if cached cred is unknown user */
450#define INVALID         -1      /* grouplen, if cache entry is invalid */
451
452struct bsdcred {
453        uid_t uid;              /* cached uid */
454        gid_t gid;              /* cached gid */
455        int grouplen;   /* length of cached groups */
456        gid_t groups[NGRPS];    /* cached groups */
457};
458
459/*
460 * Map a des credential into a unix cred.
461 * We cache the credential here so the application does
462 * not have to make an rpc call every time to interpret
463 * the credential.
464 */
465int
466authdes_getucred(adc, uid, gid, grouplen, groups)
467        struct authdes_cred *adc;
468        uid_t *uid;
469        gid_t *gid;
470        int *grouplen;
471        gid_t *groups;
472{
473        unsigned sid;
474        int i;
475        uid_t i_uid;   
476        gid_t i_gid;
477        int i_grouplen;
478        struct bsdcred *cred;
479
480        sid = adc->adc_nickname;
481        if (sid >= AUTHDES_CACHESZ) {
482                debug("invalid nickname");
483                return (0);
484        }
485        cred = (struct bsdcred *)authdes_cache[sid].localcred;
486        if (cred == NULL) {
487                cred = (struct bsdcred *)mem_alloc(sizeof(struct bsdcred));
488                authdes_cache[sid].localcred = (char *)cred;
489                cred->grouplen = INVALID;
490        }
491        if (cred->grouplen == INVALID) {
492                /*
493                 * not in cache: lookup
494                 */
495                if (!netname2user(adc->adc_fullname.name, &i_uid, &i_gid,
496                        &i_grouplen, groups))
497                {
498                        debug("unknown netname");
499                        cred->grouplen = UNKNOWN;       /* mark as lookup up, but not found */
500                        return (0);
501                }
502                debug("missed ucred cache");
503                *uid = cred->uid = i_uid;
504                *gid = cred->gid = i_gid;
505                *grouplen = cred->grouplen = i_grouplen;
506                for (i = i_grouplen - 1; i >= 0; i--) {
507                        cred->groups[i] = groups[i]; /* int to short */
508                }
509                return (1);
510        } else if (cred->grouplen == UNKNOWN) {
511                /*
512                 * Already lookup up, but no match found
513                 */     
514                return (0);
515        }
516
517        /*
518         * cached credentials
519         */
520        *uid = cred->uid;
521        *gid = cred->gid;
522        *grouplen = cred->grouplen;
523        for (i = cred->grouplen - 1; i >= 0; i--) {
524                groups[i] = cred->groups[i];    /* short to int */
525        }
526        return (1);
527}
528
529static void
530invalidate(cred)
531        char *cred;
532{
533        if (cred == NULL) {
534                return;
535        }
536        ((struct bsdcred *)cred)->grouplen = INVALID;
537}
538#endif
539
Note: See TracBrowser for help on using the repository browser.