dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libc / port / nsl / svcauth_des.c
blobf322de2e422832531de0f4ef8d9feb439fbb2548
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 * Copyright (c) 2016 by Delphix. All rights reserved.
26 * Copyright 2017 Joyent Inc
28 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
29 /* All Rights Reserved */
31 * Portions of this source code were derived from Berkeley
32 * 4.3 BSD under license from the Regents of the University of
33 * California.
37 * svcauth_des.c, server-side des authentication
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.
45 * It is up to the server to determine if the window size is
46 * too small.
50 #include "mt.h"
51 #include "rpc_mt.h"
52 #include <assert.h>
53 #include <rpc/des_crypt.h>
54 #include <rpc/rpc.h>
55 #include <sys/types.h>
56 #include <sys/param.h>
57 #include <stdlib.h>
58 #include <unistd.h>
59 #include <string.h>
60 #include <strings.h>
61 #include <sys/debug.h>
63 #include <syslog.h>
65 extern int key_decryptsession_pk(const char *, netobj *, des_block *);
67 #define USEC_PER_SEC ((ulong_t)1000000L)
68 #define BEFORE(t1, t2) timercmp(t1, t2, < /* EMPTY */)
72 * LRU cache of conversation keys and some other useful items.
74 #define DEF_AUTHDES_CACHESZ 128
75 int authdes_cachesz = DEF_AUTHDES_CACHESZ;
76 struct cache_entry {
77 des_block key; /* conversation key */
78 char *rname; /* client's name */
79 uint_t window; /* credential lifetime window */
80 struct timeval laststamp; /* detect replays of creds */
81 char *localcred; /* generic local credential */
82 int index; /* where are we in array? */
83 struct cache_entry *prev; /* prev entry on LRU list */
84 struct cache_entry *next; /* next entry on LRU list */
87 static const char __getucredstr[] = "authdes_getucred:";
89 static struct cache_entry *_rpc_authdes_cache; /* [authdes_cachesz] */
90 static struct cache_entry *cache_head; /* cache (in LRU order) */
91 static struct cache_entry *cache_tail; /* cache (in LRU order) */
94 * A rwlock_t would seem to make more sense, but it turns out we always
95 * muck with the cache entries, so would always need a write lock (in
96 * which case, we might as well use a mutex).
98 extern mutex_t authdes_lock;
101 static int cache_init(void); /* initialize the cache */
102 /* find an entry in the cache */
103 static int cache_spot(des_block *, char *, struct timeval *);
104 static void cache_ref(uint32_t); /* note that sid was ref'd */
105 static void invalidate(char *); /* invalidate entry in cache */
106 static void __msgout(int, const char *, const char *);
107 static void __msgout2(const char *, const char *);
110 * cache statistics
112 struct {
113 ulong_t ncachehits; /* times cache hit, and is not replay */
114 ulong_t ncachereplays; /* times cache hit, and is replay */
115 ulong_t ncachemisses; /* times cache missed */
116 } svcauthdes_stats;
119 * NOTE: this has to fit inside RQCRED_SIZE bytes. If you update this struct,
120 * double-check it still fits.
122 struct authdes_area {
123 struct authdes_cred area_cred;
124 char area_netname[MAXNETNAMELEN+1];
126 CTASSERT(sizeof (struct authdes_area) <= RQCRED_SIZE);
129 * Service side authenticator for AUTH_DES
131 enum auth_stat
132 __svcauth_des(struct svc_req *rqst, struct rpc_msg *msg)
134 int32_t *ixdr;
135 des_block cryptbuf[2];
136 struct authdes_cred *cred;
137 struct authdes_verf verf;
138 int status;
139 struct cache_entry *entry;
140 uint32_t sid;
141 int cache_spot_id;
142 des_block *sessionkey, init_sessionkey;
143 des_block ivec;
144 uint_t window;
145 struct authdes_area *area;
146 struct timeval timestamp;
147 uint32_t namelen;
148 int fullname_rcvd = 0;
149 int from_cache = 0;
151 (void) mutex_lock(&authdes_lock);
152 if (_rpc_authdes_cache == NULL) {
153 int ret = cache_init();
154 if (ret == -1) {
155 (void) mutex_unlock(&authdes_lock);
156 return (AUTH_FAILED);
159 (void) mutex_unlock(&authdes_lock);
161 /* LINTED pointer cast */
162 area = (struct authdes_area *)rqst->rq_clntcred;
163 cred = (struct authdes_cred *)&area->area_cred;
165 if ((uint_t)msg->rm_call.cb_cred.oa_length == 0)
166 return (AUTH_BADCRED);
168 * Get the credential
170 /* LINTED pointer cast */
171 ixdr = (int32_t *)msg->rm_call.cb_cred.oa_base;
172 cred->adc_namekind = IXDR_GET_ENUM(ixdr, enum authdes_namekind);
173 switch (cred->adc_namekind) {
174 case ADN_FULLNAME:
175 namelen = IXDR_GET_U_INT32(ixdr);
176 if (namelen > MAXNETNAMELEN)
177 return (AUTH_BADCRED);
178 cred->adc_fullname.name = area->area_netname;
179 (void) memcpy(cred->adc_fullname.name, ixdr, (uint_t)namelen);
180 cred->adc_fullname.name[namelen] = 0;
181 ixdr += (RNDUP(namelen) / BYTES_PER_XDR_UNIT);
182 cred->adc_fullname.key.key.high = (uint32_t)*ixdr++;
183 cred->adc_fullname.key.key.low = (uint32_t)*ixdr++;
184 cred->adc_fullname.window = (uint32_t)*ixdr++;
185 fullname_rcvd++;
186 break;
187 case ADN_NICKNAME:
188 cred->adc_nickname = (uint32_t)*ixdr++;
189 break;
190 default:
191 return (AUTH_BADCRED);
194 if ((uint_t)msg->rm_call.cb_verf.oa_length == 0)
195 return (AUTH_BADVERF);
197 * Get the verifier
199 /* LINTED pointer cast */
200 ixdr = (int32_t *)msg->rm_call.cb_verf.oa_base;
201 verf.adv_xtimestamp.key.high = (uint32_t)*ixdr++;
202 verf.adv_xtimestamp.key.low = (uint32_t)*ixdr++;
203 verf.adv_int_u = (uint32_t)*ixdr++;
205 (void) mutex_lock(&authdes_lock);
208 * Get the conversation key
210 if (fullname_rcvd) { /* ADN_FULLNAME */
211 netobj pkey;
212 char pkey_data[1024];
214 again:
215 init_sessionkey = cred->adc_fullname.key;
216 sessionkey = &init_sessionkey;
218 if (!__getpublickey_cached(cred->adc_fullname.name,
219 pkey_data, &from_cache)) {
221 * if the user has no public key, treat them as the
222 * unauthenticated identity - nobody. If this
223 * works, it means the client didn't find the
224 * user's keys and used nobody's secret key
225 * as a backup.
227 if (!__getpublickey_cached("nobody",
228 pkey_data, &from_cache)) {
229 __msgout(LOG_INFO,
230 "_svcauth_des: no public key for nobody or ",
231 cred->adc_fullname.name);
232 (void) mutex_unlock(&authdes_lock);
233 return (AUTH_BADCRED); /* no key */
237 * found a public key for nobody. change
238 * the fullname id to nobody, so the caller
239 * thinks the client specified nobody
240 * as the user identity.
242 (void) strcpy(cred->adc_fullname.name, "nobody");
244 pkey.n_bytes = pkey_data;
245 pkey.n_len = strlen(pkey_data) + 1;
246 if (key_decryptsession_pk(cred->adc_fullname.name, &pkey,
247 sessionkey) < 0) {
248 if (from_cache) {
249 __getpublickey_flush(cred->adc_fullname.name);
250 goto again;
252 __msgout(LOG_INFO,
253 "_svcauth_des: key_decryptsessionkey failed for",
254 cred->adc_fullname.name);
255 (void) mutex_unlock(&authdes_lock);
256 return (AUTH_BADCRED); /* key not found */
258 } else { /* ADN_NICKNAME */
259 sid = cred->adc_nickname;
260 if (sid >= authdes_cachesz) {
261 __msgout(LOG_INFO, "_svcauth_des:", "bad nickname");
262 (void) mutex_unlock(&authdes_lock);
263 return (AUTH_BADCRED); /* garbled credential */
265 /* actually check that the entry is not null */
266 entry = &_rpc_authdes_cache[sid];
267 if (entry->rname == NULL) {
268 (void) mutex_unlock(&authdes_lock);
269 return (AUTH_BADCRED); /* cached out */
271 sessionkey = &_rpc_authdes_cache[sid].key;
275 * Decrypt the timestamp
277 cryptbuf[0] = verf.adv_xtimestamp;
278 if (fullname_rcvd) { /* ADN_FULLNAME */
279 cryptbuf[1].key.high = cred->adc_fullname.window;
280 cryptbuf[1].key.low = verf.adv_winverf;
281 ivec.key.high = ivec.key.low = 0;
282 status = cbc_crypt((char *)sessionkey, (char *)cryptbuf,
283 2 * (int)sizeof (des_block), DES_DECRYPT | DES_HW,
284 (char *)&ivec);
285 } else {
286 status = ecb_crypt((char *)sessionkey, (char *)cryptbuf,
287 (int)sizeof (des_block), DES_DECRYPT | DES_HW);
289 if (DES_FAILED(status)) {
290 if (fullname_rcvd && from_cache) {
291 __getpublickey_flush(cred->adc_fullname.name);
292 goto again;
294 __msgout(LOG_ERR, "_svcauth_des: DES decryption failure for",
295 fullname_rcvd ? cred->adc_fullname.name :
296 _rpc_authdes_cache[sid].rname);
297 (void) mutex_unlock(&authdes_lock);
298 return (AUTH_FAILED); /* system error */
302 * XDR the decrypted timestamp
304 ixdr = (int32_t *)cryptbuf;
305 timestamp.tv_sec = IXDR_GET_INT32(ixdr);
306 timestamp.tv_usec = IXDR_GET_INT32(ixdr);
309 * Check for valid credentials and verifiers.
310 * They could be invalid because the key was flushed
311 * out of the cache, and so a new session should begin.
312 * Be sure and send AUTH_REJECTED{CRED, VERF} if this is the case.
315 struct timeval current;
316 int nick;
317 int winverf;
319 if (fullname_rcvd) {
320 window = IXDR_GET_U_INT32(ixdr);
321 winverf = IXDR_GET_U_INT32(ixdr);
322 if (winverf != window - 1) {
323 if (from_cache) {
324 __getpublickey_flush(
325 cred->adc_fullname.name);
326 goto again;
328 __msgout(LOG_INFO,
329 "_svcauth_des: corrupted window from",
330 cred->adc_fullname.name);
331 (void) mutex_unlock(&authdes_lock);
332 /* garbled credential or invalid secret key */
333 return (AUTH_BADCRED);
335 cache_spot_id = cache_spot(sessionkey,
336 cred->adc_fullname.name,
338 &timestamp);
339 if (cache_spot_id < 0) {
340 __msgout(LOG_INFO,
341 "_svcauth_des: replayed credential from",
342 cred->adc_fullname.name);
343 (void) mutex_unlock(&authdes_lock);
344 return (AUTH_REJECTEDCRED); /* replay */
345 } else sid = cache_spot_id;
346 nick = 0;
347 } else { /* ADN_NICKNAME */
348 window = _rpc_authdes_cache[sid].window;
349 nick = 1;
352 if ((ulong_t)timestamp.tv_usec >= USEC_PER_SEC) {
353 if (fullname_rcvd && from_cache) {
354 __getpublickey_flush(cred->adc_fullname.name);
355 goto again;
357 __msgout(LOG_INFO,
358 "_svcauth_des: invalid timestamp received from",
359 fullname_rcvd ? cred->adc_fullname.name :
360 _rpc_authdes_cache[sid].rname);
361 /* cached out (bad key), or garbled verifier */
362 (void) mutex_unlock(&authdes_lock);
363 return (nick ? AUTH_REJECTEDVERF : AUTH_BADVERF);
365 if (nick && BEFORE(&timestamp,
366 &_rpc_authdes_cache[sid].laststamp)) {
367 if (fullname_rcvd && from_cache) {
368 __getpublickey_flush(cred->adc_fullname.name);
369 goto again;
371 __msgout(LOG_INFO,
372 "_svcauth_des: timestamp is earlier than the one previously seen from",
373 fullname_rcvd ? cred->adc_fullname.name :
374 _rpc_authdes_cache[sid].rname);
375 (void) mutex_unlock(&authdes_lock);
376 return (AUTH_REJECTEDVERF); /* replay */
378 (void) gettimeofday(&current, NULL);
379 current.tv_sec -= window; /* allow for expiration */
380 if (!BEFORE(&current, &timestamp)) {
381 if (fullname_rcvd && from_cache) {
382 __getpublickey_flush(cred->adc_fullname.name);
383 goto again;
385 __msgout(LOG_INFO,
386 "_svcauth_des: timestamp expired for",
387 fullname_rcvd ? cred->adc_fullname.name :
388 _rpc_authdes_cache[sid].rname);
389 /* replay, or garbled credential */
390 (void) mutex_unlock(&authdes_lock);
391 return (nick ? AUTH_REJECTEDVERF : AUTH_BADCRED);
396 * Set up the reply verifier
398 verf.adv_nickname = sid;
401 * xdr the timestamp before encrypting
403 ixdr = (int32_t *)cryptbuf;
404 IXDR_PUT_INT32(ixdr, timestamp.tv_sec - 1);
405 IXDR_PUT_INT32(ixdr, timestamp.tv_usec);
408 * encrypt the timestamp
410 status = ecb_crypt((char *)sessionkey, (char *)cryptbuf,
411 (int)sizeof (des_block), DES_ENCRYPT | DES_HW);
412 if (DES_FAILED(status)) {
413 __msgout(LOG_ERR, "_svcauth_des: DES encryption failure for",
414 fullname_rcvd ? cred->adc_fullname.name :
415 _rpc_authdes_cache[sid].rname);
416 (void) mutex_unlock(&authdes_lock);
417 return (AUTH_FAILED); /* system error */
419 verf.adv_xtimestamp = cryptbuf[0];
422 * Serialize the reply verifier, and update rqst
424 /* LINTED pointer cast */
425 ixdr = (int32_t *)msg->rm_call.cb_verf.oa_base;
426 *ixdr++ = (int32_t)verf.adv_xtimestamp.key.high;
427 *ixdr++ = (int32_t)verf.adv_xtimestamp.key.low;
428 *ixdr++ = (int32_t)verf.adv_int_u;
430 rqst->rq_xprt->xp_verf.oa_flavor = AUTH_DES;
431 rqst->rq_xprt->xp_verf.oa_base = msg->rm_call.cb_verf.oa_base;
432 rqst->rq_xprt->xp_verf.oa_length =
433 (char *)ixdr - msg->rm_call.cb_verf.oa_base;
434 if (rqst->rq_xprt->xp_verf.oa_length > MAX_AUTH_BYTES) {
435 __msgout(LOG_ERR,
436 "_svcauth_des: Authenticator length error",
437 fullname_rcvd ? cred->adc_fullname.name :
438 _rpc_authdes_cache[sid].rname);
439 (void) mutex_unlock(&authdes_lock);
440 return (AUTH_REJECTEDVERF);
444 * We succeeded, commit the data to the cache now and
445 * finish cooking the credential.
447 entry = &_rpc_authdes_cache[sid];
448 entry->laststamp = timestamp;
449 cache_ref(sid);
450 if (cred->adc_namekind == ADN_FULLNAME) {
451 cred->adc_fullname.window = window;
452 cred->adc_nickname = sid; /* save nickname */
453 free(entry->rname);
454 entry->rname = malloc(strlen(cred->adc_fullname.name) + 1);
455 if (entry->rname != NULL) {
456 (void) strcpy(entry->rname, cred->adc_fullname.name);
457 } else {
458 __msgout(LOG_CRIT, "_svcauth_des:", "out of memory");
459 (void) mutex_unlock(&authdes_lock);
460 return (AUTH_FAILED);
462 entry->key = *sessionkey;
463 entry->window = window;
464 /* mark any cached cred invalid */
465 invalidate(entry->localcred);
466 } else { /* ADN_NICKNAME */
468 * nicknames are cooked into fullnames
470 cred->adc_namekind = ADN_FULLNAME;
471 cred->adc_fullname.name = entry->rname;
472 cred->adc_fullname.key = entry->key;
473 cred->adc_fullname.window = entry->window;
475 (void) mutex_unlock(&authdes_lock);
476 return (AUTH_OK); /* we made it! */
481 * Initialize the cache
483 static int
484 cache_init(void)
486 int i;
488 /* LOCK HELD ON ENTRY: authdes_lock */
490 assert(MUTEX_HELD(&authdes_lock));
491 _rpc_authdes_cache =
492 malloc(sizeof (struct cache_entry) * authdes_cachesz);
493 if (_rpc_authdes_cache == NULL) {
494 __msgout(LOG_CRIT, "cache_init:", "out of memory");
495 return (-1);
497 (void) memset(_rpc_authdes_cache, 0,
498 sizeof (struct cache_entry) * authdes_cachesz);
501 * Initialize the lru chain (linked-list)
503 for (i = 1; i < (authdes_cachesz - 1); i++) {
504 _rpc_authdes_cache[i].index = i;
505 _rpc_authdes_cache[i].next = &_rpc_authdes_cache[i + 1];
506 _rpc_authdes_cache[i].prev = &_rpc_authdes_cache[i - 1];
508 cache_head = &_rpc_authdes_cache[0];
509 cache_tail = &_rpc_authdes_cache[authdes_cachesz - 1];
512 * These elements of the chain need special attention...
514 cache_head->index = 0;
515 cache_tail->index = authdes_cachesz - 1;
516 cache_head->next = &_rpc_authdes_cache[1];
517 cache_head->prev = cache_tail;
518 cache_tail->next = cache_head;
519 cache_tail->prev = &_rpc_authdes_cache[authdes_cachesz - 2];
520 return (0);
525 * Find the lru victim
527 static uint32_t
528 cache_victim(void)
530 /* LOCK HELD ON ENTRY: authdes_lock */
532 assert(MUTEX_HELD(&authdes_lock));
533 return (cache_head->index); /* list in lru order */
537 * Note that sid was referenced
539 static void
540 cache_ref(uint32_t sid)
542 struct cache_entry *curr = &_rpc_authdes_cache[sid];
545 /* LOCK HELD ON ENTRY: authdes_lock */
547 assert(MUTEX_HELD(&authdes_lock));
550 * move referenced item from its place on the LRU chain
551 * to the tail of the chain while checking for special
552 * conditions (mainly for performance).
554 if (cache_tail == curr) { /* no work to do */
555 /*EMPTY*/;
556 } else if (cache_head == curr) {
557 cache_head = cache_head->next;
558 cache_tail = curr;
559 } else {
560 (curr->next)->prev = curr->prev; /* fix thy neighbor */
561 (curr->prev)->next = curr->next;
562 curr->next = cache_head; /* fix thy self... */
563 curr->prev = cache_tail;
564 cache_head->prev = curr; /* fix the head */
565 cache_tail->next = curr; /* fix the tail */
566 cache_tail = curr; /* move the tail */
571 * Find a spot in the cache for a credential containing
572 * the items given. Return -1 if a replay is detected, otherwise
573 * return the spot in the cache.
575 static int
576 cache_spot(des_block *key, char *name, struct timeval *timestamp)
578 struct cache_entry *cp;
579 int i;
580 uint32_t hi;
582 /* LOCK HELD ON ENTRY: authdes_lock */
584 assert(MUTEX_HELD(&authdes_lock));
585 hi = key->key.high;
586 for (cp = _rpc_authdes_cache, i = 0; i < authdes_cachesz; i++, cp++) {
587 if (cp->key.key.high == hi &&
588 cp->key.key.low == key->key.low &&
589 cp->rname != NULL &&
590 memcmp(cp->rname, name, strlen(name) + 1) == 0) {
591 if (BEFORE(timestamp, &cp->laststamp)) {
592 svcauthdes_stats.ncachereplays++;
593 return (-1); /* replay */
595 svcauthdes_stats.ncachehits++;
596 return (i);
597 /* refresh */
600 svcauthdes_stats.ncachemisses++;
601 return (cache_victim());
606 * Local credential handling stuff.
607 * NOTE: bsd unix dependent.
608 * Other operating systems should put something else here.
610 #define UNKNOWN -2 /* grouplen, if cached cred is unknown user */
611 #define INVALID -1 /* grouplen, if cache entry is invalid */
613 struct bsdcred {
614 uid_t uid; /* cached uid */
615 gid_t gid; /* cached gid */
616 short grouplen; /* length of cached groups */
617 gid_t groups[1]; /* cached groups allocate _SC_NGROUPS_MAX */
620 static void
621 invalidate(char *cred)
623 if (cred == NULL)
624 return;
625 /* LINTED pointer cast */
626 ((struct bsdcred *)cred)->grouplen = INVALID;
630 * Map a des credential into a unix cred.
631 * We cache the credential here so the application does
632 * not have to make an rpc call every time to interpret
633 * the credential.
636 authdes_getucred(const struct authdes_cred *adc, uid_t *uid, gid_t *gid,
637 short *grouplen, gid_t *groups)
639 uint32_t sid;
640 int i;
641 uid_t i_uid;
642 gid_t i_gid;
643 int i_grouplen;
644 struct bsdcred *cred;
646 sid = adc->adc_nickname;
647 if (sid >= authdes_cachesz) {
648 __msgout2(__getucredstr, "invalid nickname");
649 return (0);
651 (void) mutex_lock(&authdes_lock);
652 /* LINTED pointer cast */
653 cred = (struct bsdcred *)_rpc_authdes_cache[sid].localcred;
654 if (cred == NULL) {
655 static size_t bsdcred_sz;
657 if (bsdcred_sz == 0) {
658 bsdcred_sz = sizeof (struct bsdcred) +
659 (sysconf(_SC_NGROUPS_MAX) - 1) * sizeof (gid_t);
661 cred = malloc(bsdcred_sz);
662 if (cred == NULL) {
663 __msgout2(__getucredstr, "out of memory");
664 (void) mutex_unlock(&authdes_lock);
665 return (0);
667 _rpc_authdes_cache[sid].localcred = (char *)cred;
668 cred->grouplen = INVALID;
670 if (cred->grouplen == INVALID) {
672 * not in cache: lookup
674 if (!netname2user(adc->adc_fullname.name, (uid_t *)&i_uid,
675 (gid_t *)&i_gid, &i_grouplen, (gid_t *)groups)) {
676 __msgout2(__getucredstr, "unknown netname");
677 /* mark as lookup up, but not found */
678 cred->grouplen = UNKNOWN;
679 (void) mutex_unlock(&authdes_lock);
680 return (0);
682 __msgout2(__getucredstr, "missed ucred cache");
683 *uid = cred->uid = i_uid;
684 *gid = cred->gid = i_gid;
685 *grouplen = cred->grouplen = i_grouplen;
686 for (i = i_grouplen - 1; i >= 0; i--) {
687 cred->groups[i] = groups[i];
689 (void) mutex_unlock(&authdes_lock);
690 return (1);
692 if (cred->grouplen == UNKNOWN) {
694 * Already lookup up, but no match found
696 (void) mutex_unlock(&authdes_lock);
697 return (0);
701 * cached credentials
703 *uid = cred->uid;
704 *gid = cred->gid;
705 *grouplen = cred->grouplen;
706 for (i = cred->grouplen - 1; i >= 0; i--) {
707 groups[i] = cred->groups[i];
709 (void) mutex_unlock(&authdes_lock);
710 return (1);
714 static void
715 __msgout(int level, const char *str, const char *strarg)
717 (void) syslog(level, "%s %s", str, strarg);
721 static void
722 __msgout2(const char *str, const char *str2)
724 (void) syslog(LOG_DEBUG, "%s %s", str, str2);