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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
34 #include <rpc/pmap_clnt.h> /* for pmap_unset */
35 #include <string.h> /* strcmp */
36 #include <unistd.h> /* setsid */
37 #include <sys/types.h>
40 #include <netconfig.h>
41 #include <sys/resource.h> /* rlimit */
42 #include <rpcsvc/daemon_utils.h> /* DAEMON_UID and DAEMON_GID */
43 #include <priv_utils.h> /* privileges */
45 #include <sys/systeminfo.h>
52 #include <sys/resource.h>
54 #include <sys/idmap.h>
60 #define CBUFSIZ 26 /* ctime(3c) */
62 static void term_handler(int);
63 static void init_idmapd();
64 static void fini_idmapd();
66 /* The DC Locator lives inside idmap (for now). */
67 extern void init_dc_locator(void);
68 extern void fini_dc_locator(void);
70 idmapd_state_t _idmapdstate
;
74 static int dfd
= -1; /* our door server fildes, for unregistration */
75 static boolean_t degraded
= B_FALSE
;
78 static uint32_t num_threads
= 0;
79 static pthread_key_t create_threads_key
;
80 static uint32_t max_threads
= 40;
83 * Server door thread start routine.
85 * Set a TSD value to the door thread. This enables the destructor to
86 * be called when this thread exits.
90 idmapd_door_thread_start(void *arg
)
92 static void *value
= 0;
95 * Disable cancellation to avoid memory leaks from not running
96 * the thread cleanup code.
98 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE
, NULL
);
99 (void) pthread_setspecific(create_threads_key
, value
);
100 (void) door_return(NULL
, 0, NULL
, 0);
102 /* make lint happy */
107 * Server door threads creation
111 idmapd_door_thread_create(door_info_t
*dip
)
116 if ((num
= atomic_inc_32_nv(&num_threads
)) > max_threads
) {
117 atomic_dec_32(&num_threads
);
119 "thread creation refused - %d threads currently active",
123 (void) pthread_create(&thread_id
, NULL
, idmapd_door_thread_start
, NULL
);
125 "created thread ID %d - %d threads currently active",
130 * Server door thread cleanup
134 idmapd_door_thread_cleanup(void *arg
)
138 num
= atomic_dec_32_nv(&num_threads
);
140 "exiting thread ID %d - %d threads currently active",
141 pthread_self(), num
);
145 * This is needed for mech_krb5 -- we run as daemon, yes, but we want
146 * mech_krb5 to think we're root so it can get host/nodename.fqdn
147 * tickets for us so we can authenticate to AD as the machine account
148 * that we are. For more details look at the entry point in mech_krb5
149 * corresponding to gss_init_sec_context().
151 * As a side effect of faking our effective UID to mech_krb5 we will use
152 * root's default ccache (/tmp/krb5cc_0). But if that's created by
153 * another process then we won't have access to it: we run as daemon and
154 * keep PRIV_FILE_DAC_READ, which is insufficient to share the ccache
155 * with others. We putenv("KRB5CCNAME=/var/run/idmap/ccache") in main()
156 * to avoid this issue; see main().
158 * Someday we'll have gss/mech_krb5 extensions for acquiring initiator
159 * creds with keytabs/raw keys, and someday we'll have extensions to
160 * libsasl to specify creds/name to use on the initiator side, and
161 * someday we'll have extensions to libldap to pass those through to
162 * libsasl. Until then this interposer will have to do.
164 * Also, we have to tell lint to shut up: it thinks app_krb5_user_uid()
165 * is defined but not used.
169 app_krb5_user_uid(void)
176 term_handler(int sig
)
178 idmapdlog(LOG_INFO
, "Terminating.");
186 usr1_handler(int sig
)
192 static int pipe_fd
= -1;
195 daemonize_ready(void)
201 (void) write(pipe_fd
, &data
, 1);
202 (void) close(pipe_fd
);
206 daemonize_start(void)
214 (void) sigset(SIGPIPE
, SIG_IGN
);
215 devnull
= open("/dev/null", O_RDONLY
);
218 (void) dup2(devnull
, 0);
219 (void) dup2(2, 1); /* stderr only */
220 if (pipe(filedes
) < 0)
222 if ((pid
= fork1()) < 0)
228 (void) close(filedes
[1]);
229 if (read(filedes
[0], &data
, 1) == 1) {
230 /* presume success */
234 (void) wait4(pid
, &status
, 0, NULL
);
235 if (WIFEXITED(status
))
236 _exit(WEXITSTATUS(status
));
244 pipe_fd
= filedes
[1];
245 (void) close(filedes
[0]);
248 openlog("idmap", LOG_PID
, LOG_DAEMON
);
255 main(int argc
, char **argv
)
260 if (rwlock_init(&_idmapdstate
.rwlk_cfg
, USYNC_THREAD
, NULL
) != 0)
262 if (mutex_init(&_idmapdstate
.addisc_lk
, USYNC_THREAD
, NULL
) != 0)
264 if (cond_init(&_idmapdstate
.addisc_cv
, USYNC_THREAD
, NULL
) != 0)
267 _idmapdstate
.daemon_mode
= TRUE
;
268 while ((c
= getopt(argc
, argv
, "d")) != -1) {
271 _idmapdstate
.daemon_mode
= FALSE
;
274 (void) fprintf(stderr
,
275 "Usage: /usr/lib/idmapd [-d]\n");
276 return (SMF_EXIT_ERR_CONFIG
);
280 /* set locale and domain for internationalization */
281 (void) setlocale(LC_ALL
, "");
282 (void) textdomain(TEXT_DOMAIN
);
284 idmap_set_logger(idmapdlog
);
285 adutils_set_logger(idmapdlog
);
288 * Raise the fd limit to max
290 if (getrlimit(RLIMIT_NOFILE
, &rl
) != 0) {
291 idmapdlog(LOG_ERR
, "getrlimit failed");
292 } else if (rl
.rlim_cur
< rl
.rlim_max
) {
293 rl
.rlim_cur
= rl
.rlim_max
;
294 if (setrlimit(RLIMIT_NOFILE
, &rl
) != 0)
296 "Unable to raise RLIMIT_NOFILE to %d",
300 (void) mutex_init(&_svcstate_lock
, USYNC_THREAD
, NULL
);
302 if (_idmapdstate
.daemon_mode
== TRUE
) {
303 if (daemonize_start() < 0) {
304 idmapdlog(LOG_ERR
, "unable to daemonize");
310 idmap_init_tsd_key();
315 /* signal handlers that should run only after we're initialized */
316 (void) sigset(SIGTERM
, term_handler
);
317 (void) sigset(SIGUSR1
, usr1_handler
);
318 (void) sigset(SIGHUP
, idmap_cfg_hup_handler
);
320 if (__init_daemon_priv(PU_RESETGROUPS
|PU_CLEARLIMITSET
, DAEMON_UID
,
321 DAEMON_GID
, PRIV_PROC_AUDIT
, PRIV_FILE_DAC_READ
, NULL
) == -1) {
322 idmapdlog(LOG_ERR
, "unable to drop privileges");
326 __fini_daemon_priv(PRIV_PROC_FORK
, PRIV_PROC_EXEC
, PRIV_PROC_SESSION
,
327 PRIV_FILE_LINK_ANY
, PRIV_PROC_INFO
, NULL
);
329 if (_idmapdstate
.daemon_mode
== TRUE
)
332 /* With doors RPC this just wastes this thread, oh well */
341 int connmaxrec
= IDMAP_MAX_DOOR_RPC
;
344 /* create directories as root and chown to daemon uid */
345 if (create_directory(IDMAP_DBDIR
, DAEMON_UID
, DAEMON_GID
) < 0)
347 if (create_directory(IDMAP_CACHEDIR
, DAEMON_UID
, DAEMON_GID
) < 0)
351 * Set KRB5CCNAME in the environment. See app_krb5_user_uid()
352 * for more details. We blow away the existing one, if there is
355 (void) unlink(IDMAP_CACHEDIR
"/ccache");
356 (void) putenv("KRB5CCNAME=" IDMAP_CACHEDIR
"/ccache");
357 (void) putenv("MS_INTEROP=1");
359 if (sysinfo(SI_HOSTNAME
, _idmapdstate
.hostname
,
360 sizeof (_idmapdstate
.hostname
)) == -1) {
362 idmapdlog(LOG_ERR
, "unable to determine hostname, error: %d",
367 if ((error
= init_mapping_system()) < 0) {
368 idmapdlog(LOG_ERR
, "unable to initialize mapping system");
369 exit(error
< -2 ? SMF_EXIT_ERR_CONFIG
: 1);
372 (void) door_server_create(idmapd_door_thread_create
);
373 if ((error
= pthread_key_create(&create_threads_key
,
374 idmapd_door_thread_cleanup
)) != 0) {
375 idmapdlog(LOG_ERR
, "unable to create threads key (%s)",
380 xprt
= svc_door_create(idmap_prog_1
, IDMAP_PROG
, IDMAP_V1
, connmaxrec
);
382 idmapdlog(LOG_ERR
, "unable to create door RPC service");
386 if (!svc_control(xprt
, SVCSET_CONNMAXREC
, &connmaxrec
)) {
387 idmapdlog(LOG_ERR
, "unable to limit RPC request size");
394 idmapdlog(LOG_ERR
, "unable to register door");
397 if ((error
= __idmap_reg(dfd
)) != 0) {
398 idmapdlog(LOG_ERR
, "unable to register door (%s)",
403 if ((error
= allocids(_idmapdstate
.new_eph_db
,
404 8192, &_idmapdstate
.next_uid
,
405 8192, &_idmapdstate
.next_gid
)) != 0) {
406 idmapdlog(LOG_ERR
, "unable to allocate ephemeral IDs (%s)",
408 _idmapdstate
.next_uid
= IDMAP_SENTINEL_PID
;
409 _idmapdstate
.limit_uid
= IDMAP_SENTINEL_PID
;
410 _idmapdstate
.next_gid
= IDMAP_SENTINEL_PID
;
411 _idmapdstate
.limit_gid
= IDMAP_SENTINEL_PID
;
413 _idmapdstate
.limit_uid
= _idmapdstate
.next_uid
+ 8192;
414 _idmapdstate
.limit_gid
= _idmapdstate
.next_gid
+ 8192;
430 (void) __idmap_unreg(dfd
);
431 fini_mapping_system();
440 static char *fmri
= NULL
;
446 if (s
!= NULL
&& *s
== '\0')
451 if ((s
= getenv("SMF_FMRI")) == NULL
|| strlen(s
) >= sizeof (buf
))
454 (void) strlcpy(buf
, s
, sizeof (buf
));
463 * Wrappers for smf_degrade/restore_instance()
465 * smf_restore_instance() is too heavy duty to be calling every time we
466 * have a successful AD name<->SID lookup.
469 degrade_svc(int poke_discovery
, const char *reason
)
477 idmapdlog(LOG_ERR
, "Degraded operation (%s).", reason
);
482 if ((fmri
= get_fmri()) != NULL
)
483 (void) smf_degrade_instance(fmri
, 0);
486 * If the config update thread is in a state where auto-discovery could
487 * be re-tried, then this will make it try it -- a sort of auto-refresh.
490 idmap_cfg_poke_updates();
502 if ((fmri
= get_fmri()) == NULL
)
503 (void) smf_restore_instance(fmri
);
508 idmapdlog(LOG_NOTICE
, "Normal operation restored");
514 idmapdlog(int pri
, const char *format
, ...) {
515 static time_t prev_ts
;
523 /* NB: cbuf has \n */
524 (void) fprintf(stderr
, "@ %s",
528 va_start(args
, format
);
529 (void) vfprintf(stderr
, format
, args
);
530 (void) fprintf(stderr
, "\n");
534 * We don't want to fill up the logs with useless messages when
535 * we're degraded, but we still want to log.
540 va_start(args
, format
);
541 vsyslog(pri
, format
, args
);
546 trace_str(nvlist_t
*entry
, char *n1
, char *n2
, char *str
)
548 char name
[IDMAP_TRACE_NAME_MAX
+1]; /* Max used is only about 11 */
550 (void) strlcpy(name
, n1
, sizeof (name
));
552 (void) strlcat(name
, n2
, sizeof (name
));
554 (void) nvlist_add_string(entry
, name
, str
);
558 trace_int(nvlist_t
*entry
, char *n1
, char *n2
, int64_t i
)
560 char name
[IDMAP_TRACE_NAME_MAX
+1]; /* Max used is only about 11 */
562 (void) strlcpy(name
, n1
, sizeof (name
));
564 (void) strlcat(name
, n2
, sizeof (name
));
566 (void) nvlist_add_int64(entry
, name
, i
);
570 trace_sid(nvlist_t
*entry
, char *n1
, char *n2
, idmap_sid
*sid
)
574 (void) asprintf(&str
, "%s-%u", sid
->prefix
, sid
->rid
);
578 trace_str(entry
, n1
, n2
, str
);
583 trace_id(nvlist_t
*entry
, char *fromto
, idmap_id
*id
, char *name
, char *domain
)
585 trace_int(entry
, fromto
, IDMAP_TRACE_TYPE
, (int64_t)id
->idtype
);
586 if (IS_ID_SID(*id
)) {
590 (void) asprintf(&str
, "%s%s%s", name
,
591 domain
== NULL
? "" : "@",
592 domain
== NULL
? "" : domain
);
594 trace_str(entry
, fromto
, IDMAP_TRACE_NAME
, str
);
598 if (id
->idmap_id_u
.sid
.prefix
!= NULL
) {
599 trace_sid(entry
, fromto
, IDMAP_TRACE_SID
,
600 &id
->idmap_id_u
.sid
);
602 } else if (IS_ID_POSIX(*id
)) {
604 trace_str(entry
, fromto
, IDMAP_TRACE_NAME
, name
);
605 if (id
->idmap_id_u
.uid
!= IDMAP_SENTINEL_PID
) {
606 trace_int(entry
, fromto
, IDMAP_TRACE_UNIXID
,
607 (int64_t)id
->idmap_id_u
.uid
);
613 * Record a trace event. TRACE() has already decided whether or not
614 * tracing is required; what we do here is collect the data and send it
615 * to its destination - to the trace log in the response, if
616 * IDMAP_REQ_FLG_TRACE is set, and to the SMF service log, if debug/mapping
617 * is greater than zero.
620 trace(idmap_mapping
*req
, idmap_id_res
*res
, char *fmt
, ...)
630 err
= nvlist_alloc(&entry
, NV_UNIQUE_NAME
, 0);
632 (void) fprintf(stderr
, "trace nvlist_alloc(entry): %s\n",
637 trace_id(entry
, "from", &req
->id1
, req
->id1name
, req
->id1domain
);
638 trace_id(entry
, "to", &res
->id
, req
->id2name
, req
->id2domain
);
640 if (IDMAP_ERROR(res
->retcode
)) {
641 trace_int(entry
, IDMAP_TRACE_ERROR
, NULL
,
642 (int64_t)res
->retcode
);
646 (void) vasprintf(&buf
, fmt
, va
);
649 trace_str(entry
, IDMAP_TRACE_MESSAGE
, NULL
, buf
);
654 idmap_trace_print_1(stderr
, "", entry
);
656 if (req
->flag
& IDMAP_REQ_FLG_TRACE
) {
657 /* Lazily allocate the trace list */
658 if (res
->info
.trace
== NULL
) {
659 err
= nvlist_alloc(&res
->info
.trace
, 0, 0);
661 res
->info
.trace
= NULL
; /* just in case */
662 (void) fprintf(stderr
,
663 "trace nvlist_alloc(trace): %s\n",
669 (void) nvlist_add_nvlist(res
->info
.trace
, "", entry
);
670 /* Note that entry is copied, so we must still free our copy */