4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2017 Joyent Inc
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
31 * Portions of this source code were derived from Berkeley 4.3 BSD
32 * under license from the Regents of the University of California.
36 * svcauth_des.c, server-side des authentication
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.
44 * It is up to the server to determine if the window size is
48 #include <sys/types.h>
50 #include <sys/systm.h>
51 #include <sys/param.h>
52 #include <sys/stream.h>
53 #include <sys/stropts.h>
54 #include <sys/strsubr.h>
55 #include <sys/tiuser.h>
56 #include <sys/tihdr.h>
57 #include <sys/t_kuser.h>
58 #include <sys/t_lock.h>
59 #include <sys/debug.h>
62 #include <sys/cmn_err.h>
63 #include <sys/debug.h>
65 #include <rpc/types.h>
68 #include <rpc/auth_des.h>
69 #include <rpc/rpc_msg.h>
71 #include <rpc/svc_auth.h>
73 #include <rpc/des_crypt.h>
75 #define USEC_PER_SEC 1000000
76 #define BEFORE(t1, t2) timercmp(t1, t2, < /* COMMENT HERE TO DEFEAT CSTYLE */)
79 * Cache of conversation keys and some other useful items.
80 * The hash table size is controled via authdes_cachesz variable.
81 * The authdes_cachesz has to be the power of 2.
83 #define AUTHDES_CACHE_TABLE_SZ 1024
84 static int authdes_cachesz
= AUTHDES_CACHE_TABLE_SZ
;
85 #define HASH(key) ((key) & (authdes_cachesz - 1))
87 /* low water mark for the number of cache entries */
88 static int low_cache_entries
= 128;
90 struct authdes_cache_entry
{
91 uint32_t nickname
; /* nick name id */
92 uint32_t window
; /* credential lifetime window */
93 des_block key
; /* conversation key */
94 time_t ref_time
; /* time referenced previously */
95 char *rname
; /* client's name */
96 caddr_t localcred
; /* generic local credential */
97 struct authdes_cache_entry
*prev
, *next
; /* hash table linked list */
98 struct authdes_cache_entry
*lru_prev
, *lru_next
; /* LRU linked list */
99 kmutex_t lock
; /* cache entry lock */
101 static struct authdes_cache_entry
**authdes_cache
; /* [authdes_cachesz] */
102 static struct authdes_cache_entry
*lru_first
= NULL
;
103 static struct authdes_cache_entry
*lru_last
= NULL
;
104 static kmutex_t authdes_lock
; /* cache table lock */
106 static struct kmem_cache
*authdes_cache_handle
;
107 static uint32_t Nickname
= 0;
109 static struct authdes_cache_entry
*authdes_cache_new(char *,
110 des_block
*, uint32_t);
111 static struct authdes_cache_entry
*authdes_cache_get(uint32_t);
112 static void authdes_cache_reclaim(void *);
113 static void sweep_cache();
116 * After 12 hours, check and delete cache entries that have been
117 * idled for more than 10 hours.
119 static time_t authdes_sweep_interval
= 12*60*60;
120 static time_t authdes_cache_time
= 10*60*60;
121 static time_t authdes_last_swept
= 0;
126 static int authdes_ncache
= 0; /* number of current cached entries */
127 static int authdes_ncachehits
= 0; /* #times cache hit */
128 static int authdes_ncachemisses
= 0; /* #times cache missed */
130 #define NOT_DEAD(ptr) ASSERT((((intptr_t)(ptr)) != 0xdeadbeef))
131 #define IS_ALIGNED(ptr) ASSERT((((intptr_t)(ptr)) & 3) == 0)
134 * Service side authenticator for AUTH_DES
137 _svcauth_des(struct svc_req
*rqst
, struct rpc_msg
*msg
)
140 des_block cryptbuf
[2];
141 struct authdes_cred
*cred
;
142 struct authdes_verf verf
;
144 des_block
*sessionkey
;
146 uint32_t window
, winverf
, namelen
;
148 struct timeval timestamp
, current_time
;
149 struct authdes_cache_entry
*nick_entry
;
151 struct authdes_cred area_cred
;
152 char area_netname
[MAXNETNAMELEN
+1];
156 mutex_enter(&authdes_lock
);
157 if (authdes_cache
== NULL
) {
158 authdes_cache
= kmem_zalloc(
159 sizeof (struct authdes_cache_entry
*) * authdes_cachesz
,
162 mutex_exit(&authdes_lock
);
164 CTASSERT(sizeof (struct area
) <= RQCRED_SIZE
);
165 /* LINTED pointer alignment */
166 area
= (struct area
*)rqst
->rq_clntcred
;
167 cred
= (struct authdes_cred
*)&area
->area_cred
;
172 /* LINTED pointer alignment */
173 ixdr
= (int32_t *)msg
->rm_call
.cb_cred
.oa_base
;
174 cred
->adc_namekind
= IXDR_GET_ENUM(ixdr
, enum authdes_namekind
);
175 switch (cred
->adc_namekind
) {
177 namelen
= IXDR_GET_U_INT32(ixdr
);
178 if (namelen
> MAXNETNAMELEN
)
179 return (AUTH_BADCRED
);
180 cred
->adc_fullname
.name
= area
->area_netname
;
181 bcopy(ixdr
, cred
->adc_fullname
.name
, namelen
);
182 cred
->adc_fullname
.name
[namelen
] = 0;
183 ixdr
+= (RNDUP(namelen
) / BYTES_PER_XDR_UNIT
);
184 cred
->adc_fullname
.key
.key
.high
= (uint32_t)*ixdr
++;
185 cred
->adc_fullname
.key
.key
.low
= (uint32_t)*ixdr
++;
186 cred
->adc_fullname
.window
= (uint32_t)*ixdr
++;
190 cred
->adc_nickname
= (uint32_t)*ixdr
++;
194 return (AUTH_BADCRED
);
200 /* LINTED pointer alignment */
201 ixdr
= (int32_t *)msg
->rm_call
.cb_verf
.oa_base
;
202 verf
.adv_xtimestamp
.key
.high
= (uint32_t)*ixdr
++;
203 verf
.adv_xtimestamp
.key
.low
= (uint32_t)*ixdr
++;
204 verf
.adv_int_u
= (uint32_t)*ixdr
++;
208 * Get the conversation key
210 if (!nick
) { /* ADN_FULLNAME */
211 sessionkey
= &cred
->adc_fullname
.key
;
212 if (key_decryptsession(cred
->adc_fullname
.name
, sessionkey
) !=
214 return (AUTH_BADCRED
); /* key not found */
216 } else { /* ADN_NICKNAME */
217 mutex_enter(&authdes_lock
);
218 if (!(nick_entry
= authdes_cache_get(cred
->adc_nickname
))) {
219 RPCLOG(1, "_svcauth_des: nickname %d not in the cache\n",
221 mutex_exit(&authdes_lock
);
222 return (AUTH_BADCRED
); /* need refresh */
224 sessionkey
= &nick_entry
->key
;
225 mutex_enter(&nick_entry
->lock
);
226 mutex_exit(&authdes_lock
);
230 * Decrypt the timestamp
232 cryptbuf
[0] = verf
.adv_xtimestamp
;
233 if (!nick
) { /* ADN_FULLNAME */
234 cryptbuf
[1].key
.high
= cred
->adc_fullname
.window
;
235 cryptbuf
[1].key
.low
= verf
.adv_winverf
;
236 ivec
.key
.high
= ivec
.key
.low
= 0;
237 status
= cbc_crypt((char *)sessionkey
, (char *)cryptbuf
,
238 2 * sizeof (des_block
), DES_DECRYPT
, (char *)&ivec
);
239 } else { /* ADN_NICKNAME */
240 status
= ecb_crypt((char *)sessionkey
, (char *)cryptbuf
,
241 sizeof (des_block
), DES_DECRYPT
);
243 if (DES_FAILED(status
)) {
244 RPCLOG0(1, "_svcauth_des: decryption failure\n");
246 mutex_exit(&nick_entry
->lock
);
248 return (AUTH_FAILED
); /* system error */
252 * XDR the decrypted timestamp
254 ixdr
= (int32_t *)cryptbuf
;
255 timestamp
.tv_sec
= IXDR_GET_INT32(ixdr
);
256 timestamp
.tv_usec
= IXDR_GET_INT32(ixdr
);
259 * Check for valid credentials and verifiers.
260 * They could be invalid because the key was flushed
261 * out of the cache, and so a new session should begin.
262 * Be sure and send AUTH_REJECTED{CRED, VERF} if this is the case.
264 if (!nick
) { /* ADN_FULLNAME */
265 window
= IXDR_GET_U_INT32(ixdr
);
266 winverf
= IXDR_GET_U_INT32(ixdr
);
267 if (winverf
!= window
- 1) {
268 RPCLOG(1, "_svcauth_des: window verifier mismatch %d\n",
270 return (AUTH_BADCRED
); /* garbled credential */
272 } else { /* ADN_NICKNAME */
273 window
= nick_entry
->window
;
276 if (timestamp
.tv_usec
>= USEC_PER_SEC
) {
277 RPCLOG(1, "_svcauth_des: invalid usecs %ld\n",
279 /* cached out (bad key), or garbled verifier */
281 mutex_exit(&nick_entry
->lock
);
283 return (nick
? AUTH_REJECTEDVERF
: AUTH_BADVERF
);
287 current_time
.tv_sec
= now
.tv_sec
;
288 current_time
.tv_usec
= now
.tv_nsec
/ 1000;
290 current_time
.tv_sec
-= window
; /* allow for expiration */
291 if (!BEFORE(¤t_time
, ×tamp
)) {
292 RPCLOG0(1, "_svcauth_des: timestamp expired\n");
293 /* replay, or garbled credential */
295 mutex_exit(&nick_entry
->lock
);
297 return (nick
? AUTH_REJECTEDVERF
: AUTH_BADCRED
);
301 * xdr the timestamp before encrypting
303 ixdr
= (int32_t *)cryptbuf
;
304 IXDR_PUT_INT32(ixdr
, timestamp
.tv_sec
- 1);
305 IXDR_PUT_INT32(ixdr
, timestamp
.tv_usec
);
308 * encrypt the timestamp
310 status
= ecb_crypt((char *)sessionkey
, (char *)cryptbuf
,
311 sizeof (des_block
), DES_ENCRYPT
);
312 if (DES_FAILED(status
)) {
313 RPCLOG0(1, "_svcauth_des: encryption failure\n");
315 mutex_exit(&nick_entry
->lock
);
317 return (AUTH_FAILED
); /* system error */
319 verf
.adv_xtimestamp
= cryptbuf
[0];
322 * If a ADN_FULLNAME, create a new nickname cache entry.
325 mutex_enter(&authdes_lock
);
326 if (!(nick_entry
= authdes_cache_new(cred
->adc_fullname
.name
,
327 sessionkey
, window
))) {
328 RPCLOG0(1, "_svcauth_des: can not create new cache entry\n");
329 mutex_exit(&authdes_lock
);
330 return (AUTH_FAILED
);
332 mutex_enter(&nick_entry
->lock
);
333 mutex_exit(&authdes_lock
);
335 verf
.adv_nickname
= nick_entry
->nickname
;
338 * Serialize the reply verifier, and update rqst
340 /* LINTED pointer alignment */
341 ixdr
= (int32_t *)msg
->rm_call
.cb_verf
.oa_base
;
342 *ixdr
++ = (int32_t)verf
.adv_xtimestamp
.key
.high
;
343 *ixdr
++ = (int32_t)verf
.adv_xtimestamp
.key
.low
;
344 *ixdr
++ = (int32_t)verf
.adv_int_u
;
346 rqst
->rq_xprt
->xp_verf
.oa_flavor
= AUTH_DES
;
347 rqst
->rq_xprt
->xp_verf
.oa_base
= msg
->rm_call
.cb_verf
.oa_base
;
348 rqst
->rq_xprt
->xp_verf
.oa_length
=
349 (uint_t
)((char *)ixdr
- msg
->rm_call
.cb_verf
.oa_base
);
350 if (rqst
->rq_xprt
->xp_verf
.oa_length
> MAX_AUTH_BYTES
) {
351 RPCLOG0(1, "_svcauth_des: invalid oa length\n");
352 mutex_exit(&nick_entry
->lock
);
353 return (AUTH_BADVERF
);
357 * We succeeded and finish cooking the credential.
358 * nicknames are cooked into fullnames
361 cred
->adc_nickname
= nick_entry
->nickname
;
362 cred
->adc_fullname
.window
= window
;
363 } else { /* ADN_NICKNAME */
364 cred
->adc_namekind
= ADN_FULLNAME
;
365 cred
->adc_fullname
.name
= nick_entry
->rname
;
366 cred
->adc_fullname
.key
= nick_entry
->key
;
367 cred
->adc_fullname
.window
= nick_entry
->window
;
369 mutex_exit(&nick_entry
->lock
);
372 * For every authdes_sweep_interval, delete cache entries that have been
373 * idled for authdes_cache_time.
375 mutex_enter(&authdes_lock
);
376 if ((gethrestime_sec() - authdes_last_swept
) > authdes_sweep_interval
)
379 mutex_exit(&authdes_lock
);
381 return (AUTH_OK
); /* we made it! */
385 * Initialization upon loading the rpcsec module.
388 svcauthdes_init(void)
390 mutex_init(&authdes_lock
, NULL
, MUTEX_DEFAULT
, NULL
);
392 * Allocate des cache handle
394 authdes_cache_handle
= kmem_cache_create("authdes_cache_handle",
395 sizeof (struct authdes_cache_entry
), 0, NULL
, NULL
,
396 authdes_cache_reclaim
, NULL
, NULL
, 0);
400 * Final actions upon exiting the rpcsec module.
403 svcauthdes_fini(void)
405 mutex_destroy(&authdes_lock
);
406 kmem_cache_destroy(authdes_cache_handle
);
410 * Local credential handling stuff.
411 * NOTE: bsd unix dependent.
412 * Other operating systems should put something else here.
416 uid_t uid
; /* cached uid */
417 gid_t gid
; /* cached gid */
418 short valid
; /* valid creds */
419 short grouplen
; /* length of cached groups */
420 gid_t groups
[1]; /* cached groups - allocate ngroups_max */
424 * Map a des credential into a unix cred.
425 * We cache the credential here so the application does
426 * not have to make an rpc call every time to interpret
430 kauthdes_getucred(const struct authdes_cred
*adc
, cred_t
*cr
)
435 struct bsdcred
*cred
;
436 struct authdes_cache_entry
*nickentry
;
438 mutex_enter(&authdes_lock
);
439 if (!(nickentry
= authdes_cache_get(adc
->adc_nickname
))) {
440 RPCLOG0(1, "authdes_getucred: invalid nickname\n");
441 mutex_exit(&authdes_lock
);
445 mutex_enter(&nickentry
->lock
);
446 mutex_exit(&authdes_lock
);
447 /* LINTED pointer alignment */
448 cred
= (struct bsdcred
*)nickentry
->localcred
;
451 * not in cache: lookup
453 if (netname2user(adc
->adc_fullname
.name
, &i_uid
, &i_gid
,
454 &i_grouplen
, &cred
->groups
[0]) != RPC_SUCCESS
) {
456 * Probably a host principal, since at this
457 * point we have valid keys. Note that later
458 * if the principal is not in the root list
459 * for NFS, we will be mapped to that exported
460 * file system's anonymous user, typically
461 * NOBODY. keyserv KEY_GETCRED will fail for a
462 * root-netnames so we assume root here.
463 * Currently NFS is the only caller of this
464 * routine. If other RPC services call this
465 * routine, it is up to that service to
466 * differentiate between local and remote
473 RPCLOG0(2, "authdes_getucred: missed ucred cache\n");
476 cred
->grouplen
= (short)i_grouplen
;
483 if (crsetugid(cr
, cred
->uid
, cred
->gid
) != 0 ||
484 crsetgroups(cr
, cred
->grouplen
, &cred
->groups
[0]) != 0) {
485 mutex_exit(&nickentry
->lock
);
488 mutex_exit(&nickentry
->lock
);
493 * Create a new cache_entry and put it in authdes_cache table.
494 * Caller should have already locked the authdes_cache table.
496 struct authdes_cache_entry
*
497 authdes_cache_new(char *fullname
, des_block
*sessionkey
, uint32_t window
) {
499 struct authdes_cache_entry
*new, *head
;
500 struct bsdcred
*ucred
;
503 if (!(new = kmem_cache_alloc(authdes_cache_handle
, KM_SLEEP
))) {
507 if (!(new->rname
= kmem_alloc(strlen(fullname
) + 1, KM_NOSLEEP
))) {
508 kmem_cache_free(authdes_cache_handle
, new);
512 if (!(ucred
= kmem_alloc(sizeof (struct bsdcred
) +
513 (ngroups_max
- 1) * sizeof (gid_t
), KM_NOSLEEP
))) {
514 kmem_free(new->rname
, strlen(fullname
) + 1);
515 kmem_cache_free(authdes_cache_handle
, new);
519 (void) strcpy(new->rname
, fullname
);
521 new->localcred
= (caddr_t
)ucred
;
522 new->key
= *sessionkey
;
523 new->window
= window
;
524 new->ref_time
= gethrestime_sec();
525 new->nickname
= Nickname
++;
526 mutex_init(&new->lock
, NULL
, MUTEX_DEFAULT
, NULL
);
528 /* put new into the hash table */
529 index
= HASH(new->nickname
);
530 head
= authdes_cache
[index
];
531 if ((new->next
= head
) != NULL
) {
534 authdes_cache
[index
] = new;
537 /* update the LRU list */
538 new->lru_prev
= NULL
;
539 if ((new->lru_next
= lru_first
) != NULL
) {
540 lru_first
->lru_prev
= new;
551 * Get an existing cache entry from authdes_cache table.
552 * The caller should have locked the authdes_cache table.
554 struct authdes_cache_entry
*
555 authdes_cache_get(uint32_t nickname
) {
557 struct authdes_cache_entry
*cur
= NULL
;
558 int index
= HASH(nickname
);
560 ASSERT(MUTEX_HELD(&authdes_lock
));
561 for (cur
= authdes_cache
[index
]; cur
; cur
= cur
->next
) {
562 if ((cur
->nickname
== nickname
)) {
563 /* find it, update the LRU list */
564 if (cur
!= lru_first
) {
565 cur
->lru_prev
->lru_next
= cur
->lru_next
;
566 if (cur
->lru_next
!= NULL
) {
567 cur
->lru_next
->lru_prev
= cur
->lru_prev
;
569 lru_last
= cur
->lru_prev
;
571 cur
->lru_prev
= NULL
;
572 cur
->lru_next
= lru_first
;
573 lru_first
->lru_prev
= cur
;
577 cur
->ref_time
= gethrestime_sec();
578 authdes_ncachehits
++;
583 authdes_ncachemisses
++;
588 * authdes_cache_reclaim() is called by the kernel memory allocator
589 * when memory is low. This routine will reclaim 25% of the least recent
590 * used cache entries above the low water mark (low_cache_entries).
591 * If the cache entries have already hit the low water mark, it will
592 * return 1 cache entry.
596 authdes_cache_reclaim(void *pdata
) {
597 struct authdes_cache_entry
*p
;
600 mutex_enter(&authdes_lock
);
601 n
= authdes_ncache
- low_cache_entries
;
604 for (i
= 0; i
< n
; i
++) {
605 if ((p
= lru_last
) == lru_first
)
608 /* Update the hash linked list */
609 if (p
->prev
== NULL
) {
610 authdes_cache
[HASH(p
->nickname
)] = p
->next
;
612 p
->prev
->next
= p
->next
;
614 if (p
->next
!= NULL
) {
615 p
->next
->prev
= p
->prev
;
618 /* update the LRU linked list */
619 p
->lru_prev
->lru_next
= NULL
;
620 lru_last
= p
->lru_prev
;
622 kmem_free(p
->rname
, strlen(p
->rname
) + 1);
623 kmem_free(p
->localcred
, sizeof (struct bsdcred
) +
624 (ngroups_max
- 1) * sizeof (gid_t
));
625 mutex_destroy(&p
->lock
);
626 kmem_cache_free(authdes_cache_handle
, p
);
630 mutex_exit(&authdes_lock
);
631 RPCLOG(4, "_svcauth_des: %d cache entries reclaimed...\n",
636 * Walk through the LRU doubly-linked list and delete the cache
637 * entries that have not been used for more than authdes_cache_time.
639 * Caller should have locked the cache table.
643 struct authdes_cache_entry
*p
;
645 ASSERT(MUTEX_HELD(&authdes_lock
));
646 while ((p
= lru_last
) != lru_first
) {
651 * If the last LRU entry idled less than authdes_cache_time,
652 * we are done with the sweeping.
654 if (p
->ref_time
+ authdes_cache_time
> gethrestime_sec())
657 /* update the hash linked list */
658 if (p
->prev
== NULL
) {
659 authdes_cache
[HASH(p
->nickname
)] = p
->next
;
661 p
->prev
->next
= p
->next
;
663 if (p
->next
!= NULL
) {
664 p
->next
->prev
= p
->prev
;
667 /* update the LRU linked list */
668 p
->lru_prev
->lru_next
= NULL
;
669 lru_last
= p
->lru_prev
;
671 kmem_free(p
->rname
, strlen(p
->rname
) + 1);
672 kmem_free(p
->localcred
, sizeof (struct bsdcred
) +
673 (ngroups_max
- 1) * sizeof (gid_t
));
674 mutex_destroy(&p
->lock
);
675 kmem_cache_free(authdes_cache_handle
, p
);
680 authdes_last_swept
= gethrestime_sec();
681 RPCLOG(4, "_svcauth_des: sweeping cache...#caches left = %d\n",