/* * If the domain to be discovered matches the current domain (i.e the * value of either domain or fqdn configuration), then get the primary * domain information from SMF. */ static uint32_t smb_ddiscover_use_config(char *domain, smb_domainex_t *dxi) { boolean_t use; smb_domain_t *dinfo; dinfo = &dxi->d_primary; bzero(dinfo, sizeof (smb_domain_t)); if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN) return (NT_STATUS_UNSUCCESSFUL); smb_config_getdomaininfo(dinfo->di_nbname, dinfo->di_fqname, NULL, NULL, NULL); if (SMB_IS_FQDN(domain)) use = (smb_strcasecmp(dinfo->di_fqname, domain, 0) == 0); else use = (smb_strcasecmp(dinfo->di_nbname, domain, 0) == 0); if (use) smb_config_getdomaininfo(NULL, NULL, dinfo->di_sid, dinfo->di_u.di_dns.ddi_forest, dinfo->di_u.di_dns.ddi_guid); return ((use) ? NT_STATUS_SUCCESS : NT_STATUS_UNSUCCESSFUL); }
/* * smb_getfqdomainname * * In the system is in domain mode, the dns_domain property value * is returned. Otherwise, it returns the local domain obtained via * resolver. * * Returns 0 upon success. Otherwise, returns -1. */ int smb_getfqdomainname(char *buf, size_t buflen) { struct __res_state res_state; int rc; if (buf == NULL || buflen == 0) return (-1); *buf = '\0'; if (smb_config_get_secmode() == SMB_SECMODE_DOMAIN) { rc = smb_config_getstr(SMB_CI_DOMAIN_FQDN, buf, buflen); if ((rc != SMBD_SMF_OK) || (*buf == '\0')) return (-1); } else { bzero(&res_state, sizeof (struct __res_state)); if (res_ninit(&res_state)) return (-1); if (*res_state.defdname == '\0') { res_ndestroy(&res_state); return (-1); } (void) strlcpy(buf, res_state.defdname, buflen); res_ndestroy(&res_state); rc = 0; } return (rc); }
/* * lsarpc_s_PrimaryDomainInfo * * Service primary domain policy queries. In domain mode, return the * primary domain name and SID. In workgroup mode, return the local * hostname and local domain SID. * * Note: info is zeroed on entry to ensure the SID and name do not * contain spurious values if an error is returned. */ static DWORD lsarpc_s_PrimaryDomainInfo(struct mslsa_PrimaryDomainInfo *info, ndr_xa_t *mxa) { smb_domain_t di; boolean_t found; int rc; bzero(info, sizeof (struct mslsa_PrimaryDomainInfo)); if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN) found = smb_domain_lookup_type(SMB_DOMAIN_LOCAL, &di); else found = smb_domain_lookup_type(SMB_DOMAIN_PRIMARY, &di); if (!found) return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); rc = NDR_MSTRING(mxa, di.di_nbname, (ndr_mstring_t *)&info->name); info->sid = (struct mslsa_sid *)NDR_SIDDUP(mxa, di.di_binsid); if ((rc == -1) || (info->sid == NULL)) return (NT_STATUS_NO_MEMORY); return (NT_STATUS_SUCCESS); }
/* * lsarpc_s_QueryInfoPolicy * * This is the server side function for handling LSA information policy * queries. Currently, we only support primary domain and account * domain queries. This is just a front end to switch on the request * and hand it off to the appropriate function to actually deal with * obtaining and building the response. */ static int lsarpc_s_QueryInfoPolicy(void *arg, ndr_xa_t *mxa) { struct mslsa_QueryInfoPolicy *param = arg; union mslsa_PolicyInfoResUnion *ru = ¶m->ru; int security_mode; DWORD status; param->switch_value = param->info_class; switch (param->info_class) { case MSLSA_POLICY_AUDIT_EVENTS_INFO: ru->audit_events.enabled = 0; ru->audit_events.count = 1; ru->audit_events.settings = NDR_MALLOC(mxa, sizeof (DWORD)); bzero(ru->audit_events.settings, sizeof (DWORD)); status = NT_STATUS_SUCCESS; break; case MSLSA_POLICY_PRIMARY_DOMAIN_INFO: status = lsarpc_s_PrimaryDomainInfo(&ru->pd_info, mxa); break; case MSLSA_POLICY_ACCOUNT_DOMAIN_INFO: status = lsarpc_s_AccountDomainInfo(&ru->ad_info, mxa); break; case MSLSA_POLICY_SERVER_ROLE_INFO: security_mode = smb_config_get_secmode(); if (security_mode == SMB_SECMODE_DOMAIN) ru->server_role.role = LSA_ROLE_MEMBER_SERVER; else ru->server_role.role = LSA_ROLE_STANDALONE_SERVER; ru->server_role.pad = 0; status = NT_STATUS_SUCCESS; break; default: bzero(param, sizeof (struct mslsa_QueryInfoPolicy)); param->status = NT_SC_ERROR(NT_STATUS_INVALID_INFO_CLASS); return (NDR_DRC_OK); } if (status != NT_STATUS_SUCCESS) param->status = NT_SC_ERROR(status); else param->status = NT_STATUS_SUCCESS; param->address = (DWORD)(uintptr_t)ru; return (NDR_DRC_OK); }
/* * Build the SPNEGO "hint" token based on the * configured authentication mechanisms. * (NTLMSSP, and maybe Kerberos) */ void smbd_get_authconf(smb_kmod_cfg_t *kcfg) { SPNEGO_MECH_OID *mechList = MechTypeList; int mechCnt = MechTypeCnt; SPNEGO_TOKEN_HANDLE hSpnegoToken = NULL; uchar_t *pBuf = kcfg->skc_negtok; uint32_t *pBufLen = &kcfg->skc_negtok_len; ulong_t tLen = sizeof (kcfg->skc_negtok); int rc; /* * In workgroup mode, skip Kerberos. */ if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN) { mechList += MECH_OID_IDX_NTLMSSP; mechCnt -= MECH_OID_IDX_NTLMSSP; } rc = spnegoCreateNegTokenHint(mechList, mechCnt, (uchar_t *)IgnoreSPN, &hSpnegoToken); if (rc != SPNEGO_E_SUCCESS) { syslog(LOG_DEBUG, "smb_config_get_negtok: " "spnegoCreateNegTokenHint, rc=%d", rc); *pBufLen = 0; return; } rc = spnegoTokenGetBinary(hSpnegoToken, pBuf, &tLen); if (rc != SPNEGO_E_SUCCESS) { syslog(LOG_DEBUG, "smb_config_get_negtok: " "spnegoTokenGetBinary, rc=%d", rc); *pBufLen = 0; } else { *pBufLen = (uint32_t)tLen; } spnegoFreeData(hSpnegoToken); }
/* * GetPrinterData is used t obtain values from the registry for a * printer or a print server. See [MS-RPRN] for value descriptions. * The registry returns ERROR_FILE_NOT_FOUND for unknown keys. */ static int spoolss_s_GetPrinterData(void *arg, ndr_xa_t *mxa) { static spoolss_winreg_t reg[] = { { "ChangeId", 0x0050acf2 }, { "W3SvcInstalled", 0x00000000 }, { "BeepEnabled", 0x00000000 }, { "EventLog", 0x0000001f }, { "NetPopup", 0x00000000 }, { "NetPopupToComputer", 0x00000000 }, { "MajorVersion", 0x00000003 }, { "MinorVersion", 0x00000000 }, { "DsPresent", 0x00000000 } }; struct spoolss_GetPrinterData *param = arg; char *name = (char *)param->pValueName; char buf[MAXPATHLEN]; static uint8_t reserved_buf[4]; spoolss_winreg_t *rp; smb_share_t si; smb_version_t *osversion; struct utsname sysname; smb_wchar_t *wcs; uint32_t value; uint32_t status; int wcslen; int i; if (name == NULL || *name == '\0') { status = ERROR_FILE_NOT_FOUND; goto report_error; } for (i = 0; i < sizeof (reg) / sizeof (reg[0]); ++i) { param->pType = WINREG_DWORD; param->Needed = sizeof (uint32_t); rp = ®[i]; if (strcasecmp(name, rp->name) != 0) continue; if (param->Size < sizeof (uint32_t)) { param->Size = 0; goto need_more_data; } if ((param->Buf = NDR_NEW(mxa, uint32_t)) == NULL) { status = ERROR_NOT_ENOUGH_MEMORY; goto report_error; } value = rp->value; if ((strcasecmp(name, "DsPresent") == 0) && (smb_config_get_secmode() == SMB_SECMODE_DOMAIN)) value = 0x00000001; bcopy(&value, param->Buf, sizeof (uint32_t)); param->Size = sizeof (uint32_t); param->status = ERROR_SUCCESS; return (NDR_DRC_OK); } if (strcasecmp(name, "OSVersion") == 0) { param->pType = WINREG_BINARY; param->Needed = sizeof (smb_version_t); if (param->Size < sizeof (smb_version_t)) { param->Size = sizeof (smb_version_t); goto need_more_data; } if ((osversion = NDR_NEW(mxa, smb_version_t)) == NULL) { status = ERROR_NOT_ENOUGH_MEMORY; goto report_error; } smb_config_get_version(osversion); param->Buf = (uint8_t *)osversion; param->status = ERROR_SUCCESS; return (NDR_DRC_OK); } if (strcasecmp(name, "DNSMachineName") == 0) { param->pType = WINREG_SZ; buf[0] = '\0'; (void) smb_getfqhostname(buf, MAXHOSTNAMELEN); goto encode_string; } if (strcasecmp(name, "DefaultSpoolDirectory") == 0) { param->pType = WINREG_SZ; buf[0] = '\0'; if (smb_shr_get(SMB_SHARE_PRINT, &si) != NERR_Success) { status = ERROR_FILE_NOT_FOUND; goto report_error; } (void) snprintf(buf, MAXPATHLEN, "C:/%s", si.shr_path); (void) strcanon(buf, "/\\"); (void) strsubst(buf, '/', '\\'); goto encode_string; } if (strcasecmp(name, "Architecture") == 0) { param->pType = WINREG_SZ; if (uname(&sysname) < 0) (void) strlcpy(buf, "Solaris", MAXPATHLEN); else (void) snprintf(buf, MAXPATHLEN, "%s %s", sysname.sysname, sysname.machine); goto encode_string; } status = ERROR_FILE_NOT_FOUND; report_error: bzero(param, sizeof (struct spoolss_GetPrinterData)); param->Buf = reserved_buf; param->status = status; return (NDR_DRC_OK); encode_string: wcslen = smb_wcequiv_strlen(buf) + sizeof (smb_wchar_t); if (param->Size < wcslen) { param->Needed = wcslen; goto need_more_data; } if ((wcs = NDR_MALLOC(mxa, wcslen)) == NULL) { status = ERROR_NOT_ENOUGH_MEMORY; goto report_error; } (void) ndr_mbstowcs(NULL, wcs, buf, wcslen); param->Buf = (uint8_t *)wcs; param->Needed = wcslen; param->status = ERROR_SUCCESS; return (NDR_DRC_OK); need_more_data: param->Size = 0; param->Buf = reserved_buf; param->status = ERROR_MORE_DATA; return (NDR_DRC_OK); }
/* * smbd_service_init */ static int smbd_service_init(void) { static struct dir { char *name; int perm; } dir[] = { { SMB_DBDIR, 0700 }, { SMB_CVOL, 0755 }, { SMB_SYSROOT, 0755 }, { SMB_SYSTEM32, 0755 }, { SMB_VSS, 0755 } }; int rc, i; (void) mutex_lock(&smbd_service_mutex); smbd.s_pid = getpid(); for (i = 0; i < sizeof (dir)/sizeof (dir[0]); ++i) { if ((mkdir(dir[i].name, dir[i].perm) < 0) && (errno != EEXIST)) { smbd_report("mkdir %s: %s", dir[i].name, strerror(errno)); (void) mutex_unlock(&smbd_service_mutex); return (-1); } } if ((rc = smb_ccache_init(SMB_VARRUN_DIR, SMB_CCACHE_FILE)) != 0) { if (rc == -1) smbd_report("mkdir %s: %s", SMB_VARRUN_DIR, strerror(errno)); else smbd_report("unable to set KRB5CCNAME"); (void) mutex_unlock(&smbd_service_mutex); return (-1); } smbd.s_loghd = smb_log_create(SMBD_LOGSIZE, SMBD_LOGNAME); smb_codepage_init(); rc = smbd_cups_init(); if (smb_config_getbool(SMB_CI_PRINT_ENABLE)) smbd_report("print service %savailable", (rc == 0) ? "" : "un"); if (smbd_nicmon_start(SMBD_DEFAULT_INSTANCE_FMRI) != 0) smbd_report("NIC monitor failed to start"); smbd_dyndns_init(); smb_ipc_init(); if (smb_netbios_start() != 0) smbd_report("NetBIOS services failed to start"); else smbd_report("NetBIOS services started"); smbd.s_secmode = smb_config_get_secmode(); if ((rc = smb_domain_init(smbd.s_secmode)) != 0) { if (rc == SMB_DOMAIN_NOMACHINE_SID) { smbd_report( "no machine SID: check idmap configuration"); (void) mutex_unlock(&smbd_service_mutex); return (-1); } } if (smbd_dc_monitor_init() != 0) smbd_report("DC monitor initialization failed %s", strerror(errno)); if (mlsvc_init() != 0) { smbd_report("msrpc initialization failed"); (void) mutex_unlock(&smbd_service_mutex); return (-1); } smbd.s_door_srv = smbd_door_start(); smbd.s_door_opipe = smbd_opipe_start(); if (smbd.s_door_srv < 0 || smbd.s_door_opipe < 0) { smbd_report("door initialization failed %s", strerror(errno)); (void) mutex_unlock(&smbd_service_mutex); return (-1); } if (smbd_refresh_init() != 0) { (void) mutex_unlock(&smbd_service_mutex); return (-1); } dyndns_update_zones(); smbd_localtime_init(); (void) smb_lgrp_start(); smb_pwd_init(B_TRUE); if (smb_shr_start() != 0) { smbd_report("share initialization failed: %s", strerror(errno)); (void) mutex_unlock(&smbd_service_mutex); return (-1); } smbd.s_door_lmshr = smbd_share_start(); if (smbd.s_door_lmshr < 0) smbd_report("share initialization failed"); if (smbd_kernel_bind() != 0) { (void) mutex_unlock(&smbd_service_mutex); return (-1); } smbd_load_shares(); smbd_load_printers(); smbd.s_initialized = B_TRUE; smbd_report("service initialized"); (void) cond_signal(&smbd_service_cv); (void) mutex_unlock(&smbd_service_mutex); return (0); }
/* * Some older clients (Windows 98) only handle the low byte * of the max workers value. If the low byte is less than * SMB_PI_MAX_WORKERS_MIN set it to SMB_PI_MAX_WORKERS_MIN. */ void smb_load_kconfig(smb_kmod_cfg_t *kcfg) { struct utsname uts; int64_t citem; int rc; bzero(kcfg, sizeof (smb_kmod_cfg_t)); (void) smb_config_getnum(SMB_CI_MAX_WORKERS, &citem); kcfg->skc_maxworkers = (uint32_t)citem; if ((kcfg->skc_maxworkers & 0xFF) < SMB_PI_MAX_WORKERS_MIN) { kcfg->skc_maxworkers &= ~0xFF; kcfg->skc_maxworkers += SMB_PI_MAX_WORKERS_MIN; } (void) smb_config_getnum(SMB_CI_KEEPALIVE, &citem); kcfg->skc_keepalive = (uint32_t)citem; if ((kcfg->skc_keepalive != 0) && (kcfg->skc_keepalive < SMB_PI_KEEP_ALIVE_MIN)) kcfg->skc_keepalive = SMB_PI_KEEP_ALIVE_MIN; (void) smb_config_getnum(SMB_CI_MAX_CONNECTIONS, &citem); kcfg->skc_maxconnections = (uint32_t)citem; kcfg->skc_restrict_anon = smb_config_getbool(SMB_CI_RESTRICT_ANON); kcfg->skc_signing_enable = smb_config_getbool(SMB_CI_SIGNING_ENABLE); kcfg->skc_signing_required = smb_config_getbool(SMB_CI_SIGNING_REQD); kcfg->skc_netbios_enable = smb_config_getbool(SMB_CI_NETBIOS_ENABLE); kcfg->skc_ipv6_enable = smb_config_getbool(SMB_CI_IPV6_ENABLE); kcfg->skc_print_enable = smb_config_getbool(SMB_CI_PRINT_ENABLE); kcfg->skc_oplock_enable = smb_config_getbool(SMB_CI_OPLOCK_ENABLE); kcfg->skc_sync_enable = smb_config_getbool(SMB_CI_SYNC_ENABLE); kcfg->skc_traverse_mounts = smb_config_getbool(SMB_CI_TRAVERSE_MOUNTS); kcfg->skc_max_protocol = smb_config_get_max_protocol(); kcfg->skc_secmode = smb_config_get_secmode(); rc = smb_config_getnum(SMB_CI_MAXIMUM_CREDITS, &citem); if (rc != SMBD_SMF_OK) citem = SMB_PI_MAX_CREDITS; if (citem < SMB_PI_MIN_CREDITS) citem = SMB_PI_MIN_CREDITS; if (citem > SMB_PI_MAX_CREDITS) citem = SMB_PI_MAX_CREDITS; kcfg->skc_maximum_credits = (uint16_t)citem; rc = smb_config_getnum(SMB_CI_INITIAL_CREDITS, &citem); if (rc != SMBD_SMF_OK) citem = SMB_PI_MIN_CREDITS; if (citem < SMB_PI_MIN_CREDITS) citem = SMB_PI_MIN_CREDITS; if (citem > kcfg->skc_maximum_credits) citem = kcfg->skc_maximum_credits; kcfg->skc_initial_credits = (uint16_t)citem; (void) smb_getdomainname(kcfg->skc_nbdomain, sizeof (kcfg->skc_nbdomain)); (void) smb_getfqdomainname(kcfg->skc_fqdn, sizeof (kcfg->skc_fqdn)); (void) smb_getnetbiosname(kcfg->skc_hostname, sizeof (kcfg->skc_hostname)); (void) smb_config_getstr(SMB_CI_SYS_CMNT, kcfg->skc_system_comment, sizeof (kcfg->skc_system_comment)); smb_config_get_version(&kcfg->skc_version); kcfg->skc_execflags = smb_config_get_execinfo(NULL, NULL, 0); if (smb_config_get_localuuid(kcfg->skc_machine_uuid) < 0) { syslog(LOG_ERR, "smb_load_kconfig: no machine_uuid"); uuid_generate_time(kcfg->skc_machine_uuid); } /* skc_negtok, skc_negtok_len: see smbd_authsvc.c */ (void) uname(&uts); (void) snprintf(kcfg->skc_native_os, sizeof (kcfg->skc_native_os), "%s %s %s", uts.sysname, uts.release, uts.version); (void) strlcpy(kcfg->skc_native_lm, "Native SMB service", sizeof (kcfg->skc_native_lm)); }
/* * Handle a security blob we've received from the client. * Incoming type: LSA_MTYPE_ESFIRST * Outgoing types: LSA_MTYPE_ES_CONT, LSA_MTYPE_ES_DONE, * LSA_MTYPE_ERROR */ static int smbd_authsvc_esfirst(authsvc_context_t *ctx) { const spnego_mech_handler_t *mh; int idx, pref, rc; int best_pref = 1000; int best_mhidx = -1; /* * NTLMSSP header is 8+, SPNEGO is 10+ */ if (ctx->ctx_irawlen < 8) { smbd_report("authsvc: short blob"); return (NT_STATUS_INVALID_PARAMETER); } /* * We could have "Raw NTLMSSP" here intead of SPNEGO. */ if (bcmp(ctx->ctx_irawbuf, "NTLMSSP", 8) == 0) { rc = smbd_raw_ntlmssp_esfirst(ctx); return (rc); } /* * Parse the SPNEGO token, check its type. */ rc = spnegoInitFromBinary(ctx->ctx_irawbuf, ctx->ctx_irawlen, &ctx->ctx_itoken); if (rc != 0) { smbd_report("authsvc: spnego parse failed"); return (NT_STATUS_INVALID_PARAMETER); } rc = spnegoGetTokenType(ctx->ctx_itoken, &ctx->ctx_itoktype); if (rc != 0) { smbd_report("authsvc: spnego get token type failed"); return (NT_STATUS_INVALID_PARAMETER); } if (ctx->ctx_itoktype != SPNEGO_TOKEN_INIT) { smbd_report("authsvc: spnego wrong token type %d", ctx->ctx_itoktype); return (NT_STATUS_INVALID_PARAMETER); } /* * Figure out which mech type to use. We want to use the * first of the client's supported mechanisms that we also * support. Unfortunately, the spnego code does not have an * interface to walk the token's mech list, so we have to * ask about each mech type we know and keep track of which * was earliest in the token's mech list. * * Also, skip the Kerberos mechanisms in workgroup mode. */ idx = 0; mh = mech_table; if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN) { idx = MECH_TBL_IDX_NTLMSSP; mh = &mech_table[idx]; } for (; mh->mh_init != NULL; idx++, mh++) { if (spnegoIsMechTypeAvailable(ctx->ctx_itoken, mh->mh_oid, &pref) != 0) continue; if (pref < best_pref) { best_pref = pref; best_mhidx = idx; } } if (best_mhidx == -1) { smbd_report("authsvc: no supported spnego mechanism"); return (NT_STATUS_INVALID_PARAMETER); } /* Found a mutually agreeable mech. */ mh = &mech_table[best_mhidx]; ctx->ctx_mech_oid = mh->mh_oid; ctx->ctx_mh_work = mh->mh_work; ctx->ctx_mh_fini = mh->mh_fini; rc = mh->mh_init(ctx); if (rc != 0) { smbd_report("authsvc: mech init failed"); return (rc); } /* * Common to LSA_MTYPE_ESFIRST, LSA_MTYPE_ESNEXT */ rc = smbd_authsvc_escmn(ctx); return (rc); }
/* * smbd_service_init */ static int smbd_service_init(void) { static struct dir { char *name; int perm; } dir[] = { { SMB_DBDIR, 0700 }, { SMB_CVOL, 0755 }, { SMB_SYSROOT, 0755 }, { SMB_SYSTEM32, 0755 }, { SMB_VSS, 0755 }, { SMB_PIPE_DIR, 0755 }, }; int rc, i; smbd.s_pid = getpid(); /* * Stop for a debugger attach here, which is after the * fork() etc. in smb_daemonize_init() */ if (smbd.s_dbg_stop) { smbd_report("pid %d stop for debugger attach", smbd.s_pid); (void) kill(smbd.s_pid, SIGSTOP); } smbd_report("smbd starting, pid %d", smbd.s_pid); for (i = 0; i < sizeof (dir)/sizeof (dir[0]); ++i) { if ((mkdir(dir[i].name, dir[i].perm) < 0) && (errno != EEXIST)) { smbd_report("mkdir %s: %s", dir[i].name, strerror(errno)); return (-1); } } if ((rc = smb_ccache_init(SMB_VARRUN_DIR, SMB_CCACHE_FILE)) != 0) { if (rc == -1) smbd_report("mkdir %s: %s", SMB_VARRUN_DIR, strerror(errno)); else smbd_report("unable to set KRB5CCNAME"); return (-1); } smb_codepage_init(); rc = smbd_cups_init(); if (smb_config_getbool(SMB_CI_PRINT_ENABLE)) smbd_report("print service %savailable", (rc == 0) ? "" : "un"); if (smbd_nicmon_start(SMBD_DEFAULT_INSTANCE_FMRI) != 0) smbd_report("NIC monitor failed to start"); smbd_dyndns_init(); smb_ipc_init(); if (smb_config_getbool(SMB_CI_NETBIOS_ENABLE) == 0) smbd_report("NetBIOS services disabled"); else if (smb_netbios_start() != 0) smbd_report("NetBIOS services failed to start"); else smbd_report("NetBIOS services started"); smbd.s_secmode = smb_config_get_secmode(); if ((rc = smb_domain_init(smbd.s_secmode)) != 0) { if (rc == SMB_DOMAIN_NOMACHINE_SID) { smbd_report( "no machine SID: check idmap configuration"); return (-1); } } if (smbd_dc_monitor_init() != 0) smbd_report("DC monitor initialization failed %s", strerror(errno)); if (smbd_pipesvc_start() != 0) { smbd_report("pipesvc initialization failed"); return (-1); } smbd.s_door_srv = smbd_door_start(); if (smbd.s_door_srv < 0) { smbd_report("door initialization failed %s", strerror(errno)); return (-1); } dyndns_update_zones(); smbd_localtime_init(); (void) smb_lgrp_start(); smb_pwd_init(B_TRUE); if (smb_shr_start() != 0) { smbd_report("share initialization failed: %s", strerror(errno)); return (-1); } smbd.s_door_lmshr = smbd_share_start(); if (smbd.s_door_lmshr < 0) smbd_report("share initialization failed"); /* Open the driver, load the kernel config. */ if (smbd_kernel_bind() != 0) { return (-1); } smbd_load_shares(); smbd_load_printers(); smbd_spool_start(); smbd.s_initialized = B_TRUE; smbd_report("service initialized"); return (0); }