| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| */ |
| /* |
| * Copyright 2009 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| * Copyright 2012 Milan Jurik. All rights reserved. |
| */ |
| |
| #include <stdlib.h> |
| #include <alloca.h> |
| #include <signal.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #include <pthread.h> |
| #include <time.h> |
| #include <errno.h> |
| #include <door.h> |
| #include <zone.h> |
| #include <resolv.h> |
| #include <sys/socket.h> |
| #include <net/route.h> |
| #include <string.h> |
| #include <net/if.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include "nscd_common.h" |
| #include "nscd_door.h" |
| #include "nscd_config.h" |
| #include "nscd_switch.h" |
| #include "nscd_log.h" |
| #include "nscd_selfcred.h" |
| #include "nscd_frontend.h" |
| #include "nscd_admin.h" |
| |
| static void rts_mon(void); |
| static void keep_open_dns_socket(void); |
| |
| extern nsc_ctx_t *cache_ctx_p[]; |
| |
| /* |
| * Current active Configuration data for the frontend component |
| */ |
| static nscd_cfg_global_frontend_t frontend_cfg_g; |
| static nscd_cfg_frontend_t *frontend_cfg; |
| |
| static int max_servers = 0; |
| static int max_servers_set = 0; |
| static int per_user_is_on = 1; |
| |
| static char *main_execname; |
| static char **main_argv; |
| extern int _whoami; |
| extern long activity; |
| extern mutex_t activity_lock; |
| |
| static sema_t common_sema; |
| |
| static thread_key_t lookup_state_key; |
| static mutex_t create_lock = DEFAULTMUTEX; |
| static int num_servers = 0; |
| static thread_key_t server_key; |
| |
| /* |
| * Bind a TSD value to a server thread. This enables the destructor to |
| * be called if/when this thread exits. This would be a programming |
| * error, but better safe than sorry. |
| */ |
| /*ARGSUSED*/ |
| static void * |
| server_tsd_bind(void *arg) |
| { |
| static void *value = 0; |
| |
| /* disable cancellation to avoid hangs if server threads disappear */ |
| (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); |
| (void) thr_setspecific(server_key, value); |
| (void) door_return(NULL, 0, NULL, 0); |
| |
| /* make lint happy */ |
| return (NULL); |
| } |
| |
| /* |
| * Server threads are created here. |
| */ |
| /*ARGSUSED*/ |
| static void |
| server_create(door_info_t *dip) |
| { |
| (void) mutex_lock(&create_lock); |
| if (++num_servers > max_servers) { |
| num_servers--; |
| (void) mutex_unlock(&create_lock); |
| return; |
| } |
| (void) mutex_unlock(&create_lock); |
| (void) thr_create(NULL, 0, server_tsd_bind, NULL, |
| THR_BOUND|THR_DETACHED, NULL); |
| } |
| |
| /* |
| * Server thread are destroyed here |
| */ |
| /*ARGSUSED*/ |
| static void |
| server_destroy(void *arg) |
| { |
| (void) mutex_lock(&create_lock); |
| num_servers--; |
| (void) mutex_unlock(&create_lock); |
| } |
| |
| /* |
| * get clearance |
| */ |
| int |
| _nscd_get_clearance(sema_t *sema) { |
| if (sema_trywait(&common_sema) == 0) { |
| (void) thr_setspecific(lookup_state_key, NULL); |
| return (0); |
| } |
| |
| if (sema_trywait(sema) == 0) { |
| (void) thr_setspecific(lookup_state_key, (void*)1); |
| return (0); |
| } |
| |
| return (1); |
| } |
| |
| |
| /* |
| * release clearance |
| */ |
| int |
| _nscd_release_clearance(sema_t *sema) { |
| int which; |
| |
| (void) thr_getspecific(lookup_state_key, (void**)&which); |
| if (which == 0) /* from common pool */ { |
| (void) sema_post(&common_sema); |
| return (0); |
| } |
| |
| (void) sema_post(sema); |
| return (1); |
| } |
| |
| static void |
| dozip(void) |
| { |
| /* not much here */ |
| } |
| |
| /* |
| * _nscd_restart_if_cfgfile_changed() |
| * Restart if modification times of nsswitch.conf or resolv.conf have changed. |
| * |
| * If nsswitch.conf has changed then it is possible that sources for |
| * various backends have changed and therefore the current cached |
| * data may not be consistent with the new data sources. By |
| * restarting the cache will be cleared and the new configuration will |
| * be used. |
| * |
| * The check for resolv.conf is made as only the first call to |
| * res_gethostbyname() or res_getaddrbyname() causes a call to |
| * res_ninit() to occur which in turn parses resolv.conf. Therefore |
| * to benefit from changes to resolv.conf nscd must be restarted when |
| * resolv.conf is updated, removed or created. If res_getXbyY calls |
| * are removed from NSS then this check could be removed. |
| * |
| */ |
| void |
| _nscd_restart_if_cfgfile_changed() |
| { |
| |
| static mutex_t nsswitch_lock = DEFAULTMUTEX; |
| static timestruc_t last_nsswitch_check = { 0 }; |
| static timestruc_t last_nsswitch_modified = { 0 }; |
| static timestruc_t last_resolv_modified = { -1, 0 }; |
| static mutex_t restarting_lock = DEFAULTMUTEX; |
| static int restarting = 0; |
| int restart = 0; |
| time_t now = time(NULL); |
| char *me = "_nscd_restart_if_cfgfile_changed"; |
| |
| #define FLAG_RESTART_REQUIRED if (restarting == 0) {\ |
| (void) mutex_lock(&restarting_lock);\ |
| if (restarting == 0) {\ |
| restarting = 1;\ |
| restart = 1;\ |
| }\ |
| (void) mutex_unlock(&restarting_lock);\ |
| } |
| |
| if (restarting == 1) |
| return; |
| |
| if (now - last_nsswitch_check.tv_sec < _NSC_FILE_CHECK_TIME) |
| return; |
| |
| (void) mutex_lock(&nsswitch_lock); |
| |
| if (now - last_nsswitch_check.tv_sec >= _NSC_FILE_CHECK_TIME) { |
| struct stat nss_buf; |
| struct stat res_buf; |
| |
| last_nsswitch_check.tv_sec = now; |
| last_nsswitch_check.tv_nsec = 0; |
| |
| (void) mutex_unlock(&nsswitch_lock); /* let others continue */ |
| |
| if (stat("/etc/nsswitch.conf", &nss_buf) < 0) { |
| return; |
| } else if (last_nsswitch_modified.tv_sec == 0) { |
| last_nsswitch_modified = nss_buf.st_mtim; |
| } |
| |
| if (last_nsswitch_modified.tv_sec < nss_buf.st_mtim.tv_sec || |
| (last_nsswitch_modified.tv_sec == nss_buf.st_mtim.tv_sec && |
| last_nsswitch_modified.tv_nsec < nss_buf.st_mtim.tv_nsec)) { |
| FLAG_RESTART_REQUIRED; |
| } |
| |
| if (restart == 0) { |
| if (stat("/etc/resolv.conf", &res_buf) < 0) { |
| /* Unable to stat file, were we previously? */ |
| if (last_resolv_modified.tv_sec > 0) { |
| /* Yes, it must have been removed. */ |
| FLAG_RESTART_REQUIRED; |
| } else if (last_resolv_modified.tv_sec == -1) { |
| /* No, then we've never seen it. */ |
| last_resolv_modified.tv_sec = 0; |
| } |
| } else if (last_resolv_modified.tv_sec == -1) { |
| /* We've just started and file is present. */ |
| last_resolv_modified = res_buf.st_mtim; |
| } else if (last_resolv_modified.tv_sec == 0) { |
| /* Wasn't there at start-up. */ |
| FLAG_RESTART_REQUIRED; |
| } else if (last_resolv_modified.tv_sec < |
| res_buf.st_mtim.tv_sec || |
| (last_resolv_modified.tv_sec == |
| res_buf.st_mtim.tv_sec && |
| last_resolv_modified.tv_nsec < |
| res_buf.st_mtim.tv_nsec)) { |
| FLAG_RESTART_REQUIRED; |
| } |
| } |
| |
| if (restart == 1) { |
| char *fmri; |
| |
| /* |
| * if in self cred mode, kill the forker and |
| * child nscds |
| */ |
| if (_nscd_is_self_cred_on(0, NULL)) { |
| _nscd_kill_forker(); |
| _nscd_kill_all_children(); |
| } |
| |
| /* |
| * time for restart |
| */ |
| _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_INFO) |
| (me, "nscd restart due to %s or %s change\n", |
| "/etc/nsswitch.conf", "resolv.conf"); |
| /* |
| * try to restart under smf |
| */ |
| if ((fmri = getenv("SMF_FMRI")) == NULL) { |
| /* not running under smf - reexec */ |
| (void) execv(main_execname, main_argv); |
| exit(1); /* just in case */ |
| } |
| |
| if (smf_restart_instance(fmri) == 0) |
| (void) sleep(10); /* wait a bit */ |
| exit(1); /* give up waiting for resurrection */ |
| } |
| |
| } else |
| (void) mutex_unlock(&nsswitch_lock); |
| } |
| |
| uid_t |
| _nscd_get_client_euid() |
| { |
| ucred_t *uc = NULL; |
| uid_t id; |
| char *me = "get_client_euid"; |
| |
| if (door_ucred(&uc) != 0) { |
| _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG) |
| (me, "door_ucred: %s\n", strerror(errno)); |
| return ((uid_t)-1); |
| } |
| |
| id = ucred_geteuid(uc); |
| ucred_free(uc); |
| return (id); |
| } |
| |
| /* |
| * Check to see if the door client's euid is 0 or if it has required_priv |
| * privilege. Return 0 if yes, -1 otherwise. |
| * Supported values for required_priv are: |
| * - NSCD_ALL_PRIV: for all zones privileges |
| * - NSCD_READ_PRIV: for PRIV_FILE_DAC_READ privilege |
| */ |
| int |
| _nscd_check_client_priv(int required_priv) |
| { |
| int rc = 0; |
| ucred_t *uc = NULL; |
| const priv_set_t *eset; |
| char *me = "_nscd_check_client_read_priv"; |
| priv_set_t *zs; /* zone */ |
| |
| if (door_ucred(&uc) != 0) { |
| _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) |
| (me, "door_ucred: %s\n", strerror(errno)); |
| return (-1); |
| } |
| |
| if (ucred_geteuid(uc) == 0) { |
| ucred_free(uc); |
| return (0); |
| } |
| |
| eset = ucred_getprivset(uc, PRIV_EFFECTIVE); |
| switch (required_priv) { |
| case NSCD_ALL_PRIV: |
| zs = priv_str_to_set("zone", ",", NULL); |
| if (!priv_isequalset(eset, zs)) { |
| _NSCD_LOG(NSCD_LOG_FRONT_END, |
| NSCD_LOG_LEVEL_ERROR) |
| (me, "missing all zones privileges\n"); |
| rc = -1; |
| } |
| priv_freeset(zs); |
| break; |
| case NSCD_READ_PRIV: |
| if (!priv_ismember(eset, PRIV_FILE_DAC_READ)) |
| rc = -1; |
| break; |
| default: |
| _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) |
| (me, "unknown required_priv: %d\n", required_priv); |
| rc = -1; |
| break; |
| } |
| ucred_free(uc); |
| return (rc); |
| } |
| |
| static void |
| N2N_check_priv( |
| void *buf, |
| char *dc_str) |
| { |
| nss_pheader_t *phdr = (nss_pheader_t *)buf; |
| ucred_t *uc = NULL; |
| const priv_set_t *eset; |
| zoneid_t zoneid; |
| int errnum; |
| char *me = "N2N_check_priv"; |
| |
| if (door_ucred(&uc) != 0) { |
| errnum = errno; |
| _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG) |
| (me, "door_ucred: %s\n", strerror(errno)); |
| |
| NSCD_SET_STATUS(phdr, NSS_ERROR, errnum); |
| return; |
| } |
| |
| eset = ucred_getprivset(uc, PRIV_EFFECTIVE); |
| zoneid = ucred_getzoneid(uc); |
| |
| if ((zoneid != GLOBAL_ZONEID && zoneid != getzoneid()) || |
| eset != NULL ? !priv_ismember(eset, PRIV_SYS_ADMIN) : |
| ucred_geteuid(uc) != 0) { |
| |
| _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ALERT) |
| (me, "%s call failed(cred): caller pid %d, uid %d, " |
| "euid %d, zoneid %d\n", dc_str, ucred_getpid(uc), |
| ucred_getruid(uc), ucred_geteuid(uc), zoneid); |
| ucred_free(uc); |
| |
| NSCD_SET_STATUS(phdr, NSS_ERROR, EACCES); |
| return; |
| } |
| |
| _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG) |
| (me, "nscd received %s cmd from pid %d, uid %d, " |
| "euid %d, zoneid %d\n", dc_str, ucred_getpid(uc), |
| ucred_getruid(uc), ucred_geteuid(uc), zoneid); |
| |
| ucred_free(uc); |
| |
| NSCD_SET_STATUS_SUCCESS(phdr); |
| } |
| |
| void |
| _nscd_APP_check_cred( |
| void *buf, |
| pid_t *pidp, |
| char *dc_str, |
| int log_comp, |
| int log_level) |
| { |
| nss_pheader_t *phdr = (nss_pheader_t *)buf; |
| ucred_t *uc = NULL; |
| uid_t ruid; |
| uid_t euid; |
| pid_t pid; |
| int errnum; |
| char *me = "_nscd_APP_check_cred"; |
| |
| if (door_ucred(&uc) != 0) { |
| errnum = errno; |
| _NSCD_LOG(log_comp, NSCD_LOG_LEVEL_ERROR) |
| (me, "door_ucred: %s\n", strerror(errno)); |
| |
| NSCD_SET_STATUS(phdr, NSS_ERROR, errnum); |
| return; |
| } |
| |
| NSCD_SET_STATUS_SUCCESS(phdr); |
| pid = ucred_getpid(uc); |
| if (NSS_PACKED_CRED_CHECK(buf, ruid = ucred_getruid(uc), |
| euid = ucred_geteuid(uc))) { |
| if (pidp != NULL) { |
| if (*pidp == (pid_t)-1) |
| *pidp = pid; |
| else if (*pidp != pid) { |
| NSCD_SET_STATUS(phdr, NSS_ERROR, EACCES); |
| } |
| } |
| } else { |
| NSCD_SET_STATUS(phdr, NSS_ERROR, EACCES); |
| } |
| |
| ucred_free(uc); |
| |
| if (NSCD_STATUS_IS_NOT_OK(phdr)) { |
| _NSCD_LOG(log_comp, log_level) |
| (me, "%s call failed: caller pid %d (input pid = %d), ruid %d, " |
| "euid %d, header ruid %d, header euid %d\n", dc_str, |
| pid, (pidp != NULL) ? *pidp : -1, ruid, euid, |
| ((nss_pheader_t *)(buf))->p_ruid, |
| ((nss_pheader_t *)(buf))->p_euid); |
| } |
| } |
| |
| /* log error and return -1 when an invalid packed buffer header is found */ |
| static int |
| pheader_error(nss_pheader_t *phdr, uint32_t call_number) |
| { |
| char *call_num_str; |
| |
| switch (call_number) { |
| case NSCD_SEARCH: |
| call_num_str = "NSCD_SEARCH"; |
| break; |
| case NSCD_SETENT: |
| call_num_str = "NSCD_SETENT"; |
| break; |
| case NSCD_GETENT: |
| call_num_str = "NSCD_GETENT"; |
| break; |
| case NSCD_ENDENT: |
| call_num_str = "NSCD_ENDENT"; |
| break; |
| case NSCD_PUT: |
| call_num_str = "NSCD_PUT"; |
| break; |
| case NSCD_GETHINTS: |
| call_num_str = "NSCD_GETHINTS"; |
| break; |
| default: |
| call_num_str = "UNKNOWN"; |
| break; |
| } |
| |
| _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ALERT) |
| ("pheader_error", "call number %s: invalid packed buffer header\n", |
| call_num_str); |
| |
| NSCD_SET_STATUS(phdr, NSS_ERROR, EINVAL); |
| return (-1); |
| } |
| |
| /* |
| * Validate the header of a getXbyY or setent/getent/endent request. |
| * Return 0 if good, -1 otherwise. |
| * |
| * A valid header looks like the following (size is arg_size, does |
| * not include the output area): |
| * +----------------------------------+ -- |
| * | nss_pheader_t (header fixed part)| ^ |
| * | | | |
| * | pbufsiz, dbd,off, key_off, | len = sizeof(nss_pheader_t) |
| * | data_off .... | | |
| * | | v |
| * +----------------------------------+ <----- dbd_off |
| * | dbd (database description) | ^ |
| * | nss_dbd_t + up to 3 strings | | |
| * | length = sizeof(nss_dbd_t) + | len = key_off - dbd_off |
| * | length of 3 strings + | | |
| * | length of padding | | |
| * | (total length in multiple of 4) | v |
| * +----------------------------------+ <----- key_off |
| * | lookup key | ^ |
| * | nss_XbyY_key_t, content varies, | | |
| * | based on database and lookup op | len = data_off - key_off |
| * | length = data_off - key_off | | |
| * | including padding, multiple of 4 | v |
| * +----------------------------------+ <----- data_off (= arg_size) |
| * | | ^ |
| * | area to hold results | | |
| * | | len = data_len (= pbufsiz - |
| * | | | data_off) |
| * | | v |
| * +----------------------------------+ <----- pbufsiz |
| */ |
| static int |
| validate_pheader( |
| void *argp, |
| size_t arg_size, |
| uint32_t call_number) |
| { |
| nss_pheader_t *phdr = (nss_pheader_t *)(void *)argp; |
| nssuint_t l1, l2; |
| |
| /* |
| * current version is NSCD_HEADER_REV, length of the fixed part |
| * of the header must match the size of nss_pheader_t |
| */ |
| if (phdr->p_version != NSCD_HEADER_REV || |
| phdr->dbd_off != sizeof (nss_pheader_t)) |
| return (pheader_error(phdr, call_number)); |
| |
| /* |
| * buffer size and offsets must be in multiple of 4 |
| */ |
| if ((arg_size & 3) || (phdr->dbd_off & 3) || (phdr->key_off & 3) || |
| (phdr->data_off & 3)) |
| return (pheader_error(phdr, call_number)); |
| |
| /* |
| * the input arg_size is the length of the request header |
| * and should be less than NSCD_PHDR_MAXLEN |
| */ |
| if (phdr->data_off != arg_size || arg_size > NSCD_PHDR_MAXLEN) |
| return (pheader_error(phdr, call_number)); |
| |
| /* get length of the dbd area */ |
| l1 = phdr->key_off - phdr-> dbd_off; |
| |
| /* |
| * dbd area may contain padding, so length of dbd should |
| * not be less than the length of the actual data |
| */ |
| if (l1 < phdr->dbd_len) |
| return (pheader_error(phdr, call_number)); |
| |
| /* get length of the key area */ |
| l2 = phdr->data_off - phdr->key_off; |
| |
| /* |
| * key area may contain padding, so length of key area should |
| * not be less than the length of the actual data |
| */ |
| if (l2 < phdr->key_len) |
| return (pheader_error(phdr, call_number)); |
| |
| /* |
| * length of fixed part + lengths of dbd and key area = length of |
| * the request header |
| */ |
| if (sizeof (nss_pheader_t) + l1 + l2 != phdr->data_off) |
| return (pheader_error(phdr, call_number)); |
| |
| /* header length + data length = buffer length */ |
| if (phdr->data_off + phdr->data_len != phdr->pbufsiz) |
| return (pheader_error(phdr, call_number)); |
| |
| return (0); |
| } |
| |
| /* log error and return -1 when an invalid nscd to nscd buffer is found */ |
| static int |
| N2Nbuf_error(nss_pheader_t *phdr, uint32_t call_number) |
| { |
| char *call_num_str; |
| |
| switch (call_number) { |
| case NSCD_PING: |
| call_num_str = "NSCD_PING"; |
| break; |
| |
| case NSCD_IMHERE: |
| call_num_str = "NSCD_IMHERE"; |
| break; |
| |
| case NSCD_PULSE: |
| call_num_str = "NSCD_PULSE"; |
| break; |
| |
| case NSCD_FORK: |
| call_num_str = "NSCD_FORK"; |
| break; |
| |
| case NSCD_KILL: |
| call_num_str = "NSCD_KILL"; |
| break; |
| |
| case NSCD_REFRESH: |
| call_num_str = "NSCD_REFRESH"; |
| break; |
| |
| case NSCD_GETPUADMIN: |
| call_num_str = "NSCD_GETPUADMIN"; |
| break; |
| |
| case NSCD_GETADMIN: |
| call_num_str = "NSCD_GETADMIN"; |
| break; |
| |
| case NSCD_SETADMIN: |
| call_num_str = "NSCD_SETADMIN"; |
| break; |
| |
| case NSCD_KILLSERVER: |
| call_num_str = "NSCD_KILLSERVER"; |
| break; |
| default: |
| call_num_str = "UNKNOWN"; |
| break; |
| } |
| |
| _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ALERT) |
| ("N2Nbuf_error", "call number %s: invalid N2N buffer\n", call_num_str); |
| |
| NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0, |
| NSCD_DOOR_BUFFER_CHECK_FAILED); |
| |
| return (-1); |
| } |
| |
| /* |
| * Validate the buffer of an nscd to nscd request. |
| * Return 0 if good, -1 otherwise. |
| * |
| * A valid buffer looks like the following (size is arg_size): |
| * +----------------------------------+ -- |
| * | nss_pheader_t (header fixed part)| ^ |
| * | | | |
| * | pbufsiz, dbd,off, key_off, | len = sizeof(nss_pheader_t) |
| * | data_off .... | | |
| * | | v |
| * +----------------------------------+ <---dbd_off = key_off = data_off |
| * | | ^ |
| * | input data/output data | | |
| * | OR no data | len = data_len (= pbufsiz - |
| * | | | data_off) |
| * | | | len could be zero |
| * | | v |
| * +----------------------------------+ <--- pbufsiz |
| */ |
| static int |
| validate_N2Nbuf( |
| void *argp, |
| size_t arg_size, |
| uint32_t call_number) |
| { |
| nss_pheader_t *phdr = (nss_pheader_t *)(void *)argp; |
| |
| /* |
| * current version is NSCD_HEADER_REV, length of the fixed part |
| * of the header must match the size of nss_pheader_t |
| */ |
| if (phdr->p_version != NSCD_HEADER_REV || |
| phdr->dbd_off != sizeof (nss_pheader_t)) |
| return (N2Nbuf_error(phdr, call_number)); |
| |
| /* |
| * There are no dbd and key data, so the dbd, key, data |
| * offsets should be equal |
| */ |
| if (phdr->dbd_off != phdr->key_off || |
| phdr->dbd_off != phdr->data_off) |
| return (N2Nbuf_error(phdr, call_number)); |
| |
| /* |
| * the input arg_size is the buffer length and should |
| * be less or equal than NSCD_N2NBUF_MAXLEN |
| */ |
| if (phdr->pbufsiz != arg_size || arg_size > NSCD_N2NBUF_MAXLEN) |
| return (N2Nbuf_error(phdr, call_number)); |
| |
| /* header length + data length = buffer length */ |
| if (phdr->data_off + phdr->data_len != phdr->pbufsiz) |
| return (N2Nbuf_error(phdr, call_number)); |
| |
| return (0); |
| } |
| |
| static void |
| lookup(char *argp, size_t arg_size) |
| { |
| nsc_lookup_args_t largs; |
| char space[NSCD_LOOKUP_BUFSIZE]; |
| nss_pheader_t *phdr = (nss_pheader_t *)(void *)argp; |
| |
| NSCD_ALLOC_LOOKUP_BUFFER(argp, arg_size, phdr, space, |
| sizeof (space)); |
| |
| /* |
| * make sure the first couple bytes of the data area is null, |
| * so that bad strings in the packed header stop here |
| */ |
| (void) memset((char *)phdr + phdr->data_off, 0, 16); |
| |
| (void) memset(&largs, 0, sizeof (largs)); |
| largs.buffer = argp; |
| largs.bufsize = arg_size; |
| nsc_lookup(&largs, 0); |
| |
| /* |
| * only the PUN needs to keep track of the |
| * activity count to determine when to |
| * terminate itself |
| */ |
| if (_whoami == NSCD_CHILD) { |
| (void) mutex_lock(&activity_lock); |
| ++activity; |
| (void) mutex_unlock(&activity_lock); |
| } |
| |
| NSCD_SET_RETURN_ARG(phdr, arg_size); |
| (void) door_return(argp, arg_size, NULL, 0); |
| } |
| |
| static void |
| getent(char *argp, size_t arg_size) |
| { |
| char space[NSCD_LOOKUP_BUFSIZE]; |
| nss_pheader_t *phdr = (nss_pheader_t *)(void *)argp; |
| |
| NSCD_ALLOC_LOOKUP_BUFFER(argp, arg_size, phdr, space, sizeof (space)); |
| |
| nss_pgetent(argp, arg_size); |
| |
| NSCD_SET_RETURN_ARG(phdr, arg_size); |
| (void) door_return(argp, arg_size, NULL, 0); |
| } |
| |
| static int |
| is_db_per_user(void *buf, char *dblist) |
| { |
| nss_pheader_t *phdr = (nss_pheader_t *)buf; |
| nss_dbd_t *pdbd; |
| char *dbname, *dbn; |
| int len; |
| |
| /* copy db name into a temp buffer */ |
| pdbd = (nss_dbd_t *)((void *)((char *)buf + phdr->dbd_off)); |
| dbname = (char *)pdbd + pdbd->o_name; |
| len = strlen(dbname); |
| dbn = alloca(len + 2); |
| (void) memcpy(dbn, dbname, len); |
| |
| /* check if <dbname> + ',' can be found in the dblist string */ |
| dbn[len] = ','; |
| dbn[len + 1] = '\0'; |
| if (strstr(dblist, dbn) != NULL) |
| return (1); |
| |
| /* |
| * check if <dbname> can be found in the last part |
| * of the dblist string |
| */ |
| dbn[len] = '\0'; |
| if (strstr(dblist, dbn) != NULL) |
| return (1); |
| |
| return (0); |
| } |
| |
| /* |
| * Check to see if all conditions are met for processing per-user |
| * requests. Returns 1 if yes, -1 if backend is not configured, |
| * 0 otherwise. |
| */ |
| static int |
| need_per_user_door(void *buf, int whoami, uid_t uid, char **dblist) |
| { |
| nss_pheader_t *phdr = (nss_pheader_t *)buf; |
| |
| NSCD_SET_STATUS_SUCCESS(phdr); |
| |
| /* if already a per-user nscd, no need to get per-user door */ |
| if (whoami == NSCD_CHILD) |
| return (0); |
| |
| /* forker shouldn't be asked */ |
| if (whoami == NSCD_FORKER) { |
| NSCD_SET_STATUS(phdr, NSS_ERROR, ENOTSUP); |
| return (0); |
| } |
| |
| /* if door client is root, no need for a per-user door */ |
| if (uid == 0) |
| return (0); |
| |
| /* |
| * if per-user lookup is not configured, no per-user |
| * door available |
| */ |
| if (_nscd_is_self_cred_on(0, dblist) == 0) |
| return (-1); |
| |
| /* |
| * if per-user lookup is not configured for the db, |
| * don't bother |
| */ |
| if (is_db_per_user(phdr, *dblist) == 0) |
| return (0); |
| |
| return (1); |
| } |
| |
| static void |
| if_selfcred_return_per_user_door(char *argp, size_t arg_size, |
| door_desc_t *dp, int whoami) |
| { |
| nss_pheader_t *phdr = (nss_pheader_t *)((void *)argp); |
| char *dblist; |
| int door = -1; |
| int rc = 0; |
| door_desc_t desc; |
| char *space; |
| int len; |
| |
| /* |
| * check to see if self-cred is configured and |
| * need to return an alternate PUN door |
| */ |
| if (per_user_is_on == 1) { |
| rc = need_per_user_door(argp, whoami, |
| _nscd_get_client_euid(), &dblist); |
| if (rc == -1) |
| per_user_is_on = 0; |
| } |
| if (rc <= 0) { |
| /* |
| * self-cred not configured, and no error detected, |
| * return to continue the door call processing |
| */ |
| if (NSCD_STATUS_IS_OK(phdr)) |
| return; |
| else |
| /* |
| * configured but error detected, |
| * stop the door call processing |
| */ |
| (void) door_return(argp, phdr->data_off, NULL, 0); |
| } |
| |
| /* get the alternate PUN door */ |
| _nscd_proc_alt_get(argp, &door); |
| if (NSCD_GET_STATUS(phdr) != NSS_ALTRETRY) { |
| (void) door_return(argp, phdr->data_off, NULL, 0); |
| } |
| |
| /* return the alternate door descriptor */ |
| len = strlen(dblist) + 1; |
| space = alloca(arg_size + len); |
| phdr->data_len = len; |
| (void) memcpy(space, phdr, arg_size); |
| (void) strncpy((char *)space + arg_size, dblist, len); |
| dp = &desc; |
| dp->d_attributes = DOOR_DESCRIPTOR; |
| dp->d_data.d_desc.d_descriptor = door; |
| arg_size += len; |
| (void) door_return(space, arg_size, dp, 1); |
| } |
| |
| /*ARGSUSED*/ |
| static void |
| switcher(void *cookie, char *argp, size_t arg_size, |
| door_desc_t *dp, uint_t n_desc) |
| { |
| int iam; |
| pid_t ent_pid = -1; |
| nss_pheader_t *phdr = (nss_pheader_t *)((void *)argp); |
| void *uptr; |
| int len; |
| size_t buflen; |
| int callnum; |
| char *me = "switcher"; |
| |
| _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG) |
| (me, "switcher ...\n"); |
| |
| if (argp == DOOR_UNREF_DATA) { |
| (void) printf("Door Slam... exiting\n"); |
| exit(0); |
| } |
| |
| if (argp == NULL) { /* empty door call */ |
| (void) door_return(NULL, 0, 0, 0); /* return the favor */ |
| } |
| |
| /* |
| * need to restart if main nscd and config file(s) changed |
| */ |
| if (_whoami == NSCD_MAIN) |
| _nscd_restart_if_cfgfile_changed(); |
| |
| if ((phdr->nsc_callnumber & NSCDV2CATMASK) == NSCD_CALLCAT_APP) { |
| |
| /* make sure the packed buffer header is good */ |
| if (validate_pheader(argp, arg_size, |
| phdr->nsc_callnumber) == -1) |
| (void) door_return(argp, arg_size, NULL, 0); |
| |
| switch (phdr->nsc_callnumber) { |
| |
| case NSCD_SEARCH: |
| |
| /* if a fallback to main nscd, skip per-user setup */ |
| if (phdr->p_status != NSS_ALTRETRY) |
| if_selfcred_return_per_user_door(argp, arg_size, |
| dp, _whoami); |
| lookup(argp, arg_size); |
| |
| break; |
| |
| case NSCD_SETENT: |
| |
| _nscd_APP_check_cred(argp, &ent_pid, "NSCD_SETENT", |
| NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ALERT); |
| if (NSCD_STATUS_IS_OK(phdr)) { |
| if_selfcred_return_per_user_door(argp, arg_size, |
| dp, _whoami); |
| nss_psetent(argp, arg_size, ent_pid); |
| } |
| break; |
| |
| case NSCD_GETENT: |
| |
| getent(argp, arg_size); |
| break; |
| |
| case NSCD_ENDENT: |
| |
| nss_pendent(argp, arg_size); |
| break; |
| |
| case NSCD_PUT: |
| |
| _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) |
| (me, "door call NSCD_PUT not supported yet\n"); |
| |
| NSCD_SET_STATUS(phdr, NSS_ERROR, ENOTSUP); |
| break; |
| |
| case NSCD_GETHINTS: |
| |
| _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) |
| (me, "door call NSCD_GETHINTS not supported yet\n"); |
| |
| NSCD_SET_STATUS(phdr, NSS_ERROR, ENOTSUP); |
| break; |
| |
| default: |
| |
| _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) |
| (me, "Unknown name service door call op %x\n", |
| phdr->nsc_callnumber); |
| |
| NSCD_SET_STATUS(phdr, NSS_ERROR, EINVAL); |
| break; |
| } |
| |
| (void) door_return(argp, arg_size, NULL, 0); |
| } |
| |
| iam = NSCD_MAIN; |
| callnum = phdr->nsc_callnumber & ~NSCD_WHOAMI; |
| if (callnum == NSCD_IMHERE || |
| callnum == NSCD_PULSE || callnum == NSCD_FORK) |
| iam = phdr->nsc_callnumber & NSCD_WHOAMI; |
| else |
| callnum = phdr->nsc_callnumber; |
| |
| /* nscd -> nscd v2 calls */ |
| |
| /* make sure the buffer is good */ |
| if (validate_N2Nbuf(argp, arg_size, callnum) == -1) |
| (void) door_return(argp, arg_size, NULL, 0); |
| |
| switch (callnum) { |
| |
| case NSCD_PING: |
| NSCD_SET_STATUS_SUCCESS(phdr); |
| break; |
| |
| case NSCD_IMHERE: |
| _nscd_proc_iamhere(argp, dp, n_desc, iam); |
| break; |
| |
| case NSCD_PULSE: |
| N2N_check_priv(argp, "NSCD_PULSE"); |
| if (NSCD_STATUS_IS_OK(phdr)) |
| _nscd_proc_pulse(argp, iam); |
| break; |
| |
| case NSCD_FORK: |
| N2N_check_priv(argp, "NSCD_FORK"); |
| if (NSCD_STATUS_IS_OK(phdr)) |
| _nscd_proc_fork(argp, iam); |
| break; |
| |
| case NSCD_KILL: |
| N2N_check_priv(argp, "NSCD_KILL"); |
| if (NSCD_STATUS_IS_OK(phdr)) |
| exit(0); |
| break; |
| |
| case NSCD_REFRESH: |
| N2N_check_priv(argp, "NSCD_REFRESH"); |
| if (NSCD_STATUS_IS_OK(phdr)) { |
| if (_nscd_refresh() != NSCD_SUCCESS) |
| exit(1); |
| NSCD_SET_STATUS_SUCCESS(phdr); |
| } |
| break; |
| |
| case NSCD_GETPUADMIN: |
| |
| if (_nscd_is_self_cred_on(0, NULL)) { |
| _nscd_peruser_getadmin(argp, sizeof (nscd_admin_t)); |
| } else { |
| NSCD_SET_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0, |
| NSCD_SELF_CRED_NOT_CONFIGURED); |
| } |
| break; |
| |
| case NSCD_GETADMIN: |
| |
| len = _nscd_door_getadmin((void *)argp); |
| if (len == 0) |
| break; |
| |
| /* size of door buffer not big enough, allocate one */ |
| NSCD_ALLOC_DOORBUF(NSCD_GETADMIN, len, uptr, buflen); |
| |
| /* copy packed header */ |
| *(nss_pheader_t *)uptr = *(nss_pheader_t *)((void *)argp); |
| |
| /* set new buffer size */ |
| ((nss_pheader_t *)uptr)->pbufsiz = buflen; |
| |
| /* try one more time */ |
| (void) _nscd_door_getadmin((void *)uptr); |
| (void) door_return(uptr, buflen, NULL, 0); |
| break; |
| |
| case NSCD_SETADMIN: |
| N2N_check_priv(argp, "NSCD_SETADMIN"); |
| if (NSCD_STATUS_IS_OK(phdr)) |
| _nscd_door_setadmin(argp); |
| break; |
| |
| case NSCD_KILLSERVER: |
| N2N_check_priv(argp, "NSCD_KILLSERVER"); |
| if (NSCD_STATUS_IS_OK(phdr)) { |
| /* also kill the forker nscd if one is running */ |
| _nscd_kill_forker(); |
| exit(0); |
| } |
| break; |
| |
| default: |
| _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) |
| (me, "Unknown name service door call op %d\n", |
| phdr->nsc_callnumber); |
| |
| NSCD_SET_STATUS(phdr, NSS_ERROR, EINVAL); |
| |
| (void) door_return(argp, arg_size, NULL, 0); |
| break; |
| |
| } |
| (void) door_return(argp, arg_size, NULL, 0); |
| } |
| |
| int |
| _nscd_setup_server(char *execname, char **argv) |
| { |
| |
| int fd; |
| int errnum; |
| int bind_failed = 0; |
| mode_t old_mask; |
| struct stat buf; |
| sigset_t myset; |
| struct sigaction action; |
| char *me = "_nscd_setup_server"; |
| |
| main_execname = execname; |
| main_argv = argv; |
| |
| /* Any nscd process is to ignore SIGPIPE */ |
| if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { |
| errnum = errno; |
| _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) |
| (me, "signal (SIGPIPE): %s\n", strerror(errnum)); |
| return (-1); |
| } |
| |
| keep_open_dns_socket(); |
| |
| /* |
| * the max number of server threads should be fixed now, so |
| * set flag to indicate that no in-flight change is allowed |
| */ |
| max_servers_set = 1; |
| |
| (void) thr_keycreate(&lookup_state_key, NULL); |
| (void) sema_init(&common_sema, frontend_cfg_g.common_worker_threads, |
| USYNC_THREAD, 0); |
| |
| /* Establish server thread pool */ |
| (void) door_server_create(server_create); |
| if (thr_keycreate(&server_key, server_destroy) != 0) { |
| errnum = errno; |
| _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) |
| (me, "thr_keycreate (server thread): %s\n", |
| strerror(errnum)); |
| return (-1); |
| } |
| |
| /* Create a door */ |
| if ((fd = door_create(switcher, NAME_SERVICE_DOOR_COOKIE, |
| DOOR_UNREF | DOOR_NO_CANCEL)) < 0) { |
| errnum = errno; |
| _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) |
| (me, "door_create: %s\n", strerror(errnum)); |
| return (-1); |
| } |
| |
| /* if not main nscd, no more setup to do */ |
| if (_whoami != NSCD_MAIN) |
| return (fd); |
| |
| /* bind to file system */ |
| if (is_system_labeled() && (getzoneid() == GLOBAL_ZONEID)) { |
| if (stat(TSOL_NAME_SERVICE_DOOR, &buf) < 0) { |
| int newfd; |
| |
| /* make sure the door will be readable by all */ |
| old_mask = umask(0); |
| if ((newfd = creat(TSOL_NAME_SERVICE_DOOR, 0444)) < 0) { |
| errnum = errno; |
| _NSCD_LOG(NSCD_LOG_FRONT_END, |
| NSCD_LOG_LEVEL_ERROR) |
| (me, "Cannot create %s: %s\n", |
| TSOL_NAME_SERVICE_DOOR, |
| strerror(errnum)); |
| bind_failed = 1; |
| } |
| /* rstore the old file mode creation mask */ |
| (void) umask(old_mask); |
| (void) close(newfd); |
| } |
| if (symlink(TSOL_NAME_SERVICE_DOOR, NAME_SERVICE_DOOR) != 0) { |
| if (errno != EEXIST) { |
| errnum = errno; |
| _NSCD_LOG(NSCD_LOG_FRONT_END, |
| NSCD_LOG_LEVEL_ERROR) |
| (me, "Cannot symlink %s: %s\n", |
| NAME_SERVICE_DOOR, strerror(errnum)); |
| bind_failed = 1; |
| } |
| } |
| } else if (stat(NAME_SERVICE_DOOR, &buf) < 0) { |
| int newfd; |
| |
| /* make sure the door will be readable by all */ |
| old_mask = umask(0); |
| if ((newfd = creat(NAME_SERVICE_DOOR, 0444)) < 0) { |
| errnum = errno; |
| _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) |
| (me, "Cannot create %s: %s\n", NAME_SERVICE_DOOR, |
| strerror(errnum)); |
| bind_failed = 1; |
| } |
| /* rstore the old file mode creation mask */ |
| (void) umask(old_mask); |
| (void) close(newfd); |
| } |
| |
| if (bind_failed == 1) { |
| (void) door_revoke(fd); |
| return (-1); |
| } |
| |
| if (fattach(fd, NAME_SERVICE_DOOR) < 0) { |
| if ((errno != EBUSY) || |
| (fdetach(NAME_SERVICE_DOOR) < 0) || |
| (fattach(fd, NAME_SERVICE_DOOR) < 0)) { |
| errnum = errno; |
| _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) |
| (me, "fattach: %s\n", strerror(errnum)); |
| (void) door_revoke(fd); |
| return (-1); |
| } |
| } |
| |
| /* |
| * kick off routing socket monitor thread |
| */ |
| if (thr_create(NULL, NULL, |
| (void *(*)(void *))rts_mon, 0, 0, NULL) != 0) { |
| errnum = errno; |
| _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) |
| (me, "thr_create (routing socket monitor): %s\n", |
| strerror(errnum)); |
| |
| (void) door_revoke(fd); |
| return (-1); |
| } |
| |
| /* |
| * set up signal handler for SIGHUP |
| */ |
| action.sa_handler = dozip; |
| action.sa_flags = 0; |
| (void) sigemptyset(&action.sa_mask); |
| (void) sigemptyset(&myset); |
| (void) sigaddset(&myset, SIGHUP); |
| |
| if (sigaction(SIGHUP, &action, NULL) < 0) { |
| errnum = errno; |
| _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) |
| (me, "sigaction (SIGHUP): %s\n", strerror(errnum)); |
| |
| (void) door_revoke(fd); |
| return (-1); |
| } |
| |
| return (fd); |
| } |
| |
| int |
| _nscd_setup_child_server(int did) |
| { |
| |
| int errnum; |
| int fd; |
| nscd_rc_t rc; |
| char *me = "_nscd_setup_child_server"; |
| |
| /* Re-establish our own server thread pool */ |
| (void) door_server_create(server_create); |
| if (thr_keycreate(&server_key, server_destroy) != 0) { |
| errnum = errno; |
| _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG) |
| (me, "thr_keycreate failed: %s", strerror(errnum)); |
| return (-1); |
| } |
| |
| /* |
| * Create a new door. |
| * Keep DOOR_REFUSE_DESC (self-cred nscds don't fork) |
| */ |
| (void) close(did); |
| if ((fd = door_create(switcher, NAME_SERVICE_DOOR_COOKIE, |
| DOOR_REFUSE_DESC|DOOR_UNREF|DOOR_NO_CANCEL)) < 0) { |
| errnum = errno; |
| _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_DEBUG) |
| (me, "door_create failed: %s", strerror(errnum)); |
| return (-1); |
| } |
| |
| /* |
| * kick off routing socket monitor thread |
| */ |
| if (thr_create(NULL, NULL, |
| (void *(*)(void *))rts_mon, 0, 0, NULL) != 0) { |
| errnum = errno; |
| _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) |
| (me, "thr_create (routing socket monitor): %s\n", |
| strerror(errnum)); |
| (void) door_revoke(fd); |
| return (-1); |
| } |
| |
| /* |
| * start monitoring the states of the name service clients |
| */ |
| rc = _nscd_init_smf_monitor(); |
| if (rc != NSCD_SUCCESS) { |
| _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) |
| (me, "unable to start the SMF monitor (rc = %d)\n", rc); |
| |
| (void) door_revoke(fd); |
| return (-1); |
| } |
| |
| return (fd); |
| } |
| |
| nscd_rc_t |
| _nscd_alloc_frontend_cfg() |
| { |
| frontend_cfg = calloc(NSCD_NUM_DB, sizeof (nscd_cfg_frontend_t)); |
| if (frontend_cfg == NULL) |
| return (NSCD_NO_MEMORY); |
| |
| return (NSCD_SUCCESS); |
| } |
| |
| |
| /* ARGSUSED */ |
| nscd_rc_t |
| _nscd_cfg_frontend_notify( |
| void *data, |
| struct nscd_cfg_param_desc *pdesc, |
| nscd_cfg_id_t *nswdb, |
| nscd_cfg_flag_t dflag, |
| nscd_cfg_error_t **errorp, |
| void *cookie) |
| { |
| void *dp; |
| |
| /* |
| * At init time, the whole group of config params are received. |
| * At update time, group or individual parameter value could |
| * be received. |
| */ |
| |
| if (_nscd_cfg_flag_is_set(dflag, NSCD_CFG_DFLAG_INIT) || |
| _nscd_cfg_flag_is_set(dflag, NSCD_CFG_DFLAG_GROUP)) { |
| /* |
| * group data is received, copy in the |
| * entire strcture |
| */ |
| if (_nscd_cfg_flag_is_set(pdesc->pflag, NSCD_CFG_PFLAG_GLOBAL)) |
| frontend_cfg_g = *(nscd_cfg_global_frontend_t *)data; |
| else |
| frontend_cfg[nswdb->index] = |
| *(nscd_cfg_frontend_t *)data; |
| |
| } else { |
| /* |
| * individual paramater is received: copy in the |
| * parameter value. |
| */ |
| if (_nscd_cfg_flag_is_set(pdesc->pflag, NSCD_CFG_PFLAG_GLOBAL)) |
| dp = (char *)&frontend_cfg_g + pdesc->p_offset; |
| else |
| dp = (char *)&frontend_cfg[nswdb->index] + |
| pdesc->p_offset; |
| (void) memcpy(dp, data, pdesc->p_size); |
| } |
| |
| return (NSCD_SUCCESS); |
| } |
| |
| /* ARGSUSED */ |
| nscd_rc_t |
| _nscd_cfg_frontend_verify( |
| void *data, |
| struct nscd_cfg_param_desc *pdesc, |
| nscd_cfg_id_t *nswdb, |
| nscd_cfg_flag_t dflag, |
| nscd_cfg_error_t **errorp, |
| void **cookie) |
| { |
| |
| char *me = "_nscd_cfg_frontend_verify"; |
| |
| /* |
| * if max. number of server threads is set and in effect, |
| * don't allow changing of the frontend configuration |
| */ |
| if (max_servers_set) { |
| _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_INFO) |
| (me, "changing of the frontend configuration not allowed now"); |
| |
| return (NSCD_CFG_CHANGE_NOT_ALLOWED); |
| } |
| |
| return (NSCD_SUCCESS); |
| } |
| |
| /* ARGSUSED */ |
| nscd_rc_t |
| _nscd_cfg_frontend_get_stat( |
| void **stat, |
| struct nscd_cfg_stat_desc *sdesc, |
| nscd_cfg_id_t *nswdb, |
| nscd_cfg_flag_t *dflag, |
| void (**free_stat)(void *stat), |
| nscd_cfg_error_t **errorp) |
| { |
| return (NSCD_SUCCESS); |
| } |
| |
| void |
| _nscd_init_cache_sema(sema_t *sema, char *cache_name) |
| { |
| int i, j; |
| char *dbn; |
| |
| if (max_servers == 0) |
| max_servers = frontend_cfg_g.common_worker_threads + |
| frontend_cfg_g.cache_hit_threads; |
| |
| for (i = 0; i < NSCD_NUM_DB; i++) { |
| |
| dbn = NSCD_NSW_DB_NAME(i); |
| if (strcasecmp(dbn, cache_name) == 0) { |
| j = frontend_cfg[i].worker_thread_per_nsw_db; |
| (void) sema_init(sema, j, USYNC_THREAD, 0); |
| max_servers += j; |
| break; |
| } |
| } |
| } |
| |
| /* |
| * Monitor the routing socket. Address lists stored in the ipnodes |
| * cache are sorted based on destination address selection rules, |
| * so when things change that could affect that sorting (interfaces |
| * go up or down, flags change, etc.), we clear that cache so the |
| * list will be re-ordered the next time the hostname is resolved. |
| */ |
| static void |
| rts_mon(void) |
| { |
| int rt_sock, rdlen, idx; |
| union { |
| struct { |
| struct rt_msghdr rtm; |
| struct sockaddr_storage addrs[RTA_NUMBITS]; |
| } r; |
| struct if_msghdr ifm; |
| struct ifa_msghdr ifam; |
| } mbuf; |
| struct ifa_msghdr *ifam = &mbuf.ifam; |
| char *me = "rts_mon"; |
| |
| rt_sock = socket(PF_ROUTE, SOCK_RAW, 0); |
| if (rt_sock < 0) { |
| _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) |
| (me, "Failed to open routing socket: %s\n", strerror(errno)); |
| thr_exit(0); |
| } |
| |
| for (;;) { |
| rdlen = read(rt_sock, &mbuf, sizeof (mbuf)); |
| if (rdlen <= 0) { |
| if (rdlen == 0 || (errno != EINTR && errno != EAGAIN)) { |
| _NSCD_LOG(NSCD_LOG_FRONT_END, |
| NSCD_LOG_LEVEL_ERROR) |
| (me, "routing socket read: %s\n", |
| strerror(errno)); |
| thr_exit(0); |
| } |
| continue; |
| } |
| if (ifam->ifam_version != RTM_VERSION) { |
| _NSCD_LOG(NSCD_LOG_FRONT_END, |
| NSCD_LOG_LEVEL_ERROR) |
| (me, "rx unknown version (%d) on " |
| "routing socket.\n", |
| ifam->ifam_version); |
| continue; |
| } |
| switch (ifam->ifam_type) { |
| case RTM_NEWADDR: |
| case RTM_DELADDR: |
| /* if no ipnodes cache, then nothing to do */ |
| idx = get_cache_idx("ipnodes"); |
| if (cache_ctx_p[idx] == NULL || |
| cache_ctx_p[idx]->reaper_on != nscd_true) |
| break; |
| nsc_invalidate(cache_ctx_p[idx], NULL, NULL); |
| break; |
| case RTM_ADD: |
| case RTM_DELETE: |
| case RTM_CHANGE: |
| case RTM_GET: |
| case RTM_LOSING: |
| case RTM_REDIRECT: |
| case RTM_MISS: |
| case RTM_LOCK: |
| case RTM_OLDADD: |
| case RTM_OLDDEL: |
| case RTM_RESOLVE: |
| case RTM_IFINFO: |
| case RTM_CHGADDR: |
| case RTM_FREEADDR: |
| break; |
| default: |
| _NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR) |
| (me, "rx unknown msg type (%d) on routing socket.\n", |
| ifam->ifam_type); |
| break; |
| } |
| } |
| } |
| |
| static void |
| keep_open_dns_socket(void) |
| { |
| _res.options |= RES_STAYOPEN; /* just keep this udp socket open */ |
| } |