diff options
| author | Jerry Jelinek <jerry.jelinek@joyent.com> | 2015-10-27 12:10:43 +0000 |
|---|---|---|
| committer | Jerry Jelinek <jerry.jelinek@joyent.com> | 2015-10-27 12:10:43 +0000 |
| commit | 98b42f03d86a3be4f2a152e862e74b5b425578cf (patch) | |
| tree | c70c52056c6722c83a821b969a59f7c0146610c1 /usr/src | |
| parent | 67fccdb964f657a6f447c770cbc456135191eccc (diff) | |
| parent | 422418808a6580456c11a891d69016d29dae1440 (diff) | |
| download | illumos-joyent-98b42f03d86a3be4f2a152e862e74b5b425578cf.tar.gz | |
[illumos-gate merge]
commit 422418808a6580456c11a891d69016d29dae1440
6338 cannot use umem tools on mdb itself
commit b3700b074e637f8c6991b70754c88a2cfffb246b
6352 Updated DC locator for SMB and idmap
commit ed81dd52230eff1a7c7625caad21af232c36f6cb
6351 Update smbsrv dtrace scripts and install them
commit 12b65585e720714b31036daaa2b30eb76014048e
1122 smbsrv should use SPNEGO (inbound authentication)
Diffstat (limited to 'usr/src')
172 files changed, 11803 insertions, 5418 deletions
diff --git a/usr/src/Targetdirs b/usr/src/Targetdirs index 8312281535..e16b1a464d 100644 --- a/usr/src/Targetdirs +++ b/usr/src/Targetdirs @@ -25,8 +25,8 @@ # Copyright (c) 2012, Igor Kozhukhov <ikozhukhov@gmail.com> # Copyright 2012 OmniTI Computer Consulting, Inc. All rights reserved. # Copyright (c) 2013 RackTop Systems. -# Copyright 2013 Nexenta Systems, Inc. All rights reserved. # Copyright 2014 Garrett D'Amore <garrett@damore.org> +# Copyright 2014 Nexenta Systems, Inc. All rights reserved. # # @@ -230,6 +230,7 @@ DIRS= \ /usr/has/lib \ /usr/has/man \ /usr/include \ + /usr/include/ads \ /usr/include/ast \ /usr/include/fm \ /usr/include/gssapi \ diff --git a/usr/src/cmd/idmap/Makefile b/usr/src/cmd/idmap/Makefile index 3242cdfa22..f66c25fe8a 100644 --- a/usr/src/cmd/idmap/Makefile +++ b/usr/src/cmd/idmap/Makefile @@ -21,15 +21,12 @@ # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. -# -# ident "%Z%%M% %I% %E% SMI" +# Copyright 2014 Nexenta Systems, Inc. All rights reserved. # # Makefile for native identity mapping service # -SUBDIR_CMD= idmap -SUBDIR_DAEMON= idmapd -SUBDIRS= $(SUBDIR_CMD) $(SUBDIR_DAEMON) +SUBDIRS= idmap idmapd nltest test-getdc all := TARGET = all install := TARGET = install diff --git a/usr/src/cmd/idmap/idmap/namemaps.c b/usr/src/cmd/idmap/idmap/namemaps.c index c906bfeaa3..1671350a49 100644 --- a/usr/src/cmd/idmap/idmap/namemaps.c +++ b/usr/src/cmd/idmap/idmap/namemaps.c @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ @@ -244,7 +245,7 @@ idmap_stat idmap_init_ad(idmap_nm_handle_t *p) { idmap_stat rc = IDMAP_SUCCESS; - idmap_ad_disc_ds_t *dc = NULL; + ad_disc_ds_t *dc = NULL; ad_disc_t ad_ctx; ad_ctx = ad_disc_init(); diff --git a/usr/src/cmd/idmap/idmapd/Makefile b/usr/src/cmd/idmap/idmapd/Makefile index b99b8677be..a4ab35da95 100644 --- a/usr/src/cmd/idmap/idmapd/Makefile +++ b/usr/src/cmd/idmap/idmapd/Makefile @@ -20,11 +20,13 @@ # # # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2014 Nexenta Systems, Inc. All rights reserved. # PROG = idmapd MANIFEST = idmap.xml SERVEROBJS = \ + adspriv_impl.o \ directory_provider_builtin.o \ directory_provider_nsswitch.o \ directory_provider_ad.o \ @@ -35,13 +37,18 @@ SERVEROBJS = \ idmapd.o \ init.o \ idmap_lsa.o \ + krb5_lookup.o \ nldaputils.o \ - rpc_svc.o \ server.o \ wksids.o +GENOBJS = \ + adspriv_srv.o \ + rpc_svc.o + SERVERSRCS = $(SERVEROBJS:%.o=%.c) -OBJS = $(SERVEROBJS) +GENSRCS = $(GENOBJS:%.o=%.c) +OBJS = $(SERVEROBJS) $(GENOBJS) SRCS = $(SERVERSRCS) POFILES = $(OBJS:%.o=%.po) @@ -69,15 +76,24 @@ RPC_MSGOUT_OPT = -DRPC_MSGOUT=idmap_rpc_msgout ROOTMANIFESTDIR = $(ROOTSVCSYSTEM) $(ROOTMANIFEST) := FILEMODE= 444 +RPCSVC= ../../../uts/common/rpcsvc +ADS_CMN=../../../lib/libads/common -INCS += -I. -I../../../lib/libidmap/common\ - -I../../../lib/libsldap/common\ +INCS += -I. -I../../../lib/libidmap/common \ + -I../../../lib/libsldap/common \ -I../../../lib/libadutils/common \ + -I $(ADS_CMN) \ -I../../../lib/smbsrv/libsmb/common +# Should not have to do this, but the Kerberos includes are a mess. +INCS += -I $(ROOT)/usr/include/kerberosv5 + $(OBJS) := CPPFLAGS += $(INCS) -D_REENTRANT $(POFILE) := CPPFLAGS += $(INCS) +LINTFLAGS += -xerroff=E_NAME_DEF_NOT_USED2 +LINTFLAGS += -xerroff=E_NAME_USED_NOT_DEF2 + CFLAGS += $(CCVERBOSE) LDLIBS += \ -lsqlite-sys \ @@ -90,6 +106,7 @@ LDLIBS += \ -lldap \ -luuid \ -ladutils \ + -lads \ -lumem \ -lnvpair \ -luutil \ @@ -126,15 +143,22 @@ install: all $(ROOTLIBPROG) $(ROOTMANIFEST) check: $(CHKMANIFEST) clean: - $(RM) $(OBJS) - -clobber: + $(RM) $(OBJS) $(GENSRCS) lint: lint_SRCS lint_SRCS: -include ../../Makefile.targ +RPCGENFLAGS = -CMN -FRC: +adspriv_srv.o : adspriv_srv.c +adspriv_srv.c: + $(RPCGEN) $(RPCGENFLAGS) -m $(ADS_CMN)/ads_priv.x > $@ + +rpc_svc.o : rpc_svc.c + +rpc_svc.c : $(RPCSVC)/idmap_prot.x + $(RPCGEN) $(RPCGENFLAGS) -m $(RPCSVC)/idmap_prot.x > $@ + +include ../../Makefile.targ diff --git a/usr/src/cmd/idmap/idmapd/adspriv_impl.c b/usr/src/cmd/idmap/idmapd/adspriv_impl.c new file mode 100644 index 0000000000..b1dcbe3163 --- /dev/null +++ b/usr/src/cmd/idmap/idmapd/adspriv_impl.c @@ -0,0 +1,210 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + */ + + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <syslog.h> +#include <rpc/rpc.h> +#include <sys/uuid.h> +#include <smb/ntstatus.h> +#include <synch.h> +#include <thread.h> +#include <arpa/inet.h> +#include <uuid/uuid.h> + +#include "idmapd.h" +#include "libadutils.h" +#include "dsgetdc.h" +#include "ads_priv.h" + +void adspriv_program_1(struct svc_req *, register SVCXPRT *); + +SVCXPRT *dcl_xprt = NULL; + +void +init_dc_locator(void) +{ + int connmaxrec = 32 * 1024; + + dcl_xprt = svc_door_create(adspriv_program_1, + ADSPRIV_PROGRAM, ADSPRIV_V1, connmaxrec); + if (dcl_xprt == NULL) { + syslog(LOG_ERR, "unable to create door RPC service"); + return; + } + + if (!svc_control(dcl_xprt, SVCSET_CONNMAXREC, &connmaxrec)) { + syslog(LOG_ERR, "unable to limit RPC request size"); + } +} + +void +fini_dc_locator(void) +{ + if (dcl_xprt != NULL) + svc_destroy(dcl_xprt); +} + +/* + * Functions called by the (generated) adspriv_srv.c + */ + +/* ARGSUSED */ +bool_t +adspriv_null_1_svc(void *result, struct svc_req *rqstp) +{ + return (TRUE); +} + +/* ARGSUSED */ +bool_t +adspriv_forcerediscovery_1_svc( + DsForceRediscoveryArgs args, + int *res, + struct svc_req *sreq) +{ + /* Ignoring args for now. */ + + idmap_cfg_force_rediscovery(); + *res = 0; + + return (TRUE); +} + + +/* ARGSUSED */ +bool_t +adspriv_getdcname_1_svc( + DsGetDcNameArgs args, + DsGetDcNameRes *res, + struct svc_req *sreq) +{ + uuid_t uuid; + adspriv_dcinfo *dci; + idmap_pg_config_t *pgcfg; + ad_disc_ds_t *ds; + char *s; + + /* Init */ + (void) memset(res, 0, sizeof (*res)); + res->status = 0; + dci = &res->DsGetDcNameRes_u.res0; + + if (args.Flags & DS_FORCE_REDISCOVERY) + idmap_cfg_force_rediscovery(); + + /* + * We normally should wait if discovery is running. + * Sort of mis-using the background flag as a way to + * skip the wait, until we really do background disc. + */ + if ((args.Flags & DS_BACKGROUND_ONLY) == 0) { + timespec_t tv = { 15, 0 }; + int rc = 0; + int waited = 0; + + (void) mutex_lock(&_idmapdstate.addisc_lk); + + if (_idmapdstate.addisc_st != 0) + idmapdlog(LOG_DEBUG, "getdcname wait begin"); + + while (_idmapdstate.addisc_st != 0) { + waited++; + rc = cond_reltimedwait(&_idmapdstate.addisc_cv, + &_idmapdstate.addisc_lk, &tv); + if (rc == ETIME) + break; + } + (void) mutex_unlock(&_idmapdstate.addisc_lk); + + if (rc == ETIME) { + /* Caller will replace this with DC not found. */ + idmapdlog(LOG_ERR, "getdcname timeout"); + res->status = NT_STATUS_CANT_WAIT; + return (TRUE); + } + if (waited) { + idmapdlog(LOG_DEBUG, "getdcname wait done"); + } + } + + RDLOCK_CONFIG(); + pgcfg = &_idmapdstate.cfg->pgcfg; + + if (pgcfg->domain_name == NULL) { + res->status = NT_STATUS_INVALID_SERVER_STATE; + goto out; + } + + if (args.DomainName != NULL && args.DomainName[0] != '\0' && + 0 != strcasecmp(args.DomainName, pgcfg->domain_name)) { + /* + * They asked for a specific domain not our primary, + * which is not supported (and not needed). + */ + res->status = NT_STATUS_NO_SUCH_DOMAIN; + goto out; + } + + if ((ds = pgcfg->domain_controller) == NULL) { + res->status = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; + goto out; + } + + dci->dci_DcName = strdup(ds->host); + + dci->dci_DcAddr = calloc(1, INET6_ADDRSTRLEN); + if (dci->dci_DcAddr != NULL && + ad_disc_getnameinfo(dci->dci_DcAddr, INET6_ADDRSTRLEN, + &ds->addr) == 0) + dci->dci_AddrType = DS_INET_ADDRESS; + + if ((s = pgcfg->domain_guid) != NULL && + 0 == uuid_parse(s, uuid)) { + (void) memcpy(dci->dci_guid, uuid, sizeof (uuid)); + } + + if ((s = pgcfg->domain_name) != NULL) + dci->dci_DomainName = strdup(s); + + if ((s = pgcfg->forest_name) != NULL) + dci->dci_DnsForestName = strdup(s); + + dci->dci_Flags = ds->flags; + dci->dci_DcSiteName = strdup(ds->site); + + if ((s = pgcfg->site_name) != NULL) + dci->dci_ClientSiteName = strdup(s); + + /* Address in binary form too. */ + (void) memcpy(&dci->dci_sockaddr, + &ds->addr, ADSPRIV_SOCKADDR_LEN); + +out: + UNLOCK_CONFIG(); + + return (TRUE); +} + +/* ARGSUSED */ +int +adspriv_program_1_freeresult(SVCXPRT *xprt, xdrproc_t fun, caddr_t res) +{ + (void) xdr_free(fun, res); + return (TRUE); +} diff --git a/usr/src/cmd/idmap/idmapd/directory_provider_ad.c b/usr/src/cmd/idmap/idmapd/directory_provider_ad.c index 13233f6345..6bae13e963 100644 --- a/usr/src/cmd/idmap/idmapd/directory_provider_ad.c +++ b/usr/src/cmd/idmap/idmapd/directory_provider_ad.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ /* @@ -144,6 +145,7 @@ directory_provider_ad_get( /* * If we don't have any AD servers handy, we can't find anything. + * XXX: this should be using our DC, not the GC. */ if (_idmapdstate.num_gcs < 1) { return (NULL); diff --git a/usr/src/cmd/idmap/idmapd/idmap.xml b/usr/src/cmd/idmap/idmapd/idmap.xml index 1f051a2936..c1461616bc 100644 --- a/usr/src/cmd/idmap/idmapd/idmap.xml +++ b/usr/src/cmd/idmap/idmapd/idmap.xml @@ -2,6 +2,7 @@ <!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1"> <!-- Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + Copyright 2014 Nexenta Systems, Inc. All rights reserved. CDDL HEADER START @@ -104,6 +105,22 @@ name='name_cache_timeout' type='count' value='604800' /> + <propval + name='preferred_dc' + type='astring' + value='' /> + <propval + name='rediscovery_interval' + type='count' + value='3600' /> + <propval + name='use_ads' + type='boolean' + value='true' /> + <propval + name='use_lsa' + type='boolean' + value='true' /> </property_group> <property_group name='debug' type='application' > diff --git a/usr/src/cmd/idmap/idmapd/idmap_config.c b/usr/src/cmd/idmap/idmapd/idmap_config.c index b8c2867489..ad3c70b932 100644 --- a/usr/src/cmd/idmap/idmapd/idmap_config.c +++ b/usr/src/cmd/idmap/idmapd/idmap_config.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ @@ -33,15 +33,19 @@ #include <libintl.h> #include <ctype.h> #include <errno.h> -#include "idmapd.h" #include <stdio.h> #include <stdarg.h> #include <uuid/uuid.h> #include <pthread.h> #include <port.h> +#include <sys/socket.h> #include <net/route.h> #include <sys/u8_textprep.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> #include <note.h> +#include "idmapd.h" #include "addisc.h" #define MACHINE_SID_LEN (9 + 3 * 11) @@ -50,6 +54,7 @@ #define DEBUG_PG "debug" #define RECONFIGURE 1 #define POKE_AUTO_DISCOVERY 2 +#define KICK_AUTO_DISCOVERY 3 /* * Default cache timeouts. Can override via svccfg @@ -59,15 +64,30 @@ #define ID_CACHE_TMO_DEFAULT 86400 #define NAME_CACHE_TMO_DEFAULT 604800 +/* + * Default maximum time between rediscovery runs. + * config/rediscovery_interval = count: seconds + */ +#define REDISCOVERY_INTERVAL_DEFAULT 3600 + +/* + * Mininum time between rediscovery runs, in case adutils gives us a + * really short TTL (which it never should, but be defensive) + * (not configurable) seconds. + */ +#define MIN_REDISCOVERY_INTERVAL 60 + enum event_type { EVENT_NOTHING, /* Woke up for no good reason */ EVENT_TIMEOUT, /* Timeout expired */ EVENT_ROUTING, /* An interesting routing event happened */ - EVENT_DEGRADE, /* An error occurred in the mainline */ + EVENT_POKED, /* Requested from degrade_svc() */ + EVENT_KICKED, /* Force rediscovery, i.e. DC failed. */ EVENT_REFRESH, /* SMF refresh */ }; +static void idmapd_set_krb5_realm(char *); static pthread_t update_thread_handle = 0; @@ -89,46 +109,52 @@ struct enum_lookup_map trust_dir_map[] = { }; static int -generate_machine_sid(char **machine_sid) +generate_machine_uuid(char **machine_uuid) { - char *p; uuid_t uu; - int i, j, len, rlen; - uint32_t rid; - /* - * Generate and split 128-bit UUID into three 32-bit RIDs The - * machine_sid will be of the form S-1-5-21-N1-N2-N3 (that's - * four RIDs altogether). - * - * Technically we could use up to 14 random RIDs here, but it - * turns out that with some versions of Windows using SIDs with - * more than five RIDs in security descriptors causes problems. - */ - - *machine_sid = calloc(1, MACHINE_SID_LEN); - if (*machine_sid == NULL) { + *machine_uuid = calloc(1, UUID_PRINTABLE_STRING_LENGTH + 1); + if (*machine_uuid == NULL) { idmapdlog(LOG_ERR, "Out of memory"); return (-1); } - (void) strcpy(*machine_sid, "S-1-5-21"); - p = *machine_sid + strlen("S-1-5-21"); - len = MACHINE_SID_LEN - strlen("S-1-5-21"); uuid_clear(uu); - uuid_generate_random(uu); + uuid_generate_time(uu); + uuid_unparse(uu, *machine_uuid); -#if UUID_LEN != 16 -#error UUID size is not 16! -#endif + return (0); +} - for (i = 0; i < 3; i++) { - j = i * 4; - rid = (uu[j] << 24) | (uu[j + 1] << 16) | - (uu[j + 2] << 8) | (uu[j + 3]); - rlen = snprintf(p, len, "-%u", rid); - p += rlen; - len -= rlen; +static int +generate_machine_sid(char **machine_sid, char *machine_uuid) +{ + union { + uuid_t uu; + uint32_t v[4]; + } uv; + int len; + + /* + * Split the 128-bit machine UUID into three 32-bit values + * we'll use as the "sub-authorities" of the machine SID. + * The machine_sid will have the form S-1-5-21-J-K-L + * (that's four sub-authorities altogether) where: + * J = last 4 bytes of node_addr, + * K = time_mid, time_hi_and_version + * L = time_low + * (see struct uuid) + */ + + (void) memset(&uv, 0, sizeof (uv)); + (void) uuid_parse(machine_uuid, uv.uu); + + len = asprintf(machine_sid, "S-1-5-21-%u-%u-%u", + uv.v[3], uv.v[0], uv.v[1]); + + if (len == -1 || *machine_sid == NULL) { + idmapdlog(LOG_ERR, "Out of memory"); + return (-1); } return (0); @@ -344,14 +370,17 @@ scf_value2string(const char *name, scf_value_t *value) static int get_val_ds(idmap_cfg_handles_t *handles, const char *name, int defport, - idmap_ad_disc_ds_t **val) + ad_disc_ds_t **val) { - idmap_ad_disc_ds_t *servers = NULL; + char port_str[8]; + struct addrinfo hints; + struct addrinfo *ai; + ad_disc_ds_t *servers = NULL; scf_property_t *scf_prop; scf_value_t *value; scf_iter_t *iter; char *host, *portstr; - int len, i; + int err, len, i; int count = 0; int rc = -1; @@ -417,14 +446,22 @@ restart: goto destruction; } + (void) memset(&hints, 0, sizeof (hints)); + hints.ai_protocol = IPPROTO_TCP; + hints.ai_socktype = SOCK_STREAM; + host = NULL; + i = 0; while (i < count && scf_iter_next_value(iter, value) > 0) { + if (host) { + free(host); + host = NULL; + } servers[i].priority = 0; servers[i].weight = 100; servers[i].port = defport; - if ((host = scf_value2string(name, value)) == NULL) { - goto destruction; - } + if ((host = scf_value2string(name, value)) == NULL) + continue; if ((portstr = strchr(host, ':')) != NULL) { *portstr++ = '\0'; servers[i].port = strtol(portstr, @@ -432,16 +469,56 @@ restart: if (servers[i].port == 0) servers[i].port = defport; } - len = strlcpy(servers[i].host, host, - sizeof (servers->host)); - free(host); + /* + * Ignore this server if the hostname is too long + * or empty (continue without i++) + */ + len = strlen(host); + if (len == 0) { + if (DBG(CONFIG, 1)) { + idmapdlog(LOG_INFO, "%s host=\"\"", name); + } + continue; + } + if (len >= sizeof (servers->host)) { + idmapdlog(LOG_ERR, "Host name too long: %s", host); + idmapdlog(LOG_ERR, "ignoring %s value", name); + continue; + } - /* Ignore this server if the hostname is too long */ - if (len < sizeof (servers->host)) - i++; + /* + * Get the host address too. If we can't, then + * log an error and skip this host. + */ + (void) snprintf(port_str, sizeof (port_str), + "%d", servers[i].port); + ai = NULL; + err = getaddrinfo(host, port_str, &hints, &ai); + if (err != 0) { + idmapdlog(LOG_ERR, "No address for host: %s (%s)", + host, gai_strerror(err)); + idmapdlog(LOG_ERR, "ignoring %s value", name); + continue; + } + + (void) strlcpy(servers[i].host, host, + sizeof (servers->host)); + (void) memcpy(&servers[i].addr, ai->ai_addr, ai->ai_addrlen); + freeaddrinfo(ai); + + /* Added a DS to the array. */ + i++; } + free(host); + if (i == 0) { + if (DBG(CONFIG, 1)) { + idmapdlog(LOG_INFO, "%s is empty", name); + } + free(servers); + servers = NULL; + } *val = servers; rc = 0; @@ -460,7 +537,6 @@ destruction: return (rc); } - static int get_val_astring(idmap_cfg_handles_t *handles, const char *name, char **val) { @@ -836,9 +912,8 @@ update_enum(int *value, int *new, char *name, struct enum_lookup_map *map) * If nothing has changed it returns 0 else 1 */ static int -update_dirs(idmap_ad_disc_ds_t **value, idmap_ad_disc_ds_t **new, char *name) +update_dirs(ad_disc_ds_t **value, ad_disc_ds_t **new, char *name) { - int i; if (*value == *new) /* Nothing to do */ @@ -866,9 +941,13 @@ update_dirs(idmap_ad_disc_ds_t **value, idmap_ad_disc_ds_t **new, char *name) if (DBG(CONFIG, 1)) { /* List all the new DSs */ - for (i = 0; (*value)[i].host[0] != '\0'; i++) { - idmapdlog(LOG_INFO, "change %s=%s port=%d", name, - (*value)[i].host, (*value)[i].port); + char buf[64]; + ad_disc_ds_t *ds; + for (ds = *value; ds->host[0] != '\0'; ds++) { + if (ad_disc_getnameinfo(buf, sizeof (buf), &ds->addr)) + (void) strlcpy(buf, "?", sizeof (buf)); + idmapdlog(LOG_INFO, "change %s=%s addr=%s port=%d", + name, ds->host, buf, ds->port); } } return (1); @@ -1193,11 +1272,15 @@ wait_for_event(struct timespec *timeoutp) return (EVENT_TIMEOUT); case PORT_SOURCE_USER: - if (pe.portev_events == POKE_AUTO_DISCOVERY) - return (EVENT_DEGRADE); - if (pe.portev_events == RECONFIGURE) + switch (pe.portev_events) { + case RECONFIGURE: return (EVENT_REFRESH); - break; + case POKE_AUTO_DISCOVERY: + return (EVENT_POKED); + case KICK_AUTO_DISCOVERY: + return (EVENT_KICKED); + } + return (EVENT_NOTHING); case PORT_SOURCE_FD: if (pe.portev_object == rt_sock) { @@ -1243,18 +1326,19 @@ void * idmap_cfg_update_thread(void *arg) { NOTE(ARGUNUSED(arg)) - + idmap_pg_config_t *pgcfg = &_idmapdstate.cfg->pgcfg; const ad_disc_t ad_ctx = _idmapdstate.cfg->handles.ad_ctx; + int flags = CFG_DISCOVER; for (;;) { struct timespec timeout; struct timespec *timeoutp; int rc; - int ttl; + int ttl, max_ttl; (void) ad_disc_SubnetChanged(ad_ctx); - rc = idmap_cfg_load(_idmapdstate.cfg, CFG_DISCOVER); + rc = idmap_cfg_load(_idmapdstate.cfg, flags); if (rc < -1) { idmapdlog(LOG_ERR, "Fatal errors while reading " "SMF properties"); @@ -1269,17 +1353,34 @@ idmap_cfg_update_thread(void *arg) * Wait for an interesting event. Note that we might get * boring events between interesting events. If so, we loop. */ + flags = CFG_DISCOVER; for (;;) { - ttl = ad_disc_get_TTL(ad_ctx); - + /* + * If we don't know our domain name, don't bother + * with rediscovery until the next config change. + * Avoids hourly noise in workgroup mode. + */ + if (pgcfg->domain_name == NULL) + ttl = -1; + else + ttl = ad_disc_get_TTL(ad_ctx); if (ttl < 0) { timeoutp = NULL; } else { - timeoutp = &timeout; + max_ttl = (int)pgcfg->rediscovery_interval; + if (ttl > max_ttl) + ttl = max_ttl; + if (ttl < MIN_REDISCOVERY_INTERVAL) + ttl = MIN_REDISCOVERY_INTERVAL; timeout.tv_sec = ttl; timeout.tv_nsec = 0; + timeoutp = &timeout; } + if (DBG(CONFIG, 1)) + idmapdlog(LOG_DEBUG, + "_cfg_update_thread waiting"); + switch (wait_for_event(timeoutp)) { case EVENT_NOTHING: if (DBG(CONFIG, 2)) @@ -1289,16 +1390,24 @@ idmap_cfg_update_thread(void *arg) if (DBG(CONFIG, 1)) idmapdlog(LOG_INFO, "SMF refresh"); /* + * Forget any DC we had previously. + */ + flags |= CFG_FORGET_DC; + + /* * Blow away the ccache, we might have * re-joined the domain or joined a new one */ (void) unlink(IDMAP_CACHEDIR "/ccache"); break; - case EVENT_DEGRADE: - if (DBG(CONFIG, 1)) { - idmapdlog(LOG_DEBUG, - "Service degraded"); - } + case EVENT_POKED: + if (DBG(CONFIG, 1)) + idmapdlog(LOG_DEBUG, "poked"); + break; + case EVENT_KICKED: + if (DBG(CONFIG, 1)) + idmapdlog(LOG_DEBUG, "kicked"); + flags |= CFG_FORGET_DC; break; case EVENT_TIMEOUT: if (DBG(CONFIG, 1)) @@ -1426,8 +1535,7 @@ check_smf_debug_mode(idmap_cfg_handles_t *handles) * -3 -> hard smf config failures * reading from SMF. */ -static -int +static int idmap_cfg_load_smf(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg, int * const errors) { @@ -1507,6 +1615,13 @@ idmap_cfg_load_smf(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg, if (pgcfg->name_cache_timeout == 0) pgcfg->name_cache_timeout = NAME_CACHE_TMO_DEFAULT; + rc = get_val_int(handles, "rediscovery_interval", + &pgcfg->rediscovery_interval, SCF_TYPE_COUNT); + if (rc != 0) + (*errors)++; + if (pgcfg->rediscovery_interval == 0) + pgcfg->rediscovery_interval = REDISCOVERY_INTERVAL_DEFAULT; + rc = get_val_astring(handles, "domain_name", &pgcfg->domain_name); if (rc != 0) @@ -1537,12 +1652,50 @@ idmap_cfg_load_smf(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg, pgcfg->default_domain = strdup(pgcfg->domain_name); } + rc = get_val_astring(handles, "domain_guid", &s); + if (rc != 0) { + (*errors)++; + } else if (s == NULL || s[0] == '\0') { + /* OK, not set. */ + free(s); + } else { + uuid_t u; + + if (uuid_parse(s, u) != 0) { + idmapdlog(LOG_ERR, + "config/domain_guid: invalid value \"%s\" ignored", s); + free(s); + (*errors)++; + } else { + pgcfg->domain_guid = s; + pgcfg->domain_guid_auto_disc = B_FALSE; + (void) ad_disc_set_DomainGUID(handles->ad_ctx, u); + } + } + + rc = get_val_astring(handles, "machine_uuid", &pgcfg->machine_uuid); + if (rc != 0) + (*errors)++; + if (pgcfg->machine_uuid == NULL) { + /* If machine_uuid not configured, generate one */ + if (generate_machine_uuid(&pgcfg->machine_uuid) < 0) + return (-2); + rc = set_val_astring(handles, handles->config_pg, + "machine_uuid", pgcfg->machine_uuid); + if (rc != 0) + (*errors)++; + } + rc = get_val_astring(handles, "machine_sid", &pgcfg->machine_sid); if (rc != 0) (*errors)++; if (pgcfg->machine_sid == NULL) { - /* If machine_sid not configured, generate one */ - if (generate_machine_sid(&pgcfg->machine_sid) < 0) + /* + * If machine_sid not configured, generate one + * from the machine UUID. + */ + if (generate_machine_sid(&pgcfg->machine_sid, + pgcfg->machine_uuid) < 0) return (-2); rc = set_val_astring(handles, handles->config_pg, "machine_sid", pgcfg->machine_sid); @@ -1560,6 +1713,16 @@ idmap_cfg_load_smf(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg, pgcfg->domain_controller_auto_disc = B_FALSE; } + rc = get_val_ds(handles, "preferred_dc", 389, + &pgcfg->preferred_dc); + if (rc != 0) + (*errors)++; + else { + (void) ad_disc_set_PreferredDC(handles->ad_ctx, + pgcfg->preferred_dc); + pgcfg->preferred_dc_auto_disc = B_FALSE; + } + rc = get_val_astring(handles, "forest_name", &pgcfg->forest_name); if (rc != 0) (*errors)++; @@ -1633,7 +1796,6 @@ idmap_cfg_load_smf(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg, } return (rc); - } static @@ -1655,7 +1817,7 @@ discover_trusted_domains(idmap_pg_config_t *pgcfg, ad_disc_t ad_ctx) int num_trusteddomains; boolean_t new_forest; char *trusteddomain; - idmap_ad_disc_ds_t *globalcatalog; + ad_disc_ds_t *globalcatalog; idmap_trustedforest_t *trustedforests; ad_disc_domainsinforest_t *domainsinforest; @@ -1750,8 +1912,7 @@ discover_trusted_domains(idmap_pg_config_t *pgcfg, ad_disc_t ad_ctx) continue; } domainsinforest = - ad_disc_get_DomainsInForest(trusted_ctx, - NULL); + ad_disc_get_DomainsInForest(trusted_ctx, NULL); if (domainsinforest == NULL) { if (DBG(CONFIG, 1)) { idmapdlog(LOG_DEBUG, @@ -1796,72 +1957,154 @@ discover_trusted_domains(idmap_pg_config_t *pgcfg, ad_disc_t ad_ctx) * idmap_cfg_discover() is called *after* idmap_cfg_load_smf(), so it * needs to be careful not to overwrite any properties set in SMF. */ -static -void -idmap_cfg_discover(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg) +static void +idmap_cfg_discover1(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg) { ad_disc_t ad_ctx = handles->ad_ctx; + FILE *status_fp = NULL; + time_t t0, t1; - if (pgcfg->use_ads == B_FALSE) { - if (DBG(CONFIG, 1)) - idmapdlog(LOG_DEBUG, "ADS disabled."); - return; - } - + t0 = time(NULL); if (DBG(CONFIG, 1)) - idmapdlog(LOG_DEBUG, "Running discovery."); + idmapdlog(LOG_DEBUG, "Running domain discovery."); - ad_disc_refresh(ad_ctx); + (void) unlink(IDMAP_CACHEDIR "/discovery.log"); + status_fp = fopen(IDMAP_CACHEDIR "/discovery.log", "w"); + if (status_fp) { + (void) fchmod(fileno(status_fp), 0644); + ad_disc_set_StatusFP(ad_ctx, status_fp); + } if (pgcfg->domain_name == NULL) { idmapdlog(LOG_DEBUG, "No domain name specified."); - } else { - if (pgcfg->domain_controller == NULL) - pgcfg->domain_controller = - ad_disc_get_DomainController(ad_ctx, - AD_DISC_PREFER_SITE, - &pgcfg->domain_controller_auto_disc); - - if (pgcfg->forest_name == NULL) - pgcfg->forest_name = ad_disc_get_ForestName(ad_ctx, - &pgcfg->forest_name_auto_disc); - - if (pgcfg->site_name == NULL) - pgcfg->site_name = ad_disc_get_SiteName(ad_ctx, - &pgcfg->site_name_auto_disc); - - if (pgcfg->global_catalog == NULL) - pgcfg->global_catalog = - ad_disc_get_GlobalCatalog(ad_ctx, - AD_DISC_PREFER_SITE, - &pgcfg->global_catalog_auto_disc); + if (status_fp) + (void) fprintf(status_fp, "(no domain name)\n"); + goto out; + } + + if (pgcfg->domain_controller == NULL) + pgcfg->domain_controller = + ad_disc_get_DomainController(ad_ctx, + AD_DISC_PREFER_SITE, + &pgcfg->domain_controller_auto_disc); + + if (pgcfg->domain_guid == NULL) { + char buf[UUID_PRINTABLE_STRING_LENGTH]; + uchar_t *u = ad_disc_get_DomainGUID(ad_ctx, + &pgcfg->domain_guid_auto_disc); + (void) memset(buf, 0, sizeof (buf)); + if (u != NULL) { + uuid_unparse(u, buf); + pgcfg->domain_guid = strdup(buf); + } + } + + if (pgcfg->forest_name == NULL) + pgcfg->forest_name = ad_disc_get_ForestName(ad_ctx, + &pgcfg->forest_name_auto_disc); + + if (pgcfg->site_name == NULL) + pgcfg->site_name = ad_disc_get_SiteName(ad_ctx, + &pgcfg->site_name_auto_disc); + + if (DBG(CONFIG, 1)) { + log_if_unable(pgcfg->domain_name, "Domain Name"); + log_if_unable(pgcfg->domain_controller, + "Domain Controller"); + log_if_unable(pgcfg->domain_guid, "Domain GUID"); + log_if_unable(pgcfg->forest_name, "Forest Name"); + log_if_unable(pgcfg->site_name, "Site Name"); + } + +out: + if (status_fp) { + ad_disc_set_StatusFP(ad_ctx, NULL); + (void) fclose(status_fp); + status_fp = NULL; + } + + if (DBG(CONFIG, 1)) + idmapdlog(LOG_DEBUG, "Domain discovery done."); + /* + * Log when this took more than 15 sec. + */ + t1 = time(NULL); + if (t1 > (t0 + 15)) { + idmapdlog(LOG_NOTICE, "Domain discovery took %d sec.", + (int)(t1 - t0)); + idmapdlog(LOG_NOTICE, "Check the DNS configuration."); + } +} + +/* + * This is the second part of discovery, which can take a while. + * We don't want to hold up parties who just want to know what + * domain controller we're using (like smbd), so this part runs + * after we've updated that info in the "live" config and told + * such consumers to go ahead. + * + * This is a lot like idmap_cfg_discover(), but used LDAP queries + * get the forest information from the global catalog servers. + * + * Note: the previous update_* calls have usually nuked any + * useful information from pgcfg before we get here, so we + * can only use it store discovery results, not to read. + */ +static void +idmap_cfg_discover2(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg) +{ + ad_disc_t ad_ctx = handles->ad_ctx; + FILE *status_fp = NULL; + time_t t0, t1; + + t0 = time(NULL); + if (DBG(CONFIG, 1)) + idmapdlog(LOG_DEBUG, "Running forest discovery."); + + status_fp = fopen(IDMAP_CACHEDIR "/discovery.log", "a"); + if (status_fp) + ad_disc_set_StatusFP(ad_ctx, status_fp); + + if (pgcfg->global_catalog == NULL) + pgcfg->global_catalog = + ad_disc_get_GlobalCatalog(ad_ctx, + AD_DISC_PREFER_SITE, + &pgcfg->global_catalog_auto_disc); + + if (pgcfg->global_catalog != NULL) { pgcfg->domains_in_forest = ad_disc_get_DomainsInForest(ad_ctx, NULL); if (!pgcfg->disable_cross_forest_trusts) discover_trusted_domains(pgcfg, ad_ctx); + } - if (DBG(CONFIG, 1)) { - log_if_unable(pgcfg->domain_name, "Domain Name"); - log_if_unable(pgcfg->domain_controller, - "Domain Controller"); - log_if_unable(pgcfg->forest_name, "Forest Name"); - log_if_unable(pgcfg->site_name, "Site Name"); - log_if_unable(pgcfg->global_catalog, "Global Catalog"); - log_if_unable(pgcfg->domains_in_forest, - "Domains in the Forest"); - if (!pgcfg->disable_cross_forest_trusts) { - log_if_unable(pgcfg->trusted_domains, - "Trusted Domains"); - } - } + if (DBG(CONFIG, 1)) { + log_if_unable(pgcfg->global_catalog, "Global Catalog"); + log_if_unable(pgcfg->domains_in_forest, + "Domains in the Forest"); + /* Empty trusted domains list is OK. */ } - ad_disc_done(ad_ctx); + if (status_fp) { + ad_disc_set_StatusFP(ad_ctx, NULL); + (void) fclose(status_fp); + status_fp = NULL; + } if (DBG(CONFIG, 1)) - idmapdlog(LOG_DEBUG, "Discovery done."); + idmapdlog(LOG_DEBUG, "Forest discovery done."); + + /* + * Log when this took more than 30 sec. + */ + t1 = time(NULL); + if (t1 > (t0 + 30)) { + idmapdlog(LOG_NOTICE, "Forest discovery took %d sec.", + (int)(t1 - t0)); + idmapdlog(LOG_NOTICE, "Check AD join status."); + } } @@ -1889,9 +2132,11 @@ idmap_cfg_discover(idmap_cfg_handles_t *handles, idmap_pg_config_t *pgcfg) int idmap_cfg_load(idmap_cfg_t *cfg, int flags) { + const ad_disc_t ad_ctx = cfg->handles.ad_ctx; int rc = 0; int errors; int changed = 0; + int dc_changed = 0; int ad_reload_required = 0; idmap_pg_config_t new_pgcfg, *live_pgcfg; @@ -1906,10 +2151,48 @@ idmap_cfg_load(idmap_cfg_t *cfg, int flags) if ((rc = idmap_cfg_load_smf(&cfg->handles, &new_pgcfg, &errors)) < -1) goto err; - if (flags & CFG_DISCOVER) - idmap_cfg_discover(&cfg->handles, &new_pgcfg); + if (flags & CFG_DISCOVER) { + + ad_disc_refresh(ad_ctx); + + /* + * Unless we've been asked to forget the current DC, + * give preference (in order) to the preferred DC if + * configured, or the current DC. These preferences + * reduce undesirable DC changes. + */ + if (flags & CFG_FORGET_DC) { + (void) ad_disc_set_PreferredDC(ad_ctx, NULL); + } else if (new_pgcfg.preferred_dc != NULL) { + (void) ad_disc_set_PreferredDC(ad_ctx, + new_pgcfg.preferred_dc); + } else if (live_pgcfg->domain_controller != NULL) { + (void) ad_disc_set_PreferredDC(ad_ctx, + live_pgcfg->domain_controller); + } else { + (void) ad_disc_set_PreferredDC(ad_ctx, NULL); + } + + /* + * We want a way to tell adspriv_getdcname_1_svc() + * (and others) that discovery is running and therefore + * they may want to wait a bit or return an error... + */ + (void) mutex_lock(&_idmapdstate.addisc_lk); + _idmapdstate.addisc_st |= ADDISC_ST_RUNNING; + (void) mutex_unlock(&_idmapdstate.addisc_lk); + + idmap_cfg_discover1(&cfg->handles, &new_pgcfg); + + WRLOCK_CONFIG(); + (void) mutex_lock(&_idmapdstate.addisc_lk); + _idmapdstate.addisc_st = 0; + (void) cond_broadcast(&_idmapdstate.addisc_cv); + (void) mutex_unlock(&_idmapdstate.addisc_lk); + } else { + WRLOCK_CONFIG(); + } - WRLOCK_CONFIG(); /* Non-discoverable props updated here */ changed += update_uint64(&live_pgcfg->list_size_limit, @@ -1921,6 +2204,9 @@ idmap_cfg_load(idmap_cfg_t *cfg, int flags) changed += update_uint64(&live_pgcfg->name_cache_timeout, &new_pgcfg.name_cache_timeout, "name_cache_timeout"); + changed += update_uint64(&live_pgcfg->rediscovery_interval, + &new_pgcfg.rediscovery_interval, "rediscovery_interval"); + changed += update_string(&live_pgcfg->machine_sid, &new_pgcfg.machine_sid, "machine_sid"); @@ -1950,16 +2236,29 @@ idmap_cfg_load(idmap_cfg_t *cfg, int flags) changed += update_string(&live_pgcfg->nldap_winname_attr, &new_pgcfg.nldap_winname_attr, "nldap_winname_attr"); - /* Props that can be discovered and set in SMF updated here */ changed += update_string(&live_pgcfg->default_domain, &new_pgcfg.default_domain, "default_domain"); - changed += update_string(&live_pgcfg->domain_name, - &new_pgcfg.domain_name, "domain_name"); + changed += update_dirs(&live_pgcfg->preferred_dc, + &new_pgcfg.preferred_dc, "preferred_dc"); + + /* Props that can be discovered or set in SMF updated here */ + + if (update_string(&live_pgcfg->domain_name, + &new_pgcfg.domain_name, "domain_name")) { + changed++; + ad_reload_required = TRUE; + idmapd_set_krb5_realm(live_pgcfg->domain_name); + } live_pgcfg->domain_name_auto_disc = new_pgcfg.domain_name_auto_disc; - changed += update_dirs(&live_pgcfg->domain_controller, + changed += update_string(&live_pgcfg->domain_guid, + &new_pgcfg.domain_guid, "domain_guid"); + live_pgcfg->domain_guid_auto_disc = new_pgcfg.domain_guid_auto_disc; + + dc_changed = update_dirs(&live_pgcfg->domain_controller, &new_pgcfg.domain_controller, "domain_controller"); + changed += dc_changed; live_pgcfg->domain_controller_auto_disc = new_pgcfg.domain_controller_auto_disc; @@ -1971,16 +2270,40 @@ idmap_cfg_load(idmap_cfg_t *cfg, int flags) &new_pgcfg.site_name, "site_name"); live_pgcfg->site_name_auto_disc = new_pgcfg.site_name_auto_disc; - if (update_dirs(&live_pgcfg->global_catalog, - &new_pgcfg.global_catalog, "global_catalog")) { - changed++; - if (live_pgcfg->global_catalog != NULL && - live_pgcfg->global_catalog[0].host[0] != '\0') - ad_reload_required = TRUE; + if (DBG(CONFIG, 1)) { + if (changed) + idmapdlog(LOG_NOTICE, "Configuration changed"); + else + idmapdlog(LOG_NOTICE, "Configuration unchanged"); + } + + UNLOCK_CONFIG(); + + if (dc_changed != 0) { + notify_dc_changed(); } + + /* + * Discovery2 can take a while. + */ + if (flags & CFG_DISCOVER) { + if (live_pgcfg->domain_name != NULL && + live_pgcfg->forest_name != NULL) + idmap_cfg_discover2(&cfg->handles, &new_pgcfg); + ad_disc_done(ad_ctx); + } + + WRLOCK_CONFIG(); + + /* More props that can be discovered or set in SMF */ + + changed += update_dirs(&live_pgcfg->global_catalog, + &new_pgcfg.global_catalog, "global_catalog"); live_pgcfg->global_catalog_auto_disc = new_pgcfg.global_catalog_auto_disc; + /* Props that are only discovered (never in SMF) */ + if (update_domains_in_forest(&live_pgcfg->domains_in_forest, &new_pgcfg.domains_in_forest, "domains_in_forest")) { changed++; @@ -2003,11 +2326,6 @@ idmap_cfg_load(idmap_cfg_t *cfg, int flags) ad_reload_required = TRUE; } - if (ad_reload_required) - reload_ad(); - - idmap_cfg_unload(&new_pgcfg); - if (DBG(CONFIG, 1)) { if (changed) idmapdlog(LOG_NOTICE, "Configuration changed"); @@ -2017,6 +2335,11 @@ idmap_cfg_load(idmap_cfg_t *cfg, int flags) UNLOCK_CONFIG(); + if (ad_reload_required) + reload_ad(); + + idmap_cfg_unload(&new_pgcfg); + err: (void) pthread_mutex_unlock(&cfg->handles.mutex); @@ -2111,6 +2434,10 @@ idmap_cfg_unload(idmap_pg_config_t *pgcfg) free(pgcfg->domain_name); pgcfg->domain_name = NULL; } + if (pgcfg->domain_guid) { + free(pgcfg->domain_guid); + pgcfg->domain_guid = NULL; + } if (pgcfg->machine_sid) { free(pgcfg->machine_sid); pgcfg->machine_sid = NULL; @@ -2176,8 +2503,45 @@ idmap_cfg_fini(idmap_cfg_t *cfg) void idmap_cfg_poke_updates(void) { - if (idmapd_ev_port != -1) + int prev_st; + + if (DBG(CONFIG, 1)) { + idmapdlog(LOG_INFO, "idmap_cfg_poke_updates"); + } + + (void) mutex_lock(&_idmapdstate.addisc_lk); + prev_st = _idmapdstate.addisc_st; + _idmapdstate.addisc_st |= ADDISC_ST_REQUESTED; + (void) mutex_unlock(&_idmapdstate.addisc_lk); + + if (prev_st & ADDISC_ST_REQUESTED) { + idmapdlog(LOG_DEBUG, "already poked"); + } else { + idmapdlog(LOG_DEBUG, "port send poke"); (void) port_send(idmapd_ev_port, POKE_AUTO_DISCOVERY, NULL); + } +} + +void +idmap_cfg_force_rediscovery(void) +{ + int prev_st; + + if (DBG(CONFIG, 1)) { + idmapdlog(LOG_INFO, "idmap_cfg_force_rediscovery"); + } + + (void) mutex_lock(&_idmapdstate.addisc_lk); + prev_st = _idmapdstate.addisc_st; + _idmapdstate.addisc_st |= ADDISC_ST_REQUESTED; + (void) mutex_unlock(&_idmapdstate.addisc_lk); + + if (prev_st & ADDISC_ST_REQUESTED) { + idmapdlog(LOG_DEBUG, "already kicked"); + } else { + idmapdlog(LOG_DEBUG, "port send kick"); + (void) port_send(idmapd_ev_port, KICK_AUTO_DISCOVERY, NULL); + } } /*ARGSUSED*/ @@ -2339,3 +2703,31 @@ idmap_cfg_upgrade(idmap_cfg_t *cfg) return (0); } + +/* + * The LDAP code passes principal names lacking any + * realm information, which causes mech_krb5 to do + * awful things trying to figure out the realm. + * Avoid that by making sure it has a default, + * even when krb5.conf is not configured. + */ +static void +idmapd_set_krb5_realm(char *domain) +{ + static char realm[MAXHOSTNAMELEN]; + size_t ilen, olen; + int err; + + if (domain == NULL) { + (void) unsetenv("KRB5_DEFAULT_REALM"); + return; + } + + /* Convert to upper case, in place. */ + (void) strlcpy(realm, domain, sizeof (realm)); + olen = ilen = strlen(realm); + (void) u8_textprep_str(realm, &ilen, realm, &olen, + U8_TEXTPREP_TOUPPER, U8_UNICODE_LATEST, &err); + + (void) setenv("KRB5_DEFAULT_REALM", realm, 1); +} diff --git a/usr/src/cmd/idmap/idmapd/idmap_config.h b/usr/src/cmd/idmap/idmapd/idmap_config.h index 31ae7b6ad1..6792d77774 100644 --- a/usr/src/cmd/idmap/idmapd/idmap_config.h +++ b/usr/src/cmd/idmap/idmapd/idmap_config.h @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #ifndef _IDMAP_CONFIG_H @@ -31,6 +31,7 @@ #include "addisc.h" #include <libscf.h> #include <synch.h> +#include <sys/uuid.h> #ifdef __cplusplus extern "C" { @@ -66,8 +67,7 @@ typedef struct idmap_cfg_handles { */ typedef struct idmap_trustedforest { char *forest_name; - idmap_ad_disc_ds_t - *global_catalog; /* global catalog hosts */ + ad_disc_ds_t *global_catalog; /* global catalog hosts */ ad_disc_domainsinforest_t *domains_in_forest; } idmap_trustedforest_t; @@ -77,19 +77,21 @@ typedef struct idmap_pg_config { uint64_t list_size_limit; uint64_t id_cache_timeout; uint64_t name_cache_timeout; + uint64_t rediscovery_interval; + char *machine_uuid; /* machine uuid */ char *machine_sid; /* machine sid */ char *default_domain; /* default domain name */ char *domain_name; /* AD domain name */ - boolean_t domain_name_auto_disc; - idmap_ad_disc_ds_t - *domain_controller; /* domain controller hosts */ + boolean_t domain_name_auto_disc; + char *domain_guid; /* GUID (string) */ + boolean_t domain_guid_auto_disc; + ad_disc_ds_t *domain_controller; /* domain controller hosts */ boolean_t domain_controller_auto_disc; char *forest_name; /* forest name */ boolean_t forest_name_auto_disc; char *site_name; /* site name */ boolean_t site_name_auto_disc; - idmap_ad_disc_ds_t - *global_catalog; /* global catalog hosts */ + ad_disc_ds_t *global_catalog; /* global catalog hosts */ boolean_t global_catalog_auto_disc; ad_disc_domainsinforest_t *domains_in_forest; @@ -99,6 +101,9 @@ typedef struct idmap_pg_config { idmap_trustedforest_t *trusted_forests; /* Array of trusted forests */ + ad_disc_ds_t *preferred_dc; + boolean_t preferred_dc_auto_disc; + /* * Following properties are associated with directory-based * name-mappings. @@ -127,10 +132,12 @@ extern int idmap_cfg_fini(idmap_cfg_t *); extern int idmap_cfg_upgrade(idmap_cfg_t *); extern int idmap_cfg_start_updates(void); extern void idmap_cfg_poke_updates(void); +extern void idmap_cfg_force_rediscovery(void); extern void idmap_cfg_hup_handler(int); -#define CFG_DISCOVER 0x1 -#define CFG_LOG 0x2 +#define CFG_DISCOVER 0x1 /* Run discovery */ +#define CFG_FORGET_DC 0x2 /* Forget current DC. */ +#define CFG_LOG 0x4 #ifdef __cplusplus } diff --git a/usr/src/cmd/idmap/idmapd/idmap_lsa.c b/usr/src/cmd/idmap/idmapd/idmap_lsa.c index 256e8494d8..28c6c2755e 100644 --- a/usr/src/cmd/idmap/idmapd/idmap_lsa.c +++ b/usr/src/cmd/idmap/idmapd/idmap_lsa.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ /* @@ -235,3 +236,13 @@ out: } return (ret); } + +/* + * This exists just so we can avoid exposing all of idmapd to libsmb.h. + * Like the above functions, it's a door call over to smbd. + */ +void +notify_dc_changed(void) +{ + smb_notify_dc_changed(); +} diff --git a/usr/src/cmd/idmap/idmapd/idmapd.c b/usr/src/cmd/idmap/idmapd/idmapd.c index 828bfc6545..3698270ab6 100644 --- a/usr/src/cmd/idmap/idmapd/idmapd.c +++ b/usr/src/cmd/idmap/idmapd/idmapd.c @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ @@ -57,10 +58,16 @@ #include <assert.h> #include <note.h> +#define CBUFSIZ 26 /* ctime(3c) */ + static void term_handler(int); static void init_idmapd(); static void fini_idmapd(); +/* The DC Locator lives inside idmap (for now). */ +extern void init_dc_locator(void); +extern void fini_dc_locator(void); + idmapd_state_t _idmapdstate; SVCXPRT *xprt = NULL; @@ -170,6 +177,7 @@ static void term_handler(int sig) { idmapdlog(LOG_INFO, "Terminating."); + fini_dc_locator(); fini_idmapd(); _exit(0); } @@ -250,6 +258,13 @@ main(int argc, char **argv) int c; struct rlimit rl; + if (rwlock_init(&_idmapdstate.rwlk_cfg, USYNC_THREAD, NULL) != 0) + return (-1); + if (mutex_init(&_idmapdstate.addisc_lk, USYNC_THREAD, NULL) != 0) + return (-1); + if (cond_init(&_idmapdstate.addisc_cv, USYNC_THREAD, NULL) != 0) + return (-1); + _idmapdstate.daemon_mode = TRUE; while ((c = getopt(argc, argv, "d")) != -1) { switch (c) { @@ -303,6 +318,7 @@ main(int argc, char **argv) idmap_init_tsd_key(); init_idmapd(); + init_dc_locator(); /* signal handlers that should run only after we're initialized */ (void) sigset(SIGTERM, term_handler); @@ -348,6 +364,7 @@ init_idmapd() */ (void) unlink(IDMAP_CACHEDIR "/ccache"); (void) putenv("KRB5CCNAME=" IDMAP_CACHEDIR "/ccache"); + (void) putenv("MS_INTEROP=1"); if (sysinfo(SI_HOSTNAME, _idmapdstate.hostname, sizeof (_idmapdstate.hostname)) == -1) { @@ -505,7 +522,18 @@ restore_svc(void) /* printflike */ void idmapdlog(int pri, const char *format, ...) { + static time_t prev_ts; va_list args; + char cbuf[CBUFSIZ]; + time_t ts; + + ts = time(NULL); + if (prev_ts != ts) { + prev_ts = ts; + /* NB: cbuf has \n */ + (void) fprintf(stderr, "@ %s", + ctime_r(&ts, cbuf, sizeof (cbuf))); + } va_start(args, format); (void) vfprintf(stderr, format, args); diff --git a/usr/src/cmd/idmap/idmapd/idmapd.h b/usr/src/cmd/idmap/idmapd/idmapd.h index 7ed2740a34..a364cd4d9b 100644 --- a/usr/src/cmd/idmap/idmapd/idmapd.h +++ b/usr/src/cmd/idmap/idmapd/idmapd.h @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2011 Nexenta Systems, Inc. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #ifndef _IDMAPD_H @@ -104,10 +104,16 @@ typedef struct idmapd_state { adutils_ad_t **gcs; int num_dcs; adutils_ad_t **dcs; + mutex_t addisc_lk; + cond_t addisc_cv; + int addisc_st; int debug[IDMAPD_DEBUG_MAX+1]; } idmapd_state_t; extern idmapd_state_t _idmapdstate; +#define ADDISC_ST_REQUESTED 1 +#define ADDISC_ST_RUNNING 2 + #define RDLOCK_CONFIG() \ (void) rw_rdlock(&_idmapdstate.rwlk_cfg); #define WRLOCK_CONFIG() \ @@ -308,6 +314,7 @@ extern void reload_ad(); extern void idmap_init_tsd_key(void); extern void degrade_svc(int, const char *); extern void restore_svc(void); +extern void notify_dc_changed(void); extern int init_dbs(); diff --git a/usr/src/cmd/idmap/idmapd/init.c b/usr/src/cmd/idmap/idmapd/init.c index 3e5dde7ad8..8516e07f4f 100644 --- a/usr/src/cmd/idmap/idmapd/init.c +++ b/usr/src/cmd/idmap/idmapd/init.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ /* @@ -44,8 +44,6 @@ init_mapping_system() { int rc = 0; - if (rwlock_init(&_idmapdstate.rwlk_cfg, USYNC_THREAD, NULL) != 0) - return (-1); if ((rc = load_config()) < 0) return (rc); @@ -139,7 +137,7 @@ reload_gcs() * If that stops working we'll go into degraded mode anyways * when it does. */ - degrade_svc(0, + idmapdlog(LOG_INFO, "Global Catalog servers not configured/discoverable"); return; } @@ -272,7 +270,7 @@ reload_dcs(void) * If that stops working we'll go into degraded mode anyways * when it does. */ - degrade_svc(0, + idmapdlog(LOG_INFO, "Domain controller servers not configured/discoverable"); return; } diff --git a/usr/src/cmd/idmap/idmapd/krb5_lookup.c b/usr/src/cmd/idmap/idmapd/krb5_lookup.c new file mode 100644 index 0000000000..a45fc5d8f3 --- /dev/null +++ b/usr/src/cmd/idmap/idmapd/krb5_lookup.c @@ -0,0 +1,156 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <syslog.h> + +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <sys/note.h> +#include <synch.h> +#include <thread.h> + +#include "idmapd.h" +#include "libadutils.h" +#include "locate_plugin.h" + +/* osconf.h - sigh */ +#define KRB5_DEFAULT_PORT 88 +#define DEFAULT_KADM5_PORT 749 +#define DEFAULT_KPASSWD_PORT 464 + +/* + * This is an "override plugin" used by libkrb5. See: + * lib/gss_mechs/mech_krb5/krb5/os/locate_kdc.c + * + * The interface is based on: + * http://web.mit.edu/~kerberos/krb5-1.12/doc/plugindev/locate.html + */ + +/* + * Called by krb5int_locate_server / override_locate_server + */ + +krb5_error_code +_krb5_override_service_locator( + void *arg0, + enum locate_service_type svc, + const char *realm, + int socktype, + int family, + int (*cbfunc)(void *, int, struct sockaddr *), + void *cbdata) +{ + _NOTE(ARGUNUSED(arg0)) + idmap_pg_config_t *pgcfg; + ad_disc_ds_t *ds; + int rc = KRB5_PLUGIN_NO_HANDLE; + short port; + + /* + * Is this a service we want to override? + */ + switch (svc) { + case locate_service_kdc: + case locate_service_master_kdc: + port = htons(KRB5_DEFAULT_PORT); + break; + case locate_service_kadmin: + port = htons(DEFAULT_KADM5_PORT); + break; + case locate_service_kpasswd: + port = htons(DEFAULT_KPASSWD_PORT); + break; + case locate_service_krb524: + default: + return (rc); + } + + RDLOCK_CONFIG(); + pgcfg = &_idmapdstate.cfg->pgcfg; + + /* + * Is this a realm we want to override? + */ + if (pgcfg->domain_name == NULL) + goto out; + if (0 != strcasecmp(realm, pgcfg->domain_name)) + goto out; + + /* + * Yes, this is our domain. Have a DC? + */ + if ((ds = pgcfg->domain_controller) == NULL) { + rc = KRB5_REALM_CANT_RESOLVE; + goto out; + } + + switch (family) { + case AF_UNSPEC: + break; /* OK */ + case AF_INET: + case AF_INET6: + if (family == ds->addr.ss_family) + break; /* OK */ + /* else fallthrough */ + default: + rc = KRB5_ERR_NO_SERVICE; + goto out; + } + + /* + * Provide the service address we have. + */ + switch (ds->addr.ss_family) { + case AF_INET: { + struct sockaddr_in sin; + struct sockaddr_in *dsa = (void *)&ds->addr; + (void) memset(&sin, 0, sizeof (sin)); + sin.sin_family = AF_INET; + sin.sin_port = port; + (void) memcpy(&sin.sin_addr, &dsa->sin_addr, + sizeof (sin.sin_addr)); + rc = cbfunc(cbdata, socktype, (struct sockaddr *)&sin); + break; + } + case AF_INET6: { + struct sockaddr_in6 sin6; + struct sockaddr_in6 *dsa6 = (void *)&ds->addr; + (void) memset(&sin6, 0, sizeof (sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_port = port; + (void) memcpy(&sin6.sin6_addr, &dsa6->sin6_addr, + sizeof (sin6.sin6_addr)); + rc = cbfunc(cbdata, socktype, (struct sockaddr *)&sin6); + break; + } + default: + rc = KRB5_ERR_NO_SERVICE; + goto out; + } + /* rc from cbfunc is special. */ + if (rc) + rc = ENOMEM; + +out: + UNLOCK_CONFIG(); + + return (rc); +} diff --git a/usr/src/cmd/idmap/idmapd/rpc_svc.c b/usr/src/cmd/idmap/idmapd/rpc_svc.c deleted file mode 100644 index f959d7b918..0000000000 --- a/usr/src/cmd/idmap/idmapd/rpc_svc.c +++ /dev/null @@ -1,334 +0,0 @@ -/* - * 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 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -/* - * Please do not edit this file. - * It was generated using rpcgen. - * Edit idmap_prot.x and rebuild this file with - * rpcgen -CMNm -o idmap_prot_svc.c.new ../../../uts/common/rpcsvc/idmap_prot.x - * then merge as required. A recent version of rpcgen is needed to - * produce this exact file; when the revised rpcgen is available in the - * build environment this file can be built automatically. - */ - -#include "../../../uts/common/rpcsvc/idmap_prot.h" -#include <stdio.h> -#include <stdlib.h> /* getenv, exit */ -#include <signal.h> -#include <rpc/pmap_clnt.h> /* for pmap_unset */ -#include <string.h> /* strcmp */ -#include <unistd.h> /* setsid */ -#include <sys/types.h> -#include <memory.h> -#include <stropts.h> -#include <sys/resource.h> /* rlimit */ -#include <syslog.h> - -#ifndef SIG_PF -#define SIG_PF void(*)(int) -#endif - -#ifdef DEBUG -#define RPC_SVC_FG -#endif - -#define _RPCSVC_CLOSEDOWN 120 -extern int _rpcpmstart; /* Started by a port monitor ? */ - -/* States a server can be in wrt request */ - -#define _IDLE 0 -#define _SERVED 1 - -/* LINTED static unused if no main */ -static int _rpcsvcstate = _IDLE; /* Set when a request is serviced */ -static int _rpcsvccount = 0; /* Number of requests being serviced */ -mutex_t _svcstate_lock; /* lock for _rpcsvcstate, _rpcsvccount */ - -#if defined(RPC_MSGOUT) -extern void RPC_MSGOUT(const char *, ...); -#else /* defined(RPC_MSGOUT) */ -static void -RPC_MSGOUT(const char *fmt, char *msg) -{ -#ifdef RPC_SVC_FG - if (_rpcpmstart) - syslog(LOG_ERR, fmt, msg); - else { - (void) fprintf(stderr, fmt, msg); - (void) putc('\n', stderr); - } -#else - syslog(LOG_ERR, fmt, msg); -#endif -} -#endif /* defined(RPC_MSGOUT) */ - -/* ARGSUSED */ -int -_idmap_null_1( - void *argp, - void *result, - struct svc_req *rqstp) -{ - return (idmap_null_1_svc(result, rqstp)); -} - -int -_idmap_get_mapped_ids_1( - idmap_mapping_batch *argp, - idmap_ids_res *result, - struct svc_req *rqstp) -{ - return (idmap_get_mapped_ids_1_svc(*argp, result, rqstp)); -} - -int -_idmap_list_mappings_1( - idmap_list_mappings_1_argument *argp, - idmap_mappings_res *result, - struct svc_req *rqstp) -{ - return (idmap_list_mappings_1_svc( - argp->lastrowid, - argp->limit, - argp->flag, - result, rqstp)); -} - -int -_idmap_list_namerules_1( - idmap_list_namerules_1_argument *argp, - idmap_namerules_res *result, - struct svc_req *rqstp) -{ - return (idmap_list_namerules_1_svc( - argp->rule, - argp->lastrowid, - argp->limit, - result, rqstp)); -} - -int -_idmap_update_1( - idmap_update_batch *argp, - idmap_update_res *result, - struct svc_req *rqstp) -{ - return (idmap_update_1_svc(*argp, result, rqstp)); -} - -int -_idmap_get_mapped_id_by_name_1( - idmap_mapping *argp, - idmap_mappings_res *result, - struct svc_req *rqstp) -{ - return (idmap_get_mapped_id_by_name_1_svc(*argp, result, rqstp)); -} - -int -_idmap_get_prop_1( - idmap_prop_type *argp, - idmap_prop_res *result, - struct svc_req *rqstp) -{ - return (idmap_get_prop_1_svc(*argp, result, rqstp)); -} - -int -_directory_get_common_1( - directory_get_common_1_argument *argp, - directory_results_rpc *result, - struct svc_req *rqstp) -{ - return (directory_get_common_1_svc( - argp->ids, - argp->types, - argp->attrs, - result, rqstp)); -} - -int -_idmap_flush_1( - idmap_flush_op *argp, - idmap_retcode *result, - struct svc_req *rqstp) -{ - return (idmap_flush_1_svc(*argp, result, rqstp)); -} - -void -idmap_prog_1(struct svc_req *rqstp, register SVCXPRT *transp) -{ - union { - idmap_mapping_batch idmap_get_mapped_ids_1_arg; - idmap_list_mappings_1_argument idmap_list_mappings_1_arg; - idmap_list_namerules_1_argument idmap_list_namerules_1_arg; - idmap_update_batch idmap_update_1_arg; - idmap_mapping idmap_get_mapped_id_by_name_1_arg; - idmap_prop_type idmap_get_prop_1_arg; - directory_get_common_1_argument directory_get_common_1_arg; - idmap_flush_op idmap_flush_1_arg; - } argument; - union { - idmap_ids_res idmap_get_mapped_ids_1_res; - idmap_mappings_res idmap_list_mappings_1_res; - idmap_namerules_res idmap_list_namerules_1_res; - idmap_update_res idmap_update_1_res; - idmap_mappings_res idmap_get_mapped_id_by_name_1_res; - idmap_prop_res idmap_get_prop_1_res; - directory_results_rpc directory_get_common_1_res; - idmap_retcode idmap_flush_1_res; - } result; - bool_t retval; - xdrproc_t _xdr_argument, _xdr_result; - bool_t (*local)(char *, void *, struct svc_req *); - - (void) mutex_lock(&_svcstate_lock); - _rpcsvccount++; - (void) mutex_unlock(&_svcstate_lock); - switch (rqstp->rq_proc) { - case IDMAP_NULL: - _xdr_argument = (xdrproc_t) - xdr_void; - _xdr_result = (xdrproc_t) - xdr_void; - local = (bool_t (*) (char *, void *, struct svc_req *)) - _idmap_null_1; - break; - - case IDMAP_GET_MAPPED_IDS: - _xdr_argument = (xdrproc_t) - xdr_idmap_mapping_batch; - _xdr_result = (xdrproc_t) - xdr_idmap_ids_res; - local = (bool_t (*) (char *, void *, struct svc_req *)) - _idmap_get_mapped_ids_1; - break; - - case IDMAP_LIST_MAPPINGS: - _xdr_argument = (xdrproc_t) - xdr_idmap_list_mappings_1_argument; - _xdr_result = (xdrproc_t) - xdr_idmap_mappings_res; - local = (bool_t (*) (char *, void *, struct svc_req *)) - _idmap_list_mappings_1; - break; - - case IDMAP_LIST_NAMERULES: - _xdr_argument = (xdrproc_t) - xdr_idmap_list_namerules_1_argument; - _xdr_result = (xdrproc_t) - xdr_idmap_namerules_res; - local = (bool_t (*) (char *, void *, struct svc_req *)) - _idmap_list_namerules_1; - break; - - case IDMAP_UPDATE: - _xdr_argument = (xdrproc_t) - xdr_idmap_update_batch; - _xdr_result = (xdrproc_t) - xdr_idmap_update_res; - local = (bool_t (*) (char *, void *, struct svc_req *)) - _idmap_update_1; - break; - - case IDMAP_GET_MAPPED_ID_BY_NAME: - _xdr_argument = (xdrproc_t) - xdr_idmap_mapping; - _xdr_result = (xdrproc_t) - xdr_idmap_mappings_res; - local = (bool_t (*) (char *, void *, struct svc_req *)) - _idmap_get_mapped_id_by_name_1; - break; - - case IDMAP_GET_PROP: - _xdr_argument = (xdrproc_t) - xdr_idmap_prop_type; - _xdr_result = (xdrproc_t) - xdr_idmap_prop_res; - local = (bool_t (*) (char *, void *, struct svc_req *)) - _idmap_get_prop_1; - break; - - case DIRECTORY_GET_COMMON: - _xdr_argument = (xdrproc_t) - xdr_directory_get_common_1_argument; - _xdr_result = (xdrproc_t) - xdr_directory_results_rpc; - local = (bool_t (*) (char *, void *, struct svc_req *)) - _directory_get_common_1; - break; - - case IDMAP_FLUSH: - _xdr_argument = (xdrproc_t) - xdr_idmap_flush_op; - _xdr_result = (xdrproc_t) - xdr_idmap_retcode; - local = (bool_t (*) (char *, void *, struct svc_req *)) - _idmap_flush_1; - break; - - default: - svcerr_noproc(transp); - (void) mutex_lock(&_svcstate_lock); - _rpcsvccount--; - _rpcsvcstate = _SERVED; - (void) mutex_unlock(&_svcstate_lock); - return; /* CSTYLED */ - } - (void) memset((char *)&argument, 0, sizeof (argument)); - if (!svc_getargs(transp, _xdr_argument, (caddr_t)&argument)) { - svcerr_decode(transp); - (void) mutex_lock(&_svcstate_lock); - _rpcsvccount--; - _rpcsvcstate = _SERVED; - (void) mutex_unlock(&_svcstate_lock); - return; /* CSTYLED */ - } - retval = (bool_t)(*local)((char *)&argument, (void *)&result, rqstp); - if (_xdr_result && retval > 0 && - !svc_sendreply(transp, _xdr_result, (char *)&result)) { - svcerr_systemerr(transp); - } - if (!svc_freeargs(transp, _xdr_argument, (caddr_t)&argument)) { - RPC_MSGOUT("%s", - "unable to free arguments"); - exit(1); - } - if (_xdr_result != NULL) { - if (!idmap_prog_1_freeresult(transp, _xdr_result, - (caddr_t)&result)) - RPC_MSGOUT("%s", - "unable to free results"); - - } - (void) mutex_lock(&_svcstate_lock); - _rpcsvccount--; - _rpcsvcstate = _SERVED; - (void) mutex_unlock(&_svcstate_lock); - return; /* CSTYLED */ -} diff --git a/usr/src/cmd/idmap/nltest/Makefile b/usr/src/cmd/idmap/nltest/Makefile new file mode 100644 index 0000000000..1e6cb034fd --- /dev/null +++ b/usr/src/cmd/idmap/nltest/Makefile @@ -0,0 +1,67 @@ +# +# 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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2014 Nexenta Systems, Inc. All rights reserved. +# + +PROG = nltest +CLIENTOBJS = nltest.o + +CLIENTSRCS = $(CLIENTOBJS:%.o=%.c) +POFILES = $(CLIENTOBJS:.o=.po) +OBJS = $(CLIENTOBJS) +SRCS = $(CLIENTSRCS) + +include ../../Makefile.cmd + +POFILE = $(PROG)_all.po +LDLIBS += -lads -luuid +FILEMODE = 0555 + +INCS += -I. + +CFLAGS += $(CCVERBOSE) +$(OBJS) := CPPFLAGS += $(INCS) -D_REENTRANT +$(POFILE) := CPPFLAGS += $(INCS) + +lint_SRCS := CPPFLAGS += $(INCS) + +.KEEP_STATE: + +all: $(PROG) + +$(PROG): $(OBJS) + $(LINK.c) -g -o $@ $(OBJS) $(LDLIBS) + $(POST_PROCESS) + +$(POFILE): $(POFILES) + $(RM) $@ + cat $(POFILES) > $@ + +install: all $(ROOTUSRSBINPROG) + +clean: + $(RM) $(OBJS) + +lint: lint_SRCS + +include ../../Makefile.targ diff --git a/usr/src/cmd/idmap/nltest/Run.sh b/usr/src/cmd/idmap/nltest/Run.sh new file mode 100755 index 0000000000..f77af1e7d9 --- /dev/null +++ b/usr/src/cmd/idmap/nltest/Run.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2014 Nexenta Systems, Inc. All rights reserved. +# + +# Helper program to run nltest + + +[ -n "$CODEMGR_WS" ] || { + echo "Need a buildenv to set CODEMGR_WS=..." + exit 1; +} + + +ROOT=${CODEMGR_WS}/proto/root_i386 +LD_LIBRARY_PATH=$ROOT/usr/lib:$ROOT/lib +export LD_LIBRARY_PATH + +$ROOT/usr/sbin/nltest "$@" diff --git a/usr/src/cmd/idmap/nltest/nltest.c b/usr/src/cmd/idmap/nltest/nltest.c new file mode 100644 index 0000000000..002fe0f157 --- /dev/null +++ b/usr/src/cmd/idmap/nltest/nltest.c @@ -0,0 +1,331 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + */ + + +#include <stdio.h> +#include <libintl.h> +#include <stdlib.h> +#include <strings.h> +#include <err.h> +#include <ads/dsgetdc.h> +#include <smb/nterror.h> +#include <uuid/uuid.h> + + +static void dclist_usage(void); +static int cmd_dclist(char *); +static void dcname_usage(void); +static int cmd_dcname(char *); +static void dsgetdc_usage(void); +static int cmd_dsgetdc(char *); +static void dsgetdcname_usage(void); +static int cmd_dsgetdcname(char *); +static void kick_usage(void); +static int cmd_kick(char *); +static void help(void); + +typedef int cmd_fn_t (char *); +typedef void cmd_usage_t (void); + + +static struct commands { + const char *name; /* name of subcommand */ + cmd_fn_t *fn; /* pointer to subcommand handler function */ + cmd_usage_t *usage; /* pointer to subcommand help function */ + int optreq; /* does this have a required optval */ +} commands[] = { + {"dclist", cmd_dclist, dclist_usage, 0}, + {"dcname", cmd_dcname, dcname_usage, 0}, + {"dsgetdc", cmd_dsgetdc, dsgetdc_usage, 0}, + {"dsgetdcname", cmd_dsgetdcname, dsgetdcname_usage, 0}, + {"kick", cmd_kick, kick_usage, 0}, + {NULL, NULL, NULL, 0} +}; + + +/* + * lookupcmd + */ +static struct commands * +lookupcmd(const char *name) +{ + struct commands *cmd; + + for (cmd = commands; cmd->name; cmd++) { + if (strcasecmp(cmd->name, name) == 0) + return (cmd); + } + return (NULL); +} + +/* + * dclist + */ +static void +dclist_usage(void) +{ + (void) printf(gettext("usage: nltest dclist... \n")); + exit(1); +} + +/* ARGSUSED */ +static int +cmd_dclist(char *optval) +{ + (void) printf("cmd_dclist() \n"); + return (0); +} + +/* + * dcname + */ +static void +dcname_usage(void) +{ + (void) printf(gettext("usage: nltest dcname... \n")); + exit(1); +} + +/* ARGSUSED */ +static int +cmd_dcname(char *optval) +{ + (void) printf("cmd_dcname() \n"); + return (0); +} + +/* + * dsgetdc + */ +static void +dsgetdc_usage(void) +{ + (void) printf(gettext("usage: nltest dsgetdc... \n")); + exit(1); +} + +/* ARGSUSED */ +static int +cmd_dsgetdc(char *optval) +{ + (void) printf("cmd_dsgetdc() \n"); + return (0); +} + +/* + * dsgetdcname + */ +static void +dsgetdcname_usage(void) +{ + (void) printf(gettext("usage: nltest dsgetdcname domainname \n")); + exit(1); +} + +static int +cmd_dsgetdcname(char *domname) +{ + char uuid_buf[UUID_PRINTABLE_STRING_LENGTH]; + int err = 0; + char *atype; + DOMAIN_CONTROLLER_INFO *dcinfo; + + if (domname != NULL) + (void) printf(" Domain name supplied: %s \n", domname); + + err = DsGetDcName(NULL, domname, NULL, NULL, 0, &dcinfo); + + switch (err) { + case 0: + break; + case ERROR_NO_SUCH_DOMAIN: + (void) printf("Domain controller not found.\n"); + (void) printf("See: /var/run/idmap/discovery.log\n"); + exit(1); + default: + (void) printf("Unexpected error %d\n", err); + exit(1); + } + + switch (dcinfo->DomainControllerAddressType) { + case DS_INET_ADDRESS: + atype = "inet"; + break; + case DS_NETBIOS_ADDRESS: + atype = "netbios"; + break; + default: + atype = "?"; + break; + } + + uuid_unparse(dcinfo->DomainGuid, uuid_buf); + + (void) printf("Data Returned from DsGetDcName() call: \n"); + (void) printf(" DC Name: %s \n", dcinfo->DomainControllerName); + (void) printf(" DC Addr: %s \n", dcinfo->DomainControllerAddress); + (void) printf(" DC Addr Type: %s \n", atype); + (void) printf(" Domain Name: %s \n", dcinfo->DomainName); + (void) printf(" Domain GUID: %s \n", uuid_buf); + (void) printf(" DNS Forest Name: %s \n", dcinfo->DnsForestName); + (void) printf(" Flags: 0x%x \n", dcinfo->Flags); + (void) printf(" DC Site Name: %s \n", dcinfo->DcSiteName); + (void) printf(" Client Site Name: %s \n", dcinfo->ClientSiteName); + + return (0); +} + +/* + * kick + */ +static void +kick_usage(void) +{ + (void) printf(gettext("usage: nltest /KICK \n")); + exit(1); +} + + +static int +cmd_kick(char *domname) +{ + int flags = 0; + int result; + + result = _DsForceRediscovery(domname, flags); + + return (result); +} + +/* + * help functions + */ + +static void +help(void) { + (void) printf("\n"); + /* + * TODO: We may want to revise this help text. It's basically + * a copy-paste from: + * http://technet.microsoft.com/en-us/library/cc731935.aspx + */ + (void) printf(gettext("usage: %s /subcommand\n"), + (char *)getexecname()); + (void) printf(gettext("where subcommands are:\n" +#if 0 /* not yet */ + " dclist Lists all domain controllers in the domain.\n" + " dcname Lists the PDC or PDC emulator.\n" + " dsgetdc Queries DNS server for list of DCs and" + " their IP addresses and contacts each DC to check" + " for connectivity.\n" +#endif + " dsgetdcname returns the name of a domain controller in a" + " specified domain\n" + " help display help on specified subcommand\n" + " kick trigger domain controller re-discovery\n" + "\n")); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + struct commands *cmd; + int err = 0; + char *option_cmd = NULL; + char *arg; + char *p; + char *optname; + char *optval = NULL; + int i; + int optind = 1; + + /* + * Parse options. + */ + while (optind < argc) { + arg = argv[optind]; + optname = NULL; + optval = NULL; + + /* Is this an option? */ + if (arg[0] == '/') { + optname = arg + 1; + optind++; + + /* + * May have /optname:value + */ + if ((p = strchr(optname, ':')) != NULL) { + *p++ = '\0'; + optval = p; + } + } else if (arg[0] == '-' && arg[1] == '-') { + optname = arg + 2; + optind++; + + /* + * May have --optname=value + */ + if ((p = strchr(optname, '=')) != NULL) { + *p++ = '\0'; + optval = p; + } + } else { + /* Not an option. Stop parsing. */ + break; + } + + /* + * Handle each optname (and maybe its optval) + * Might put this logic in a table of options. + * (including a flag for "optval required", + * so that check could be factored out) + */ + for (cmd = commands; cmd->name; cmd++) { + if (!strcasecmp(optname, cmd->name)) { + /* cmd->name requires an optval */ + if (optval == NULL && optind < argc) + optval = argv[optind++]; + + if (optval == NULL && cmd->optreq > 0) { + (void) fprintf(stderr, + "%s: option %s requires a value\n", + argv[0], optname); + return (1); + } + option_cmd = optname; + } + } + } + + /* + * Handle remaining non-option arguments + */ + for (i = optind; i < argc; i++) { + (void) printf("arg: %s\n", argv[i]); + } + + if (option_cmd == NULL) + help(); + + cmd = lookupcmd(option_cmd); + if (cmd == NULL) + err = 1; + else + err = cmd->fn(optval); + + return (err); +} diff --git a/usr/src/cmd/idmap/test-getdc/.dbxrc b/usr/src/cmd/idmap/test-getdc/.dbxrc new file mode 100644 index 0000000000..29a559ed45 --- /dev/null +++ b/usr/src/cmd/idmap/test-getdc/.dbxrc @@ -0,0 +1,23 @@ + +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2014 Nexenta Systems, Inc. All rights reserved. +# + +set -o emacs + +setenv ROOT ${CODEMGR_WS}/proto/root_i386 +setenv LD_LIBRARY_PATH ${ROOT}/usr/lib:${ROOT}/lib + +loadobject -load ${ROOT}/usr/lib/libadutils.so.1 +debug ./test-getdc diff --git a/usr/src/cmd/idmap/test-getdc/Makefile b/usr/src/cmd/idmap/test-getdc/Makefile new file mode 100644 index 0000000000..10238ebb01 --- /dev/null +++ b/usr/src/cmd/idmap/test-getdc/Makefile @@ -0,0 +1,70 @@ +# +# 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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2014 Nexenta Systems, Inc. All rights reserved. +# + +PROG = test-getdc +OBJS = getdc_main.o + +SRCS = $(OBJS:%.o=%.c) +POFILES = $(OBJS:.o=.po) + +include ../../Makefile.cmd + +POFILE = $(PROG)_all.po + +LDLIBS += -ladutils -lnsl -lumem +LDFLAGS += -R'$$ORIGIN/../lib' + +INCS += -I. -I../../../lib/libadutils/common + +LINTFLAGS += -xerroff=E_NAME_DEF_NOT_USED2 + +CFLAGS += $(CCVERBOSE) +$(OBJS) := CPPFLAGS += $(INCS) -D_REENTRANT +$(POFILE) := CPPFLAGS += $(INCS) + +lint_SRCS := CPPFLAGS += $(INCS) + +.KEEP_STATE: + +all: $(PROG) + +$(PROG): $(OBJS) FRC + $(LINK.c) -g -o $@ $(OBJS) $(LDLIBS) + $(POST_PROCESS) + +$(POFILE): $(POFILES) + $(RM) $@ + cat $(POFILES) > $@ + +install: all $(ROOTPROG) + +clean: + $(RM) $(OBJS) + +lint: lint_SRCS + +include ../../Makefile.targ + +FRC: diff --git a/usr/src/cmd/idmap/test-getdc/getdc_main.c b/usr/src/cmd/idmap/test-getdc/getdc_main.c new file mode 100644 index 0000000000..a0465bcc07 --- /dev/null +++ b/usr/src/cmd/idmap/test-getdc/getdc_main.c @@ -0,0 +1,154 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + */ + + +#include <sys/note.h> +#include <stdarg.h> +#include <stdio.h> +#include <addisc.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +int debug; +char *domainname = NULL; + +void print_ds(ad_disc_ds_t *); +void mylogger(int pri, const char *format, ...); + +int +main(int argc, char *argv[]) +{ + ad_disc_t ad_ctx = NULL; + boolean_t autodisc; + ad_disc_ds_t *dc, *gc; + char *s; + int c; + + while ((c = getopt(argc, argv, "d")) != -1) { + switch (c) { + case '?': + (void) fprintf(stderr, "bad option: -%c\n", optopt); + return (1); + case 'd': + debug++; + break; + } + } + + if (optind < argc) + domainname = argv[optind]; + + adutils_set_logger(mylogger); + adutils_set_debug(AD_DEBUG_ALL, debug); + + ad_ctx = ad_disc_init(); + ad_disc_set_StatusFP(ad_ctx, stdout); + + if (domainname) + (void) ad_disc_set_DomainName(ad_ctx, domainname); + + ad_disc_refresh(ad_ctx); + + dc = ad_disc_get_DomainController(ad_ctx, + AD_DISC_PREFER_SITE, &autodisc); + if (dc == NULL) { + (void) printf("getdc failed\n"); + return (1); + } + (void) printf("Found a DC:\n"); + print_ds(dc); + free(dc); + + s = ad_disc_get_ForestName(ad_ctx, NULL); + (void) printf("Forest: %s\n", s); + free(s); + + s = ad_disc_get_SiteName(ad_ctx, NULL); + (void) printf("Site: %s\n", s); + free(s); + + gc = ad_disc_get_GlobalCatalog(ad_ctx, + AD_DISC_PREFER_SITE, &autodisc); + if (gc != NULL) { + (void) printf("Found a GC:\n"); + print_ds(gc); + free(gc); + } + + ad_disc_done(ad_ctx); + ad_disc_fini(ad_ctx); + ad_ctx = NULL; + + return (0); +} + +void +print_ds(ad_disc_ds_t *ds) +{ + char buf[64]; + + for (; ds->host[0] != '\0'; ds++) { + const char *p; + + (void) printf("Name: %s\n", ds->host); + (void) printf(" flags: 0x%X\n", ds->flags); + if (ds->addr.ss_family == AF_INET) { + struct sockaddr_in *sin; + sin = (struct sockaddr_in *)&ds->addr; + p = inet_ntop(AF_INET, &sin->sin_addr, + buf, sizeof (buf)); + if (p == NULL) + p = "?"; + (void) printf(" A %s %d\n", p, ds->port); + } + if (ds->addr.ss_family == AF_INET6) { + struct sockaddr_in6 *sin6; + sin6 = (struct sockaddr_in6 *)&ds->addr; + p = inet_ntop(AF_INET6, &sin6->sin6_addr, + buf, sizeof (buf)); + if (p == NULL) + p = "?"; + (void) printf(" AAAA %s %d\n", p, ds->port); + } + } +} + +/* printflike */ +void +mylogger(int pri, const char *format, ...) +{ + _NOTE(ARGUNUSED(pri)) + va_list args; + + va_start(args, format); + (void) vfprintf(stderr, format, args); + (void) fprintf(stderr, "\n"); + va_end(args); +} + +/* + * This is a unit-test program. Always enable libumem debugging. + */ +const char * +_umem_debug_init(void) +{ + return ("default,verbose"); /* $UMEM_DEBUG setting */ +} + +const char * +_umem_logging_init(void) +{ + return ("fail,contents"); /* $UMEM_LOGGING setting */ +} diff --git a/usr/src/cmd/mdb/common/modules/libumem/misc.c b/usr/src/cmd/mdb/common/modules/libumem/misc.c index bedb166aee..8f07d475df 100644 --- a/usr/src/cmd/mdb/common/modules/libumem/misc.c +++ b/usr/src/cmd/mdb/common/modules/libumem/misc.c @@ -23,11 +23,9 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include "misc.h" -#define UMEM_OBJNAME "libumem.so" +#define UMEM_OBJNAME "libumem.so.1" int umem_debug_level = 0; int umem_is_standalone = 0; diff --git a/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c b/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c index d184f2a3e8..113f5bf458 100644 --- a/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c +++ b/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c @@ -972,7 +972,8 @@ smb_dcmd_request(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) static const char *smb_user_state[SMB_USER_STATE_SENTINEL] = { - "LOGGED_IN", + "LOGGING_ON", + "LOGGED_ON", "LOGGING_OFF", "LOGGED_OFF" }; diff --git a/usr/src/cmd/smbsrv/dtrace/Makefile b/usr/src/cmd/smbsrv/dtrace/Makefile index b0bc255933..fb892315fa 100644 --- a/usr/src/cmd/smbsrv/dtrace/Makefile +++ b/usr/src/cmd/smbsrv/dtrace/Makefile @@ -22,10 +22,11 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" +# Copyright 2014 Nexenta Systems, Inc. All rights reserved. # -SRCS= cifs.d msrpc.d smbnode.d smbvfs.d stype.d +SRCS= smbd-all.d smbd-authsvc.d smbd-doorsvc.d smbd-pipesvc.d \ + smbnode.d smbsrv.d smbvfs.d include ../../Makefile.cmd diff --git a/usr/src/cmd/smbsrv/dtrace/cifs.d b/usr/src/cmd/smbsrv/dtrace/cifs.d deleted file mode 100644 index c9748b4b40..0000000000 --- a/usr/src/cmd/smbsrv/dtrace/cifs.d +++ /dev/null @@ -1,275 +0,0 @@ -#!/usr/sbin/dtrace -s -/* - * 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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. - */ - -/* -#pragma D option flowindent -*/ - -/* - * Usage: ./cifs.d -p `pgrep smbd` - * - * On multi-processor systems, it may be easier to follow the output - * if run on a single processor: see psradm. For example, to disable - * the second processor on a dual-processor system: psradm -f 1 - */ - -BEGIN -{ - printf("CIFS Trace Started"); - printf("\n\n"); -} - -END -{ - printf("CIFS Trace Ended"); - printf("\n\n"); -} - -sdt:smbsrv::-smb_op*-start -{ - sr = (struct smb_request *)arg0; - - printf("cmd=%d [uid=%d tid=%d]", - sr->smb_com, sr->smb_uid, sr->smb_tid); - - self->status = 0; -} - -sdt:smbsrv::-smb_op*-done -{ - sr = (struct smb_request *)arg0; - - printf("cmd[%d]: status=0x%08x (class=%d code=%d)", - sr->smb_com, sr->smb_error.status, - sr->smb_error.errcls, sr->smb_error.errcode); - - self->status = sr->smb_error.status; -} - -sdt:smbsrv::-smb_op-Negotiate-done -{ - sr = (struct smb_request *)arg0; - negprot = (smb_arg_negotiate_t *)arg1; - - printf("dialect=%s index=%u caps=0x%08x maxmpx=%u tz=%d time=%u", - stringof(negprot->ni_name), - negprot->ni_index, - negprot->ni_capabilities, - negprot->ni_maxmpxcount, - negprot->ni_tzcorrection, - negprot->ni_servertime.tv_sec); - - printf(" [status=0x%08x (class=%d code=%d)]", - sr->smb_error.status, - sr->smb_error.errcls, sr->smb_error.errcode); - - self->status = sr->smb_error.status; -} - -sdt:smbsrv::-smb_op-SessionSetupX-start -{ - sr = (struct smb_request *)arg0; - ssetup = (smb_arg_sessionsetup_t *)arg1; - - printf("[%s] %s %s %s", - (sr->session->s_local_port == 139) ? "NBT" : "TCP", - (sr->session->s_local_port == 139) ? - stringof(sr->session->workstation) : "", - stringof(ssetup->ssi_domain), - stringof(ssetup->ssi_user)); - - printf(" maxmpx=%u vc=%u maxbuf=%u", - ssetup->ssi_maxmpxcount, - sr->session->vcnumber, - sr->session->smb_msg_size); -} - -sdt:smbsrv::-smb_op-SessionSetupX-done -{ - sr = (struct smb_request *)arg0; - ssetup = (smb_arg_sessionsetup_t *)arg1; - - printf("%s/%s: smbuid=%d (%s)", - stringof(sr->uid_user->u_domain), - stringof(sr->uid_user->u_name), - sr->smb_uid, - (ssetup->ssi_guest == 0) ? "user" : "guest"); - - printf(" [status=0x%08x (class=%d code=%d)]", - sr->smb_error.status, - sr->smb_error.errcls, sr->smb_error.errcode); - - self->status = sr->smb_error.status; -} - -sdt:smbsrv::-smb_op-LogoffX-start -{ - sr = (struct smb_request *)arg0; - - printf("uid %d: %s/%s", sr->smb_uid, - stringof(sr->uid_user->u_domain), - stringof(sr->uid_user->u_name)); -} - -sdt:smbsrv::-smb_op-TreeConnectX-start -{ - tcon = (struct tcon *)arg1; - - printf("[%s] %s", - stringof(tcon->service), - stringof(tcon->path)); -} - -sdt:smbsrv::-smb_op-TreeConnectX-done -{ - sr = (struct smb_request *)arg0; - - printf("tid %d: %s", sr->smb_tid, - (sr->smb_error.status == 0) ? - stringof(sr->tid_tree->t_sharename) : ""); - - printf(" [status=0x%08x (class=%d code=%d)]", - sr->smb_error.status, - sr->smb_error.errcls, sr->smb_error.errcode); -} - -sdt:smbsrv::-smb_op-TreeDisconnect-start -{ - sr = (struct smb_request *)arg0; - - printf("tid %d: %s", sr->smb_tid, - stringof(sr->tid_tree->t_sharename)); - discard(self->status); -} - -sdt:smbsrv::-smb_op-Open-start, -sdt:smbsrv::-smb_op-OpenX-start, -sdt:smbsrv::-smb_op-Create-start, -sdt:smbsrv::-smb_op-CreateNew-start, -sdt:smbsrv::-smb_op-CreateTemporary-start, -sdt:smbsrv::-smb_op-CreateDirectory-start, -sdt:smbsrv::-smb_op-NtCreateX-start, -sdt:smbsrv::-smb_op-NtTransactCreate-start -{ - op = (struct open_param *)arg1; - - printf("%s", stringof(op->fqi.fq_path.pn_path)); -} - -sdt:smbsrv::-smb_op-Open-done, -sdt:smbsrv::-smb_op-OpenX-done, -sdt:smbsrv::-smb_op-Create-done, -sdt:smbsrv::-smb_op-CreateNew-done, -sdt:smbsrv::-smb_op-CreateTemporary-done, -sdt:smbsrv::-smb_op-CreateDirectory-done, -sdt:smbsrv::-smb_op-NtCreateX-done, -sdt:smbsrv::-smb_op-NtTransactCreate-done -{ - sr = (struct smb_request *)arg0; - - printf("%s: fid=%u", - stringof(sr->arg.open.fqi.fq_path.pn_path), sr->smb_fid); -} - -sdt:smbsrv::-smb_op-Read-start, -sdt:smbsrv::-smb_op-LockAndRead-start, -sdt:smbsrv::-smb_op-ReadX-start, -sdt:smbsrv::-smb_op-ReadRaw-start, -sdt:smbsrv::-smb_op-Write-start, -sdt:smbsrv::-smb_op-WriteAndClose-start, -sdt:smbsrv::-smb_op-WriteAndUnlock-start, -sdt:smbsrv::-smb_op-WriteX-start, -sdt:smbsrv::-smb_op-WriteRaw-start -{ - sr = (struct smb_request *)arg0; - rw = (smb_rw_param_t *)arg1; - - printf("fid=%d: %u bytes at offset %u", - sr->smb_fid, rw->rw_count, rw->rw_offset); -} - -sdt:smbsrv::-smb_op-Read-done, -sdt:smbsrv::-smb_op-LockAndRead-done, -sdt:smbsrv::-smb_op-ReadX-done, -sdt:smbsrv::-smb_op-ReadRaw-done -/self->status == 0/ -{ - sr = (struct smb_request *)arg0; - rw = (smb_rw_param_t *)arg1; - - printf("fid=%d: %u bytes at offset %u", - sr->smb_fid, rw->rw_count, rw->rw_offset); -} - -sdt:smbsrv::-smb_op-Rename-start -{ - p = (struct dirop *)arg1; - - printf("%s to %s", - stringof(p->fqi.fq_path.pn_path), - stringof(p->dst_fqi.fq_path.pn_path)); -} - -sdt:smbsrv::-smb_op-CheckDirectory-start, -sdt:smbsrv::-smb_op-CreateDirectory-start, -sdt:smbsrv::-smb_op-DeleteDirectory-start, -sdt:smbsrv::-smb_op-Delete-start -{ - p = (struct dirop *)arg1; - - printf("%s", stringof(p->fqi.fq_path.pn_path)); -} - -/* -smb_dispatch_request:entry, -smb_dispatch_request:return, -smb_pre_*:return, -smb_com_*:return, -smb_post_*:return, -smbsr_status:return, -smbsr_errno:return -{ -} - -smb_pre_*:entry, -smb_com_*:entry, -smb_post_*:entry -{ - sr = (struct smb_request *)arg0; - - printf("cmd=%d [uid=%d tid=%d]", - sr->smb_com, sr->smb_uid, sr->smb_tid); -} - -smbsr_status:entry -{ - printf("status=0x%08x class=%d, code=%d\n", arg1, arg2, arg3); -} - -smbsr_errno:entry -{ - printf("errno=%d\n", arg1); -} -*/ diff --git a/usr/src/cmd/smbsrv/dtrace/msrpc.d b/usr/src/cmd/smbsrv/dtrace/msrpc.d deleted file mode 100644 index bf744df7cc..0000000000 --- a/usr/src/cmd/smbsrv/dtrace/msrpc.d +++ /dev/null @@ -1,450 +0,0 @@ -#!/usr/sbin/dtrace -s -/* - * 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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - */ - -/* - * Usage: ./msrpc.d -p `pgrep smbd` - * - * On multi-processor systems, it may be easier to follow the output - * if run on a single processor: see psradm. For example, to disable - * the second processor on a dual-processor system: psradm -f 1 - * - * This script can be used to trace NDR operations and MSRPC requests. - * In order to put these operations in context, SMB session and tree - * requests are also traced. - * - * Output formatting is as follows: - * - * UI 03 ... rpc_vers get 1@0 = 5 {05} - * UI 03 ... rpc_vers_minor get 1@1 = 0 {00} - * - * U Marshalling flag (M=marshal, U=unmarshal) - * I Direction flag (I=in, O=out) - * ... Field name - * get PDU operation (get or put) - * 1@0 Bytes @ offset (i.e. 1 byte at offset 0) - * {05} Value - * - * The value formatting is limited to 10 bytes, after which an ellipsis - * will be inserted before the closing brace. If the value is 1 or 2 - * bytes, an attempt will be made to present an ASCII value but this may - * or may not be relevent. - * - * The following example shows the header from a bind response: - * - * trace:entry MO 03 ... rpc_vers put 1@0 = 5 {05} - * trace:entry MO 03 ... rpc_vers_minor put 1@1 = 0 {00} - * trace:entry MO 03 ... ptype put 1@2 = 12 {0c} - * trace:entry MO 03 ... pfc_flags put 1@3 = 3 {03} - * trace:entry MO 04 .... intg_char_rep put 1@4 = 16 {10} - * trace:entry MO 04 .... float_rep put 1@5 = 0 {00} - * trace:entry MO 04 .... _spare[0] put 1@6 = 0 {00} - * trace:entry MO 04 .... _spare[1] put 1@7 = 0 {00} - * trace:entry MO 03 ... frag_length put 2@8 = 68 {44 00} D - * trace:entry MO 03 ... auth_length put 2@10 = 0 {00 00} - * trace:entry MO 03 ... call_id put 4@12 = 1 {01 00 00 00} - * trace:entry MO 02 .. max_xmit_frag put 2@16 = 4280 {b8 10} - * trace:entry MO 02 .. max_recv_frag put 2@18 = 4280 {b8 10} - * trace:entry MO 02 .. assoc_group_id put 4@20 = 1192620711 {a7 f2 15 47} - * trace:entry MO 02 .. sec_addr.length put 2@24 = 12 {0c 00} - * trace:entry MO 02 .. sec_addr.port_spec[0] put 1@26 = 92 {5c} \ - * trace:entry MO 02 .. sec_addr.port_spec[1] put 1@27 = 80 {50} P - * trace:entry MO 02 .. sec_addr.port_spec[2] put 1@28 = 73 {49} I - * trace:entry MO 02 .. sec_addr.port_spec[3] put 1@29 = 80 {50} P - * trace:entry MO 02 .. sec_addr.port_spec[4] put 1@30 = 69 {45} E - * trace:entry MO 02 .. sec_addr.port_spec[5] put 1@31 = 92 {5c} \ - * trace:entry MO 02 .. sec_addr.port_spec[6] put 1@32 = 108 {6c} l - * trace:entry MO 02 .. sec_addr.port_spec[7] put 1@33 = 115 {73} s - * trace:entry MO 02 .. sec_addr.port_spec[8] put 1@34 = 97 {61} a - * trace:entry MO 02 .. sec_addr.port_spec[9] put 1@35 = 115 {73} s - * trace:entry MO 02 .. sec_addr.port_spec[10] put 1@36 = 115 {73} s - * trace:entry MO 02 .. sec_addr.port_spec[11] put 1@37 = 0 {00} - */ - -BEGIN -{ - printf("MSRPC Trace Started"); - printf("\n\n"); -} - -END -{ - printf("MSRPC Trace Ended"); - printf("\n\n"); -} - -/* - * SmbSessionSetupX, SmbLogoffX - * SmbTreeConnect, SmbTreeDisconnect - */ -smb_tree*:entry, -smb_com_*:entry, -smb_com_*:return, -smb_com_session_setup_andx:entry, -smb_com_logoff_andx:entry, -smb_tree_connect:return, -smb_tree_disconnect:entry, -smb_tree_disconnect:return, -smb_opipe_open:entry, -smb_opipe_door_call:entry, -smb_opipe_door_upcall:entry, -door_ki_upcall:entry -{ -} - -smb_com_session_setup_andx:return, -smb_user*:return, -smb_tree*:return, -smb_opipe_open:return, -smb_opipe_door_call:return, -smb_opipe_door_upcall:return, -door_ki_upcall:return -{ - printf("rc=0x%08x", arg1); -} - -sdt:smbsrv::smb-sessionsetup-clntinfo -{ - user_info = (smb_logon_t *)arg0; - - printf("domain\\username=%s\\%s\n\n", - stringof(user_info->lg_domain), - stringof(user_info->lg_username)); -} - -smb_tree_connect:entry -{ - sr = (smb_request_t *)arg0; - - printf("share=%s service=%s", - stringof(sr->arg.tcon.path), - stringof(sr->arg.tcon.service)); -} - -smb_com_logoff_andx:return -{ -} - -/* - * Raise error functions (no return). - */ -smbsr_status:entry -{ - printf("status=0x%08x class=%d, code=%d", arg1, arg2, arg3); -} - -smbsr_errno:entry -{ - printf("errno=%d", arg1); -} - -smbsr_status:return, -smbsr_errno:return -{ -} - -/* - * MSRPC activity. - */ -pid$target::ndr_svc_bind:entry, -pid$target::ndr_svc_bind:return, -pid$target::ndr_svc_request:entry, -pid$target::ndr_svc_request:return -{ -} - -pid$target::smb_trace:entry, -pid$target::ndo_trace:entry -{ - printf("%s", copyinstr(arg0)); -} - -/* - * LSARPC - */ -pid$target::lsarpc_s_CloseHandle:entry, -pid$target::lsarpc_s_QuerySecurityObject:entry, -pid$target::lsarpc_s_EnumAccounts:entry, -pid$target::lsarpc_s_EnumTrustedDomain:entry, -pid$target::lsarpc_s_OpenAccount:entry, -pid$target::lsarpc_s_EnumPrivsAccount:entry, -pid$target::lsarpc_s_LookupPrivValue:entry, -pid$target::lsarpc_s_LookupPrivName:entry, -pid$target::lsarpc_s_LookupPrivDisplayName:entry, -pid$target::lsarpc_s_QueryInfoPolicy:entry, -pid$target::lsarpc_s_OpenDomainHandle:entry, -pid$target::lsarpc_s_OpenDomainHandle:entry, -pid$target::lsarpc_s_LookupSids:entry, -pid$target::lsarpc_s_LookupNames:entry, -pid$target::lsarpc_s_GetConnectedUser:entry, -pid$target::lsarpc_s_LookupSids2:entry, -pid$target::lsarpc_s_LookupNames2:entry -{ -} - -pid$target::lsarpc_s_CloseHandle:return, -pid$target::lsarpc_s_QuerySecurityObject:return, -pid$target::lsarpc_s_EnumAccounts:return, -pid$target::lsarpc_s_EnumTrustedDomain:return, -pid$target::lsarpc_s_OpenAccount:return, -pid$target::lsarpc_s_EnumPrivsAccount:return, -pid$target::lsarpc_s_LookupPrivValue:return, -pid$target::lsarpc_s_LookupPrivName:return, -pid$target::lsarpc_s_LookupPrivDisplayName:return, -pid$target::lsarpc_s_QueryInfoPolicy:return, -pid$target::lsarpc_s_OpenDomainHandle:return, -pid$target::lsarpc_s_OpenDomainHandle:return, -pid$target::lsarpc_s_LookupSids:return, -pid$target::lsarpc_s_LookupNames:return, -pid$target::lsarpc_s_GetConnectedUser:return, -pid$target::lsarpc_s_LookupSids2:return, -pid$target::lsarpc_s_LookupNames2:return -{ -} - -pid$target::lsar_lookup_names:entry -{ - printf("%s", copyinstr(arg1)); -} - -pid$target::lsar_lookup_*:entry -{ -} - -pid$target::lsar_lookup_*:return -{ - printf("0x%08x", arg1); -} - -pid$target::lsar_*:entry -{ -} - -pid$target::lsar_*:return -{ - printf("0x%08x", arg1); -} - -/* - * NetLogon - */ -pid$target::netr_*:entry -{ -} - -pid$target::netr_*:return -{ - printf("0x%08x", arg1); -} - -/* - * SAMR - */ -pid$target::samr_s_Connect:entry, -pid$target::samr_s_CloseHandle:entry, -pid$target::samr_s_LookupDomain:entry, -pid$target::samr_s_EnumLocalDomains:entry, -pid$target::samr_s_OpenDomain:entry, -pid$target::samr_s_QueryDomainInfo:entry, -pid$target::samr_s_QueryInfoDomain2:entry, -pid$target::samr_s_LookupNames:entry, -pid$target::samr_s_OpenUser:entry, -pid$target::samr_s_DeleteUser:entry, -pid$target::samr_s_QueryUserInfo:entry, -pid$target::samr_s_QueryUserGroups:entry, -pid$target::samr_s_OpenGroup:entry, -pid$target::samr_s_Connect2:entry, -pid$target::samr_s_GetUserPwInfo:entry, -pid$target::samr_s_CreateUser:entry, -pid$target::samr_s_ChangeUserPasswd:entry, -pid$target::samr_s_GetDomainPwInfo:entry, -pid$target::samr_s_SetUserInfo:entry, -pid$target::samr_s_Connect4:entry, -pid$target::samr_s_Connect5:entry, -pid$target::samr_s_QueryDispInfo:entry, -pid$target::samr_s_OpenAlias:entry, -pid$target::samr_s_CreateDomainAlias:entry, -pid$target::samr_s_SetAliasInfo:entry, -pid$target::samr_s_QueryAliasInfo:entry, -pid$target::samr_s_DeleteDomainAlias:entry, -pid$target::samr_s_EnumDomainAliases:entry, -pid$target::samr_s_EnumDomainGroups:entry -{ -} - -pid$target::samr_s_Connect:return, -pid$target::samr_s_CloseHandle:return, -pid$target::samr_s_LookupDomain:return, -pid$target::samr_s_EnumLocalDomains:return, -pid$target::samr_s_OpenDomain:return, -pid$target::samr_s_QueryDomainInfo:return, -pid$target::samr_s_QueryInfoDomain2:return, -pid$target::samr_s_LookupNames:return, -pid$target::samr_s_OpenUser:return, -pid$target::samr_s_DeleteUser:return, -pid$target::samr_s_QueryUserInfo:return, -pid$target::samr_s_QueryUserGroups:return, -pid$target::samr_s_OpenGroup:return, -pid$target::samr_s_Connect2:return, -pid$target::samr_s_GetUserPwInfo:return, -pid$target::samr_s_CreateUser:return, -pid$target::samr_s_ChangeUserPasswd:return, -pid$target::samr_s_GetDomainPwInfo:return, -pid$target::samr_s_SetUserInfo:return, -pid$target::samr_s_Connect4:return, -pid$target::samr_s_Connect5:return, -pid$target::samr_s_QueryDispInfo:return, -pid$target::samr_s_OpenAlias:return, -pid$target::samr_s_CreateDomainAlias:return, -pid$target::samr_s_SetAliasInfo:return, -pid$target::samr_s_QueryAliasInfo:return, -pid$target::samr_s_DeleteDomainAlias:return, -pid$target::samr_s_EnumDomainAliases:return, -pid$target::samr_s_EnumDomainGroups:return -{ -} - -/* - * SPOOLSS - */ -pid$target::spoolss_*:entry, -pid$target::spoolss_*:return -{ -} - -/* - * SVCCTL - */ -pid$target::svcctl_s_*:entry, -pid$target::svcctl_s_*:return -{ -} - -/* - * SRVSVC - */ -pid$target::srvsvc_s_NetConnectEnum:entry, -pid$target::srvsvc_s_NetFileEnum:entry, -pid$target::srvsvc_s_NetFileClose:entry, -pid$target::srvsvc_s_NetShareGetInfo:entry, -pid$target::srvsvc_s_NetShareSetInfo:entry, -pid$target::srvsvc_s_NetSessionEnum:entry, -pid$target::srvsvc_s_NetSessionDel:entry, -pid$target::srvsvc_s_NetServerGetInfo:entry, -pid$target::srvsvc_s_NetRemoteTOD:entry, -pid$target::srvsvc_s_NetNameValidate:entry, -pid$target::srvsvc_s_NetShareAdd:entry, -pid$target::srvsvc_s_NetShareDel:entry, -pid$target::srvsvc_s_NetShareEnum:entry, -pid$target::srvsvc_s_NetShareEnumSticky:entry, -pid$target::srvsvc_s_NetGetFileSecurity:entry, -pid$target::srvsvc_s_NetSetFileSecurity:entry -{ -} - -pid$target::srvsvc_s_NetConnectEnum:return, -pid$target::srvsvc_s_NetFileEnum:return, -pid$target::srvsvc_s_NetFileClose:return, -pid$target::srvsvc_s_NetShareGetInfo:return, -pid$target::srvsvc_s_NetShareSetInfo:return, -pid$target::srvsvc_s_NetSessionEnum:return, -pid$target::srvsvc_s_NetSessionDel:return, -pid$target::srvsvc_s_NetServerGetInfo:return, -pid$target::srvsvc_s_NetRemoteTOD:return, -pid$target::srvsvc_s_NetNameValidate:return, -pid$target::srvsvc_s_NetShareAdd:return, -pid$target::srvsvc_s_NetShareDel:return, -pid$target::srvsvc_s_NetShareEnum:return, -pid$target::srvsvc_s_NetShareEnumSticky:return, -pid$target::srvsvc_s_NetGetFileSecurity:return, -pid$target::srvsvc_s_NetSetFileSecurity:return -{ -} - -/* - * WinReg - */ -pid$target::winreg_s_*:entry, -pid$target::winreg_s_*:return -{ -} - -/* - * Workstation - */ -pid$target::wkssvc_s_*:entry, -pid$target::wkssvc_s_*:return -{ -} - -/* - * SMBRDR - */ -pid$target::smbrdr_tree_connect:entry -{ - printf("%s %s %s", - copyinstr(arg0), - copyinstr(arg1), - copyinstr(arg2)); -} - -pid$target::smbrdr_open_pipe:entry -{ - printf("%s %s %s %s", - copyinstr(arg0), - copyinstr(arg1), - copyinstr(arg2), - copyinstr(arg3)); -} - -pid$target::smbrdr_tree_disconnect:entry, -pid$target::smbrdr_close_pipe:entry, -pid$target::smbrdr_ntcreatex:entry, -pid$target::smbrdr_transact:entry, -pid$target::smbrdr_readx*:entry -{ -} - -pid$target::smbrdr_tree_connect:return, -pid$target::smbrdr_tree_disconnect:return, -pid$target::smbrdr_open_pipe:return, -pid$target::smbrdr_close_pipe:return, -pid$target::smbrdr_ntcreatex:return, -pid$target::smbrdr_transact:return, -pid$target::smbrdr_readx*:return -{ - printf("%d", arg1); -} - -pid$target::ndr_clnt_get_frags:entry, -pid$target::ndr_clnt_get_frag:entry -{ -} - -pid$target::ndr_clnt_get_frags:return, -pid$target::ndr_clnt_get_frag:return -{ - printf("%d", arg1); -} diff --git a/usr/src/cmd/smbsrv/dtrace/smbd-all.d b/usr/src/cmd/smbsrv/dtrace/smbd-all.d new file mode 100644 index 0000000000..caf2426c6b --- /dev/null +++ b/usr/src/cmd/smbsrv/dtrace/smbd-all.d @@ -0,0 +1,136 @@ +#!/usr/sbin/dtrace -s +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + */ + +/* + * User-level dtrace for smbd. Watch everything it does. + * Usage: dtrace -s smbd-all.d -p `pgrep smbd` + */ + +#pragma D option flowindent + +self int trace; +self int mask; + +/* + * Trace everything in smbd + */ +pid$target:*smbd::entry, +pid$target:libmlsvc.so.1::entry, +pid$target:libmlrpc.so.1::entry, +pid$target:libsmbns.so.1::entry, +pid$target:libsmb.so.1::entry, +pid$target:libads.so.1::entry +{ + self->trace++; +} + +/* + * If traced and not masked, print entry/return + */ +pid$target:*smbd::entry, +pid$target:libmlsvc.so.1::entry, +pid$target:libmlrpc.so.1::entry, +pid$target:libsmbns.so.1::entry, +pid$target:libsmb.so.1::entry, +pid$target:libads.so.1::entry +/self->trace > 0 && self->mask == 0/ +{ + printf("\t0x%x", arg0); + printf("\t0x%x", arg1); + printf("\t0x%x", arg2); + printf("\t0x%x", arg3); + printf("\t0x%x", arg4); + printf("\t0x%x", arg5); +} + +/* + * Mask (don't print) all function calls below these functions. + * These make many boring, repetitive function calls like + * smb_mbtowc, smb_msgbuf_has_space, ... + * + * Also, libmlrpc has rather deep call stacks, particularly under + * ndr_encode_decode_common(), so this stops traces below there. + * Remove that from the mask actions to see the details. + */ +pid$target::ht_findfirst:entry, +pid$target::ht_findnext:entry, +pid$target::ndr_encode_decode_common:entry, +pid$target::smb_msgbuf_decode:entry, +pid$target::smb_msgbuf_encode:entry, +pid$target::smb_strlwr:entry, +pid$target::smb_strupr:entry, +pid$target::smb_wcequiv_strlen:entry +{ + self->mask++; +} + +/* + * Get some of the smbd debug messages, etc. + */ +pid$target:libsmb.so.1:smb_trace:entry +/self->trace > 0 && self->mask == 0/ +{ + printf("%s", copyinstr(arg0)); +} + +pid$target:libsmb.so.1:smb_syslog:entry +/self->trace > 0 && self->mask == 0/ +{ + printf("%s", copyinstr(arg1)); +} + +pid$target:libc_hwcap1.so.1:syslog:entry +/self->trace > 0 && self->mask == 0/ +{ + printf("%s", copyinstr(arg1)); +} + +/* + * Now inverses of above, unwind order. + */ + +pid$target::ht_findfirst:return, +pid$target::ht_findnext:return, +pid$target::ndr_encode_decode_common:return, +pid$target::smb_msgbuf_decode:return, +pid$target::smb_msgbuf_encode:return, +pid$target::smb_strlwr:return, +pid$target::smb_strupr:return, +pid$target::smb_wcequiv_strlen:return +{ + self->mask--; +} + +pid$target:*smbd::return, +pid$target:libmlsvc.so.1::return, +pid$target:libmlrpc.so.1::return, +pid$target:libsmbns.so.1::return, +pid$target:libsmb.so.1::return, +pid$target:libads.so.1::return +/self->trace > 0 && self->mask == 0/ +{ + printf("\t0x%x", arg1); +} + +pid$target:*smbd::return, +pid$target:libmlsvc.so.1::return, +pid$target:libmlrpc.so.1::return, +pid$target:libsmbns.so.1::return, +pid$target:libsmb.so.1::return, +pid$target:libads.so.1::return +{ + self->trace--; +} diff --git a/usr/src/cmd/smbsrv/dtrace/smbd-authsvc.d b/usr/src/cmd/smbsrv/dtrace/smbd-authsvc.d new file mode 100644 index 0000000000..0c9cb6a0ed --- /dev/null +++ b/usr/src/cmd/smbsrv/dtrace/smbd-authsvc.d @@ -0,0 +1,104 @@ +#!/usr/sbin/dtrace -s +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + */ + +/* + * User-level dtrace for the smbd authentication service + * Usage: dtrace -s smbd-authsvc.d -p `pgrep smbd` + */ + +#pragma D option flowindent + +self int trace; +self int mask; + +/* + * The smbd_authsvc_work() function is a good place to start tracing + * to watch authentication. This function executes all the actions + * associated with a single session setup conversation (even though + * that conversation will usually involve multiple SMB requests). + */ +pid$target:*smbd:smbd_authsvc_work:entry +{ + self->trace++; +} + +/* + * If traced and not masked, print entry/return + */ +pid$target:*smbd::entry, +pid$target:libmlsvc.so.1::entry, +pid$target:libmlrpc.so.1::entry, +pid$target:libsmbns.so.1::entry, +pid$target:libsmb.so.1::entry, +pid$target:libsmbfs.so.1::entry +/self->trace > 0 && self->mask == 0/ +{ + printf("\t0x%x", arg0); + printf("\t0x%x", arg1); + printf("\t0x%x", arg2); + printf("\t0x%x", arg3); + printf("\t0x%x", arg4); + printf("\t0x%x", arg5); +} + +/* + * Mask (don't print) all function calls below these functions. + * These make many boring, repetitive function calls like + * smb_mbtowc, smb_msgbuf_has_space, ... + * + * Also, libmlrpc has rather deep call stacks, particularly under + * ndr_encode_decode_common(), so this stops traces below there. + * Remove that from the mask actions to see the details. + */ +pid$target::ndr_encode_decode_common:entry, +pid$target::smb_msgbuf_decode:entry, +pid$target::smb_msgbuf_encode:entry, +pid$target::smb_strlwr:entry, +pid$target::smb_strupr:entry, +pid$target::smb_wcequiv_strlen:entry +{ + self->mask++; +} + +/* + * Now inverses of above, unwind order. + */ + +pid$target::ndr_encode_decode_common:return, +pid$target::smb_msgbuf_decode:return, +pid$target::smb_msgbuf_encode:return, +pid$target::smb_strlwr:return, +pid$target::smb_strupr:return, +pid$target::smb_wcequiv_strlen:return +{ + self->mask--; +} + +pid$target:*smbd::return, +pid$target:libmlsvc.so.1::return, +pid$target:libmlrpc.so.1::return, +pid$target:libsmbns.so.1::return, +pid$target:libsmb.so.1::return, +pid$target:libsmbfs.so.1::return +/self->trace > 0 && self->mask == 0/ +{ + printf("\t0x%x", arg1); +} + +pid$target:*smbd:smbd_authsvc_work:return +{ + self->trace--; +} diff --git a/usr/src/cmd/smbsrv/dtrace/smbd-doorsvc.d b/usr/src/cmd/smbsrv/dtrace/smbd-doorsvc.d new file mode 100644 index 0000000000..51ffa68ad7 --- /dev/null +++ b/usr/src/cmd/smbsrv/dtrace/smbd-doorsvc.d @@ -0,0 +1,95 @@ +#!/usr/sbin/dtrace -s +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + */ + +/* + * User-level dtrace for smbd + * Usage: dtrace -s smbd-doorsvc.d -p `pgrep smbd` + */ + +#pragma D option flowindent + +self int trace; +self int mask; + +/* + * smbd_door_dispatch_op() is the logical top of smbd door service calls. + */ +pid$target:*smbd:smbd_door_dispatch_op:entry +{ + self->trace++; +} + +/* + * If traced and not masked, print entry/return + */ +pid$target:*smbd::entry, +pid$target:libmlsvc.so.1::entry, +pid$target:libmlrpc.so.1::entry, +pid$target:libsmbns.so.1::entry, +pid$target:libsmb.so.1::entry, +pid$target:libsmbfs.so.1::entry +/self->trace > 0 && self->mask == 0/ +{ + printf("\t0x%x", arg0); + printf("\t0x%x", arg1); + printf("\t0x%x", arg2); + printf("\t0x%x", arg3); + printf("\t0x%x", arg4); + printf("\t0x%x", arg5); +} + +/* + * Mask (don't print) all function calls below these functions. + * These make many boring, repetitive function calls like + * smb_mbtowc, smb_msgbuf_has_space, ... + */ +pid$target::smb_msgbuf_decode:entry, +pid$target::smb_msgbuf_encode:entry, +pid$target::smb_strlwr:entry, +pid$target::smb_strupr:entry, +pid$target::smb_wcequiv_strlen:entry +{ + self->mask++; +} + +/* + * Now inverses of above, unwind order. + */ + +pid$target::smb_msgbuf_decode:return, +pid$target::smb_msgbuf_encode:return, +pid$target::smb_strlwr:return, +pid$target::smb_strupr:return, +pid$target::smb_wcequiv_strlen:return +{ + self->mask--; +} + +pid$target:*smbd::return, +pid$target:libmlsvc.so.1::return, +pid$target:libmlrpc.so.1::return, +pid$target:libsmbns.so.1::return, +pid$target:libsmb.so.1::return, +pid$target:libsmbfs.so.1::return +/self->trace > 0 && self->mask == 0/ +{ + printf("\t0x%x", arg1); +} + +pid$target:*smbd:smbd_door_dispatch_op:return +{ + self->trace--; +} diff --git a/usr/src/cmd/smbsrv/dtrace/smbd-pipesvc.d b/usr/src/cmd/smbsrv/dtrace/smbd-pipesvc.d new file mode 100644 index 0000000000..1fe6ca8223 --- /dev/null +++ b/usr/src/cmd/smbsrv/dtrace/smbd-pipesvc.d @@ -0,0 +1,116 @@ +#!/usr/sbin/dtrace -s +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + */ + +/* + * User-level dtrace for smbd + * Usage: dtrace -s smbd-pipesvc.d -p `pgrep smbd` + */ + +#pragma D option flowindent + +self int trace; +self int mask; + +/* + * The smbd_authsvc_work() function is a good place to start tracing + * to watch RPC service actions. This worker handles all activity + * for a given named pipe instance, including the payload from all + * SMB read/write requests on this endpoint. + */ +pid$target:*smbd:pipesvc_worker:entry +{ + self->trace++; +} + +/* + * If traced and not masked, print entry/return + */ +pid$target:*smbd::entry, +pid$target:libmlsvc.so.1::entry, +pid$target:libmlrpc.so.1::entry, +pid$target:libsmbns.so.1::entry, +pid$target:libsmb.so.1::entry +/self->trace > 0 && self->mask == 0/ +{ + printf("\t0x%x", arg0); + printf("\t0x%x", arg1); + printf("\t0x%x", arg2); + printf("\t0x%x", arg3); + printf("\t0x%x", arg4); + printf("\t0x%x", arg5); +} + +/* + * Mask (don't print) all function calls below these functions. + * These make many boring, repetitive function calls like + * smb_mbtowc, smb_msgbuf_has_space, ... + * + * Also, libmlrpc has rather deep call stacks, particularly under + * ndr_encode_decode_common(), so this stops traces below there. + * Remove that from the mask actions to see the details. + */ +pid$target::ht_findfirst:entry, +pid$target::ht_findnext:entry, +pid$target::ndr_encode_decode_common:entry, +pid$target::smb_msgbuf_decode:entry, +pid$target::smb_msgbuf_encode:entry, +pid$target::smb_strlwr:entry, +pid$target::smb_strupr:entry, +pid$target::smb_wcequiv_strlen:entry +{ + self->mask++; +} + +/* + * Now inverses of above, unwind order. + */ + +pid$target::ht_findfirst:return, +pid$target::ht_findnext:return, +pid$target::ndr_encode_decode_common:return, +pid$target::smb_msgbuf_decode:return, +pid$target::smb_msgbuf_encode:return, +pid$target::smb_strlwr:return, +pid$target::smb_strupr:return, +pid$target::smb_wcequiv_strlen:return +{ + self->mask--; +} + +pid$target:*smbd::return, +pid$target:libmlsvc.so.1::return, +pid$target:libmlrpc.so.1::return, +pid$target:libsmbns.so.1::return, +pid$target:libsmb.so.1::return +/self->trace > 0 && self->mask == 0/ +{ + printf("\t0x%x", arg1); +} + +/* + * This function in libmlrpc prints out lots of internal state. + * Comment it out if you don't want that noise. + */ +pid$target:libmlrpc.so.1:ndo_trace:entry +/self->trace > 0 && self->mask == 0/ +{ + printf("ndo_trace: %s", copyinstr(arg0)); +} + +pid$target:*smbd:pipesvc_worker:return +{ + self->trace--; +} diff --git a/usr/src/cmd/smbsrv/dtrace/smbsrv.d b/usr/src/cmd/smbsrv/dtrace/smbsrv.d new file mode 100644 index 0000000000..162839bd71 --- /dev/null +++ b/usr/src/cmd/smbsrv/dtrace/smbsrv.d @@ -0,0 +1,89 @@ +#!/usr/sbin/dtrace -s +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + */ + +/* + * Developer dtrace program for smbsrv + * Usage: dtrace -s smbsrv.d + */ + +#pragma D option flowindent + +self int trace; +self int mask; + +/* + * Trace almost everything + */ +fbt:smbsrv::entry +{ + self->trace++; +} + +/* + * If traced and not masked, print entry/return + */ +fbt:smbsrv::entry +/self->trace > 0 && self->mask == 0/ +{ + printf("\t0x%x", arg0); + printf("\t0x%x", arg1); + printf("\t0x%x", arg2); + printf("\t0x%x", arg3); + printf("\t0x%x", arg4); + printf("\t0x%x", arg5); +} + +/* + * Mask (don't print) all function calls below these functions. + * These make many boring, repetitive function calls like + * smb_mbtowc, mbc_marshal_... + */ +fbt::smb_mbc_vdecodef:entry, +fbt::smb_mbc_vencodef:entry, +fbt::smb_msgbuf_decode:entry, +fbt::smb_msgbuf_encode:entry, +fbt::smb_strlwr:entry, +fbt::smb_strupr:entry, +fbt::smb_wcequiv_strlen:entry +{ + self->mask++; +} + +/* + * Now inverses of above, unwind order. + */ + +fbt::smb_mbc_vdecodef:return, +fbt::smb_mbc_vencodef:return, +fbt::smb_msgbuf_decode:return, +fbt::smb_msgbuf_encode:return, +fbt::smb_strlwr:return, +fbt::smb_strupr:return, +fbt::smb_wcequiv_strlen:return +{ + self->mask--; +} + +fbt:smbsrv::return +/self->trace > 0 && self->mask == 0/ +{ + printf("\t0x%x", arg1); +} + +fbt:smbsrv::return +{ + self->trace--; +} diff --git a/usr/src/cmd/smbsrv/dtrace/stype.d b/usr/src/cmd/smbsrv/dtrace/stype.d deleted file mode 100644 index ce157e5877..0000000000 --- a/usr/src/cmd/smbsrv/dtrace/stype.d +++ /dev/null @@ -1,202 +0,0 @@ -#!/usr/sbin/dtrace -s - -/* - * 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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. - */ - -#pragma D option flowindent - -/* - * Usage: ./stype.d -p `pgrep smbd` - * - * On multi-processor systems, it may be easier to follow the output - * if run on a single processor: see psradm. For example, to disable - * the second processor on a dual-processor system: psradm -f 1 - */ - -BEGIN -{ - printf("CIFS Trace Started"); - printf("\n\n"); -} - -END -{ - printf("CIFS Trace Ended"); - printf("\n\n"); -} - -sdt:smbsrv::-smb_op-SessionSetupX-start -{ - sr = (struct smb_request *)arg0; - ssetup = (smb_arg_sessionsetup_t *)arg1; - - printf("[%s] %s %s %s", - (sr->session->s_local_port == 139) ? "NBT" : "TCP", - (sr->session->s_local_port == 139) ? - stringof(sr->session->workstation) : "", - stringof(ssetup->ssi_domain), - stringof(ssetup->ssi_user)); - - printf(" maxmpx=%u vc=%u maxbuf=%u", - ssetup->ssi_maxmpxcount, - sr->session->vcnumber, - sr->session->smb_msg_size); -} - -sdt:smbsrv::-smb_op-SessionSetupX-done -{ - sr = (struct smb_request *)arg0; - ssetup = (smb_arg_sessionsetup_t *)arg1; - - printf("%s/%s: smbuid=%d (%s)", - stringof(sr->uid_user->u_domain), - stringof(sr->uid_user->u_name), - sr->smb_uid, - (ssetup->ssi_guest == 0) ? "user" : "guest"); - - printf(" [status=0x%08x (class=%d code=%d)]", - sr->smb_error.status, - sr->smb_error.errcls, sr->smb_error.errcode); -} - -sdt:smbsrv::-smb_op-LogoffX-start -{ - sr = (struct smb_request *)arg0; - - printf("uid %d: %s/%s", sr->smb_uid, - stringof(sr->uid_user->u_domain), - stringof(sr->uid_user->u_name)); -} - -sdt:smbsrv::-smb_op-TreeConnectX-start -{ - tcon = (struct tcon *)arg1; - - printf("[%s] %s", - stringof(tcon->service), - stringof(tcon->path)); -} - -sdt:smbsrv::-smb_op-TreeConnectX-done -{ - sr = (struct smb_request *)arg0; - - printf("tid %d: %s", sr->smb_tid, - (sr->smb_error.status == 0) ? - stringof(sr->tid_tree->t_sharename) : ""); - - printf(" [status=0x%08x (class=%d code=%d)]", - sr->smb_error.status, - sr->smb_error.errcls, sr->smb_error.errcode); -} - -sdt:smbsrv::-smb_op-TreeDisconnect-start -{ - sr = (struct smb_request *)arg0; - - printf("tid %d: %s", sr->smb_tid, - (sr->tid_tree == 0) ? "" : - stringof(sr->tid_tree->t_sharename)); -} - -/* - * Error functions - */ -smbsr_status:entry -{ - printf("status=0x%08x class=%d, code=%d", arg1, arg2, arg3); -} - -smbsr_errno:entry -{ - printf("errno=%d", arg1); -} - -smbsr_status:return, -smbsr_errno:return -{ -} - -/* - * Share/tree connect. - */ -smb_tree_connect:entry -{ -} - -smb_tree_get_sharename:entry -{ - printf("uncpath=%s", stringof(arg0)); -} - -smb_tree_connect_disk:entry -{ - printf("sharename=%s", stringof(arg1)); - self->stype = 0; -} - -smb_tree_connect_printq:entry -{ - printf("sharename=%s", stringof(arg1)); - self->stype = 1; -} - -smb_tree_connect_ipc:entry -{ - printf("sharename=%s", stringof(arg1)); - self->stype = 3; -} - -smb_tree_connect:return, -smb_tree_get_sharename:return, -smb_tree_connect_disk:return, -smb_tree_connect_ipc:return -{ - printf("rc=0x%08x", arg1); -} - -smb_tree_alloc:entry -/self->stype == 0/ -{ - printf("share=%s service=%s", stringof(arg1), stringof(arg2)); -} - -smb_tree_alloc:return -/self->stype == 0/ -{ - printf("FS=%s flags=0x%08x", - stringof(((smb_tree_t *)arg1)->t_typename), - ((smb_tree_t *)arg1)->t_flags); -} - -smb_tree_disconnect:entry, -smb_tree_disconnect:return -{ -} - -smb_tree_log:entry -{ - printf("%s: %s", stringof(arg1), stringof(arg2)); -} diff --git a/usr/src/cmd/smbsrv/fksmbd/Makefile b/usr/src/cmd/smbsrv/fksmbd/Makefile index 615818e44a..ca3a8b5fb9 100644 --- a/usr/src/cmd/smbsrv/fksmbd/Makefile +++ b/usr/src/cmd/smbsrv/fksmbd/Makefile @@ -26,11 +26,15 @@ PROG= fksmbd OBJS_SMBD= \ + smbd_authsvc.o \ smbd_doorsvc.o \ smbd_join.o \ + smbd_krb5lookup.o \ + smbd_krb5ssp.o \ smbd_logon.o \ smbd_main.o \ smbd_nicmon.o \ + smbd_ntlmssp.o \ smbd_pipesvc.o \ smbd_share_doorsvc.o \ smbd_spool.o \ @@ -59,6 +63,9 @@ INCS += -I../../../uts/common INCS += -I../../../uts/common/smbsrv INCS += -I../../../common/smbsrv +# Should not have to do this, but the Kerberos includes are a mess. +INCS += -I $(ROOT)/usr/include/kerberosv5 + C99MODE= -xc99=%all C99LMODE= -Xc99=%all @@ -74,12 +81,15 @@ CPPFLAGS += $(INCS) LDFLAGS += $(ZNOLAZYLOAD) LDFLAGS += -R/usr/lib/smbsrv -LDLIBS += -L$(ROOT)/usr/lib/smbsrv +LDLIBS += -L$(ROOT)/usr/lib/smbsrv LDLIBS += -lfksmbsrv -lfakekernel -LDLIBS += -lmlsvc -lmlrpc -lsmbns -lsmb -LDLIBS += -lzfs -lcmdutils -lbsm -lsocket -lnsl -lscf -lumem +# prefer to keep libs ordered by dependence +LDLIBS += -lmlsvc -lmlrpc -lsmbns -lsmb -lsmbfs -lgss +LDLIBS += -lzfs -lbsm -lscf -lcmdutils -lsocket -lnsl -lumem +$(PROG) := LDLIBS += -lkrb5 LINTFLAGS += -xerroff=E_NAME_DEF_NOT_USED2 +LINTFLAGS += -xerroff=E_NAME_USED_NOT_DEF2 LINTFLAGS += -xerroff=E_INCONS_ARG_DECL2 LINTFLAGS += -xerroff=E_INCONS_VAL_TYPE_DECL2 diff --git a/usr/src/cmd/smbsrv/fksmbd/README b/usr/src/cmd/smbsrv/fksmbd/README index 63779932d2..da72ffd1a8 100644 --- a/usr/src/cmd/smbsrv/fksmbd/README +++ b/usr/src/cmd/smbsrv/fksmbd/README @@ -55,10 +55,14 @@ To run it under mdb (with mdb modules build here): mdb -L $ROOT/usr/lib/mdb/proc:/usr/lib/mdb/proc ... where ... is one of: fksmbd, core.nnn, -p $PID -There are also a couple dtrace scripts in here for watching -either all activity or only selected areas. (see *.d) +There are also some dtrace scripts in here, and in ../dtrace +for watching either all activity or only selected areas. Run these like: dtrace -s Watch-all.d -p $PID -o output +These two (from over in ../dtrace) also work with fksmbd: + dtrace -s smbd-authsvc.d -p `pgrep fksmbd` -o output + dtrace -s smbd-pipesvc.d -p `pgrep fksmbd` -o output + Here are a couple simple, handy tests you can try: diff --git a/usr/src/cmd/smbsrv/fksmbd/Run.sh b/usr/src/cmd/smbsrv/fksmbd/Run.sh index f1f4b5973b..0e42825dab 100755 --- a/usr/src/cmd/smbsrv/fksmbd/Run.sh +++ b/usr/src/cmd/smbsrv/fksmbd/Run.sh @@ -12,7 +12,7 @@ # # -# Copyright 2013 Nexenta Systems, Inc. All rights reserved. +# Copyright 2014 Nexenta Systems, Inc. All rights reserved. # # Helper program to run fksmbd (user-space smbd for debugging) @@ -32,6 +32,23 @@ then exit 1; fi +if [[ ! -r /var/smb/smbpasswd ]] +then + echo "Need readable /var/smb/smbpasswd, i.e." + echo 'chgrp staff /var/smb/smbpasswd' + echo 'chmod 440 /var/smb/smbpasswd' + exit 1; +fi + +if [[ -e /var/smb/.pwd.lock && ! -w /var/smb/.pwd.lock ]] +then + echo "Need to cleanup /var/smb/.pwd.lock, i.e." + echo "rm -f /var/smb/.pwd.lock" + exit 1; +fi + +# OK, setup env. to run it. + export SMBD_DOOR_NAME="/tmp/fksmbd_door" export SMB_SHARE_DNAME="/tmp/fksmbshare_door" diff --git a/usr/src/cmd/smbsrv/fksmbd/Watch-pipesvc.d b/usr/src/cmd/smbsrv/fksmbd/Watch-pipesvc.d deleted file mode 100644 index f11156001d..0000000000 --- a/usr/src/cmd/smbsrv/fksmbd/Watch-pipesvc.d +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/sbin/dtrace -s -/* - * This file and its contents are supplied under the terms of the - * Common Development and Distribution License ("CDDL"), version 1.0. - * You may only use this file in accordance with the terms of version - * 1.0 of the CDDL. - * - * A full copy of the text of the CDDL should have accompanied this - * source. A copy of the CDDL is also available via the Internet at - * http://www.illumos.org/license/CDDL. - */ - -/* - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. - */ - -/* - * User-level dtrace for smbd - * Usage: dtrace -s ThisScript.d -p PID - */ - -#pragma D option flowindent - -pid$target:fksmbd:pipesvc_worker:entry -{ - self->trace++; -} -pid$target:fksmbd:pipesvc_worker:return -{ - self->trace--; -} - -pid$target:fksmbd::entry, -pid$target:libfksmbsrv.so.1::entry, -pid$target:libmlsvc.so.1::entry, -pid$target:libmlrpc.so.1::entry, -pid$target:libsmbns.so.1::entry, -pid$target:libsmb.so.1::entry -/self->trace/ -{ - printf("\t0x%x", arg0); - printf("\t0x%x", arg1); - printf("\t0x%x", arg2); - printf("\t0x%x", arg3); -} - -pid$target:fksmbd::return, -pid$target:libfksmbsrv.so.1::return, -pid$target:libmlsvc.so.1::return, -pid$target:libmlrpc.so.1::return, -pid$target:libsmbns.so.1::return, -pid$target:libsmb.so.1::return -/self->trace/ -{ - printf("\t0x%x", arg1); -} - -pid$target:libmlrpc.so.1:ndo_trace:entry -/self->trace/ -{ - printf("ndo_trace: %s", copyinstr(arg0)); -} diff --git a/usr/src/cmd/smbsrv/fksmbd/fksmbd_kmod.c b/usr/src/cmd/smbsrv/fksmbd/fksmbd_kmod.c index 65fc7cecbb..4e4b17fcf1 100644 --- a/usr/src/cmd/smbsrv/fksmbd/fksmbd_kmod.c +++ b/usr/src/cmd/smbsrv/fksmbd/fksmbd_kmod.c @@ -10,7 +10,7 @@ */ /* - * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ /* @@ -62,11 +62,31 @@ static void fksmbd_adjust_config(smb_ioc_header_t *ioc_hdr) { smb_ioc_cfg_t *ioc = (smb_ioc_cfg_t *)ioc_hdr; + char *s; ioc->maxconnections = 10; ioc->maxworkers = 20; smbd_report("maxconnections=%d, maxworkers=%d", ioc->maxconnections, ioc->maxworkers); + + if ((s = getenv("SMB_SIGNING")) != NULL) { + ioc->signing_enable = 0; + ioc->signing_required = 0; + switch (s[0]) { + case 'e': + ioc->signing_enable = 1; + break; + case 'r': + ioc->signing_enable = 1; + ioc->signing_required = 1; + break; + default: + smbd_report("env SMB_SIGNING invalid"); + break; + } + } + smbd_report("signing: enable=%d, required=%d", + ioc->signing_enable, ioc->signing_required); } boolean_t diff --git a/usr/src/cmd/smbsrv/smbadm/Makefile b/usr/src/cmd/smbsrv/smbadm/Makefile index 3cdc52d13f..a31acdef20 100644 --- a/usr/src/cmd/smbsrv/smbadm/Makefile +++ b/usr/src/cmd/smbsrv/smbadm/Makefile @@ -22,6 +22,8 @@ # Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright 2014 Nexenta Systems, Inc. All rights reserved. +# PROG= smbadm SRCS= smbadm.c @@ -29,7 +31,7 @@ SRCS= smbadm.c include ../../Makefile.cmd include ../Makefile.smbsrv.defs -LDLIBS += -L$(ROOT)/usr/lib/smbsrv -lsmb -lsecdb -lumem +LDLIBS += -L$(ROOT)/usr/lib/smbsrv -lsmb -lsmbns -lsecdb -lumem LDFLAGS += -R/usr/lib/smbsrv .KEEP_STATE: diff --git a/usr/src/cmd/smbsrv/smbadm/smbadm.c b/usr/src/cmd/smbsrv/smbadm/smbadm.c index 2ab0e6e06d..f181d45556 100644 --- a/usr/src/cmd/smbsrv/smbadm/smbadm.c +++ b/usr/src/cmd/smbsrv/smbadm/smbadm.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ /* @@ -46,6 +46,7 @@ #include <auth_attr.h> #include <locale.h> #include <smbsrv/libsmb.h> +#include <smbsrv/libsmbns.h> #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" @@ -97,8 +98,8 @@ static char *progname; static boolean_t smbadm_checkauth(const char *); static void smbadm_usage(boolean_t); -static int smbadm_join_workgroup(const char *); -static int smbadm_join_domain(const char *, const char *); +static int smbadm_join_workgroup(const char *, boolean_t); +static int smbadm_join_domain(const char *, const char *, boolean_t); static void smbadm_extract_domain(char *, char **, char **); static int smbadm_join(int, char **); @@ -230,12 +231,12 @@ smbadm_cmdusage(FILE *fp, smbadm_cmdinfo_t *cmd) case HELP_JOIN: #if 0 /* Don't document "-p" yet, still needs work (NX 11960) */ - (void) fprintf(fp, gettext("\t%s -p domain\n" - "\t%s -u username domain\n\t%s -w workgroup\n"), + (void) fprintf(fp, gettext("\t%s [-y] -p domain\n" + "\t%s [-y] -u username domain\n\t%s [-y] -w workgroup\n"), cmd->name, cmd->name, cmd->name); #else - (void) fprintf(fp, gettext("\t%s -u username domain\n" - "\t%s -w workgroup\n"), cmd->name, cmd->name); + (void) fprintf(fp, gettext("\t%s [-y] -u username domain\n" + "\t%s [-y] -w workgroup\n"), cmd->name, cmd->name); #endif return; @@ -461,9 +462,10 @@ smbadm_join(int argc, char **argv) char *domain = NULL; char *username = NULL; uint32_t mode = 0; + boolean_t do_prompt = B_TRUE; char option; - while ((option = getopt(argc, argv, "pu:w")) != -1) { + while ((option = getopt(argc, argv, "pu:wy")) != -1) { if (mode != 0) { (void) fprintf(stderr, gettext( "join options are mutually exclusive\n")); @@ -484,6 +486,10 @@ smbadm_join(int argc, char **argv) mode = SMB_SECMODE_WORKGRP; break; + case 'y': + do_prompt = B_FALSE; + break; + default: smbadm_usage(B_FALSE); break; @@ -509,9 +515,9 @@ smbadm_join(int argc, char **argv) } if (mode == SMB_SECMODE_WORKGRP) { - return (smbadm_join_workgroup(domain)); + return (smbadm_join_workgroup(domain, do_prompt)); } - return (smbadm_join_domain(domain, username)); + return (smbadm_join_domain(domain, username, do_prompt)); } /* @@ -520,11 +526,13 @@ smbadm_join(int argc, char **argv) * with no formal membership mechanism. */ static int -smbadm_join_workgroup(const char *workgroup) +smbadm_join_workgroup(const char *workgroup, boolean_t prompt) { smb_joininfo_t jdi; + smb_joinres_t jdres; uint32_t status; + bzero(&jdres, sizeof (jdres)); bzero(&jdi, sizeof (jdi)); jdi.mode = SMB_SECMODE_WORKGRP; (void) strlcpy(jdi.domain_name, workgroup, sizeof (jdi.domain_name)); @@ -535,10 +543,10 @@ smbadm_join_workgroup(const char *workgroup) smbadm_usage(B_FALSE); } - if (!smbadm_join_prompt(jdi.domain_name)) + if (prompt && !smbadm_join_prompt(jdi.domain_name)) return (0); - if ((status = smb_join(&jdi)) != NT_STATUS_SUCCESS) { + if ((status = smb_join(&jdi, &jdres)) != NT_STATUS_SUCCESS) { (void) fprintf(stderr, gettext("failed to join %s: %s\n"), jdi.domain_name, xlate_nt_status(status)); return (1); @@ -559,14 +567,15 @@ smbadm_join_workgroup(const char *workgroup) * to be appended to the username using '+' as a scripting convenience. */ static int -smbadm_join_domain(const char *domain, const char *username) +smbadm_join_domain(const char *domain, const char *username, boolean_t prompt) { smb_joininfo_t jdi; - uint32_t status; - char *prompt; + smb_joinres_t jdres; + char *passwd_prompt; char *p; - int len; + int len, rc; + bzero(&jdres, sizeof (jdres)); bzero(&jdi, sizeof (jdi)); jdi.mode = SMB_SECMODE_DOMAIN; (void) strlcpy(jdi.domain_name, domain, sizeof (jdi.domain_name)); @@ -577,7 +586,7 @@ smbadm_join_domain(const char *domain, const char *username) smbadm_usage(B_FALSE); } - if (!smbadm_join_prompt(jdi.domain_name)) + if (prompt && !smbadm_join_prompt(jdi.domain_name)) return (0); /* @@ -609,9 +618,9 @@ smbadm_join_domain(const char *domain, const char *username) } if (*jdi.domain_passwd == '\0') { - prompt = gettext("Enter domain password: "); + passwd_prompt = gettext("Enter domain password: "); - if ((p = getpassphrase(prompt)) == NULL) { + if ((p = getpassphrase(passwd_prompt)) == NULL) { (void) fprintf(stderr, gettext( "missing password\n")); smbadm_usage(B_FALSE); @@ -625,41 +634,77 @@ smbadm_join_domain(const char *domain, const char *username) (void) printf(gettext("Joining %s ... this may take a minute ...\n"), jdi.domain_name); - status = smb_join(&jdi); + rc = smb_join(&jdi, &jdres); + if (rc != 0) { + (void) printf(gettext("Cannot call the SMB service. " + " (error %d: %s) " + "Please check the service status " + "(svcs -vx network/smb/server)\n"), + rc, strerror(rc)); + bzero(&jdi, sizeof (jdi)); + return (1); + } - switch (status) { + switch (jdres.status) { case NT_STATUS_SUCCESS: - (void) printf(gettext("Successfully joined %s\n"), - jdi.domain_name); + (void) printf(gettext( + "Successfully joined domain %s using AD server %s\n"), + jdi.domain_name, jdres.dc_name); bzero(&jdi, sizeof (jdi)); smbadm_restart_service(); return (0); case NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND: - (void) fprintf(stderr, - gettext("failed to find any domain controllers for %s\n"), + /* See: smb_ads_lookup_msdcs */ + (void) fprintf(stderr, gettext( + "failed to find any AD servers for domain: %s\n"), jdi.domain_name); - bzero(&jdi, sizeof (jdi)); - return (1); + goto common; case NT_STATUS_BAD_NETWORK_PATH: - (void) fprintf(stderr, - gettext("failed to resolve domain controller name\n")); - bzero(&jdi, sizeof (jdi)); - return (1); + /* See: smbrdr_ctx_new / smb_ctx_resolve */ + (void) fprintf(stderr, gettext( + "failed to resolve address of AD server: %s\n"), + jdres.dc_name); + goto common; case NT_STATUS_NETWORK_ACCESS_DENIED: + /* See: smbrdr_ctx_new / smb_ctx_get_ssn */ + (void) fprintf(stderr, gettext( + "failed to authenticate with AD server: %s\n"), + jdres.dc_name); + goto common; + case NT_STATUS_BAD_NETWORK_NAME: - (void) fprintf(stderr, - gettext("failed connecting to domain controller\n")); - bzero(&jdi, sizeof (jdi)); - return (1); + /* + * See: smbrdr_ctx_new / smb_ctx_get_tree + * and: ndr_rpc_bind / smb_fh_open + */ + (void) fprintf(stderr, gettext( + "failed connecting to services on AD server: %s\n"), + jdres.dc_name); + goto common; default: - (void) fprintf(stderr, gettext("failed to join %s: %s\n"), - jdi.domain_name, xlate_nt_status(status)); - (void) fprintf(stderr, gettext("Please refer to the system log" - " for more information.\n")); + (void) fprintf(stderr, gettext( + "failed to join domain %s\n"), + jdi.domain_name); + if (jdres.dc_name[0] != '\0') { + (void) fprintf(stderr, gettext( + "using AD server: %s\n"), + jdres.dc_name); + } + /* FALLTHROUGH */ + common: + if (jdres.join_err != 0) { + (void) fprintf(stderr, "%s\n", + smb_ads_strerror(jdres.join_err)); + } else if (jdres.status != 0) { + (void) fprintf(stderr, "(%s)\n", + xlate_nt_status(jdres.status)); + } + (void) fprintf(stderr, gettext("Please refer to the " + "service log for more information.\n")); bzero(&jdi, sizeof (jdi)); return (1); } @@ -749,10 +794,11 @@ smbadm_list(int argc, char **argv) (!smb_inet_iszero(&srvipaddr))) { (void) smb_inet_ntop(&srvipaddr, ipstr, SMB_IPSTRLEN(srvipaddr.a_family)); - (void) printf(gettext("\t[+%s.%s] [%s]\n"), - srvname, fqdn, ipstr); + (void) printf(gettext("\t[+%s] [%s]\n"), + srvname, ipstr); } + /* Print the local and domain SID. */ smb_domain_show(); return (0); } diff --git a/usr/src/cmd/smbsrv/smbd/Makefile b/usr/src/cmd/smbsrv/smbd/Makefile index 8fd9ccb74a..eaa86dc5ae 100644 --- a/usr/src/cmd/smbsrv/smbd/Makefile +++ b/usr/src/cmd/smbsrv/smbd/Makefile @@ -26,11 +26,15 @@ PROG= smbd OBJS= \ + smbd_authsvc.o \ smbd_doorsvc.o \ smbd_join.o \ + smbd_krb5lookup.o \ + smbd_krb5ssp.o \ smbd_logon.o \ smbd_main.o \ smbd_nicmon.o \ + smbd_ntlmssp.o \ smbd_pipesvc.o \ smbd_share_doorsvc.o \ smbd_spool.o \ @@ -54,18 +58,25 @@ $(ROOTSVCMETHOD):= FILEMODE = 0555 $(ROOTVARSMBDLL):= FILEMODE = 0755 LINTFLAGS += -xerroff=E_NAME_DEF_NOT_USED2 +LINTFLAGS += -xerroff=E_NAME_USED_NOT_DEF2 CFLAGS += $(CCVERBOSE) CPPFLAGS += -D_REENTRANT CPPFLAGS += -Dsyslog=smb_syslog $(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG +# Should not have to do this, but the Kerberos includes are a mess. +CPPFLAGS += -I $(ROOT)/usr/include/kerberosv5 + C99MODE = -xc99=%all C99LMODE = -Xc99=%all -LDLIBS += -L$(ROOT)/usr/lib/smbsrv -lmlsvc -lmlrpc -lsmbns -lsmb \ - -lzfs -lbsm -lsocket -lnsl -lscf -lumem -lcmdutils LDFLAGS += -R/usr/lib/smbsrv +LDLIBS += -L$(ROOT)/usr/lib/smbsrv +# prefer to keep libs ordered by dependence +LDLIBS += -lmlsvc -lmlrpc -lsmbns -lsmb -lsmbfs -lgss +LDLIBS += -lzfs -lbsm -lscf -lcmdutils -lsocket -lnsl -lumem +$(PROG) := LDLIBS += -lkrb5 $(ENABLE_SMB_PRINTING) CPPFLAGS += -DHAVE_CUPS @@ -100,3 +111,5 @@ include ../../Makefile.targ install: all .WAIT $(ROOTETCDEFAULTFILES) $(ROOTMANIFEST) \ $(ROOTSMBDFILE) ${ROOTSVCMETHOD} $(ROOTVARSMBDLL) + +.KEEP_STATE: diff --git a/usr/src/cmd/smbsrv/smbd/server.xml b/usr/src/cmd/smbsrv/smbd/server.xml index 10b0c82a87..d8dc32c114 100644 --- a/usr/src/cmd/smbsrv/smbd/server.xml +++ b/usr/src/cmd/smbsrv/smbd/server.xml @@ -181,7 +181,7 @@ file. <propval name='restrict_anonymous' type='boolean' value='false' override='true'/> <propval name='signing_enabled' type='boolean' - value='false' override='true'/> + value='true' override='true'/> <propval name='signing_required' type='boolean' value='false' override='true'/> <propval name='signing_check' type='boolean' diff --git a/usr/src/cmd/smbsrv/smbd/smbd.h b/usr/src/cmd/smbsrv/smbd/smbd.h index 3ec5877fac..436120a745 100644 --- a/usr/src/cmd/smbsrv/smbd/smbd.h +++ b/usr/src/cmd/smbsrv/smbd/smbd.h @@ -52,10 +52,11 @@ void smbd_dc_monitor_refresh(void); smb_token_t *smbd_user_auth_logon(smb_logon_t *); void smbd_user_nonauth_logon(uint32_t); void smbd_user_auth_logoff(uint32_t); -uint32_t smbd_join(smb_joininfo_t *); +void smbd_join(smb_joininfo_t *, smb_joinres_t *); void smbd_set_secmode(int); boolean_t smbd_online(void); void smbd_online_wait(const char *); +void smbd_get_authconf(smb_kmod_cfg_t *); void smbd_spool_start(void); void smbd_spool_stop(void); @@ -81,6 +82,7 @@ typedef struct smbd { boolean_t s_shutting_down; /* shutdown control */ volatile uint_t s_refreshes; boolean_t s_kbound; /* B_TRUE if bound to kernel */ + int s_authsvc_sock; int s_door_lmshr; int s_door_srv; int s_door_opipe; @@ -89,6 +91,7 @@ typedef struct smbd { smb_inaddr_t s_pdc; boolean_t s_pdc_changed; pthread_t s_refresh_tid; + pthread_t s_authsvc_tid; pthread_t s_localtime_tid; pthread_t s_spool_tid; pthread_t s_dc_monitor_tid; @@ -140,6 +143,9 @@ void smbd_door_return(smbd_door_t *, char *, size_t, door_desc_t *, uint_t); void *smbd_door_dispatch_op(void *); +int smbd_authsvc_start(void); +void smbd_authsvc_stop(void); + /* For fksmbd */ void fksmbd_init(void); int fksmbd_door_dispatch(smb_doorarg_t *); diff --git a/usr/src/cmd/smbsrv/smbd/smbd_authsvc.c b/usr/src/cmd/smbsrv/smbd/smbd_authsvc.c new file mode 100644 index 0000000000..0b6af80bd8 --- /dev/null +++ b/usr/src/cmd/smbsrv/smbd/smbd_authsvc.c @@ -0,0 +1,973 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + */ + +/* + * SMB authentication service + * + * This service listens on a local AF_UNIX socket, spawning a + * thread to service each connection. The client-side of such + * connections is the in-kernel SMB service, with an open and + * connect done in the SMB session setup handler. + */ + +#include <sys/types.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> +#include <signal.h> +#include <stdio.h> +#include <note.h> +#include <net/if.h> +#include <net/route.h> +#include <sys/sockio.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <netinet/in.h> +#include <fcntl.h> +#include <pthread.h> +#include <syslog.h> +#include <smbsrv/libsmb.h> +#include <netsmb/spnego.h> + +#include "smbd.h" +#include "smbd_authsvc.h" + +/* Arbitrary value outside the (small) range of valid OIDs */ +#define special_mech_raw_NTLMSSP (spnego_mech_oid_NTLMSSP + 100) + +static struct sockaddr_un smbauth_sockname = { + AF_UNIX, SMB_AUTHSVC_SOCKNAME }; + +typedef struct spnego_mech_handler { + int mh_oid; /* SPNEGO_MECH_OID */ + int (*mh_init)(authsvc_context_t *); + int (*mh_work)(authsvc_context_t *); + void (*mh_fini)(authsvc_context_t *); +} spnego_mech_handler_t; + +static int smbd_authsock_create(void); +static void smbd_authsock_destroy(void); +static void *smbd_authsvc_listen(void *); +static void *smbd_authsvc_work(void *); +static void smbd_authsvc_flood(void); + +static int smbd_authsvc_oldreq(authsvc_context_t *); +static int smbd_authsvc_clinfo(authsvc_context_t *); +static int smbd_authsvc_esfirst(authsvc_context_t *); +static int smbd_authsvc_esnext(authsvc_context_t *); +static int smbd_authsvc_escmn(authsvc_context_t *); +static int smbd_authsvc_gettoken(authsvc_context_t *); +static int smbd_raw_ntlmssp_esfirst(authsvc_context_t *); +static int smbd_raw_ntlmssp_esnext(authsvc_context_t *); + +/* + * We can get relatively large tokens now, thanks to krb5 PAC. + * Might be better to size these buffers dynamically, but these + * are all short-lived so not bothering with that for now. + */ +int smbd_authsvc_bufsize = 65000; + +static mutex_t smbd_authsvc_mutex = DEFAULTMUTEX; + +/* + * The maximum number of authentication thread is limited by the + * smbsrv smb_threshold_...(->sv_ssetup_ct) mechanism. However, + * due to occasional delays closing these auth. sockets, we need + * a little "slack" on the number of threads we'll allow, as + * compared with the in-kernel limit. We could perhaps just + * remove this limit now, but want it for extra safety. + */ +int smbd_authsvc_maxthread = SMB_AUTHSVC_MAXTHREAD + 32; +int smbd_authsvc_thrcnt = 0; /* current thrcnt */ +int smbd_authsvc_hiwat = 0; /* largest thrcnt seen */ +#ifdef DEBUG +int smbd_authsvc_slowdown = 0; +#endif + +/* + * These are the mechanisms we support, in order of preference. + * But note: it's really the _client's_ preference that matters. + * See &pref in the spnegoIsMechTypeAvailable() calls below. + * Careful with this table; the code below knows its format and + * may skip the fist two entries to ommit Kerberos. + */ +static const spnego_mech_handler_t +mech_table[] = { + { + spnego_mech_oid_Kerberos_V5, + smbd_krb5ssp_init, + smbd_krb5ssp_work, + smbd_krb5ssp_fini + }, + { + spnego_mech_oid_Kerberos_V5_Legacy, + smbd_krb5ssp_init, + smbd_krb5ssp_work, + smbd_krb5ssp_fini + }, +#define MECH_TBL_IDX_NTLMSSP 2 + { + spnego_mech_oid_NTLMSSP, + smbd_ntlmssp_init, + smbd_ntlmssp_work, + smbd_ntlmssp_fini + }, + { + /* end marker */ + spnego_mech_oid_NotUsed, + NULL, NULL, NULL + }, +}; + +static const spnego_mech_handler_t +smbd_auth_mech_raw_ntlmssp = { + special_mech_raw_NTLMSSP, + smbd_ntlmssp_init, + smbd_ntlmssp_work, + smbd_ntlmssp_fini +}; + + +/* + * Start the authentication service. + * Returns non-zero on error. + */ +int +smbd_authsvc_start(void) +{ + pthread_attr_t attr; + pthread_t tid; + int rc; + + rc = smbd_authsock_create(); + if (rc) + return (rc); + + (void) pthread_attr_init(&attr); + (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + rc = pthread_create(&tid, &attr, smbd_authsvc_listen, &smbd); + (void) pthread_attr_destroy(&attr); + if (rc) { + smbd_authsock_destroy(); + return (rc); + } + + smbd.s_authsvc_tid = tid; + return (0); +} + +void +smbd_authsvc_stop(void) +{ + + if (smbd.s_authsvc_tid != 0) { + (void) pthread_kill(smbd.s_authsvc_tid, SIGTERM); + smbd.s_authsvc_tid = 0; + } +} + +static int +smbd_authsock_create(void) +{ + int sock = -1; + + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) { + smbd_report("authsvc, socket create failed, %d", errno); + return (errno); + } + + (void) unlink(smbauth_sockname.sun_path); + if (bind(sock, (struct sockaddr *)&smbauth_sockname, + sizeof (smbauth_sockname)) < 0) { + smbd_report("authsvc, socket bind failed, %d", errno); + (void) close(sock); + return (errno); + } + + if (listen(sock, SOMAXCONN) < 0) { + smbd_report("authsvc, socket listen failed, %d", errno); + (void) close(sock); + return (errno); + } + + smbd.s_authsvc_sock = sock; + return (0); +} + +static void +smbd_authsock_destroy(void) +{ + int fid; + + if ((fid = smbd.s_authsvc_sock) != -1) { + smbd.s_authsvc_sock = -1; + (void) close(fid); + } +} + +static void * +smbd_authsvc_listen(void *arg) +{ + authsvc_context_t *ctx; + pthread_attr_t attr; + pthread_t tid; + socklen_t slen; + int ls, ns, rc; + + _NOTE(ARGUNUSED(arg)) + + (void) pthread_attr_init(&attr); + (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + ls = smbd.s_authsvc_sock; + for (;;) { + + slen = 0; + ns = accept(ls, NULL, &slen); + if (ns < 0) { + switch (errno) { + case ECONNABORTED: + continue; + case EINTR: + /* normal termination */ + goto out; + default: + smbd_report("authsvc, socket accept failed," + " %d", errno); + goto out; + } + } + + /* + * Limit the number of auth. sockets + * (and the threads that service them). + */ + (void) mutex_lock(&smbd_authsvc_mutex); + if (smbd_authsvc_thrcnt >= smbd_authsvc_maxthread) { + (void) mutex_unlock(&smbd_authsvc_mutex); + (void) close(ns); + smbd_authsvc_flood(); + continue; + } + smbd_authsvc_thrcnt++; + if (smbd_authsvc_hiwat < smbd_authsvc_thrcnt) + smbd_authsvc_hiwat = smbd_authsvc_thrcnt; + (void) mutex_unlock(&smbd_authsvc_mutex); + + ctx = smbd_authctx_create(); + if (ctx == NULL) { + smbd_report("authsvc, can't allocate context"); + (void) mutex_lock(&smbd_authsvc_mutex); + smbd_authsvc_thrcnt--; + (void) mutex_unlock(&smbd_authsvc_mutex); + (void) close(ns); + goto out; + } + ctx->ctx_socket = ns; + + rc = pthread_create(&tid, &attr, smbd_authsvc_work, ctx); + if (rc) { + smbd_report("authsvc, thread create failed, %d", rc); + (void) mutex_lock(&smbd_authsvc_mutex); + smbd_authsvc_thrcnt--; + (void) mutex_unlock(&smbd_authsvc_mutex); + smbd_authctx_destroy(ctx); + goto out; + } + ctx = NULL; /* given to the new thread */ + } + +out: + (void) pthread_attr_destroy(&attr); + smbd_authsock_destroy(); + return (NULL); +} + +static void +smbd_authsvc_flood(void) +{ + static uint_t count; + static time_t last_report; + time_t now = time(NULL); + + count++; + if (last_report + 60 < now) { + last_report = now; + smbd_report("authsvc: flooded %u", count); + count = 0; + } +} + +authsvc_context_t * +smbd_authctx_create(void) +{ + authsvc_context_t *ctx; + + ctx = malloc(sizeof (*ctx)); + if (ctx == NULL) + return (NULL); + bzero(ctx, sizeof (*ctx)); + + ctx->ctx_irawlen = smbd_authsvc_bufsize; + ctx->ctx_irawbuf = malloc(ctx->ctx_irawlen); + ctx->ctx_orawlen = smbd_authsvc_bufsize; + ctx->ctx_orawbuf = malloc(ctx->ctx_orawlen); + if (ctx->ctx_irawbuf == NULL || ctx->ctx_orawbuf == NULL) + goto errout; + + ctx->ctx_ibodylen = smbd_authsvc_bufsize; + ctx->ctx_ibodybuf = malloc(ctx->ctx_ibodylen); + ctx->ctx_obodylen = smbd_authsvc_bufsize; + ctx->ctx_obodybuf = malloc(ctx->ctx_obodylen); + if (ctx->ctx_ibodybuf == NULL || ctx->ctx_obodybuf == NULL) + goto errout; + + return (ctx); + +errout: + smbd_authctx_destroy(ctx); + return (NULL); +} + +void +smbd_authctx_destroy(authsvc_context_t *ctx) +{ + if (ctx->ctx_socket != -1) { + (void) close(ctx->ctx_socket); + ctx->ctx_socket = -1; + } + + if (ctx->ctx_token != NULL) + smb_token_destroy(ctx->ctx_token); + + if (ctx->ctx_itoken != NULL) + spnegoFreeData(ctx->ctx_itoken); + if (ctx->ctx_otoken != NULL) + spnegoFreeData(ctx->ctx_otoken); + + free(ctx->ctx_irawbuf); + free(ctx->ctx_orawbuf); + free(ctx->ctx_ibodybuf); + free(ctx->ctx_obodybuf); + + free(ctx); +} + +/* + * Limit how long smbd_authsvc_work will wait for the client to + * send us the next part of the authentication sequence. + */ +static struct timeval recv_tmo = { 30, 0 }; + +/* + * Also set a timeout for send, where we're sending a response to + * the client side (in smbsrv). That should always be waiting in + * recv by the time we send, so a short timeout is OK. + */ +static struct timeval send_tmo = { 15, 0 }; + +static void * +smbd_authsvc_work(void *arg) +{ + authsvc_context_t *ctx = arg; + smb_lsa_msg_hdr_t hdr; + int sock = ctx->ctx_socket; + int len, rc; + + if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, + (char *)&send_tmo, sizeof (send_tmo)) != 0) { + smbd_report("authsvc_work: set set timeout: %m"); + goto out; + } + + if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, + (char *)&recv_tmo, sizeof (recv_tmo)) != 0) { + smbd_report("authsvc_work: set recv timeout: %m"); + goto out; + } + + for (;;) { + + len = recv(sock, &hdr, sizeof (hdr), MSG_WAITALL); + if (len <= 0) { + /* normal termination */ + break; + } + if (len != sizeof (hdr)) { + smbd_report("authsvc_work: read header failed"); + break; + } + + if (hdr.lmh_msglen > smbd_authsvc_bufsize) { + smbd_report("authsvc_work: msg too large"); + break; + } + + if (hdr.lmh_msglen > 0) { + len = recv(sock, ctx->ctx_irawbuf, hdr.lmh_msglen, + MSG_WAITALL); + if (len != hdr.lmh_msglen) { + smbd_report("authsvc_work: read mesg failed"); + break; + } + } + ctx->ctx_irawtype = hdr.lmh_msgtype; + ctx->ctx_irawlen = hdr.lmh_msglen; + ctx->ctx_orawlen = smbd_authsvc_bufsize; + ctx->ctx_ibodylen = smbd_authsvc_bufsize; + ctx->ctx_obodylen = smbd_authsvc_bufsize; + + /* + * The real work happens here. + */ + rc = smbd_authsvc_dispatch(ctx); + if (rc) + break; + + hdr.lmh_msgtype = ctx->ctx_orawtype; + hdr.lmh_msglen = ctx->ctx_orawlen; + len = send(sock, &hdr, sizeof (hdr), 0); + if (len != sizeof (hdr)) { + smbd_report("authsvc_work: send failed"); + break; + } + + if (ctx->ctx_orawlen > 0) { + len = send(sock, ctx->ctx_orawbuf, + ctx->ctx_orawlen, 0); + if (len != ctx->ctx_orawlen) { + smbd_report("authsvc_work: send failed"); + break; + } + } + } + +out: + if (ctx->ctx_mh_fini) + (ctx->ctx_mh_fini)(ctx); + + smbd_authctx_destroy(ctx); + + (void) mutex_lock(&smbd_authsvc_mutex); + smbd_authsvc_thrcnt--; + (void) mutex_unlock(&smbd_authsvc_mutex); + + return (NULL); /* implied pthread_exit() */ +} + +/* + * Dispatch based on message type LSA_MTYPE_... + * Non-zero return here ends the conversation. + */ +int +smbd_authsvc_dispatch(authsvc_context_t *ctx) +{ + int rc; + + switch (ctx->ctx_irawtype) { + + case LSA_MTYPE_OLDREQ: +#ifdef DEBUG + if (smbd_authsvc_slowdown) + (void) sleep(smbd_authsvc_slowdown); +#endif + rc = smbd_authsvc_oldreq(ctx); + break; + + case LSA_MTYPE_CLINFO: + rc = smbd_authsvc_clinfo(ctx); + break; + + case LSA_MTYPE_ESFIRST: + rc = smbd_authsvc_esfirst(ctx); + break; + + case LSA_MTYPE_ESNEXT: +#ifdef DEBUG + if (smbd_authsvc_slowdown) + (void) sleep(smbd_authsvc_slowdown); +#endif + rc = smbd_authsvc_esnext(ctx); + break; + + case LSA_MTYPE_GETTOK: + rc = smbd_authsvc_gettoken(ctx); + break; + + /* response types */ + case LSA_MTYPE_OK: + case LSA_MTYPE_ERROR: + case LSA_MTYPE_TOKEN: + case LSA_MTYPE_ES_CONT: + case LSA_MTYPE_ES_DONE: + default: + return (-1); + } + + if (rc != 0) { + smb_lsa_eresp_t *er = ctx->ctx_orawbuf; + ctx->ctx_orawtype = LSA_MTYPE_ERROR; + ctx->ctx_orawlen = sizeof (*er); + er->ler_ntstatus = rc; + er->ler_errclass = 0; + er->ler_errcode = 0; + } + return (0); +} + +static int +smbd_authsvc_oldreq(authsvc_context_t *ctx) +{ + smb_logon_t user_info; + XDR xdrs; + smb_token_t *token = NULL; + int rc = 0; + + bzero(&user_info, sizeof (user_info)); + xdrmem_create(&xdrs, ctx->ctx_irawbuf, ctx->ctx_irawlen, + XDR_DECODE); + if (!smb_logon_xdr(&xdrs, &user_info)) { + xdr_destroy(&xdrs); + return (NT_STATUS_INVALID_PARAMETER); + } + xdr_destroy(&xdrs); + + token = smbd_user_auth_logon(&user_info); + xdr_free(smb_logon_xdr, (char *)&user_info); + if (token == NULL) + return (NT_STATUS_ACCESS_DENIED); + + ctx->ctx_token = token; + + return (rc); +} + +static int +smbd_authsvc_clinfo(authsvc_context_t *ctx) +{ + + if (ctx->ctx_irawlen != sizeof (smb_lsa_clinfo_t)) + return (NT_STATUS_INTERNAL_ERROR); + (void) memcpy(&ctx->ctx_clinfo, ctx->ctx_irawbuf, + sizeof (smb_lsa_clinfo_t)); + + ctx->ctx_orawtype = LSA_MTYPE_OK; + ctx->ctx_orawlen = 0; + return (0); +} + +/* + * 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); +} + +/* + * Handle a security blob we've received from the client. + * Incoming type: LSA_MTYPE_ESNEXT + * Outgoing types: LSA_MTYPE_ES_CONT, LSA_MTYPE_ES_DONE, + * LSA_MTYPE_ERROR + */ +static int +smbd_authsvc_esnext(authsvc_context_t *ctx) +{ + int rc; + + /* + * Make sure LSA_MTYPE_ESFIRST was handled + * previously, so we have a work function. + */ + if (ctx->ctx_mh_work == NULL) + return (NT_STATUS_INVALID_PARAMETER); + + if (ctx->ctx_mech_oid == special_mech_raw_NTLMSSP) { + rc = smbd_raw_ntlmssp_esnext(ctx); + return (rc); + } + + /* + * Cleanup state from previous calls. + */ + if (ctx->ctx_itoken != NULL) { + spnegoFreeData(ctx->ctx_itoken); + ctx->ctx_itoken = NULL; + } + + /* + * Parse the SPNEGO token, check its type. + */ + rc = spnegoInitFromBinary(ctx->ctx_irawbuf, + ctx->ctx_irawlen, &ctx->ctx_itoken); + if (rc != 0) + return (NT_STATUS_INVALID_PARAMETER); + + rc = spnegoGetTokenType(ctx->ctx_itoken, &ctx->ctx_itoktype); + if (rc != 0) + return (NT_STATUS_INVALID_PARAMETER); + + if (ctx->ctx_itoktype != SPNEGO_TOKEN_TARG) + return (NT_STATUS_INVALID_PARAMETER); + + rc = smbd_authsvc_escmn(ctx); + return (rc); +} + +static int +smbd_authsvc_escmn(authsvc_context_t *ctx) +{ + SPNEGO_MECH_OID oid; + ulong_t toklen; + int rc; + + /* + * Cleanup state from previous calls. + */ + if (ctx->ctx_otoken != NULL) { + spnegoFreeData(ctx->ctx_otoken); + ctx->ctx_otoken = NULL; + } + + /* + * Extract the payload (mech token). + */ + toklen = ctx->ctx_ibodylen; + rc = spnegoGetMechToken(ctx->ctx_itoken, + ctx->ctx_ibodybuf, &toklen); + switch (rc) { + case SPNEGO_E_SUCCESS: + break; + case SPNEGO_E_ELEMENT_UNAVAILABLE: + toklen = 0; + break; + case SPNEGO_E_BUFFER_TOO_SMALL: + return (NT_STATUS_BUFFER_TOO_SMALL); + default: + return (NT_STATUS_INTERNAL_ERROR); + } + ctx->ctx_ibodylen = toklen; + + /* + * Now that we have the incoming "body" (mech. token), + * call the back-end mech-specific work function to + * create the outgoing "body" (mech. token). + * + * The worker must fill in: ctx->ctx_negresult, + * and: ctx->ctx_obodylen, but ctx->ctx_obodybuf + * is optional, and is typically NULL after the + * final message of an auth sequence, where + * negresult == spnego_negresult_complete. + */ + rc = ctx->ctx_mh_work(ctx); + if (rc != 0) + return (rc); + + /* + * Wrap the outgoing body in a negTokenTarg SPNEGO token. + * The selected mech. OID is returned only when the + * incoming token was of type SPNEGO_TOKEN_INIT. + */ + if (ctx->ctx_itoktype == SPNEGO_TOKEN_INIT) { + /* tell the client the selected mech. */ + oid = ctx->ctx_mech_oid; + } else { + /* Ommit the "supported mech." field. */ + oid = spnego_mech_oid_NotUsed; + } + + /* + * Determine the spnego "negresult" from the + * reply message type (from the work func). + */ + switch (ctx->ctx_orawtype) { + case LSA_MTYPE_ERROR: + ctx->ctx_negresult = spnego_negresult_rejected; + break; + case LSA_MTYPE_ES_DONE: + ctx->ctx_negresult = spnego_negresult_success; + break; + case LSA_MTYPE_ES_CONT: + ctx->ctx_negresult = spnego_negresult_incomplete; + break; + default: + return (-1); + } + + rc = spnegoCreateNegTokenTarg( + oid, + ctx->ctx_negresult, + ctx->ctx_obodybuf, /* may be NULL */ + ctx->ctx_obodylen, + NULL, 0, + &ctx->ctx_otoken); + + /* + * Convert the SPNEGO token into binary form, + * writing it to the output buffer. + */ + toklen = smbd_authsvc_bufsize; + rc = spnegoTokenGetBinary(ctx->ctx_otoken, + (uchar_t *)ctx->ctx_orawbuf, &toklen); + if (rc) + rc = NT_STATUS_INTERNAL_ERROR; + ctx->ctx_orawlen = (uint_t)toklen; + + return (rc); +} + +/* + * Wrapper for "Raw NTLMSSP", which is exactly like the + * normal (SPNEGO-wrapped) NTLMSSP but without SPNEGO. + * Setup back-end handler for: special_mech_raw_NTLMSSP + * Compare with smbd_authsvc_esfirst(). + */ +static int +smbd_raw_ntlmssp_esfirst(authsvc_context_t *ctx) +{ + const spnego_mech_handler_t *mh; + int rc; + + mh = &smbd_auth_mech_raw_ntlmssp; + rc = mh->mh_init(ctx); + if (rc != 0) + return (rc); + + ctx->ctx_mech_oid = mh->mh_oid; + ctx->ctx_mh_work = mh->mh_work; + ctx->ctx_mh_fini = mh->mh_fini; + + rc = smbd_raw_ntlmssp_esnext(ctx); + + return (rc); +} + + +/* + * Wrapper for "Raw NTLMSSP", which is exactly like the + * normal (SPNEGO-wrapped) NTLMSSP but without SPNEGO. + * Just copy "raw" to "body", and vice versa. + * Compare with smbd_authsvc_esnext, smbd_authsvc_escmn + */ +static int +smbd_raw_ntlmssp_esnext(authsvc_context_t *ctx) +{ + int rc; + + ctx->ctx_ibodylen = ctx->ctx_irawlen; + (void) memcpy(ctx->ctx_ibodybuf, + ctx->ctx_irawbuf, ctx->ctx_irawlen); + + rc = ctx->ctx_mh_work(ctx); + + ctx->ctx_orawlen = ctx->ctx_obodylen; + (void) memcpy(ctx->ctx_orawbuf, + ctx->ctx_obodybuf, ctx->ctx_obodylen); + + return (rc); +} + + +/* + * After a successful authentication, request the access token. + */ +static int +smbd_authsvc_gettoken(authsvc_context_t *ctx) +{ + XDR xdrs; + smb_token_t *token = NULL; + int rc = 0; + int len; + + if ((token = ctx->ctx_token) == NULL) + return (NT_STATUS_ACCESS_DENIED); + + /* + * Encode the token response + */ + len = xdr_sizeof(smb_token_xdr, token); + if (len > ctx->ctx_orawlen) { + if ((ctx->ctx_orawbuf = realloc(ctx->ctx_orawbuf, len)) == + NULL) { + return (NT_STATUS_INTERNAL_ERROR); + } + } + + ctx->ctx_orawtype = LSA_MTYPE_TOKEN; + ctx->ctx_orawlen = len; + xdrmem_create(&xdrs, ctx->ctx_orawbuf, len, XDR_ENCODE); + if (!smb_token_xdr(&xdrs, token)) + rc = NT_STATUS_INTERNAL_ERROR; + xdr_destroy(&xdrs); + + return (rc); +} + +/* + * Initialization time code to figure out what mechanisms we support. + * Careful with this table; the code below knows its format and may + * skip the fist two entries to ommit Kerberos. + */ +static SPNEGO_MECH_OID MechTypeList[] = { + spnego_mech_oid_Kerberos_V5, + spnego_mech_oid_Kerberos_V5_Legacy, +#define MECH_OID_IDX_NTLMSSP 2 + spnego_mech_oid_NTLMSSP, +}; +static int MechTypeCnt = sizeof (MechTypeList) / + sizeof (MechTypeList[0]); + +/* This string is just like Windows. */ +static char IgnoreSPN[] = "not_defined_in_RFC4178@please_ignore"; + +/* + * 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); +} diff --git a/usr/src/cmd/smbsrv/smbd/smbd_authsvc.h b/usr/src/cmd/smbsrv/smbd/smbd_authsvc.h new file mode 100644 index 0000000000..116eebfc84 --- /dev/null +++ b/usr/src/cmd/smbsrv/smbd/smbd_authsvc.h @@ -0,0 +1,82 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + */ + +#ifndef _SMBD_AUTHSVC_H +#define _SMBD_AUTHSVC_H + +/* + * Declarations shared with authsvc modules. + */ + +#include <sys/types.h> +#include <smbsrv/libsmb.h> + +/* + * This is the common authsvc_context shared by all back-ends. + * Note that ctx_mech_oid is really SPNEGO_MECH_OID, and the + * ctx_itoken, ctx_otoken members are SPNEGO_TOKEN_HANDLE, + * but this is using the underlying types so as to avoid + * dragging in spnego.h here. + */ +typedef struct authsvc_context { + int ctx_socket; + int ctx_mech_oid; + int (*ctx_mh_work)(struct authsvc_context *); + void (*ctx_mh_fini)(struct authsvc_context *); + int ctx_itoktype; + int ctx_negresult; + + /* (in,out) SPNEGO token handles */ + void *ctx_itoken; + void *ctx_otoken; + + /* (in,out) raw (buf,len,type) */ + void *ctx_irawbuf; + uint_t ctx_irawlen; + int ctx_irawtype; + void *ctx_orawbuf; + uint_t ctx_orawlen; + int ctx_orawtype; + + /* (in,out) body (buf,len) */ + void *ctx_ibodybuf; + uint_t ctx_ibodylen; + void *ctx_obodybuf; + uint_t ctx_obodylen; + + /* who is the client */ + smb_lsa_clinfo_t ctx_clinfo; + + /* final authentication token */ + struct smb_token *ctx_token; + + /* private data for the back-end */ + void *ctx_backend; +} authsvc_context_t; + +int smbd_krb5ssp_init(authsvc_context_t *); +int smbd_krb5ssp_work(authsvc_context_t *); +void smbd_krb5ssp_fini(authsvc_context_t *); + +int smbd_ntlmssp_init(authsvc_context_t *); +int smbd_ntlmssp_work(authsvc_context_t *); +void smbd_ntlmssp_fini(authsvc_context_t *); + +/* Exposed for unit tests. */ +int smbd_authsvc_dispatch(authsvc_context_t *); +authsvc_context_t *smbd_authctx_create(void); +void smbd_authctx_destroy(authsvc_context_t *); + +#endif /* _SMBD_AUTHSVC_H */ diff --git a/usr/src/cmd/smbsrv/smbd/smbd_doorsvc.c b/usr/src/cmd/smbsrv/smbd/smbd_doorsvc.c index e21a9beaf4..6de84cadbe 100644 --- a/usr/src/cmd/smbsrv/smbd/smbd_doorsvc.c +++ b/usr/src/cmd/smbsrv/smbd/smbd_doorsvc.c @@ -38,6 +38,7 @@ #include <fcntl.h> #include <pthread.h> #include <strings.h> +#include <note.h> #include <smbsrv/smb_door.h> #include <smbsrv/smb_xdr.h> #include <smbsrv/smb_token.h> @@ -74,6 +75,7 @@ static int smbd_dop_quota_set(smbd_arg_t *); static int smbd_dop_dfs_get_referrals(smbd_arg_t *); static int smbd_dop_shr_hostaccess(smbd_arg_t *); static int smbd_dop_shr_exec(smbd_arg_t *); +static int smbd_dop_notify_dc_changed(smbd_arg_t *); typedef int (*smbd_dop_t)(smbd_arg_t *); @@ -100,7 +102,8 @@ smbd_doorop_t smbd_doorops[] = { { SMB_DR_QUOTA_SET, smbd_dop_quota_set }, { SMB_DR_DFS_GET_REFERRALS, smbd_dop_dfs_get_referrals }, { SMB_DR_SHR_HOSTACCESS, smbd_dop_shr_hostaccess }, - { SMB_DR_SHR_EXEC, smbd_dop_shr_exec } + { SMB_DR_SHR_EXEC, smbd_dop_shr_exec }, + { SMB_DR_NOTIFY_DC_CHANGED, smbd_dop_notify_dc_changed } }; static int smbd_ndoorop = (sizeof (smbd_doorops) / sizeof (smbd_doorops[0])); @@ -572,27 +575,10 @@ smbd_dop_user_auth_logoff(smbd_arg_t *arg) static int smbd_dop_user_auth_logon(smbd_arg_t *arg) { - smb_logon_t *user_info; - smb_token_t *token; + _NOTE(ARGUNUSED(arg)) - user_info = smb_logon_decode((uint8_t *)arg->data, - arg->datalen); - if (user_info == NULL) - return (SMB_DOP_DECODE_ERROR); - - token = smbd_user_auth_logon(user_info); - - smb_logon_free(user_info); - - if (token == NULL) - return (SMB_DOP_EMPTYBUF); - - arg->rbuf = (char *)smb_token_encode(token, &arg->rsize); - smb_token_destroy(token); - - if (arg->rbuf == NULL) - return (SMB_DOP_ENCODE_ERROR); - return (SMB_DOP_SUCCESS); + /* No longer used */ + return (SMB_DOP_EMPTYBUF); } static int @@ -684,17 +670,18 @@ static int smbd_dop_join(smbd_arg_t *arg) { smb_joininfo_t jdi; - uint32_t status; + smb_joinres_t jdres; bzero(&jdi, sizeof (smb_joininfo_t)); + bzero(&jdres, sizeof (smb_joinres_t)); if (smb_common_decode(arg->data, arg->datalen, smb_joininfo_xdr, &jdi) != 0) return (SMB_DOP_DECODE_ERROR); - status = smbd_join(&jdi); + smbd_join(&jdi, &jdres); - arg->rbuf = smb_common_encode(&status, xdr_uint32_t, &arg->rsize); + arg->rbuf = smb_common_encode(&jdres, smb_joinres_xdr, &arg->rsize); if (arg->rbuf == NULL) return (SMB_DOP_ENCODE_ERROR); @@ -709,7 +696,7 @@ smbd_dop_get_dcinfo(smbd_arg_t *arg) if (!smb_domain_getinfo(&dxi)) return (SMB_DOP_EMPTYBUF); - arg->rbuf = smb_string_encode(dxi.d_dc, &arg->rsize); + arg->rbuf = smb_string_encode(dxi.d_dci.dc_name, &arg->rsize); if (arg->rbuf == NULL) return (SMB_DOP_ENCODE_ERROR); @@ -844,7 +831,7 @@ smbd_dop_ads_find_host(smbd_arg_t *arg) if (smb_string_decode(&fqdn, arg->data, arg->datalen) != 0) return (SMB_DOP_DECODE_ERROR); - if ((hinfo = smb_ads_find_host(fqdn.buf, NULL)) != NULL) + if ((hinfo = smb_ads_find_host(fqdn.buf)) != NULL) hostname = hinfo->name; xdr_free(smb_string_xdr, (char *)&fqdn); @@ -997,3 +984,13 @@ smbd_dop_shr_exec(smbd_arg_t *arg) return (SMB_DOP_ENCODE_ERROR); return (SMB_DOP_SUCCESS); } + +/* ARGSUSED */ +static int +smbd_dop_notify_dc_changed(smbd_arg_t *arg) +{ + + smbd_dc_monitor_refresh(); + + return (SMB_DOP_SUCCESS); +} diff --git a/usr/src/cmd/smbsrv/smbd/smbd_join.c b/usr/src/cmd/smbsrv/smbd/smbd_join.c index bca6a1c9e9..cfff2b11f6 100644 --- a/usr/src/cmd/smbsrv/smbd/smbd_join.c +++ b/usr/src/cmd/smbsrv/smbd/smbd_join.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ #include <syslog.h> @@ -30,7 +30,9 @@ #include <unistd.h> #include <string.h> #include <strings.h> -#include <sys/errno.h> +#include <errno.h> +#include <netinet/in.h> +#include <netinet/tcp.h> #include <smbsrv/libsmb.h> #include <smbsrv/libsmbns.h> @@ -49,9 +51,10 @@ static cond_t smbd_dc_cv; static void *smbd_dc_monitor(void *); static void smbd_dc_update(void); +static int smbd_dc_check(smb_domainex_t *); /* Todo: static boolean_t smbd_set_netlogon_cred(void); */ -static uint32_t smbd_join_workgroup(smb_joininfo_t *); -static uint32_t smbd_join_domain(smb_joininfo_t *); +static void smbd_join_workgroup(smb_joininfo_t *, smb_joinres_t *); +static void smbd_join_domain(smb_joininfo_t *, smb_joinres_t *); /* * Launch the DC discovery and monitor thread. @@ -78,26 +81,23 @@ smbd_dc_monitor_init(void) return (rc); } +/* + * Refresh the DC monitor. Called from SMF refresh and when idmap + * finds a different DC from what we were using previously. + * Update our domain (and current DC) information. + */ void smbd_dc_monitor_refresh(void) { - char site[MAXHOSTNAMELEN]; - smb_inaddr_t pdc; - site[0] = '\0'; - bzero(&pdc, sizeof (smb_inaddr_t)); - (void) smb_config_getstr(SMB_CI_ADS_SITE, site, MAXHOSTNAMELEN); - (void) smb_config_getip(SMB_CI_DOMAIN_SRV, &pdc); + syslog(LOG_INFO, "smbd_dc_monitor_refresh"); + + smb_ddiscover_refresh(); (void) mutex_lock(&smbd_dc_mutex); - if ((bcmp(&smbd.s_pdc, &pdc, sizeof (smb_inaddr_t)) != 0) || - (smb_strcasecmp(smbd.s_site, site, 0) != 0)) { - bcopy(&pdc, &smbd.s_pdc, sizeof (smb_inaddr_t)); - (void) strlcpy(smbd.s_site, site, MAXHOSTNAMELEN); - smbd.s_pdc_changed = B_TRUE; - (void) cond_signal(&smbd_dc_cv); - } + smbd.s_pdc_changed = B_TRUE; + (void) cond_signal(&smbd_dc_cv); (void) mutex_unlock(&smbd_dc_mutex); } @@ -106,15 +106,19 @@ smbd_dc_monitor_refresh(void) static void * smbd_dc_monitor(void *arg) { - boolean_t ds_not_responding = B_FALSE; - boolean_t ds_cfg_changed = B_FALSE; + smb_domainex_t di; + boolean_t ds_not_responding; + boolean_t ds_cfg_changed; timestruc_t delay; int i; - smbd_dc_update(); + /* Wait for smb_dclocator_init() to complete. */ smbd_online_wait("smbd_dc_monitor"); + smbd_dc_update(); while (smbd_online()) { + ds_not_responding = B_FALSE; + ds_cfg_changed = B_FALSE; delay.tv_sec = SMBD_DC_MONITOR_INTERVAL; delay.tv_nsec = 0; @@ -124,12 +128,28 @@ smbd_dc_monitor(void *arg) if (smbd.s_pdc_changed) { smbd.s_pdc_changed = B_FALSE; ds_cfg_changed = B_TRUE; + /* NB: smb_ddiscover_refresh was called. */ } (void) mutex_unlock(&smbd_dc_mutex); + if (ds_cfg_changed) { + syslog(LOG_DEBUG, "smbd_dc_monitor: config changed"); + goto rediscover; + } + + if (!smb_domain_getinfo(&di)) { + syslog(LOG_DEBUG, "smbd_dc_monitor: no domain info"); + goto rediscover; + } + + if (di.d_dci.dc_name[0] == '\0') { + syslog(LOG_DEBUG, "smbd_dc_monitor: no DC name"); + goto rediscover; + } + for (i = 0; i < SMBD_DC_MONITOR_ATTEMPTS; ++i) { - if (dssetup_check_service() == 0) { + if (smbd_dc_check(&di) == 0) { ds_not_responding = B_FALSE; break; } @@ -138,13 +158,21 @@ smbd_dc_monitor(void *arg) (void) sleep(SMBD_DC_MONITOR_RETRY_INTERVAL); } - if (ds_not_responding) + if (ds_not_responding) { syslog(LOG_NOTICE, - "smbd_dc_monitor: domain service not responding"); + "smbd_dc_monitor: DC not responding: %s", + di.d_dci.dc_name); + smb_ddiscover_bad_dc(di.d_dci.dc_name); + } if (ds_not_responding || ds_cfg_changed) { - ds_cfg_changed = B_FALSE; - smb_ads_refresh(); + rediscover: + /* + * An smb_ads_refresh will be done by the + * smb_ddiscover_service when necessary. + * Note: smbd_dc_monitor_refresh was already + * called if appropriate. + */ smbd_dc_update(); } } @@ -154,6 +182,56 @@ smbd_dc_monitor(void *arg) } /* + * Simply attempt a connection to the DC. + */ +static int +smbd_dc_check(smb_domainex_t *di) +{ + struct sockaddr sa; + int salen = 0; + int sock = -1; + int tmo = 5 * 1000; /* 5 sec. */ + int rc; + + bzero(&sa, sizeof (sa)); + switch (di->d_dci.dc_addr.a_family) { + case AF_INET: { + struct sockaddr_in *sin = (void *)&sa; + sin->sin_family = AF_INET; + sin->sin_port = htons(IPPORT_SMB); + sin->sin_addr.s_addr = di->d_dci.dc_addr.a_ipv4; + salen = sizeof (*sin); + break; + } + case AF_INET6: { + struct sockaddr_in6 *sin6 = (void *)&sa; + sin6->sin6_family = AF_INET6; + sin6->sin6_port = htons(IPPORT_SMB); + (void) memcpy(&sin6->sin6_addr, + &di->d_dci.dc_addr.a_ipv6, + sizeof (in6_addr_t)); + salen = sizeof (*sin6); + break; + } + default: + return (-1); + } + + sock = socket(di->d_dci.dc_addr.a_family, SOCK_STREAM, 0); + if (sock < 0) + return (errno); + (void) setsockopt(sock, IPPROTO_TCP, + TCP_CONN_ABORT_THRESHOLD, &tmo, sizeof (tmo)); + + rc = connect(sock, &sa, salen); + if (rc < 0) + rc = errno; + + (void) close(sock); + return (rc); +} + +/* * Locate a domain controller in the current resource domain and Update * the Netlogon credential chain. * @@ -167,12 +245,22 @@ smbd_dc_update(void) smb_domain_t *di; DWORD status; - if (smb_getfqdomainname(domain, MAXHOSTNAMELEN) != 0) { - (void) smb_getdomainname(domain, MAXHOSTNAMELEN); - (void) smb_strupr(domain); + /* + * Don't want this active until we're a domain member. + */ + if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN) + return; + + if (smb_getfqdomainname(domain, MAXHOSTNAMELEN) != 0) + return; + + if (domain[0] == '\0') { + syslog(LOG_NOTICE, + "smbd_dc_update: no domain name set"); + return; } - if (!smb_locate_dc(domain, "", &info)) { + if (!smb_locate_dc(domain, &info)) { syslog(LOG_NOTICE, "smbd_dc_update: %s: locate failed", domain); return; @@ -180,24 +268,15 @@ smbd_dc_update(void) di = &info.d_primary; syslog(LOG_INFO, - "smbd_dc_update: %s: located %s", domain, info.d_dc); + "smbd_dc_update: %s: located %s", domain, info.d_dci.dc_name); - status = mlsvc_netlogon(info.d_dc, di->di_nbname); + status = mlsvc_netlogon(info.d_dci.dc_name, di->di_nbname); if (status != NT_STATUS_SUCCESS) { syslog(LOG_NOTICE, "failed to establish NETLOGON credential chain"); - - /* - * Restart required because the domain changed - * or the credential chain setup failed. - */ - syslog(LOG_NOTICE, - "smbd_dc_update: smb/server restart required"); - - if (smb_smf_restart_service() != 0) - syslog(LOG_ERR, - "restart failed: run 'svcs -xv smb/server'" - " for more information"); + syslog(LOG_NOTICE, " with server %s for domain %s (%s)", + info.d_dci.dc_name, domain, + xlate_nt_status(status)); } } @@ -209,95 +288,49 @@ smbd_dc_update(void) * If the security mode or domain name is being changed, * the caller must restart the service. */ -uint32_t -smbd_join(smb_joininfo_t *info) +void +smbd_join(smb_joininfo_t *info, smb_joinres_t *res) { - uint32_t status; - dssetup_clear_domain_info(); if (info->mode == SMB_SECMODE_WORKGRP) - status = smbd_join_workgroup(info); + smbd_join_workgroup(info, res); else - status = smbd_join_domain(info); - - return (status); + smbd_join_domain(info, res); } -static uint32_t -smbd_join_workgroup(smb_joininfo_t *info) +static void +smbd_join_workgroup(smb_joininfo_t *info, smb_joinres_t *res) { char nb_domain[SMB_PI_MAX_DOMAIN]; + syslog(LOG_DEBUG, "smbd: join workgroup: %s", info->domain_name); + (void) smb_config_getstr(SMB_CI_DOMAIN_NAME, nb_domain, sizeof (nb_domain)); smbd_set_secmode(SMB_SECMODE_WORKGRP); smb_config_setdomaininfo(info->domain_name, "", "", "", ""); + (void) smb_config_set_idmap_domain(""); + (void) smb_config_refresh_idmap(); if (strcasecmp(nb_domain, info->domain_name)) smb_browser_reconfig(); - return (NT_STATUS_SUCCESS); + res->status = NT_STATUS_SUCCESS; } -static uint32_t -smbd_join_domain(smb_joininfo_t *info) +static void +smbd_join_domain(smb_joininfo_t *info, smb_joinres_t *res) { - static unsigned char zero_hash[SMBAUTH_HASH_SZ]; - smb_domainex_t dxi; - smb_domain_t *di; - uint32_t status; - /* - * Ensure that any previous membership of this domain has - * been cleared from the environment before we start. This - * will ensure that we don't attempt a NETLOGON_SAMLOGON - * when attempting to find the PDC. - */ - (void) smb_config_setbool(SMB_CI_DOMAIN_MEMB, B_FALSE); - - /* Clear DNS local (ADS) lookup cache too. */ - smb_ads_refresh(); - - /* - * Use a NULL session while searching for a DC, and - * while getting information about the domain. - */ - smb_ipc_set(MLSVC_ANON_USER, zero_hash); - - if (!smb_locate_dc(info->domain_name, "", &dxi)) { - syslog(LOG_ERR, "smbd: failed locating " - "domain controller for %s", - info->domain_name); - status = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; - goto errout; - } + syslog(LOG_DEBUG, "smbd: join domain: %s", info->domain_name); /* info->domain_name could either be NetBIOS domain name or FQDN */ - status = mlsvc_join(&dxi, info->domain_username, info->domain_passwd); - if (status != NT_STATUS_SUCCESS) { + mlsvc_join(info, res); + if (res->status == 0) { + smbd_set_secmode(SMB_SECMODE_DOMAIN); + } else { syslog(LOG_ERR, "smbd: failed joining %s (%s)", - info->domain_name, xlate_nt_status(status)); - goto errout; + info->domain_name, xlate_nt_status(res->status)); } - - /* - * Success! - * - * Strange, mlsvc_join does some of the work to - * save the config, then the rest happens here. - * Todo: Do the config update all in one place. - */ - di = &dxi.d_primary; - smbd_set_secmode(SMB_SECMODE_DOMAIN); - smb_config_setdomaininfo(di->di_nbname, di->di_fqname, - di->di_sid, - di->di_u.di_dns.ddi_forest, - di->di_u.di_dns.ddi_guid); - smb_ipc_commit(); - return (status); - -errout: - smb_ipc_rollback(); - return (status); } diff --git a/usr/src/cmd/smbsrv/smbd/smbd_krb5lookup.c b/usr/src/cmd/smbsrv/smbd/smbd_krb5lookup.c new file mode 100644 index 0000000000..af98b15b1b --- /dev/null +++ b/usr/src/cmd/smbsrv/smbd/smbd_krb5lookup.c @@ -0,0 +1,158 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <syslog.h> + +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <sys/note.h> + +#include <smbsrv/libsmbns.h> + +#include "smbd.h" +#include "locate_plugin.h" + +/* osconf.h - sigh */ +#define KRB5_DEFAULT_PORT 88 +#define DEFAULT_KADM5_PORT 749 +#define DEFAULT_KPASSWD_PORT 464 + +/* + * This is an "override plugin" used by libkrb5. See: + * lib/gss_mechs/mech_krb5/krb5/os/locate_kdc.c + * + * The interface is based on: + * http://web.mit.edu/~kerberos/krb5-1.12/doc/plugindev/locate.html + */ + +/* + * Called by krb5int_locate_server / override_locate_server + */ + +krb5_error_code +_krb5_override_service_locator( + void *arg0, + enum locate_service_type svc, + const char *realm, + int socktype, + int family, + int (*cbfunc)(void *, int, struct sockaddr *), + void *cbdata) +{ + _NOTE(ARGUNUSED(arg0)) + smb_domainex_t dxi; + int rc = KRB5_PLUGIN_NO_HANDLE; + short port; + + /* + * Is this a service we want to override? + */ + switch (svc) { + case locate_service_kdc: + case locate_service_master_kdc: + port = htons(KRB5_DEFAULT_PORT); + break; + case locate_service_kadmin: + port = htons(DEFAULT_KADM5_PORT); + break; + case locate_service_kpasswd: + port = htons(DEFAULT_KPASSWD_PORT); + break; + case locate_service_krb524: + default: + return (rc); + } + + /* + * What's my domain? Note: have to get this in a way + * that works while join domain is underway. + */ + if (!smb_domain_getinfo(&dxi)) { + smbd_report("_krb5_override_service_locator " + "failed getting domain info"); + return (KRB5_ERR_HOST_REALM_UNKNOWN); + } + + /* + * Is this a realm we want to override? + */ + if (0 != strcasecmp(realm, dxi.d_primary.di_fqname)) { + syslog(LOG_DEBUG, "_krb5_override_service_locator, " + "realm=%s, fqdn=%s", realm, dxi.d_primary.di_fqname); + return (rc); + } + + /* + * Yes, this is our domain. Have a DC? + */ + if (dxi.d_dci.dc_name[0] == '\0' || + dxi.d_dci.dc_addr.a_family == 0) + return (KRB5_REALM_CANT_RESOLVE); + + switch (family) { + case AF_UNSPEC: + break; /* OK */ + case AF_INET: + case AF_INET6: + if (family == dxi.d_dci.dc_addr.a_family) + break; /* OK */ + /* else fallthrough */ + default: + return (KRB5_ERR_NO_SERVICE); + } + + /* + * Provide the service address we have. + */ + switch (dxi.d_dci.dc_addr.a_family) { + case AF_INET: { + struct sockaddr_in sin; + (void) memset(&sin, 0, sizeof (sin)); + sin.sin_family = AF_INET; + sin.sin_port = port; + (void) memcpy(&sin.sin_addr, &dxi.d_dci.dc_addr.a_ipv4, + sizeof (sin.sin_addr)); + rc = cbfunc(cbdata, socktype, (struct sockaddr *)&sin); + /* rc from cbfunc is special. */ + if (rc) + rc = ENOMEM; + break; + } + case AF_INET6: { + struct sockaddr_in6 sin6; + (void) memset(&sin6, 0, sizeof (sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_port = port; + (void) memcpy(&sin6.sin6_addr, &dxi.d_dci.dc_addr.a_ipv6, + sizeof (sin6.sin6_addr)); + rc = cbfunc(cbdata, socktype, (struct sockaddr *)&sin6); + /* rc from cbfunc is special. */ + if (rc) + rc = ENOMEM; + break; + } + default: + rc = KRB5_ERR_NO_SERVICE; + break; + } + + return (rc); +} diff --git a/usr/src/cmd/smbsrv/smbd/smbd_krb5ssp.c b/usr/src/cmd/smbsrv/smbd/smbd_krb5ssp.c new file mode 100644 index 0000000000..ff0dff251b --- /dev/null +++ b/usr/src/cmd/smbsrv/smbd/smbd_krb5ssp.c @@ -0,0 +1,350 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + */ + +/* + * SPNEGO back-end for Kerberos. See [MS-KILE] + */ + +#include <sys/types.h> +#include <gssapi/gssapi_ext.h> +#include <gssapi/gssapi_krb5.h> +#include <krb5.h> +#include "smbd.h" +#include "smbd_authsvc.h" + +/* From krb5/krb/pac.c (should have been exported) */ +#define PAC_LOGON_INFO 1 + +typedef struct krb5ssp_backend { + gss_ctx_id_t be_gssctx; + char *be_username; + gss_buffer_desc be_authz_pac; + krb5_context be_kctx; + krb5_pac be_kpac; + krb5_data be_pac; +} krb5ssp_backend_t; + +static uint32_t +get_authz_data_pac( + gss_ctx_id_t context_handle, + gss_buffer_t ad_data); + +static uint32_t +get_ssnkey(authsvc_context_t *ctx); + + +/* + * Initialize this context for Kerberos, if possible. + * + * Should not get here unless libsmb smb_config_get_negtok + * includes the Kerberos5 Mech OIDs in our spnego hint. + * + * Todo: allocate ctx->ctx_backend + * See: krb5_gss_accept_sec_context() + */ +int +smbd_krb5ssp_init(authsvc_context_t *ctx) +{ + krb5ssp_backend_t *be; + + be = malloc(sizeof (*be)); + if (be == 0) + return (NT_STATUS_NO_MEMORY); + bzero(be, sizeof (*be)); + be->be_gssctx = GSS_C_NO_CONTEXT; + ctx->ctx_backend = be; + + return (0); +} + +/* + * Todo: free ctx->ctx_backend + */ +void +smbd_krb5ssp_fini(authsvc_context_t *ctx) +{ + krb5ssp_backend_t *be = ctx->ctx_backend; + uint32_t minor; + + if (be == NULL) + return; + + if (be->be_kctx != NULL) { + krb5_free_data_contents(be->be_kctx, &be->be_pac); + + if (be->be_kpac != NULL) + krb5_pac_free(be->be_kctx, be->be_kpac); + + krb5_free_context(be->be_kctx); + } + + (void) gss_release_buffer(NULL, &be->be_authz_pac); + + free(be->be_username); + + if (be->be_gssctx != GSS_C_NO_CONTEXT) { + (void) gss_delete_sec_context(&minor, &be->be_gssctx, + GSS_C_NO_BUFFER); + } + + free(be); +} + +/* + * Handle a Kerberos auth message. + * + * State across messages is in ctx->ctx_backend + */ +int +smbd_krb5ssp_work(authsvc_context_t *ctx) +{ + gss_buffer_desc intok, outtok; + gss_buffer_desc namebuf; + krb5ssp_backend_t *be = ctx->ctx_backend; + gss_name_t gname = NULL; + OM_uint32 major, minor, ret_flags; + gss_OID name_type = GSS_C_NULL_OID; + gss_OID mech_type = GSS_C_NULL_OID; + krb5_error_code kerr; + uint32_t status; + + intok.length = ctx->ctx_ibodylen; + intok.value = ctx->ctx_ibodybuf; + bzero(&outtok, sizeof (gss_buffer_desc)); + bzero(&namebuf, sizeof (gss_buffer_desc)); + + /* Do this early, for error message support. */ + kerr = krb5_init_context(&be->be_kctx); + if (kerr != 0) { + smbd_report("krb5ssp, krb5_init_ctx: %s", + krb5_get_error_message(be->be_kctx, kerr)); + return (NT_STATUS_INTERNAL_ERROR); + } + + major = gss_accept_sec_context(&minor, &be->be_gssctx, + GSS_C_NO_CREDENTIAL, &intok, + GSS_C_NO_CHANNEL_BINDINGS, &gname, &mech_type, &outtok, + &ret_flags, NULL, NULL); + + if (outtok.length == 0) + ctx->ctx_obodylen = 0; + else if (outtok.length <= ctx->ctx_obodylen) { + ctx->ctx_obodylen = outtok.length; + (void) memcpy(ctx->ctx_obodybuf, outtok.value, outtok.length); + free(outtok.value); + outtok.value = NULL; + } else { + free(ctx->ctx_obodybuf); + ctx->ctx_obodybuf = outtok.value; + ctx->ctx_obodylen = outtok.length; + outtok.value = NULL; + } + + if (GSS_ERROR(major)) { + smbd_report("krb5ssp: gss_accept_sec_context, " + "mech=0x%x, major=0x%x, minor=0x%x", + (int)mech_type, major, minor); + smbd_report(" krb5: %s", + krb5_get_error_message(be->be_kctx, minor)); + return (NT_STATUS_WRONG_PASSWORD); + } + + switch (major) { + case GSS_S_COMPLETE: + break; + case GSS_S_CONTINUE_NEEDED: + if (outtok.length > 0) { + ctx->ctx_orawtype = LSA_MTYPE_ES_CONT; + /* becomes NT_STATUS_MORE_PROCESSING_REQUIRED */ + return (0); + } + return (NT_STATUS_WRONG_PASSWORD); + default: + return (NT_STATUS_WRONG_PASSWORD); + } + + /* + * OK, we got GSS_S_COMPLETE. Get the name so we can use it + * in log messages if we get failures decoding the PAC etc. + * Then get the PAC, decode it, build the logon token. + */ + + if (gname != NULL && GSS_S_COMPLETE == + gss_display_name(&minor, gname, &namebuf, &name_type)) { + /* Save the user name. */ + be->be_username = strdup(namebuf.value); + (void) gss_release_buffer(&minor, &namebuf); + (void) gss_release_name(&minor, &gname); + if (be->be_username == NULL) { + return (NT_STATUS_NO_MEMORY); + } + } + + /* + * Extract the KRB5_AUTHDATA_WIN2K_PAC data. + */ + status = get_authz_data_pac(be->be_gssctx, + &be->be_authz_pac); + if (status) + return (status); + + kerr = krb5_pac_parse(be->be_kctx, be->be_authz_pac.value, + be->be_authz_pac.length, &be->be_kpac); + if (kerr) { + smbd_report("krb5ssp, krb5_pac_parse: %s", + krb5_get_error_message(be->be_kctx, kerr)); + return (NT_STATUS_UNSUCCESSFUL); + } + + kerr = krb5_pac_get_buffer(be->be_kctx, be->be_kpac, + PAC_LOGON_INFO, &be->be_pac); + if (kerr) { + smbd_report("krb5ssp, krb5_pac_get_buffer: %s", + krb5_get_error_message(be->be_kctx, kerr)); + return (NT_STATUS_UNSUCCESSFUL); + } + + ctx->ctx_token = calloc(1, sizeof (smb_token_t)); + if (ctx->ctx_token == NULL) + return (NT_STATUS_NO_MEMORY); + + status = smb_decode_krb5_pac(ctx->ctx_token, be->be_pac.data, + be->be_pac.length); + if (status) + return (status); + + status = get_ssnkey(ctx); + if (status) + return (status); + + if (!smb_token_setup_common(ctx->ctx_token)) + return (NT_STATUS_UNSUCCESSFUL); + + /* Success! */ + ctx->ctx_orawtype = LSA_MTYPE_ES_DONE; + + return (0); +} + +/* + * See: GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID + * and: KRB5_AUTHDATA_WIN2K_PAC + */ +static const gss_OID_desc +oid_ex_authz_data_pac = { + 13, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0a\x81\x00" }; + +/* + * See: krb5_gss_inquire_sec_context_by_oid() + * and krb5_gss_inquire_sec_context_by_oid_ops[], + * gss_krb5int_extract_authz_data_from_sec_context() + */ +static uint32_t +get_authz_data_pac( + gss_ctx_id_t context_handle, + gss_buffer_t ad_data) +{ + gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET; + OM_uint32 major, minor; + uint32_t status = NT_STATUS_UNSUCCESSFUL; + + if (ad_data == NULL) + goto out; + + major = gss_inquire_sec_context_by_oid( + &minor, + context_handle, + (gss_OID)&oid_ex_authz_data_pac, + &data_set); + if (GSS_ERROR(major)) { + smbd_report("krb5ssp, gss_inquire...PAC, " + "major=0x%x, minor=0x%x", major, minor); + goto out; + } + + if ((data_set == GSS_C_NO_BUFFER_SET) || (data_set->count == 0)) { + goto out; + } + + /* Only need the first element? */ + ad_data->length = data_set->elements[0].length; + ad_data->value = malloc(ad_data->length); + if (ad_data->value == NULL) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + bcopy(data_set->elements[0].value, ad_data->value, ad_data->length); + status = 0; + +out: + (void) gss_release_buffer_set(&minor, &data_set); + + return (status); +} + +/* + * Get the session key, and save it in the token. + * + * See: krb5_gss_inquire_sec_context_by_oid(), + * krb5_gss_inquire_sec_context_by_oid_ops[], and + * gss_krb5int_inq_session_key + */ +static uint32_t +get_ssnkey(authsvc_context_t *ctx) +{ + krb5ssp_backend_t *be = ctx->ctx_backend; + gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET; + OM_uint32 major, minor; + size_t keylen; + uint32_t status = NT_STATUS_UNSUCCESSFUL; + + major = gss_inquire_sec_context_by_oid(&minor, + be->be_gssctx, GSS_C_INQ_SSPI_SESSION_KEY, &data_set); + if (GSS_ERROR(major)) { + smbd_report("krb5ssp, failed to get session key, " + "major=0x%x, minor=0x%x", major, minor); + goto out; + } + + /* + * The key is in the first element + */ + if (data_set == GSS_C_NO_BUFFER_SET || + data_set->count == 0 || + data_set->elements[0].length == 0 || + data_set->elements[0].value == NULL) { + smbd_report("krb5ssp: Session key is missing"); + goto out; + } + if ((keylen = data_set->elements[0].length) < SMBAUTH_HASH_SZ) { + smbd_report("krb5ssp: Session key too short (%d)", + data_set->elements[0].length); + goto out; + } + + ctx->ctx_token->tkn_ssnkey.val = malloc(keylen); + if (ctx->ctx_token->tkn_ssnkey.val == NULL) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + ctx->ctx_token->tkn_ssnkey.len = keylen; + bcopy(data_set->elements[0].value, + ctx->ctx_token->tkn_ssnkey.val, keylen); + status = 0; + +out: + (void) gss_release_buffer_set(&minor, &data_set); + return (status); +} diff --git a/usr/src/cmd/smbsrv/smbd/smbd_logon.c b/usr/src/cmd/smbsrv/smbd/smbd_logon.c index ad19f46655..fa7dae801b 100644 --- a/usr/src/cmd/smbsrv/smbd/smbd_logon.c +++ b/usr/src/cmd/smbsrv/smbd/smbd_logon.c @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #include <sys/types.h> @@ -84,6 +85,7 @@ smbd_user_auth_logon(smb_logon_t *user_info) smb_audit_t *entry; adt_session_data_t *ah; adt_event_data_t *event; + smb_logon_t tmp_user; au_tid_addr_t termid; char sidbuf[SMB_SID_STRSZ]; char *username; @@ -94,12 +96,27 @@ smbd_user_auth_logon(smb_logon_t *user_info) int status; int retval; - if ((token = smb_logon(user_info)) == NULL) { + if (user_info->lg_username == NULL || + user_info->lg_domain == NULL || + user_info->lg_workstation == NULL) { + return (NULL); + } + + tmp_user = *user_info; + if (tmp_user.lg_username[0] == '\0') { + tmp_user.lg_flags |= SMB_ATF_ANON; + tmp_user.lg_e_username = "anonymous"; + } else { + tmp_user.lg_e_username = tmp_user.lg_username; + } + tmp_user.lg_e_domain = tmp_user.lg_domain; + + if ((token = smb_logon(&tmp_user)) == NULL) { uid = ADT_NO_ATTRIB; gid = ADT_NO_ATTRIB; sid = NT_NULL_SIDSTR; - username = user_info->lg_e_username; - domain = user_info->lg_e_domain; + username = tmp_user.lg_e_username; + domain = tmp_user.lg_e_domain; status = ADT_FAILURE; retval = ADT_FAIL_VALUE_AUTH; } else { diff --git a/usr/src/cmd/smbsrv/smbd/smbd_main.c b/usr/src/cmd/smbsrv/smbd/smbd_main.c index 59b11eb702..b1c9dbc770 100644 --- a/usr/src/cmd/smbsrv/smbd/smbd_main.c +++ b/usr/src/cmd/smbsrv/smbd/smbd_main.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #include <sys/types.h> @@ -433,6 +433,7 @@ smbd_service_init(void) { SMB_SYSTEM32, 0755 }, { SMB_VSS, 0755 }, { SMB_PIPE_DIR, 0755 }, + { "/var/smb/lipc", 0755 }, }; int rc, i; @@ -457,6 +458,12 @@ smbd_service_init(void) } } + /* + * This environment variable tells mech_krb5 to give us + * MS-compatible behavior. + */ + (void) putenv("MS_INTEROP=1"); + if ((rc = smb_ccache_init(SMB_VARRUN_DIR, SMB_CCACHE_FILE)) != 0) { if (rc == -1) smbd_report("mkdir %s: %s", SMB_VARRUN_DIR, @@ -503,6 +510,11 @@ smbd_service_init(void) return (-1); } + if (smbd_authsvc_start() != 0) { + smbd_report("authsvc 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)); @@ -555,6 +567,7 @@ smbd_service_fini(void) smb_lgrp_stop(); smbd_pipesvc_stop(); smbd_door_stop(); + smbd_authsvc_stop(); smbd_spool_stop(); smbd_kernel_unbind(); smbd_share_stop(); @@ -708,6 +721,7 @@ smbd_kernel_bind(void) if (smbd.s_kbound) { smb_load_kconfig(&cfg); + smbd_get_authconf(&cfg); rc = smb_kmod_setcfg(&cfg); if (rc < 0) smbd_report("kernel configuration update failed: %s", @@ -738,6 +752,7 @@ smbd_kernel_start(void) int rc; smb_load_kconfig(&cfg); + smbd_get_authconf(&cfg); rc = smb_kmod_setcfg(&cfg); if (rc != 0) { smbd_report("kernel config ioctl error: %s", strerror(rc)); diff --git a/usr/src/cmd/smbsrv/smbd/smbd_ntlmssp.c b/usr/src/cmd/smbsrv/smbd/smbd_ntlmssp.c new file mode 100644 index 0000000000..8027e3272b --- /dev/null +++ b/usr/src/cmd/smbsrv/smbd/smbd_ntlmssp.c @@ -0,0 +1,595 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + */ + +/* + * SPNEGO back-end for NTLMSSP. See [MS-NLMP] + */ + +#include <sys/types.h> +#include <sys/byteorder.h> +#include <strings.h> +#include "smbd.h" +#include "smbd_authsvc.h" +#include "netsmb/ntlmssp.h" +#include <assert.h> + +/* A shorter alias for a crazy long name from [MS-NLMP] */ +#define NTLMSSP_NEGOTIATE_NTLM2 \ + NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY + +/* Need this in a header somewhere */ +#ifdef _LITTLE_ENDIAN +/* little-endian values on little-endian */ +#define htolel(x) ((uint32_t)(x)) +#define letohl(x) ((uint32_t)(x)) +#else /* (BYTE_ORDER == LITTLE_ENDIAN) */ +/* little-endian values on big-endian (swap) */ +#define letohl(x) BSWAP_32(x) +#define htolel(x) BSWAP_32(x) +#endif /* (BYTE_ORDER == LITTLE_ENDIAN) */ + +typedef struct ntlmssp_backend { + uint32_t expect_type; + uint32_t clnt_flags; + uint32_t srv_flags; + char srv_challenge[8]; +} ntlmssp_backend_t; + +struct genhdr { + char h_id[8]; /* "NTLMSSP" */ + uint32_t h_type; +}; + +struct sec_buf { + uint16_t sb_length; + uint16_t sb_maxlen; + uint32_t sb_offset; +}; + +struct nego_hdr { + char h_id[8]; + uint32_t h_type; + uint32_t h_flags; + /* workstation domain, name (place holders) */ + uint16_t ws_dom[4]; + uint16_t ws_name[4]; +}; + +struct auth_hdr { + char h_id[8]; + uint32_t h_type; + struct sec_buf h_lm_resp; + struct sec_buf h_nt_resp; + struct sec_buf h_domain; + struct sec_buf h_user; + struct sec_buf h_wksta; + struct sec_buf h_essn_key; /* encrypted session key */ + uint32_t h_flags; + /* Version struct (optional) */ + /* MIC hash (optional) */ +}; + +/* Allow turning these off for debugging, etc. */ +int smbd_signing_enabled = 1; + +int smbd_constant_challenge = 0; +static uint8_t constant_chal[8] = { + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88 }; + +static int smbd_ntlmssp_negotiate(authsvc_context_t *); +static int smbd_ntlmssp_authenticate(authsvc_context_t *); +static int encode_avpair_str(smb_msgbuf_t *, uint16_t, char *); +static int decode_secbuf_bin(smb_msgbuf_t *, struct sec_buf *, void **); +static int decode_secbuf_str(smb_msgbuf_t *, struct sec_buf *, char **); + +/* + * Initialize this context for NTLMSSP, if possible. + */ +int +smbd_ntlmssp_init(authsvc_context_t *ctx) +{ + ntlmssp_backend_t *be; + + be = malloc(sizeof (*be)); + if (be == 0) + return (NT_STATUS_NO_MEMORY); + bzero(be, sizeof (*be)); + be->expect_type = NTLMSSP_MSGTYPE_NEGOTIATE; + ctx->ctx_backend = be; + + return (0); +} + +void +smbd_ntlmssp_fini(authsvc_context_t *ctx) +{ + free(ctx->ctx_backend); +} + +/* + * Handle an auth message + */ +int +smbd_ntlmssp_work(authsvc_context_t *ctx) +{ + struct genhdr *ihdr = ctx->ctx_ibodybuf; + ntlmssp_backend_t *be = ctx->ctx_backend; + uint32_t mtype; + int rc; + + if (ctx->ctx_ibodylen < sizeof (*ihdr)) + return (NT_STATUS_INVALID_PARAMETER); + + if (bcmp(ihdr->h_id, "NTLMSSP", 8)) + return (NT_STATUS_INVALID_PARAMETER); + mtype = letohl(ihdr->h_type); + if (mtype != be->expect_type) + return (NT_STATUS_INVALID_PARAMETER); + + switch (mtype) { + case NTLMSSP_MSGTYPE_NEGOTIATE: + ctx->ctx_orawtype = LSA_MTYPE_ES_CONT; + rc = smbd_ntlmssp_negotiate(ctx); + break; + case NTLMSSP_MSGTYPE_AUTHENTICATE: + ctx->ctx_orawtype = LSA_MTYPE_ES_DONE; + rc = smbd_ntlmssp_authenticate(ctx); + break; + + default: + case NTLMSSP_MSGTYPE_CHALLENGE: + /* Sent by servers, not received. */ + rc = NT_STATUS_INVALID_PARAMETER; + break; + } + + return (rc); +} + +#if (MAXHOSTNAMELEN < NETBIOS_NAME_SZ) +#error "MAXHOSTNAMELEN < NETBIOS_NAME_SZ" +#endif + +/* + * Handle an NTLMSSP_MSGTYPE_NEGOTIATE message, and reply + * with an NTLMSSP_MSGTYPE_CHALLENGE message. + * See: [MS-NLMP] 2.2.1.1, 3.2.5.1.1 + */ +static int +smbd_ntlmssp_negotiate(authsvc_context_t *ctx) +{ + char tmp_name[MAXHOSTNAMELEN]; + ntlmssp_backend_t *be = ctx->ctx_backend; + struct nego_hdr *ihdr = ctx->ctx_ibodybuf; + smb_msgbuf_t mb; + uint8_t *save_scan; + int secmode; + int mbflags; + int rc; + size_t var_start, var_end; + uint16_t var_size; + + if (ctx->ctx_ibodylen < sizeof (*ihdr)) + return (NT_STATUS_INVALID_PARAMETER); + be->clnt_flags = letohl(ihdr->h_flags); + + /* + * Looks like we can ignore ws_dom, ws_name. + * Otherwise would parse those here. + */ + + secmode = smb_config_get_secmode(); + if (smbd_constant_challenge) { + (void) memcpy(be->srv_challenge, constant_chal, + sizeof (be->srv_challenge)); + } else { + randomize(be->srv_challenge, sizeof (be->srv_challenge)); + } + + /* + * Compute srv_flags + */ + be->srv_flags = + NTLMSSP_REQUEST_TARGET | + NTLMSSP_NEGOTIATE_NTLM | + NTLMSSP_NEGOTIATE_TARGET_INFO; + be->srv_flags |= be->clnt_flags & ( + NTLMSSP_NEGOTIATE_NTLM2 | + NTLMSSP_NEGOTIATE_128 | + NTLMSSP_NEGOTIATE_KEY_EXCH | + NTLMSSP_NEGOTIATE_56); + + if (smbd_signing_enabled) { + be->srv_flags |= be->clnt_flags & ( + NTLMSSP_NEGOTIATE_SIGN | + NTLMSSP_NEGOTIATE_SEAL | + NTLMSSP_NEGOTIATE_ALWAYS_SIGN); + } + + if (be->clnt_flags & NTLMSSP_NEGOTIATE_UNICODE) + be->srv_flags |= NTLMSSP_NEGOTIATE_UNICODE; + else if (be->clnt_flags & NTLMSSP_NEGOTIATE_OEM) + be->srv_flags |= NTLMSSP_NEGOTIATE_OEM; + + /* LM Key is mutually exclusive with NTLM2 */ + if ((be->srv_flags & NTLMSSP_NEGOTIATE_NTLM2) == 0 && + (be->clnt_flags & NTLMSSP_NEGOTIATE_LM_KEY) != 0) + be->srv_flags |= NTLMSSP_NEGOTIATE_LM_KEY; + + /* Get our "target name" */ + if (secmode == SMB_SECMODE_DOMAIN) { + be->srv_flags |= NTLMSSP_TARGET_TYPE_DOMAIN; + rc = smb_getdomainname(tmp_name, NETBIOS_NAME_SZ); + } else { + be->srv_flags |= NTLMSSP_TARGET_TYPE_SERVER; + rc = smb_getnetbiosname(tmp_name, NETBIOS_NAME_SZ); + } + if (rc) + goto errout; + + /* + * Build the NTLMSSP_MSGTYPE_CHALLENGE message. + */ + mbflags = SMB_MSGBUF_NOTERM; + if (be->srv_flags & NTLMSSP_NEGOTIATE_UNICODE) + mbflags |= SMB_MSGBUF_UNICODE; + smb_msgbuf_init(&mb, ctx->ctx_obodybuf, ctx->ctx_obodylen, mbflags); + + /* + * Fixed size parts + */ + rc = smb_msgbuf_encode( + &mb, "8clwwll8cllwwl", /* offset, name (fmt) */ + "NTLMSSP", /* 0: signature (8c) */ + NTLMSSP_MSGTYPE_CHALLENGE, /* 8: type (l) */ + 0, 0, 0, /* filled later: 12: target name (wwl) */ + be->srv_flags, /* 20: flags (l) */ + be->srv_challenge, /* 24: (8c) */ + 0, 0, /* 32: reserved (ll) */ + 0, 0, 0); /* filled later: 40: target info (wwl) */ +#define TARGET_NAME_OFFSET 12 +#define TARGET_INFO_OFFSET 40 + if (rc < 0) + goto errout; + + /* + * Variable length parts. + * + * Target name + */ + var_start = smb_msgbuf_used(&mb); + rc = smb_msgbuf_encode(&mb, "u", tmp_name); + var_end = smb_msgbuf_used(&mb); + var_size = (uint16_t)(var_end - var_start); + if (rc < 0) + goto errout; + + /* overwrite target name offset+lengths */ + save_scan = mb.scan; + mb.scan = mb.base + TARGET_NAME_OFFSET; + (void) smb_msgbuf_encode(&mb, "wwl", var_size, var_size, var_start); + mb.scan = save_scan; + + /* + * Target info (AvPairList) + * + * These AV pairs are like our name/value pairs, but have + * numeric identifiers instead of names. There are many + * of these, but we put only the four expected by Windows: + * NetBIOS computer name + * NetBIOS domain name + * DNS computer name + * DNS domain name + * Note that "domain" above (even "DNS domain") refers to + * the AD domain of which we're a member, which may be + * _different_ from the configured DNS domain. + * + * Also note that in "workgroup" mode (not a domain member) + * all "domain" fields should be set to the same values as + * the "computer" fields ("bare" host name, not FQDN). + */ + var_start = smb_msgbuf_used(&mb); + + /* NetBIOS Computer Name */ + if (smb_getnetbiosname(tmp_name, NETBIOS_NAME_SZ)) + goto errout; + if (encode_avpair_str(&mb, MsvAvNbComputerName, tmp_name) < 0) + goto errout; + + if (secmode != SMB_SECMODE_DOMAIN) { + /* + * Workgroup mode. Set all to hostname. + * tmp_name = netbios hostname from above. + */ + if (encode_avpair_str(&mb, MsvAvNbDomainName, tmp_name) < 0) + goto errout; + /* + * Want the bare computer name here (not FQDN). + */ + if (smb_gethostname(tmp_name, MAXHOSTNAMELEN, SMB_CASE_LOWER)) + goto errout; + if (encode_avpair_str(&mb, MsvAvDnsComputerName, tmp_name) < 0) + goto errout; + if (encode_avpair_str(&mb, MsvAvDnsDomainName, tmp_name) < 0) + goto errout; + } else { + /* + * Domain mode. Use real host and domain values. + */ + + /* NetBIOS Domain Name */ + if (smb_getdomainname(tmp_name, NETBIOS_NAME_SZ)) + goto errout; + if (encode_avpair_str(&mb, MsvAvNbDomainName, tmp_name) < 0) + goto errout; + + /* DNS Computer Name */ + if (smb_getfqhostname(tmp_name, MAXHOSTNAMELEN)) + goto errout; + if (encode_avpair_str(&mb, MsvAvDnsComputerName, tmp_name) < 0) + goto errout; + + /* DNS Domain Name */ + if (smb_getfqdomainname(tmp_name, MAXHOSTNAMELEN)) + goto errout; + if (encode_avpair_str(&mb, MsvAvDnsDomainName, tmp_name) < 0) + goto errout; + } + + /* End marker */ + if (smb_msgbuf_encode(&mb, "ww", MsvAvEOL, 0) < 0) + goto errout; + var_end = smb_msgbuf_used(&mb); + var_size = (uint16_t)(var_end - var_start); + + /* overwrite target offset+lengths */ + save_scan = mb.scan; + mb.scan = mb.base + TARGET_INFO_OFFSET; + (void) smb_msgbuf_encode(&mb, "wwl", var_size, var_size, var_start); + mb.scan = save_scan; + + ctx->ctx_obodylen = smb_msgbuf_used(&mb); + smb_msgbuf_term(&mb); + + be->expect_type = NTLMSSP_MSGTYPE_AUTHENTICATE; + + return (0); + +errout: + smb_msgbuf_term(&mb); + return (NT_STATUS_INTERNAL_ERROR); +} + +static int +encode_avpair_str(smb_msgbuf_t *mb, uint16_t AvId, char *name) +{ + int rc; + uint16_t len; + + len = smb_wcequiv_strlen(name); + rc = smb_msgbuf_encode(mb, "wwU", AvId, len, name); + return (rc); +} + +/* + * Handle an NTLMSSP_MSGTYPE_AUTHENTICATE message. + * See: [MS-NLMP] 2.2.1.3, 3.2.5.1.2 + */ +static int +smbd_ntlmssp_authenticate(authsvc_context_t *ctx) +{ + struct auth_hdr hdr; + smb_msgbuf_t mb; + smb_logon_t user_info; + smb_token_t *token = NULL; + ntlmssp_backend_t *be = ctx->ctx_backend; + void *lm_resp; + void *nt_resp; + char *domain; + char *user; + char *wksta; + void *essn_key; /* encrypted session key (optional) */ + int mbflags; + uint_t status = NT_STATUS_INTERNAL_ERROR; + char combined_challenge[SMBAUTH_CHAL_SZ]; + unsigned char kxkey[SMBAUTH_HASH_SZ]; + boolean_t ntlm_v1x = B_FALSE; + + bzero(&user_info, sizeof (user_info)); + + /* + * Parse the NTLMSSP_MSGTYPE_AUTHENTICATE message. + */ + if (ctx->ctx_ibodylen < sizeof (hdr)) + return (NT_STATUS_INVALID_PARAMETER); + mbflags = SMB_MSGBUF_NOTERM; + if (be->srv_flags & NTLMSSP_NEGOTIATE_UNICODE) + mbflags |= SMB_MSGBUF_UNICODE; + smb_msgbuf_init(&mb, ctx->ctx_ibodybuf, ctx->ctx_ibodylen, mbflags); + bzero(&hdr, sizeof (hdr)); + + if (smb_msgbuf_decode(&mb, "12.") < 0) + goto errout; + if (decode_secbuf_bin(&mb, &hdr.h_lm_resp, &lm_resp) < 0) + goto errout; + if (decode_secbuf_bin(&mb, &hdr.h_nt_resp, &nt_resp) < 0) + goto errout; + if (decode_secbuf_str(&mb, &hdr.h_domain, &domain) < 0) + goto errout; + if (decode_secbuf_str(&mb, &hdr.h_user, &user) < 0) + goto errout; + if (decode_secbuf_str(&mb, &hdr.h_wksta, &wksta) < 0) + goto errout; + if (decode_secbuf_bin(&mb, &hdr.h_essn_key, &essn_key) < 0) + goto errout; + if (smb_msgbuf_decode(&mb, "l", &be->clnt_flags) < 0) + goto errout; + + if (be->clnt_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) { + if (hdr.h_essn_key.sb_length < 16 || essn_key == NULL) + goto errout; + } + + user_info.lg_level = NETR_NETWORK_LOGON; + user_info.lg_flags = 0; + + user_info.lg_ntlm_flags = be->clnt_flags; + user_info.lg_username = (user) ? user : ""; + user_info.lg_domain = (domain) ? domain : ""; + user_info.lg_workstation = (wksta) ? wksta : ""; + + user_info.lg_clnt_ipaddr = + ctx->ctx_clinfo.lci_clnt_ipaddr; + user_info.lg_local_port = 445; + + user_info.lg_challenge_key.len = SMBAUTH_CHAL_SZ; + user_info.lg_challenge_key.val = (uint8_t *)be->srv_challenge; + + user_info.lg_nt_password.len = hdr.h_nt_resp.sb_length; + user_info.lg_nt_password.val = nt_resp; + + user_info.lg_lm_password.len = hdr.h_lm_resp.sb_length; + user_info.lg_lm_password.val = lm_resp; + + user_info.lg_native_os = ctx->ctx_clinfo.lci_native_os; + user_info.lg_native_lm = ctx->ctx_clinfo.lci_native_lm; + + /* + * If we're doing extended session security, the challenge + * this OWF was computed with is different. [MS-NLMP 3.3.1] + * It's: MD5(concat(ServerChallenge,ClientChallenge)) + * where the ClientChallenge is in the LM resp. field. + */ + if (user_info.lg_nt_password.len == SMBAUTH_LM_RESP_SZ && + user_info.lg_lm_password.len >= SMBAUTH_CHAL_SZ && + (be->clnt_flags & NTLMSSP_NEGOTIATE_NTLM2) != 0) { + smb_auth_ntlm2_mkchallenge(combined_challenge, + be->srv_challenge, lm_resp); + user_info.lg_challenge_key.val = + (uint8_t *)combined_challenge; + user_info.lg_lm_password.len = 0; + ntlm_v1x = B_TRUE; + } + + /* + * This (indirectly) calls smb_auth_validate() to + * check that the client gave us a valid hash. + */ + token = smbd_user_auth_logon(&user_info); + if (token == NULL) { + status = NT_STATUS_ACCESS_DENIED; + goto errout; + } + + if (token->tkn_ssnkey.val != NULL && + token->tkn_ssnkey.len == SMBAUTH_HASH_SZ) { + + /* + * At this point, token->tkn_session_key is the + * "Session Base Key" [MS-NLMP] 3.2.5.1.2 + * Compute the final session key. First need the + * "Key Exchange Key" [MS-NLMP] 3.4.5.1 + */ + if (ntlm_v1x) { + smb_auth_ntlm2_kxkey(kxkey, + be->srv_challenge, lm_resp, + token->tkn_ssnkey.val); + } else { + /* KXKEY is the Session Base Key. */ + (void) memcpy(kxkey, token->tkn_ssnkey.val, + SMBAUTH_HASH_SZ); + } + + /* + * If the client give us an encrypted session key, + * decrypt it (RC4) using the "key exchange key". + */ + if (be->clnt_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) { + /* RC4 args: result, key, data */ + (void) smb_auth_RC4(token->tkn_ssnkey.val, + SMBAUTH_HASH_SZ, kxkey, SMBAUTH_HASH_SZ, + essn_key, hdr.h_essn_key.sb_length); + } else { + /* Final key is the KXKEY */ + (void) memcpy(token->tkn_ssnkey.val, kxkey, + SMBAUTH_HASH_SZ); + } + } + + ctx->ctx_token = token; + ctx->ctx_obodylen = 0; + + smb_msgbuf_term(&mb); + return (0); + +errout: + smb_msgbuf_term(&mb); + return (status); +} + +static int +decode_secbuf_bin(smb_msgbuf_t *mb, struct sec_buf *sb, void **binp) +{ + int rc; + + *binp = NULL; + rc = smb_msgbuf_decode( + mb, "wwl", + &sb->sb_length, + &sb->sb_maxlen, + &sb->sb_offset); + if (rc < 0) + return (rc); + + if (sb->sb_offset > mb->max) + return (SMB_MSGBUF_UNDERFLOW); + if (sb->sb_length > (mb->max - sb->sb_offset)) + return (SMB_MSGBUF_UNDERFLOW); + if (sb->sb_length == 0) + return (rc); + + *binp = mb->base + sb->sb_offset; + return (0); +} + +static int +decode_secbuf_str(smb_msgbuf_t *mb, struct sec_buf *sb, char **cpp) +{ + uint8_t *save_scan; + int rc; + + *cpp = NULL; + rc = smb_msgbuf_decode( + mb, "wwl", + &sb->sb_length, + &sb->sb_maxlen, + &sb->sb_offset); + if (rc < 0) + return (rc); + + if (sb->sb_offset > mb->max) + return (SMB_MSGBUF_UNDERFLOW); + if (sb->sb_length > (mb->max - sb->sb_offset)) + return (SMB_MSGBUF_UNDERFLOW); + if (sb->sb_length == 0) + return (rc); + + save_scan = mb->scan; + mb->scan = mb->base + sb->sb_offset; + rc = smb_msgbuf_decode(mb, "#u", (int)sb->sb_length, cpp); + mb->scan = save_scan; + + return (rc); +} diff --git a/usr/src/common/smbsrv/smb_msgbuf.c b/usr/src/common/smbsrv/smb_msgbuf.c index ff94a6243b..54cb75e066 100644 --- a/usr/src/common/smbsrv/smb_msgbuf.c +++ b/usr/src/common/smbsrv/smb_msgbuf.c @@ -52,7 +52,6 @@ static int buf_decode(smb_msgbuf_t *, char *, va_list ap); static int buf_encode(smb_msgbuf_t *, char *, va_list ap); static void *smb_msgbuf_malloc(smb_msgbuf_t *, size_t); static int smb_msgbuf_chkerc(char *text, int erc); -static void buf_decode_wcs(smb_wchar_t *, smb_wchar_t *, int wcstrlen); /* * Returns the offset or number of bytes used within the buffer. @@ -216,16 +215,19 @@ buf_decode(smb_msgbuf_t *mb, char *fmt, va_list ap) { uint32_t ival; uint8_t c; - uint8_t *cvalp; - uint8_t **cvalpp; + uint8_t *bvalp; uint16_t *wvalp; uint32_t *lvalp; uint64_t *llvalp; - smb_wchar_t *wcs; + char *cvalp; + char **cvalpp; + smb_wchar_t wchar; + boolean_t repc_specified; int repc; int rc; while ((c = *fmt++) != 0) { + repc_specified = B_FALSE; repc = 1; if (c == ' ' || c == '\t') @@ -247,9 +249,11 @@ buf_decode(smb_msgbuf_t *mb, char *fmt, va_list ap) repc = repc * 10 + c - '0'; c = *fmt++; } while ('0' <= c && c <= '9'); + repc_specified = B_TRUE; } else if (c == '#') { repc = va_arg(ap, int); c = *fmt++; + repc_specified = B_TRUE; } switch (c) { @@ -260,26 +264,26 @@ buf_decode(smb_msgbuf_t *mb, char *fmt, va_list ap) mb->scan += repc; break; - case 'c': + case 'c': /* get char */ if (smb_msgbuf_has_space(mb, repc) == 0) return (SMB_MSGBUF_UNDERFLOW); - cvalp = va_arg(ap, uint8_t *); - bcopy(mb->scan, cvalp, repc); + bvalp = va_arg(ap, uint8_t *); + bcopy(mb->scan, bvalp, repc); mb->scan += repc; break; - case 'b': + case 'b': /* get byte */ if (smb_msgbuf_has_space(mb, repc) == 0) return (SMB_MSGBUF_UNDERFLOW); - cvalp = va_arg(ap, uint8_t *); + bvalp = va_arg(ap, uint8_t *); while (repc-- > 0) { - *cvalp++ = *mb->scan++; + *bvalp++ = *mb->scan++; } break; - case 'w': + case 'w': /* get word */ rc = smb_msgbuf_has_space(mb, repc * sizeof (uint16_t)); if (rc == 0) return (SMB_MSGBUF_UNDERFLOW); @@ -291,7 +295,7 @@ buf_decode(smb_msgbuf_t *mb, char *fmt, va_list ap) } break; - case 'l': + case 'l': /* get long */ rc = smb_msgbuf_has_space(mb, repc * sizeof (int32_t)); if (rc == 0) return (SMB_MSGBUF_UNDERFLOW); @@ -303,7 +307,7 @@ buf_decode(smb_msgbuf_t *mb, char *fmt, va_list ap) } break; - case 'q': + case 'q': /* get quad */ rc = smb_msgbuf_has_space(mb, repc * sizeof (int64_t)); if (rc == 0) return (SMB_MSGBUF_UNDERFLOW); @@ -320,26 +324,30 @@ buf_decode(smb_msgbuf_t *mb, char *fmt, va_list ap) goto unicode_translation; /*FALLTHROUGH*/ - case 's': - ival = strlen((const char *)mb->scan) + 1; - if (smb_msgbuf_has_space(mb, ival) == 0) + case 's': /* get string */ + if (!repc_specified) + repc = strlen((const char *)mb->scan) + 1; + if (smb_msgbuf_has_space(mb, repc) == 0) return (SMB_MSGBUF_UNDERFLOW); - - if ((cvalp = smb_msgbuf_malloc(mb, ival * 2)) == 0) + if ((cvalp = smb_msgbuf_malloc(mb, repc * 2)) == 0) return (SMB_MSGBUF_UNDERFLOW); - - if ((ival = smb_stombs((char *)cvalp, - (char *)mb->scan, ival * 2)) == - (uint32_t)-1) { - return (SMB_MSGBUF_DATA_ERROR); - } - - cvalpp = va_arg(ap, uint8_t **); + cvalpp = va_arg(ap, char **); *cvalpp = cvalp; - mb->scan += (ival+1); + /* Translate OEM to mbs */ + while (repc > 0) { + wchar = *mb->scan++; + repc--; + if (wchar == 0) + break; + ival = smb_wctomb(cvalp, wchar); + cvalp += ival; + } + *cvalp = '\0'; + if (repc > 0) + mb->scan += repc; break; - case 'U': /* Convert from unicode */ + case 'U': /* get unicode string */ unicode_translation: /* * Unicode strings are always word aligned. @@ -348,35 +356,43 @@ unicode_translation: * may be longer than the wide-chars. */ smb_msgbuf_word_align(mb); - /*LINTED E_BAD_PTR_CAST_ALIGN*/ - wcs = (smb_wchar_t *)mb->scan; - - /* count the null wchar */ - repc = sizeof (smb_wchar_t); - while (*wcs++) - repc += sizeof (smb_wchar_t); - + if (!repc_specified) { + /* + * Count bytes, including the null. + */ + uint8_t *tmp_scan = mb->scan; + repc = 2; /* the null */ + while ((wchar = LE_IN16(tmp_scan)) != 0) { + tmp_scan += 2; + repc += 2; + } + } if (smb_msgbuf_has_space(mb, repc) == 0) return (SMB_MSGBUF_UNDERFLOW); - - /* Decode wchar string into host byte-order */ - if ((wcs = smb_msgbuf_malloc(mb, repc)) == 0) - return (SMB_MSGBUF_UNDERFLOW); - - /*LINTED E_BAD_PTR_CAST_ALIGN*/ - buf_decode_wcs(wcs, (smb_wchar_t *)mb->scan, - repc / sizeof (smb_wchar_t)); - - /* Get space for translated string */ + /* + * Get space for translated string + * Allocates worst-case size. + */ if ((cvalp = smb_msgbuf_malloc(mb, repc * 2)) == 0) return (SMB_MSGBUF_UNDERFLOW); - - /* Translate string */ - (void) smb_wcstombs((char *)cvalp, wcs, repc * 2); - - cvalpp = va_arg(ap, uint8_t **); + cvalpp = va_arg(ap, char **); *cvalpp = cvalp; - mb->scan += repc; + /* + * Translate unicode to mbs, stopping after + * null or repc limit. + */ + while (repc >= 2) { + wchar = LE_IN16(mb->scan); + mb->scan += 2; + repc -= 2; + if (wchar == 0) + break; + ival = smb_wctomb(cvalp, wchar); + cvalp += ival; + } + *cvalp = '\0'; + if (repc > 0) + mb->scan += repc; break; case 'M': @@ -447,15 +463,17 @@ buf_encode(smb_msgbuf_t *mb, char *fmt, va_list ap) uint16_t wval; uint32_t lval; uint64_t llval; - uint32_t ival; - uint8_t *cvalp; + uint8_t *bvalp; + char *cvalp; uint8_t c; - smb_wchar_t wcval; + smb_wchar_t wchar; int count; - int repc = 1; + boolean_t repc_specified; + int repc; int rc; while ((c = *fmt++) != 0) { + repc_specified = B_FALSE; repc = 1; if (c == ' ' || c == '\t') @@ -477,9 +495,11 @@ buf_encode(smb_msgbuf_t *mb, char *fmt, va_list ap) repc = repc * 10 + c - '0'; c = *fmt++; } while ('0' <= c && c <= '9'); + repc_specified = B_TRUE; } else if (c == '#') { repc = va_arg(ap, int); c = *fmt++; + repc_specified = B_TRUE; } switch (c) { @@ -491,16 +511,16 @@ buf_encode(smb_msgbuf_t *mb, char *fmt, va_list ap) *mb->scan++ = 0; break; - case 'c': + case 'c': /* put char */ if (smb_msgbuf_has_space(mb, repc) == 0) return (SMB_MSGBUF_OVERFLOW); - cvalp = va_arg(ap, uint8_t *); - bcopy(cvalp, mb->scan, repc); + bvalp = va_arg(ap, uint8_t *); + bcopy(bvalp, mb->scan, repc); mb->scan += repc; break; - case 'b': + case 'b': /* put byte */ if (smb_msgbuf_has_space(mb, repc) == 0) return (SMB_MSGBUF_OVERFLOW); @@ -510,7 +530,7 @@ buf_encode(smb_msgbuf_t *mb, char *fmt, va_list ap) } break; - case 'w': + case 'w': /* put word */ rc = smb_msgbuf_has_space(mb, repc * sizeof (uint16_t)); if (rc == 0) return (SMB_MSGBUF_OVERFLOW); @@ -522,7 +542,7 @@ buf_encode(smb_msgbuf_t *mb, char *fmt, va_list ap) } break; - case 'l': + case 'l': /* put long */ rc = smb_msgbuf_has_space(mb, repc * sizeof (int32_t)); if (rc == 0) return (SMB_MSGBUF_OVERFLOW); @@ -534,7 +554,7 @@ buf_encode(smb_msgbuf_t *mb, char *fmt, va_list ap) } break; - case 'q': + case 'q': /* put quad */ rc = smb_msgbuf_has_space(mb, repc * sizeof (int64_t)); if (rc == 0) return (SMB_MSGBUF_OVERFLOW); @@ -551,66 +571,79 @@ buf_encode(smb_msgbuf_t *mb, char *fmt, va_list ap) goto unicode_translation; /* FALLTHROUGH */ - case 's': - cvalp = va_arg(ap, uint8_t *); - ival = strlen((const char *)cvalp) + 1; - - if (smb_msgbuf_has_space(mb, ival) == 0) + case 's': /* put string */ + cvalp = va_arg(ap, char *); + if (!repc_specified) { + repc = smb_sbequiv_strlen(cvalp); + if (repc == -1) + return (SMB_MSGBUF_OVERFLOW); + if (!(mb->flags & SMB_MSGBUF_NOTERM)) + repc++; + } + if (smb_msgbuf_has_space(mb, repc) == 0) return (SMB_MSGBUF_OVERFLOW); - - ival = - smb_mbstos((char *)mb->scan, (const char *)cvalp); - mb->scan += ival + 1; + while (repc > 0) { + count = smb_mbtowc(&wchar, cvalp, + MTS_MB_CHAR_MAX); + if (count < 0) + return (SMB_MSGBUF_DATA_ERROR); + cvalp += count; + if (wchar == 0) + break; + *mb->scan++ = (uint8_t)wchar; + repc--; + if (wchar & 0xff00) { + *mb->scan++ = wchar >> 8; + repc--; + } + } + if (*cvalp == '\0' && repc > 0 && + (mb->flags & SMB_MSGBUF_NOTERM) == 0) { + *mb->scan++ = 0; + repc--; + } + while (repc > 0) { + *mb->scan++ = 0; + repc--; + } break; - case 'U': /* unicode */ + case 'U': /* put unicode string */ unicode_translation: /* * Unicode strings are always word aligned. */ smb_msgbuf_word_align(mb); - cvalp = va_arg(ap, uint8_t *); - - for (;;) { - rc = smb_msgbuf_has_space(mb, - sizeof (smb_wchar_t)); - if (rc == 0) - return (SMB_MSGBUF_OVERFLOW); - - count = smb_mbtowc(&wcval, (const char *)cvalp, + cvalp = va_arg(ap, char *); + if (!repc_specified) { + repc = smb_wcequiv_strlen(cvalp); + if (!(mb->flags & SMB_MSGBUF_NOTERM)) + repc += 2; + } + if (!smb_msgbuf_has_space(mb, repc)) + return (SMB_MSGBUF_OVERFLOW); + while (repc >= 2) { + count = smb_mbtowc(&wchar, cvalp, MTS_MB_CHAR_MAX); - - if (count < 0) { + if (count < 0) return (SMB_MSGBUF_DATA_ERROR); - } else if (count == 0) { - /* - * No longer need to do this now that - * mbtowc correctly writes the null - * before returning zero but paranoia - * wins. - */ - wcval = 0; - count = 1; - } - - /* Write wchar in wire-format */ - LE_OUT16(mb->scan, wcval); - - if (*cvalp == 0) { - /* - * End of string. Check to see whether - * or not to include the null - * terminator. - */ - if ((mb->flags & SMB_MSGBUF_NOTERM) == - 0) - mb->scan += - sizeof (smb_wchar_t); + cvalp += count; + if (wchar == 0) break; - } - mb->scan += sizeof (smb_wchar_t); - cvalp += count; + LE_OUT16(mb->scan, wchar); + mb->scan += 2; + repc -= 2; + } + if (*cvalp == '\0' && repc >= 2 && + (mb->flags & SMB_MSGBUF_NOTERM) == 0) { + LE_OUT16(mb->scan, 0); + mb->scan += 2; + repc -= 2; + } + while (repc > 0) { + *mb->scan++ = 0; + repc--; } break; @@ -695,15 +728,3 @@ smb_msgbuf_chkerc(char *text, int erc) } return (erc); } - -static void -buf_decode_wcs(smb_wchar_t *dst_wcstr, smb_wchar_t *src_wcstr, int wcstrlen) -{ - int i; - - for (i = 0; i < wcstrlen; i++) { - *dst_wcstr = LE_IN16(src_wcstr); - dst_wcstr++; - src_wcstr++; - } -} diff --git a/usr/src/common/smbsrv/smb_token.c b/usr/src/common/smbsrv/smb_token.c index b77b9a44a0..e1f63c6df7 100644 --- a/usr/src/common/smbsrv/smb_token.c +++ b/usr/src/common/smbsrv/smb_token.c @@ -165,17 +165,4 @@ smb_logon_free(smb_logon_t *obj) xdr_free(smb_logon_xdr, (char *)obj); free(obj); } -#else /* _KERNEL */ -/* - * Tokens are allocated in the kernel via XDR. - * Call xdr_free before freeing the token structure. - */ -void -smb_token_free(smb_token_t *token) -{ - if (token != NULL) { - xdr_free(smb_token_xdr, (char *)token); - kmem_free(token, sizeof (smb_token_t)); - } -} #endif /* _KERNEL */ diff --git a/usr/src/common/smbsrv/smb_token_xdr.c b/usr/src/common/smbsrv/smb_token_xdr.c index 6b44899768..fdad7ed5ca 100644 --- a/usr/src/common/smbsrv/smb_token_xdr.c +++ b/usr/src/common/smbsrv/smb_token_xdr.c @@ -21,6 +21,8 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ /* @@ -154,15 +156,6 @@ smb_posix_grps_helper_xdr(XDR *xdrs, char **identity) return (TRUE); } -static bool_t -smb_session_key_xdr(XDR *xdrs, smb_session_key_t *objp) -{ - if (!xdr_vector(xdrs, (char *)objp->data, 16, - sizeof (uint8_t), (xdrproc_t)xdr_uint8_t)) - return (FALSE); - return (TRUE); -} - bool_t smb_logon_xdr(XDR *xdrs, smb_logon_t *objp) { @@ -190,6 +183,8 @@ smb_logon_xdr(XDR *xdrs, smb_logon_t *objp) return (FALSE); if (!smb_buf32_xdr(xdrs, &objp->lg_lm_password)) return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->lg_ntlm_flags)) + return (FALSE); if (!xdr_int(xdrs, &objp->lg_native_os)) return (FALSE); if (!xdr_int(xdrs, &objp->lg_native_lm)) @@ -278,8 +273,7 @@ smb_token_xdr(XDR *xdrs, smb_token_t *objp) return (FALSE); if (!xdr_uint32_t(xdrs, &objp->tkn_audit_sid)) return (FALSE); - if (!xdr_pointer(xdrs, (char **)&objp->tkn_session_key, - sizeof (smb_session_key_t), (xdrproc_t)smb_session_key_xdr)) + if (!smb_buf32_xdr(xdrs, &objp->tkn_ssnkey)) return (FALSE); if (!smb_posix_grps_helper_xdr(xdrs, (char **)&objp->tkn_posix_grps)) return (FALSE); diff --git a/usr/src/common/smbsrv/smb_xdr.c b/usr/src/common/smbsrv/smb_xdr.c index 54e0cc8222..e7640554aa 100644 --- a/usr/src/common/smbsrv/smb_xdr.c +++ b/usr/src/common/smbsrv/smb_xdr.c @@ -97,7 +97,8 @@ smb_doorhdr_opname(uint32_t op) { SMB_DR_QUOTA_SET, "quota_set" }, { SMB_DR_DFS_GET_REFERRALS, "dfs_get_referrals" }, { SMB_DR_SHR_HOSTACCESS, "share_hostaccess" }, - { SMB_DR_SHR_EXEC, "share_exec" } + { SMB_DR_SHR_EXEC, "share_exec" }, + { SMB_DR_NOTIFY_DC_CHANGED, "notify_dc_changed" } }; int i; diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile index c27abf0246..eee92a6d40 100644 --- a/usr/src/lib/Makefile +++ b/usr/src/lib/Makefile @@ -19,12 +19,12 @@ # CDDL HEADER END # -# Copyright 2011 Nexenta Systems, Inc. All rights reserved. # Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright (c) 2012 by Delphix. All rights reserved. # Copyright 2015, Joyent, Inc. # Copyright (c) 2013 Gary Mills # Copyright 2014 Garrett D'Amore <garrett@damore.org> +# Copyright 2014 Nexenta Systems, Inc. All rights reserved. # Copyright (c) 2015 Gary Mills include ../Makefile.master @@ -251,6 +251,7 @@ SUBDIRS += \ libshare \ libsqlite \ libidmap \ + libads \ libadutils \ libipmi \ libexacct/demo \ @@ -375,6 +376,7 @@ i386_MSGSUBDIRS= libfdisk HDRSUBDIRS= \ auditd_plugins \ + libads \ libast \ libbrand \ libbsm \ @@ -665,7 +667,8 @@ libexacct/demo: libexacct libproject libsocket libnsl libtsalarm: libpcp smbsrv: libsocket libnsl libmd libxnet libpthread librt \ libshare libidmap pkcs11 libsqlite libcryptoutil \ - libreparse libcmdutils libfakekernel + libreparse libcmdutils libresolv libsmbfs libuuid \ + libfakekernel libads libv12n: libds libuuid libvrrpadm: libsocket libdladm libscf libvscan: libscf diff --git a/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/get_in_tkt.c b/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/get_in_tkt.c index 3138920e9b..967c8f7fe4 100644 --- a/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/get_in_tkt.c +++ b/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/get_in_tkt.c @@ -1,5 +1,7 @@ /* - * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. */ + * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + */ /* * lib/krb5/krb/get_in_tkt.c * @@ -130,6 +132,12 @@ send_as_request2(krb5_context context, reply.data = 0; + /* Solaris Kerberos (illumos) */ + if (krb5_getenv("MS_INTEROP")) { + /* Don't bother with UDP. */ + tcp_only = 1; + } + /* set the nonce if the caller expects us to do it */ if (request->nonce == 0) { if ((retval = krb5_timeofday(context, &time_now))) diff --git a/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/send_tgs.c b/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/send_tgs.c index f3e159e6f9..10cdcdd502 100644 --- a/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/send_tgs.c +++ b/usr/src/lib/gss_mechs/mech_krb5/krb5/krb/send_tgs.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ /* @@ -176,6 +177,12 @@ krb5_send_tgs2(krb5_context context, krb5_flags kdcoptions, if (!in_cred->ticket.length) return(KRB5_NO_TKT_SUPPLIED); + /* Solaris Kerberos (illumos) */ + if (krb5_getenv("MS_INTEROP")) { + /* Don't bother with UDP. */ + tcp_only = 1; + } + memset((char *)&tgsreq, 0, sizeof(tgsreq)); tgsreq.kdc_options = kdcoptions; diff --git a/usr/src/lib/gss_mechs/mech_krb5/krb5/os/def_realm.c b/usr/src/lib/gss_mechs/mech_krb5/krb5/os/def_realm.c index c94bf33608..c4034223e8 100644 --- a/usr/src/lib/gss_mechs/mech_krb5/krb5/os/def_realm.c +++ b/usr/src/lib/gss_mechs/mech_krb5/krb5/os/def_realm.c @@ -31,6 +31,8 @@ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #include "k5-int.h" @@ -138,6 +140,18 @@ krb5_get_default_realm(krb5_context context, char **lrealm) if (!context || (context->magic != KV5M_CONTEXT)) return KV5M_CONTEXT; + /* + * Solaris Kerberos: (illumos) + * Another way to provide the default realm. + */ + if (!context->default_realm) { + if ((realm = getenv("KRB5_DEFAULT_REALM")) != NULL) { + context->default_realm = strdup(realm); + if (context->default_realm == NULL) + return ENOMEM; + } + } + if (!context->default_realm) { context->default_realm = 0; if (context->profile != 0) { @@ -192,11 +206,13 @@ krb5_get_default_realm(krb5_context context, char **lrealm) } } else #endif /* KRB5_DNS_LOOKUP */ - { + if (getenv("MS_INTEROP") == NULL) { /* * Solaris Kerberos: - * Try to find a realm based on one of the local IP addresses + * Try to find a realm based on one of the local IP addresses. + * Don't do this for AD, which often does _not_ support any + * DNS reverse lookup, making these queries take forever. */ (void) krb5int_foreach_localaddr(context, krb5int_address_get_realm, 0, 0); diff --git a/usr/src/lib/gss_mechs/mech_krb5/krb5/os/locate_kdc.c b/usr/src/lib/gss_mechs/mech_krb5/krb5/os/locate_kdc.c index 94662ca8e6..38bb3c1363 100644 --- a/usr/src/lib/gss_mechs/mech_krb5/krb5/os/locate_kdc.c +++ b/usr/src/lib/gss_mechs/mech_krb5/krb5/os/locate_kdc.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ /* @@ -71,6 +72,10 @@ #include <syslog.h> #include <locale.h> +#if USE_DLOPEN +#include <dlfcn.h> +#endif + /* for old Unixes and friends ... */ #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 64 @@ -446,6 +451,72 @@ module_locate_server (krb5_context ctx, const krb5_data *realm, return 0; } +/* XXX - move to locate_plugin.h? */ +typedef krb5_error_code (*krb5_lookup_func)( + void *, + enum locate_service_type svc, const char *realm, + int socktype, int family, + int (*cbfunc)(void *,int,struct sockaddr *), + void *cbdata); + +/* + * Solaris Kerberos (illumos) + * + * Allow main programs to provide an override function for _locate_server, + * named _krb5_override_service_locator(). If that function is found in + * the main program, it's called like a service locator plugin function. + * If it returns KRB5_PLUGIN_NO_HANDLE, continue with other _locate_server + * functions. If it returns anything else (zero or some other error), + * that return is "final" (no other _locate_server functions are called). + * This mechanism is used by programs like "idmapd" that want to completely + * control service location. + */ +static krb5_error_code +override_locate_server (krb5_context ctx, const krb5_data *realm, + struct addrlist *addrlist, + enum locate_service_type svc, int socktype, int family) +{ + struct module_callback_data cbdata = { 0, }; + krb5_error_code code; + void *dlh; + krb5_lookup_func lookup_func; + + Tprintf("in override_locate_server\n"); + cbdata.lp = addrlist; + + if ((dlh = dlopen(0, RTLD_FIRST | RTLD_LAZY)) == NULL) { + Tprintf("dlopen failed\n"); + return KRB5_PLUGIN_NO_HANDLE; + } + lookup_func = (krb5_lookup_func) dlsym( + dlh, "_krb5_override_service_locator"); + dlclose(dlh); + if (lookup_func == NULL) { + Tprintf("dlsym failed\n"); + return KRB5_PLUGIN_NO_HANDLE; + } + + code = lookup_func(ctx, svc, realm->data, socktype, family, + module_callback, &cbdata); + if (code == KRB5_PLUGIN_NO_HANDLE) { + Tprintf("override lookup routine returned KRB5_PLUGIN_NO_HANDLE\n"); + return code; + } + if (code != 0) { + /* Module encountered an actual error. */ + Tprintf("override lookup routine returned error %d: %s\n", + code, error_message(code)); + return code; + } + + /* Got something back, yippee. */ + Tprintf("now have %d addrs in list %p\n", addrlist->naddrs, addrlist); + print_addrlist(addrlist); + + return 0; +} +/* Solaris Kerberos (illumos) */ + static krb5_error_code prof_locate_server (krb5_context context, const krb5_data *realm, char ***hostlist, @@ -799,6 +870,20 @@ krb5int_locate_server (krb5_context context, const krb5_data *realm, *addrlist = al; + /* + * Solaris Kerberos (illumos) + * Allow main programs to override _locate_server() + */ + code = override_locate_server(context, realm, &al, svc, socktype, family); + if (code != KRB5_PLUGIN_NO_HANDLE) { + if (code == 0) + *addrlist = al; + else if (al.space) + free_list (&al); + return (code); + } + /* Solaris Kerberos (illumos) */ + code = module_locate_server(context, realm, &al, svc, socktype, family); Tprintf("module_locate_server returns %d\n", code); if (code == KRB5_PLUGIN_NO_HANDLE) { diff --git a/usr/src/lib/gss_mechs/mech_krb5/mech/inq_context.c b/usr/src/lib/gss_mechs/mech_krb5/mech/inq_context.c index b15cbbf14b..b5ec62c921 100644 --- a/usr/src/lib/gss_mechs/mech_krb5/mech/inq_context.c +++ b/usr/src/lib/gss_mechs/mech_krb5/mech/inq_context.c @@ -1,4 +1,5 @@ /* + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -258,10 +259,12 @@ gss_krb5int_extract_authz_data_from_sec_context( const gss_OID desired_object, gss_buffer_set_t *data_set) { + gss_buffer_desc ad_data; OM_uint32 major_status; + krb5_error_code code; krb5_gss_ctx_id_rec *ctx; int ad_type = 0; - size_t i; + int i, j; *data_set = GSS_C_NO_BUFFER_SET; @@ -280,7 +283,6 @@ gss_krb5int_extract_authz_data_from_sec_context( if (ctx->authdata != NULL) { for (i = 0; ctx->authdata[i] != NULL; i++) { if (ctx->authdata[i]->ad_type == ad_type) { - gss_buffer_desc ad_data; ad_data.length = ctx->authdata[i]->length; ad_data.value = ctx->authdata[i]->contents; @@ -289,10 +291,39 @@ gss_krb5int_extract_authz_data_from_sec_context( &ad_data, data_set); if (GSS_ERROR(major_status)) break; + } else if (ctx->authdata[i]->ad_type == KRB5_AUTHDATA_IF_RELEVANT) { + /* + * Solaris Kerberos (illumos) + * Unwrap the AD-IF-RELEVANT object and look inside. + */ + krb5_authdata **ad_if_relevant = NULL; + code = krb5_decode_authdata_container(ctx->k5_context, + KRB5_AUTHDATA_IF_RELEVANT, + ctx->authdata[i], + &ad_if_relevant); + if (code != 0) + continue; + + for (j = 0; ad_if_relevant[j] != NULL; j++) { + if (ad_if_relevant[j]->ad_type == ad_type) { + ad_data.length = ad_if_relevant[j]->length; + ad_data.value = ad_if_relevant[j]->contents; + + major_status = generic_gss_add_buffer_set_member(minor_status, + &ad_data, data_set); + if (GSS_ERROR(major_status)) { + krb5_free_authdata(ctx->k5_context, ad_if_relevant); + goto break2; + } + } + } + krb5_free_authdata(ctx->k5_context, ad_if_relevant); + /* Solaris Kerberos (illumos) */ } } } +break2: if (GSS_ERROR(major_status)) { OM_uint32 tmp; diff --git a/usr/src/lib/libads/Makefile b/usr/src/lib/libads/Makefile new file mode 100644 index 0000000000..41b86fab1d --- /dev/null +++ b/usr/src/lib/libads/Makefile @@ -0,0 +1,78 @@ +# +# 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 2014 Nexenta Systems, Inc. All rights reserved. +# + +include $(SRC)/lib/Makefile.lib + +HDRS= dsgetdc.h +HDRDIR= common + +ROOTHDRDIR= $(ROOT)/usr/include/ads +ROOTHDRS= $(HDRS:%=$(ROOTHDRDIR)/%) + +# ISA targets +SUBDIRS = $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +DERIVED_FILES= \ + common/ads_priv.h \ + common/adspriv_xdr.c + +RPCGENFLAGS = -CMN + +CLEANFILES += $(DERIVED_FILES) + +all := TARGET = all +clean := TARGET = clean +clobber := TARGET = clobber +install := TARGET = install +lint := TARGET = lint + +.KEEP_STATE: + +all: $(DERIVED_FILES) .WAIT $(SUBDIRS) + +install: all .WAIT $(SUBDIRS) + +install_h: $(DERIVED_FILES) $(ROOTHDRS) + +clean clobber lint: $(SUBDIRS) + +check: $(CHECKHDRS) + +common/ads_priv.h: $(HDRDIR)/ads_priv.x + $(RPCGEN) $(RPCGENFLAGS) -h $(HDRDIR)/ads_priv.x > $@ + +common/adspriv_xdr.c: $(HDRDIR)/ads_priv.x + $(RPCGEN) $(RPCGENFLAGS) -c $(HDRDIR)/ads_priv.x > $@ + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include $(SRC)/Makefile.msg.targ +include $(SRC)/lib/Makefile.targ diff --git a/usr/src/lib/libads/Makefile.com b/usr/src/lib/libads/Makefile.com new file mode 100644 index 0000000000..1e1d7d968d --- /dev/null +++ b/usr/src/lib/libads/Makefile.com @@ -0,0 +1,62 @@ +# +# 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 2014 Nexenta Systems, Inc. All rights reserved. +# + +LIBRARY = libads.a +VERS = .1 +LINT_OBJECTS = dsgetdc.o poke.o +OBJECTS = $(LINT_OBJECTS) adspriv_xdr.o + +include ../../Makefile.lib + +C99MODE= -xc99=%all +C99LMODE= -Xc99=%all + +LIBS = $(DYNLIB) $(LINTLIB) +LDLIBS += -lnsl -lc +SRCDIR = ../common +$(LINTLIB):= SRCS = $(SRCDIR)/$(LINTSRC) + +CFLAGS += $(CCVERBOSE) +CPPFLAGS += -D_REENTRANT -I$(SRCDIR) -I.. +# CPPFLAGS += -I$(SRC)/lib/libldap5/include/ldap + +CERRWARN += -_gcc=-Wno-type-limits +CERRWARN += -_gcc=-Wno-uninitialized +CERRWARN += -_gcc=-Wno-unused-variable + +.KEEP_STATE: + +all: $(LIBS) + +lint := OBJECTS = $(LINT_OBJECTS) + +lint: lintcheck + +#LINTFLAGS += -erroff=E_CONSTANT_CONDITION +#LINTFLAGS64 += -erroff=E_CONSTANT_CONDITION + +include ../../Makefile.targ diff --git a/usr/src/lib/libads/amd64/Makefile b/usr/src/lib/libads/amd64/Makefile new file mode 100644 index 0000000000..6ad55a66d4 --- /dev/null +++ b/usr/src/lib/libads/amd64/Makefile @@ -0,0 +1,31 @@ +# +# 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 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# Copyright 2014 Nexenta Systems, Inc. All rights reserved. +# + +include ../Makefile.com +include ../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) diff --git a/usr/src/lib/libads/common/ads_priv.x b/usr/src/lib/libads/common/ads_priv.x new file mode 100644 index 0000000000..5bcba2033b --- /dev/null +++ b/usr/src/lib/libads/common/ads_priv.x @@ -0,0 +1,107 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +%/* +% * Copyright 2014 Nexenta Systems, Inc. All rights reserved. +% */ + +%/* +% * from ads_priv.x +% * Active Directory Services (ADS) Private interface between +% * libads and the ADS deamon. (RPC over doors) +% */ + +#ifdef RPC_HDR +%/* +% * Declarations for the ADS API +% */ + +#elif RPC_SVC +% +%/* +% * Server side stubs for the ADS API +% */ +% +#elif RPC_CLNT +% +%/* +% * Client side stubs for the ADS API +% */ +% +#elif RPC_XDR +%/* +% * XDR routines for the ADS API +% */ +#endif + +const ADSPRIV_MAX_XFER = 16384; +const ADSPRIV_GUID_LEN = 16; +const ADSPRIV_SOCKADDR_LEN = 256; +const ADSPRIV_STR_MAX = 256; + +typedef opaque adspriv_guid[ADSPRIV_GUID_LEN]; +typedef opaque adspriv_sockaddr[ADSPRIV_SOCKADDR_LEN]; + +/* + * Structure returned from DsGetDcName + * NB: Keep same as DOMAIN_CONTROLLER_INFO + */ +struct adspriv_dcinfo { + string dci_DcName<ADSPRIV_STR_MAX>; + string dci_DcAddr<ADSPRIV_STR_MAX>; + unsigned int dci_AddrType; + adspriv_guid dci_guid; + string dci_DomainName<ADSPRIV_STR_MAX>; + string dci_DnsForestName<ADSPRIV_STR_MAX>; + unsigned int dci_Flags; + string dci_DcSiteName<ADSPRIV_STR_MAX>; + string dci_ClientSiteName<ADSPRIV_STR_MAX>; + adspriv_sockaddr dci_sockaddr; +}; + +/* + * DsForceRediscovery args + */ +struct DsForceRediscoveryArgs { + unsigned int Flags; + string DomainName<ADSPRIV_STR_MAX>; +}; + +/* + * DsGetDcName args, result + */ +struct DsGetDcNameArgs { + string ComputerName<ADSPRIV_STR_MAX>; + string DomainName<ADSPRIV_STR_MAX>; + string DomainGuid<ADSPRIV_STR_MAX>; + string SiteName<ADSPRIV_STR_MAX>; + unsigned int Flags; +}; + +union DsGetDcNameRes switch (int status) { +case 0: + adspriv_dcinfo res0; +default: + void; +}; + +program ADSPRIV_PROGRAM { + version ADSPRIV_V1 { + void + ADSPRIV_NULL(void) = 0; + + int + ADSPRIV_ForceRediscovery(DsForceRediscoveryArgs) = 1; + + DsGetDcNameRes + ADSPRIV_GetDcName(DsGetDcNameArgs) = 2; + } = 1; +} = 100001; diff --git a/usr/src/lib/libads/common/dsgetdc.c b/usr/src/lib/libads/common/dsgetdc.c new file mode 100644 index 0000000000..a8b45707b9 --- /dev/null +++ b/usr/src/lib/libads/common/dsgetdc.c @@ -0,0 +1,162 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + */ + +/* + * MS-compatible Directory Server Discovery API, DsGetDC...() + */ + +#include <stdlib.h> +#include <string.h> +#include <smb/nterror.h> +#include <smb/ntstatus.h> +#include <arpa/inet.h> +#include "dsgetdc.h" +#include "ads_priv.h" +#include <assert.h> + +#define DSGETDC_VALID_FLAGS ( \ + DS_FORCE_REDISCOVERY | \ + DS_DIRECTORY_SERVICE_REQUIRED | \ + DS_DIRECTORY_SERVICE_PREFERRED | \ + DS_GC_SERVER_REQUIRED | \ + DS_PDC_REQUIRED | \ + DS_BACKGROUND_ONLY | \ + DS_IP_REQUIRED | \ + DS_KDC_REQUIRED | \ + DS_TIMESERV_REQUIRED | \ + DS_WRITABLE_REQUIRED | \ + DS_GOOD_TIMESERV_PREFERRED | \ + DS_AVOID_SELF | \ + DS_ONLY_LDAP_NEEDED | \ + DS_IS_FLAT_NAME | \ + DS_IS_DNS_NAME | \ + DS_RETURN_FLAT_NAME | \ + DS_RETURN_DNS_NAME) + +static struct timeval TIMEOUT = { 15, 0 }; + +/* + * The Windows version of this would return a single allocation, + * where any strings pointed to in the returned structure would be + * stored in space following the top-level returned structure. + * This allows NetApiBufferFree() to be the same as free(). + * + * However, we don't have an easy way to do that right now, so + * the dcinfo returned here will be free'd with DsFreeDcInfo(). + */ +uint32_t +_DsGetDcName(const char *ComputerName, + const char *DomainName, const struct uuid *DomainGuid, + const char *SiteName, uint32_t Flags, + DOMAIN_CONTROLLER_INFO **dcinfo) +{ + DsGetDcNameArgs args; + DsGetDcNameRes res; + CLIENT *clnt = NULL; + enum clnt_stat clstat; + + *dcinfo = NULL; + (void) memset(&args, 0, sizeof (args)); + (void) memset(&res, 0, sizeof (res)); + + /* + * Later check for over constrained optional args here, + * and return (ERROR_INVALID_PARAMETER); + */ + + if (Flags & ~DSGETDC_VALID_FLAGS) + return (ERROR_INVALID_FLAGS); + + /* + * Call the ADS deamon. + */ + clnt = clnt_door_create(ADSPRIV_PROGRAM, ADSPRIV_V1, ADSPRIV_MAX_XFER); + if (clnt == NULL) + return (RPC_S_NOT_LISTENING); + + args.ComputerName = (char *)ComputerName; + args.DomainName = (char *)DomainName; + if (DomainGuid != NULL) + (void) memcpy(&args.DomainGuid, DomainGuid, + sizeof (args.DomainGuid)); + args.SiteName = (char *)SiteName; + args.Flags = Flags; + + clstat = clnt_call(clnt, ADSPRIV_GetDcName, + (xdrproc_t)xdr_DsGetDcNameArgs, (caddr_t)&args, + (xdrproc_t)xdr_DsGetDcNameRes, (caddr_t)&res, TIMEOUT); + + clnt_destroy(clnt); + if (clstat != RPC_SUCCESS) + return (RPC_S_CALL_FAILED); + if (res.status != 0) + return (res.status); + + *dcinfo = malloc(sizeof (**dcinfo)); + if (*dcinfo == NULL) + return (ERROR_NOT_ENOUGH_MEMORY); + + /* + * We have taken pains to make these two the same. + * DOMAIN_CONTROLLER_INFO / struct adspriv_dcinfo + */ + /* LINTED E_TRUE_LOGICAL_EXPR */ + assert(sizeof (**dcinfo) == sizeof (res.DsGetDcNameRes_u.res0)); + (void) memcpy(*dcinfo, &res.DsGetDcNameRes_u.res0, sizeof (**dcinfo)); + + /* + * NB: Do NOT xdr_free the result, because we're + * returning a copy of it to the caller. + */ + return (0); +} + +int +DsGetDcName(const char *ComputerName, + const char *DomainName, const struct uuid *DomainGuid, + const char *SiteName, uint32_t Flags, + DOMAIN_CONTROLLER_INFO **dcinfo) +{ + uint32_t status; + int rc; + + status = _DsGetDcName(ComputerName, DomainName, DomainGuid, + SiteName, Flags, dcinfo); + + switch (status) { + case 0: + rc = 0; + break; + case NT_STATUS_NO_SUCH_DOMAIN: /* Specified domain unknown */ + case NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND: + case NT_STATUS_CANT_WAIT: /* or gave up waiting. */ + case NT_STATUS_INVALID_SERVER_STATE: /* not in domain mode. */ + rc = ERROR_NO_SUCH_DOMAIN; + break; + default: + rc = ERROR_INTERNAL_ERROR; + break; + } + return (rc); +} + +void +DsFreeDcInfo(DOMAIN_CONTROLLER_INFO *dci) +{ + if (dci != NULL) { + xdr_free(xdr_DsGetDcNameRes, (char *)dci); + free(dci); + } +} diff --git a/usr/src/lib/libads/common/dsgetdc.h b/usr/src/lib/libads/common/dsgetdc.h new file mode 100644 index 0000000000..175b2cce65 --- /dev/null +++ b/usr/src/lib/libads/common/dsgetdc.h @@ -0,0 +1,161 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + */ + +/* + * Declarations intentionally similar to the MSDN SDK file + * winsdk/Include/DsGetDC.h + */ + + +#ifndef _ADS_DSGETDC_H +#define _ADS_DSGETDC_H + +#include <sys/types.h> +#include <sys/uuid.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Flags to passed to DsGetDcName + */ + +#define DS_FORCE_REDISCOVERY 0x00000001 + +#define DS_DIRECTORY_SERVICE_REQUIRED 0x00000010 +#define DS_DIRECTORY_SERVICE_PREFERRED 0x00000020 +#define DS_GC_SERVER_REQUIRED 0x00000040 +#define DS_PDC_REQUIRED 0x00000080 +#define DS_BACKGROUND_ONLY 0x00000100 +#define DS_IP_REQUIRED 0x00000200 +#define DS_KDC_REQUIRED 0x00000400 +#define DS_TIMESERV_REQUIRED 0x00000800 +#define DS_WRITABLE_REQUIRED 0x00001000 +#define DS_GOOD_TIMESERV_PREFERRED 0x00002000 +#define DS_AVOID_SELF 0x00004000 +#define DS_ONLY_LDAP_NEEDED 0x00008000 + + +#define DS_IS_FLAT_NAME 0x00010000 +#define DS_IS_DNS_NAME 0x00020000 + +#define DS_RETURN_DNS_NAME 0x40000000 +#define DS_RETURN_FLAT_NAME 0x80000000 + +/* + * Structure returned from DsGetDcName + * NB: Keep same as adspriv_dcinfo + */ + +typedef struct _DOMAIN_CONTROLLER_INFO { + char *DomainControllerName; + char *DomainControllerAddress; + uint32_t DomainControllerAddressType; + uuid_t DomainGuid; + char *DomainName; + char *DnsForestName; + uint32_t Flags; + char *DcSiteName; + char *ClientSiteName; + uint8_t _sockaddr[256]; +} DOMAIN_CONTROLLER_INFO, *PDOMAIN_CONTROLLER_INFO; + +/* + * Values for DomainControllerAddressType + */ + +#define DS_INET_ADDRESS 1 +#define DS_NETBIOS_ADDRESS 2 + +/* + * Values for returned Flags + */ + +#define DS_PDC_FLAG 0x00000001 /* DC is PDC of Domain */ +#define DS_GC_FLAG 0x00000004 /* DC is a GC of forest */ +#define DS_LDAP_FLAG 0x00000008 /* supports an LDAP server */ +#define DS_DS_FLAG 0x00000010 /* supports a DS and is a */ + /* Domain Controller */ +#define DS_KDC_FLAG 0x00000020 /* is running KDC service */ +#define DS_TIMESERV_FLAG 0x00000040 /* is running time service */ +#define DS_CLOSEST_FLAG 0x00000080 /* DC is in closest site */ + /* to the client */ +#define DS_WRITABLE_FLAG 0x00000100 /* DC has a writable DS */ +#define DS_GOOD_TIMESERV_FLAG 0x00000200 /* is running time service */ + /* (and has clock hardware) */ +#define DS_NDNC_FLAG 0x00000400 /* DomainName is non-domain */ + /* NC serviced by the */ + /* LDAP server */ +#define DS_PING_FLAGS 0x0000FFFF /* Flags returned on ping */ + +#define DS_DNS_CONTROLLER_FLAG 0x20000000 /* DC Name is a DNS name */ +#define DS_DNS_DOMAIN_FLAG 0x40000000 /* DomainName is a DNS name */ +#define DS_DNS_FOREST_FLAG 0x80000000 /* ForestName is a DNS name */ + + +/* + * Function Prototypes + */ + +/* Offial API. Returns an NT error number. */ +extern int +DsGetDcName(const char *ComputerName, + const char *DomainName, const struct uuid *DomainGuid, + const char *SiteName, uint32_t Flags, + DOMAIN_CONTROLLER_INFO **dcinfo); + +/* internal version of above - returns a detailed NT status */ +extern uint32_t +_DsGetDcName(const char *ComputerName, + const char *DomainName, const struct uuid *DomainGuid, + const char *SiteName, uint32_t Flags, + DOMAIN_CONTROLLER_INFO **dcinfo); + +extern int +DsGetSiteName( + const char *ComputerName, + char **SiteName); + +/* + * XXX: Others from DsGetDc.h we may want later: + * DsValidateSubnetName() + * DsAddressToSiteNames() + * DsAddressToSiteNamesEx() + * DsEnumerateDomainTrusts() + * DsGetForestTrustInformation() + * DsGetDcSiteCoverage() + * DsDeregisterDnsHostRecords() + * DsGetDcOpen(), DsGetDcNext(), DsGetDcClose() + */ + +/* + * Until we can easily allocate a DC Info as one big hunk. + * This will free a DC Info returned by DsGetDcName(). + */ +extern void +DsFreeDcInfo(DOMAIN_CONTROLLER_INFO *); + +/* + * Internal function to force DC Rediscovery. + */ +extern int +_DsForceRediscovery(char *domain, int flags); + +#ifdef __cplusplus +} +#endif + +#endif /* _ADS_DSGETDC_H */ diff --git a/usr/src/lib/libads/common/llib-lads b/usr/src/lib/libads/common/llib-lads new file mode 100644 index 0000000000..7cdc5c1188 --- /dev/null +++ b/usr/src/lib/libads/common/llib-lads @@ -0,0 +1,31 @@ +/* + * 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 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + */ + +/* LINTLIBRARY */ +/* PROTOLIB1 */ + +#include "dsgetdc.h" diff --git a/usr/src/lib/libads/common/mapfile-vers b/usr/src/lib/libads/common/mapfile-vers new file mode 100644 index 0000000000..9773b4d315 --- /dev/null +++ b/usr/src/lib/libads/common/mapfile-vers @@ -0,0 +1,54 @@ +# +# 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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2014 Nexenta Systems, Inc. All rights reserved. +# + +# +# MAPFILE HEADER START +# +# WARNING: STOP NOW. DO NOT MODIFY THIS FILE. +# Object versioning must comply with the rules detailed in +# +# usr/src/lib/README.mapfiles +# +# You should not be making modifications here until you've read the most current +# copy of that file. If you need help, contact a gatekeeper for guidance. +# +# MAPFILE HEADER END +# + +$mapfile_version 2 + +SYMBOL_VERSION SUNWprivate { + global: + _DsForceRediscovery; + _DsGetDcName; + DsFreeDcInfo; + DsGetDcName; + xdr_adspriv_dcinfo; + xdr_DsForceRediscoveryArgs; + xdr_DsGetDcNameArgs; + xdr_DsGetDcNameRes; + local: + *; +}; diff --git a/usr/src/lib/libads/common/poke.c b/usr/src/lib/libads/common/poke.c new file mode 100644 index 0000000000..3c3b45d24f --- /dev/null +++ b/usr/src/lib/libads/common/poke.c @@ -0,0 +1,58 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + */ + +/* + * Private API to force DC Rediscovery. + */ + +#include <stdlib.h> +#include <string.h> +#include <smb/nterror.h> +#include <arpa/inet.h> +#include "dsgetdc.h" +#include "ads_priv.h" +#include <assert.h> + +static struct timeval TIMEOUT = { 15, 0 }; + +int +_DsForceRediscovery(char *domain, int flags) +{ + DsForceRediscoveryArgs args; + CLIENT *clnt = NULL; + enum clnt_stat clstat; + int res; + + (void) memset(&args, 0, sizeof (args)); + args.Flags = flags; + args.DomainName = domain; + + /* + * Call the ADS deamon. + */ + clnt = clnt_door_create(ADSPRIV_PROGRAM, ADSPRIV_V1, ADSPRIV_MAX_XFER); + if (clnt == NULL) + return (RPC_S_NOT_LISTENING); + + clstat = clnt_call(clnt, ADSPRIV_ForceRediscovery, + (xdrproc_t)xdr_DsForceRediscoveryArgs, (caddr_t)&args, + (xdrproc_t)xdr_int, (caddr_t)&res, TIMEOUT); + + clnt_destroy(clnt); + if (clstat != RPC_SUCCESS) + return (RPC_S_CALL_FAILED); + + return (res); +} diff --git a/usr/src/lib/libads/i386/Makefile b/usr/src/lib/libads/i386/Makefile new file mode 100644 index 0000000000..3cf14ec6d8 --- /dev/null +++ b/usr/src/lib/libads/i386/Makefile @@ -0,0 +1,30 @@ +# +# 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 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# Copyright 2014 Nexenta Systems, Inc. All rights reserved. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libads/sparc/Makefile b/usr/src/lib/libads/sparc/Makefile new file mode 100644 index 0000000000..3cf14ec6d8 --- /dev/null +++ b/usr/src/lib/libads/sparc/Makefile @@ -0,0 +1,30 @@ +# +# 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 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# Copyright 2014 Nexenta Systems, Inc. All rights reserved. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libads/sparcv9/Makefile b/usr/src/lib/libads/sparcv9/Makefile new file mode 100644 index 0000000000..6ad55a66d4 --- /dev/null +++ b/usr/src/lib/libads/sparcv9/Makefile @@ -0,0 +1,31 @@ +# +# 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 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# Copyright 2014 Nexenta Systems, Inc. All rights reserved. +# + +include ../Makefile.com +include ../../Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) diff --git a/usr/src/lib/libadutils/Makefile.com b/usr/src/lib/libadutils/Makefile.com index 8776b44c86..67014cb75e 100644 --- a/usr/src/lib/libadutils/Makefile.com +++ b/usr/src/lib/libadutils/Makefile.com @@ -22,11 +22,13 @@ # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright 2014 Nexenta Systems, Inc. All rights reserved. +# LIBRARY = libadutils.a VERS = .1 -OBJECTS = adutils.o addisc.o adutils_threadfuncs.o -LINT_OBJECTS = adutils.o addisc.o adutils_threadfuncs.o +OBJECTS = adutils.o addisc.o adutils_threadfuncs.o \ + ldap_ping.o srv_query.o include ../../Makefile.lib @@ -34,18 +36,17 @@ C99MODE= -xc99=%all C99LMODE= -Xc99=%all LIBS = $(DYNLIB) $(LINTLIB) -LDLIBS += -lc -lldap -lresolv -lsocket -lnsl +LDLIBS += -lldap -lresolv -lsocket -lnsl -lc SRCDIR = ../common $(LINTLIB):= SRCS = $(SRCDIR)/$(LINTSRC) CFLAGS += $(CCVERBOSE) -CPPFLAGS += -D_REENTRANT -I$(SRCDIR) -I$(SRC)/lib/libldap5/include/ldap +CPPFLAGS += -D_REENTRANT -I$(SRCDIR) +CPPFLAGS += -I$(SRC)/lib/libldap5/include/ldap CERRWARN += -_gcc=-Wno-type-limits CERRWARN += -_gcc=-Wno-uninitialized -lint := OBJECTS = $(LINT_OBJECTS) - .KEEP_STATE: all: $(LIBS) diff --git a/usr/src/lib/libadutils/common/addisc.c b/usr/src/lib/libadutils/common/addisc.c index a0bbfe1c0f..f6ac98d11a 100644 --- a/usr/src/lib/libadutils/common/addisc.c +++ b/usr/src/lib/libadutils/common/addisc.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ /* @@ -83,12 +84,10 @@ #include <assert.h> #include <stdlib.h> #include <net/if.h> -#include <net/if.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/sockio.h> #include <netinet/in.h> -#include <netinet/in.h> #include <arpa/inet.h> #include <arpa/nameser.h> #include <resolv.h> @@ -96,11 +95,14 @@ #include <ctype.h> #include <errno.h> #include <ldap.h> +#include <note.h> #include <sasl/sasl.h> #include <sys/u8_textprep.h> #include <syslog.h> +#include <uuid/uuid.h> +#include <ads/dsgetdc.h> #include "adutils_impl.h" -#include "addisc.h" +#include "addisc_impl.h" /* * These set some sanity policies for discovery. After a discovery @@ -113,70 +115,10 @@ #define MINIMUM_TTL (5 * 60) #define MAXIMUM_TTL (20 * 60) -enum ad_item_state { - AD_STATE_INVALID = 0, /* The value is not valid */ - AD_STATE_FIXED, /* The value was fixed by caller */ - AD_STATE_AUTO /* The value is auto discovered */ - }; - -enum ad_data_type { - AD_STRING = 123, - AD_DIRECTORY, - AD_DOMAINS_IN_FOREST, - AD_TRUSTED_DOMAINS - }; - - -typedef struct ad_subnet { - char subnet[24]; -} ad_subnet_t; - - -typedef struct ad_item { - enum ad_item_state state; - enum ad_data_type type; - void *value; - time_t expires; - unsigned int version; /* Version is only changed */ - /* if the value changes */ -#define PARAM1 0 -#define PARAM2 1 - int param_version[2]; - /* These holds the version of */ - /* dependents so that a dependent */ - /* change can be detected */ -} ad_item_t; - -typedef struct ad_disc { - struct __res_state res_state; - int res_ninitted; - ad_subnet_t *subnets; - boolean_t subnets_changed; - time_t subnets_last_check; - time_t expires_not_before; - time_t expires_not_after; - ad_item_t domain_name; /* DNS hostname string */ - ad_item_t domain_controller; /* Directory hostname and */ - /* port array */ - ad_item_t site_name; /* String */ - ad_item_t forest_name; /* DNS forestname string */ - ad_item_t global_catalog; /* Directory hostname and */ - /* port array */ - ad_item_t domains_in_forest; /* DNS domainname and SID */ - /* array */ - ad_item_t trusted_domains; /* DNS domainname and trust */ - /* direction array */ - /* Site specfic versions */ - ad_item_t site_domain_controller; /* Directory hostname and */ - /* port array */ - ad_item_t site_global_catalog; /* Directory hostname and */ - /* port array */ - int debug[AD_DEBUG_MAX+1]; /* Debug levels */ -} ad_disc; - #define DNS_MAX_NAME NS_MAXDNAME +#define GC_PORT 3268 /* SRV RR names for various queries */ #define LDAP_SRV_HEAD "_ldap._tcp." @@ -194,8 +136,23 @@ typedef struct ad_disc { * We try res_ninit() whenever we don't have one. res_ninit() fails if * idmapd is running before the network is up! */ -#define DO_RES_NINIT(ctx) if (!(ctx)->res_ninitted) \ - (ctx)->res_ninitted = (res_ninit(&ctx->res_state) != -1) +#define DO_RES_NINIT(ctx) \ + if (!(ctx)->res_ninitted) \ + (void) do_res_ninit(ctx) + +#define DO_GETNAMEINFO(b, l, s) \ + if (ad_disc_getnameinfo(b, l, s) != 0) \ + (void) strlcpy(b, "?", l) + +#define DEBUG1STATUS(ctx, ...) do { \ + if (DBG(DISC, 1)) \ + logger(LOG_DEBUG, __VA_ARGS__); \ + if (ctx->status_fp) { \ + (void) fprintf(ctx->status_fp, __VA_ARGS__); \ + (void) fprintf(ctx->status_fp, "\n"); \ + } \ + _NOTE(CONSTCOND) \ +} while (0) #define is_fixed(item) \ ((item)->state == AD_STATE_FIXED) @@ -203,13 +160,65 @@ typedef struct ad_disc { #define is_changed(item, num, param) \ ((item)->param_version[num] != (param)->version) +void * uuid_dup(void *); + +static ad_item_t *validate_SiteName(ad_disc_t ctx); +static ad_item_t *validate_PreferredDC(ad_disc_t ctx); + /* * Function definitions */ -static ad_item_t * -validate_SiteName(ad_disc_t ctx); +static int +do_res_ninit(ad_disc_t ctx) +{ + int rc; + + rc = res_ninit(&ctx->res_state); + if (rc != 0) + return (rc); + ctx->res_ninitted = 1; + /* + * The SRV records returnd by AD can be larger than 512 bytes, + * so we'd like to use TCP for those searches. Unfortunately, + * the TCP connect timeout seen by the resolver is very long + * (more than a couple minutes) and we can't wait that long. + * Don't do use TCP until we can override the timeout. + * + * Note that some queries will try TCP anyway. + */ +#if 0 + ctx->res_state.options |= RES_USEVC; +#endif + return (0); +} + +/* + * Private getnameinfo(3socket) variant tailored to our needs. + */ +int +ad_disc_getnameinfo(char *obuf, int olen, struct sockaddr_storage *ss) +{ + struct sockaddr *sa; + int eai, slen; + + sa = (void *)ss; + switch (sa->sa_family) { + case AF_INET: + slen = sizeof (struct sockaddr_in); + break; + case AF_INET6: + slen = sizeof (struct sockaddr_in6); + break; + default: + return (EAI_FAMILY); + } + + eai = getnameinfo(sa, slen, obuf, olen, NULL, 0, NI_NUMERICHOST); + + return (eai); +} static void update_version(ad_item_t *item, int num, ad_item_t *param) @@ -240,6 +249,8 @@ update_item(ad_item_t *item, void *value, enum ad_item_state state, if (item->value != NULL && value != NULL) { if ((item->type == AD_STRING && strcmp(item->value, value) != 0) || + (item->type == AD_UUID && + ad_disc_compare_uuid(item->value, value) != 0)|| (item->type == AD_DIRECTORY && ad_disc_compare_ds(item->value, value) != 0)|| (item->type == AD_DOMAINS_IN_FOREST && @@ -262,10 +273,29 @@ update_item(ad_item_t *item, void *value, enum ad_item_state state, item->expires = time(NULL) + ttl; } +/* Compare UUIDs */ +int +ad_disc_compare_uuid(uuid_t *u1, uuid_t *u2) +{ + int rc; + + rc = memcmp(u1, u2, UUID_LEN); + return (rc); +} + +void * +uuid_dup(void *src) +{ + void *dst; + dst = malloc(UUID_LEN); + if (dst != NULL) + (void) memcpy(dst, src, UUID_LEN); + return (dst); +} /* Compare DS lists */ int -ad_disc_compare_ds(idmap_ad_disc_ds_t *ds1, idmap_ad_disc_ds_t *ds2) +ad_disc_compare_ds(ad_disc_ds_t *ds1, ad_disc_ds_t *ds2) { int i, j; int num_ds1; @@ -298,17 +328,17 @@ ad_disc_compare_ds(idmap_ad_disc_ds_t *ds1, idmap_ad_disc_ds_t *ds2) /* Copy a list of DSs */ -static idmap_ad_disc_ds_t * -ds_dup(const idmap_ad_disc_ds_t *srv) +static ad_disc_ds_t * +ds_dup(const ad_disc_ds_t *srv) { int i; int size; - idmap_ad_disc_ds_t *new = NULL; + ad_disc_ds_t *new = NULL; for (i = 0; srv[i].host[0] != '\0'; i++) continue; - size = (i + 1) * sizeof (idmap_ad_disc_ds_t); + size = (i + 1) * sizeof (ad_disc_ds_t); new = malloc(size); if (new != NULL) (void) memcpy(new, srv, size); @@ -603,237 +633,13 @@ DN_to_DNS(const char *dn_name) } -/* Make a list of subnet object DNs from a list of subnets */ -static char ** -subnets_to_DNs(ad_subnet_t *subnets, const char *base_dn) -{ - char **results; - int i, j; - - for (i = 0; subnets[i].subnet[0] != '\0'; i++) - continue; - - results = calloc(i + 1, sizeof (char *)); - if (results == NULL) - return (NULL); - - for (i = 0; subnets[i].subnet[0] != '\0'; i++) { - (void) asprintf(&results[i], "CN=%s,CN=Subnets,CN=Sites,%s", - subnets[i].subnet, base_dn); - if (results[i] == NULL) { - for (j = 0; j < i; j++) - free(results[j]); - free(results); - return (NULL); - } - } - - return (results); -} - - -/* Compare SRC RRs; used with qsort() */ -static int -srvcmp(idmap_ad_disc_ds_t *s1, idmap_ad_disc_ds_t *s2) -{ - if (s1->priority < s2->priority) - return (1); - else if (s1->priority > s2->priority) - return (-1); - - if (s1->weight < s2->weight) - return (1); - else if (s1->weight > s2->weight) - return (-1); - - return (0); -} - - -/* - * Query or search the SRV RRs for a given name. - * - * If name == NULL then search (as in res_nsearch(3RESOLV), honoring any - * search list/option), else query (as in res_nquery(3RESOLV)). - * - * The output TTL will be the one of the SRV RR with the lowest TTL. - */ -idmap_ad_disc_ds_t * -srv_query(res_state state, const char *svc_name, const char *dname, - char **rrname, uint32_t *ttl) -{ - idmap_ad_disc_ds_t *srv; - idmap_ad_disc_ds_t *srv_res = NULL; - union { - HEADER hdr; - uchar_t buf[NS_MAXMSG]; - } msg; - int len, cnt, qdcount, ancount; - uchar_t *ptr, *eom; - uchar_t *end; - uint16_t type; - /* LINTED E_FUNC_SET_NOT_USED */ - uint16_t class; - uint32_t rttl; - uint16_t size; - char namebuf[NS_MAXDNAME]; - - if (state == NULL) - return (NULL); - - /* Set negative result TTL */ - *ttl = 5 * 60; - - /* 1. query necessary resource records */ - - /* Search, querydomain or query */ - if (rrname != NULL) { - *rrname = NULL; - if (DBG(DNS, 1)) { - logger(LOG_DEBUG, "Looking for SRV RRs '%s.*'", - svc_name); - } - len = res_nsearch(state, svc_name, C_IN, T_SRV, - msg.buf, sizeof (msg.buf)); - if (len < 0) { - if (DBG(DNS, 0)) { - logger(LOG_DEBUG, - "DNS search for '%s' failed (%s)", - svc_name, hstrerror(state->res_h_errno)); - } - return (NULL); - } - } else if (dname != NULL) { - if (DBG(DNS, 1)) { - logger(LOG_DEBUG, "Looking for SRV RRs '%s.%s' ", - svc_name, dname); - } - - len = res_nquerydomain(state, svc_name, dname, C_IN, T_SRV, - msg.buf, sizeof (msg.buf)); - - if (len < 0) { - if (DBG(DNS, 0)) { - logger(LOG_DEBUG, "DNS: %s.%s: %s", - svc_name, dname, - hstrerror(state->res_h_errno)); - } - return (NULL); - } - } - - if (len > sizeof (msg.buf)) { - logger(LOG_ERR, - "DNS query %ib message doesn't fit into %ib buffer", - len, sizeof (msg.buf)); - return (NULL); - } - - /* 2. parse the reply, skip header and question sections */ - - ptr = msg.buf + sizeof (msg.hdr); - eom = msg.buf + len; - qdcount = ntohs(msg.hdr.qdcount); - ancount = ntohs(msg.hdr.ancount); - - for (cnt = qdcount; cnt > 0; --cnt) { - if ((len = dn_skipname(ptr, eom)) < 0) { - logger(LOG_ERR, "DNS query invalid message format"); - return (NULL); - } - ptr += len + QFIXEDSZ; - } - - /* 3. walk through the answer section */ - - srv_res = calloc(ancount + 1, sizeof (idmap_ad_disc_ds_t)); - if (srv_res == NULL) { - logger(LOG_ERR, "Out of memory"); - return (NULL); - } - - *ttl = (uint32_t)-1; - - for (srv = srv_res, cnt = ancount; - cnt > 0; --cnt, srv++) { - - len = dn_expand(msg.buf, eom, ptr, namebuf, - sizeof (namebuf)); - if (len < 0) { - logger(LOG_ERR, "DNS query invalid message format"); - goto err; - } - if (rrname != NULL && *rrname == NULL) { - *rrname = strdup(namebuf); - if (*rrname == NULL) { - logger(LOG_ERR, "Out of memory"); - goto err; - } - } - ptr += len; - NS_GET16(type, ptr); - NS_GET16(class, ptr); - NS_GET32(rttl, ptr); - NS_GET16(size, ptr); - if ((end = ptr + size) > eom) { - logger(LOG_ERR, "DNS query invalid message format"); - goto err; - } - - if (type != T_SRV) { - ptr = end; - continue; - } - - NS_GET16(srv->priority, ptr); - NS_GET16(srv->weight, ptr); - NS_GET16(srv->port, ptr); - len = dn_expand(msg.buf, eom, ptr, srv->host, - sizeof (srv->host)); - if (len < 0) { - logger(LOG_ERR, "DNS query invalid SRV record"); - goto err; - } - - if (rttl < *ttl) - *ttl = rttl; - - if (DBG(DNS, 1)) { - logger(LOG_DEBUG, " %s", namebuf); - logger(LOG_DEBUG, - " ttl=%d pri=%d weight=%d %s:%d", - rttl, srv->priority, srv->weight, - srv->host, srv->port); - } - - /* 3. move ptr to the end of current record */ - - ptr = end; - } - - if (ancount > 1) - qsort(srv_res, ancount, sizeof (*srv_res), - (int (*)(const void *, const void *))srvcmp); - - return (srv_res); - -err: - free(srv_res); - if (rrname != NULL) { - free(*rrname); - *rrname = NULL; - } - return (NULL); -} - - /* * A utility function to bind to a Directory server */ static LDAP * -ldap_lookup_init(idmap_ad_disc_ds_t *ds) +ldap_lookup_init(ad_disc_ds_t *ds) { int i; int rc, ldversion; @@ -844,6 +650,11 @@ ldap_lookup_init(idmap_ad_disc_ds_t *ds) LDAP *ld = NULL; for (i = 0; ds[i].host[0] != '\0'; i++) { + if (DBG(LDAP, 2)) { + logger(LOG_DEBUG, "adutils: ldap_lookup_init, host %s", + ds[i].host); + } + ld = ldap_init(ds[i].host, ds[i].port); if (ld == NULL) { if (DBG(LDAP, 1)) { @@ -896,60 +707,6 @@ ldap_lookup_init(idmap_ad_disc_ds_t *ds) /* - * A utility function to get the value of some attribute of one of one - * or more AD LDAP objects named by the dn_list; first found one wins. - */ -static char * -ldap_lookup_entry_attr(LDAP **ld, idmap_ad_disc_ds_t *domainControllers, - char **dn_list, char *attr) -{ - int i; - int rc; - int scope = LDAP_SCOPE_BASE; - char *attrs[2]; - LDAPMessage *results = NULL; - LDAPMessage *entry; - char **values = NULL; - char *val = NULL; - - attrs[0] = attr; - attrs[1] = NULL; - - if (*ld == NULL) - *ld = ldap_lookup_init(domainControllers); - - if (*ld == NULL) - return (NULL); - - for (i = 0; dn_list[i] != NULL; i++) { - rc = ldap_search_s(*ld, dn_list[i], scope, - "(objectclass=*)", attrs, 0, &results); - if (rc == LDAP_SUCCESS) { - for (entry = ldap_first_entry(*ld, results); - entry != NULL && values == NULL; - entry = ldap_next_entry(*ld, entry)) { - values = ldap_get_values( - *ld, entry, attr); - } - - if (values != NULL) { - (void) ldap_msgfree(results); - val = strdup(values[0]); - ldap_value_free(values); - return (val); - } - } - if (results != NULL) { - (void) ldap_msgfree(results); - results = NULL; - } - } - - return (NULL); -} - - -/* * Lookup the trusted domains in the global catalog. * * Returns: @@ -958,7 +715,7 @@ ldap_lookup_entry_attr(LDAP **ld, idmap_ad_disc_ds_t *domainControllers, * NULL an error occured */ ad_disc_trusteddomains_t * -ldap_lookup_trusted_domains(LDAP **ld, idmap_ad_disc_ds_t *globalCatalog, +ldap_lookup_trusted_domains(LDAP **ld, ad_disc_ds_t *globalCatalog, char *base_dn) { int scope = LDAP_SCOPE_SUBTREE; @@ -978,8 +735,10 @@ ldap_lookup_trusted_domains(LDAP **ld, idmap_ad_disc_ds_t *globalCatalog, if (*ld == NULL) *ld = ldap_lookup_init(globalCatalog); - if (*ld == NULL) + if (*ld == NULL) { + logger(LOG_ERR, "adutils: ldap_lookup_init failed"); return (NULL); + } attrs[0] = "trustPartner"; attrs[1] = "trustDirection"; @@ -1039,6 +798,9 @@ ldap_lookup_trusted_domains(LDAP **ld, idmap_ad_disc_ds_t *globalCatalog, trusted_domains = calloc(1, sizeof (ad_disc_trusteddomains_t)); if (DBG(DISC, 1)) logger(LOG_DEBUG, " not found"); + } else { + if (DBG(DISC, 1)) + logger(LOG_DEBUG, " rc=%d", rc); } if (results != NULL) (void) ldap_msgfree(results); @@ -1051,7 +813,7 @@ ldap_lookup_trusted_domains(LDAP **ld, idmap_ad_disc_ds_t *globalCatalog, * This functions finds all the domains in a forest. */ ad_disc_domainsinforest_t * -ldap_lookup_domains_in_forest(LDAP **ld, idmap_ad_disc_ds_t *globalCatalogs) +ldap_lookup_domains_in_forest(LDAP **ld, ad_disc_ds_t *globalCatalogs) { static char *attrs[] = { "objectSid", @@ -1064,27 +826,34 @@ ldap_lookup_domains_in_forest(LDAP **ld, idmap_ad_disc_ds_t *globalCatalogs) int nresults; ad_disc_domainsinforest_t *domains = NULL; - if (DBG(DISC, 2)) + if (DBG(DISC, 1)) logger(LOG_DEBUG, "Looking for domains in forest..."); if (*ld == NULL) *ld = ldap_lookup_init(globalCatalogs); - if (*ld == NULL) + if (*ld == NULL) { + logger(LOG_ERR, "adutils: ldap_lookup_init failed"); return (NULL); + } /* Find domains */ rc = ldap_search_s(*ld, "", LDAP_SCOPE_SUBTREE, "(objectClass=Domain)", attrs, 0, &result); + if (rc != LDAP_SUCCESS) { + logger(LOG_ERR, "adutils: ldap_search, rc=%d", rc); + goto err; + } if (DBG(DISC, 1)) logger(LOG_DEBUG, "Domains in forest:"); - if (rc != LDAP_SUCCESS) - goto err; nresults = ldap_count_entries(*ld, result); domains = calloc(nresults + 1, sizeof (*domains)); - if (domains == NULL) + if (domains == NULL) { + if (DBG(DISC, 1)) + logger(LOG_DEBUG, " (nomem)"); goto err; + } for (entry = ldap_first_entry(*ld, result); entry != NULL; @@ -1162,7 +931,9 @@ ad_disc_init(void) DO_RES_NINIT(ctx); ctx->domain_name.type = AD_STRING; + ctx->domain_guid.type = AD_UUID; ctx->domain_controller.type = AD_DIRECTORY; + ctx->preferred_dc.type = AD_DIRECTORY; ctx->site_name.type = AD_STRING; ctx->forest_name.type = AD_STRING; ctx->global_catalog.type = AD_DIRECTORY; @@ -1189,9 +960,15 @@ ad_disc_fini(ad_disc_t ctx) if (ctx->domain_name.value != NULL) free(ctx->domain_name.value); + if (ctx->domain_guid.value != NULL) + free(ctx->domain_guid.value); + if (ctx->domain_controller.value != NULL) free(ctx->domain_controller.value); + if (ctx->preferred_dc.value != NULL) + free(ctx->preferred_dc.value); + if (ctx->site_name.value != NULL) free(ctx->site_name.value); @@ -1220,17 +997,25 @@ ad_disc_fini(ad_disc_t ctx) void ad_disc_refresh(ad_disc_t ctx) { - if (ctx->res_ninitted) + if (ctx->res_ninitted) { res_ndestroy(&ctx->res_state); + ctx->res_ninitted = 0; + } (void) memset(&ctx->res_state, 0, sizeof (ctx->res_state)); - ctx->res_ninitted = res_ninit(&ctx->res_state) != -1; + DO_RES_NINIT(ctx); if (ctx->domain_name.state == AD_STATE_AUTO) ctx->domain_name.state = AD_STATE_INVALID; + if (ctx->domain_guid.state == AD_STATE_AUTO) + ctx->domain_guid.state = AD_STATE_INVALID; + if (ctx->domain_controller.state == AD_STATE_AUTO) ctx->domain_controller.state = AD_STATE_INVALID; + if (ctx->preferred_dc.state == AD_STATE_AUTO) + ctx->preferred_dc.state = AD_STATE_INVALID; + if (ctx->site_name.state == AD_STATE_AUTO) ctx->site_name.state = AD_STATE_INVALID; @@ -1270,15 +1055,77 @@ ad_disc_done(ad_disc_t ctx) ctx->expires_not_after = now + MAXIMUM_TTL; } +static void +log_cds(ad_disc_t ctx, ad_disc_cds_t *cds) +{ + char buf[INET6_ADDRSTRLEN]; + struct addrinfo *ai; + + if (!DBG(DISC, 1) && ctx->status_fp == NULL) + return; + + DEBUG1STATUS(ctx, "Candidate servers:"); + if (cds->cds_ds.host[0] == '\0') { + DEBUG1STATUS(ctx, " (empty list)"); + return; + } + + while (cds->cds_ds.host[0] != '\0') { + + DEBUG1STATUS(ctx, " %s p=%d w=%d", + cds->cds_ds.host, + cds->cds_ds.priority, + cds->cds_ds.weight); + + ai = cds->cds_ai; + if (ai == NULL) { + DEBUG1STATUS(ctx, " (no address)"); + } + while (ai != NULL) { + int eai; + + eai = getnameinfo(ai->ai_addr, ai->ai_addrlen, + buf, sizeof (buf), NULL, 0, NI_NUMERICHOST); + if (eai != 0) + (void) strlcpy(buf, "?", sizeof (buf)); + + DEBUG1STATUS(ctx, " %s", buf); + ai = ai->ai_next; + } + cds++; + } +} + +static void +log_ds(ad_disc_t ctx, ad_disc_ds_t *ds) +{ + char buf[INET6_ADDRSTRLEN]; + + if (!DBG(DISC, 1) && ctx->status_fp == NULL) + return; + + DEBUG1STATUS(ctx, "Responding servers:"); + if (ds->host[0] == '\0') { + DEBUG1STATUS(ctx, " (empty list)"); + return; + } + + while (ds->host[0] != '\0') { + + DEBUG1STATUS(ctx, " %s", ds->host); + DO_GETNAMEINFO(buf, sizeof (buf), &ds->addr); + DEBUG1STATUS(ctx, " %s", buf); + + ds++; + } +} /* Discover joined Active Directory domainName */ static ad_item_t * validate_DomainName(ad_disc_t ctx) { - idmap_ad_disc_ds_t *domain_controller = NULL; char *dname, *srvname; - uint32_t ttl = 0; - int len; + int len, rc; if (is_valid(&ctx->domain_name)) return (&ctx->domain_name); @@ -1286,23 +1133,21 @@ validate_DomainName(ad_disc_t ctx) /* Try to find our domain by searching for DCs for it */ DO_RES_NINIT(ctx); - if (DBG(DISC, 2)) + if (DBG(DISC, 1)) logger(LOG_DEBUG, "Looking for our AD domain name..."); - domain_controller = srv_query(&ctx->res_state, - LDAP_SRV_HEAD DC_SRV_TAIL, - ctx->domain_name.value, &srvname, &ttl); + rc = srv_getdom(&ctx->res_state, + LDAP_SRV_HEAD DC_SRV_TAIL, &srvname); /* * If we can't find DCs by via res_nsearch() then there's no * point in trying anything else to discover the AD domain name. */ - if (domain_controller == NULL) { + if (rc < 0) { if (DBG(DISC, 1)) logger(LOG_DEBUG, "Can't find our domain name."); return (NULL); } - free(domain_controller); /* * We have the FQDN of the SRV RR name, so now we extract the * domainname suffix from it. @@ -1324,7 +1169,14 @@ validate_DomainName(ad_disc_t ctx) if (DBG(DISC, 1)) logger(LOG_DEBUG, "Our domain name: %s", dname); - update_item(&ctx->domain_name, dname, AD_STATE_AUTO, ttl); + + /* + * There is no "time to live" on the discovered domain, + * so passing zero as TTL here, making it non-expiring. + * Note that current consumers do not auto-discover the + * domain name, though a future installer could. + */ + update_item(&ctx->domain_name, dname, AD_STATE_AUTO, 0); return (&ctx->domain_name); } @@ -1354,12 +1206,16 @@ ad_disc_get_DomainName(ad_disc_t ctx, boolean_t *auto_discovered) static ad_item_t * validate_DomainController(ad_disc_t ctx, enum ad_disc_req req) { - uint32_t ttl = 0; - idmap_ad_disc_ds_t *domain_controller = NULL; + ad_disc_ds_t *dc = NULL; + ad_disc_cds_t *cdc = NULL; boolean_t validate_global = B_FALSE; boolean_t validate_site = B_FALSE; ad_item_t *domain_name_item; + char *domain_name; ad_item_t *site_name_item = NULL; + char *site_name; + ad_item_t *prefer_dc_item; + ad_disc_ds_t *prefer_dc = NULL; /* If the values is fixed there will not be a site specific version */ if (is_fixed(&ctx->domain_controller)) @@ -1368,12 +1224,17 @@ validate_DomainController(ad_disc_t ctx, enum ad_disc_req req) domain_name_item = validate_DomainName(ctx); if (domain_name_item == NULL) return (NULL); + domain_name = (char *)domain_name_item->value; + + /* Get (optional) preferred DC. */ + prefer_dc_item = validate_PreferredDC(ctx); + if (prefer_dc_item != NULL) + prefer_dc = prefer_dc_item->value; if (req == AD_DISC_GLOBAL) validate_global = B_TRUE; else { - site_name_item = validate_SiteName(ctx); - if (site_name_item != NULL) + if (is_fixed(&ctx->site_name)) validate_site = B_TRUE; else if (req == AD_DISC_PREFER_SITE) validate_global = B_TRUE; @@ -1383,43 +1244,44 @@ validate_DomainController(ad_disc_t ctx, enum ad_disc_req req) if (!is_valid(&ctx->domain_controller) || is_changed(&ctx->domain_controller, PARAM1, domain_name_item)) { - if (DBG(DISC, 2)) { - logger(LOG_DEBUG, "Looking for DCs for %s", - domain_name_item->value); - } + /* * Lookup DNS SRV RR named * _ldap._tcp.dc._msdcs.<DomainName> */ + DEBUG1STATUS(ctx, "DNS SRV query, dom=%s", + domain_name); DO_RES_NINIT(ctx); - domain_controller = srv_query(&ctx->res_state, + cdc = srv_query(&ctx->res_state, LDAP_SRV_HEAD DC_SRV_TAIL, - domain_name_item->value, NULL, &ttl); + domain_name, prefer_dc); - if (DBG(DISC, 1)) { - logger(LOG_DEBUG, "DCs for %s:", - domain_name_item->value); - } - if (domain_controller == NULL) { - if (DBG(DISC, 1)) - logger(LOG_DEBUG, " not found"); + if (cdc == NULL) { + DEBUG1STATUS(ctx, "(no DNS response)"); return (NULL); } + log_cds(ctx, cdc); - if (DBG(DISC, 1)) { - int i; - - for (i = 0; - domain_controller[i].host[0] != '\0'; - i++) { - logger(LOG_DEBUG, " %s:%d", - domain_controller[i].host, - domain_controller[i].port); - } + /* + * Filter out unresponsive servers, and + * save the domain info we get back. + */ + dc = ldap_ping( + ctx, + cdc, + domain_name, + DS_DS_FLAG); + srv_free(cdc); + cdc = NULL; + + if (dc == NULL) { + DEBUG1STATUS(ctx, "(no LDAP response)"); + return (NULL); } + log_ds(ctx, dc); - update_item(&ctx->domain_controller, domain_controller, - AD_STATE_AUTO, ttl); + update_item(&ctx->domain_controller, dc, + AD_STATE_AUTO, dc->ttl); update_version(&ctx->domain_controller, PARAM1, domain_name_item); } @@ -1427,54 +1289,55 @@ validate_DomainController(ad_disc_t ctx, enum ad_disc_req req) } if (validate_site) { + site_name_item = &ctx->site_name; + site_name = (char *)site_name_item->value; + if (!is_valid(&ctx->site_domain_controller) || is_changed(&ctx->site_domain_controller, PARAM1, domain_name_item) || is_changed(&ctx->site_domain_controller, PARAM2, site_name_item)) { char rr_name[DNS_MAX_NAME]; - if (DBG(DISC, 2)) { - logger(LOG_DEBUG, - "Looking for DCs for %s in %s", - domain_name_item->value, - site_name_item->value); - } + /* * Lookup DNS SRV RR named * _ldap._tcp.<SiteName>._sites.dc._msdcs.<DomainName> */ + DEBUG1STATUS(ctx, "DNS SRV query, dom=%s, site=%s", + domain_name, site_name); (void) snprintf(rr_name, sizeof (rr_name), LDAP_SRV_HEAD SITE_SRV_MIDDLE DC_SRV_TAIL, - site_name_item->value); + site_name); DO_RES_NINIT(ctx); - domain_controller = srv_query(&ctx->res_state, rr_name, - domain_name_item->value, NULL, &ttl); - if (DBG(DISC, 1)) { - logger(LOG_DEBUG, - "DCs for %s in %s", - domain_name_item->value, - site_name_item->value); - } - if (domain_controller == NULL) { - if (DBG(DISC, 1)) - logger(LOG_DEBUG, " not found"); + cdc = srv_query(&ctx->res_state, rr_name, + domain_name, prefer_dc); + + if (cdc == NULL) { + DEBUG1STATUS(ctx, "(no DNS response)"); return (NULL); } + log_cds(ctx, cdc); - if (DBG(DISC, 1)) { - int i; - - for (i = 0; - domain_controller[i].host[0] != '\0'; - i++) { - logger(LOG_DEBUG, " %s:%d", - domain_controller[i].host, - domain_controller[i].port); - } + /* + * Filter out unresponsive servers, and + * save the domain info we get back. + */ + dc = ldap_ping( + ctx, + cdc, + domain_name, + DS_DS_FLAG); + srv_free(cdc); + cdc = NULL; + + if (dc == NULL) { + DEBUG1STATUS(ctx, "(no LDAP response)"); + return (NULL); } + log_ds(ctx, dc); - update_item(&ctx->site_domain_controller, - domain_controller, AD_STATE_AUTO, ttl); + update_item(&ctx->site_domain_controller, dc, + AD_STATE_AUTO, dc->ttl); update_version(&ctx->site_domain_controller, PARAM1, domain_name_item); update_version(&ctx->site_domain_controller, PARAM2, @@ -1485,12 +1348,12 @@ validate_DomainController(ad_disc_t ctx, enum ad_disc_req req) return (NULL); } -idmap_ad_disc_ds_t * +ad_disc_ds_t * ad_disc_get_DomainController(ad_disc_t ctx, enum ad_disc_req req, - boolean_t *auto_discovered) + boolean_t *auto_discovered) { ad_item_t *domain_controller_item; - idmap_ad_disc_ds_t *domain_controller = NULL; + ad_disc_ds_t *domain_controller = NULL; domain_controller_item = validate_DomainController(ctx, req); @@ -1506,161 +1369,68 @@ ad_disc_get_DomainController(ad_disc_t ctx, enum ad_disc_req req, } -/* Discover site name (for multi-homed systems the first one found wins) */ +/* + * Discover the Domain GUID + * This info comes from validate_DomainController() + */ static ad_item_t * -validate_SiteName(ad_disc_t ctx) +validate_DomainGUID(ad_disc_t ctx) { - LDAP *ld = NULL; - ad_subnet_t *subnets = NULL; - char **dn_subnets = NULL; - char *dn_root[2]; - char *config_naming_context = NULL; - char *site_object = NULL; - char *site_name = NULL; - char *forest_name; - int len; - boolean_t update_required = B_FALSE; ad_item_t *domain_controller_item; - if (is_fixed(&ctx->site_name)) - return (&ctx->site_name); + if (is_fixed(&ctx->domain_guid)) + return (&ctx->domain_guid); - /* Can't rely on site-specific DCs */ domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL); if (domain_controller_item == NULL) return (NULL); - if (!is_valid(&ctx->site_name) || - is_changed(&ctx->site_name, PARAM1, domain_controller_item) || - ctx->subnets == NULL || ctx->subnets_changed) { - subnets = find_subnets(); - ctx->subnets_last_check = time(NULL); - update_required = B_TRUE; - } else if (ctx->subnets_last_check + 60 < time(NULL)) { - /* NEEDSWORK magic constant 60 above */ - subnets = find_subnets(); - ctx->subnets_last_check = time(NULL); - if (cmpsubnets(ctx->subnets, subnets) != 0) - update_required = B_TRUE; - } - - if (!update_required) { - free(subnets); - return (&ctx->site_name); - } - - if (subnets == NULL) + if (!is_valid(&ctx->domain_guid)) return (NULL); - dn_root[0] = ""; - dn_root[1] = NULL; + return (&ctx->domain_guid); +} - if (DBG(DISC, 1)) - logger(LOG_DEBUG, "Getting site name"); - config_naming_context = ldap_lookup_entry_attr( - &ld, ctx->domain_controller.value, - dn_root, "configurationNamingContext"); - if (config_naming_context == NULL) - goto out; - /* - * configurationNamingContext also provides the Forest - * Name. - */ - if (!is_fixed(&ctx->forest_name)) { - /* - * The configurationNamingContext should be of - * form: - * CN=Configuration,<DNforestName> - * Remove the first part and convert to DNS form - * (replace ",DC=" with ".") - */ - char *str = "CN=Configuration,"; - int len = strlen(str); - if (strncasecmp(config_naming_context, str, len) == 0) { - forest_name = DN_to_DNS(config_naming_context + len); - if (DBG(DISC, 1)) { - logger(LOG_DEBUG, " forest: %s", - forest_name); - } - update_item(&ctx->forest_name, forest_name, - AD_STATE_AUTO, 0); - update_version(&ctx->forest_name, PARAM1, - domain_controller_item); - } - } +uchar_t * +ad_disc_get_DomainGUID(ad_disc_t ctx, boolean_t *auto_discovered) +{ + ad_item_t *domain_guid_item; + uchar_t *domain_guid = NULL; - if (DBG(DISC, 2)) - logger(LOG_DEBUG, " CNC: %s", config_naming_context); + domain_guid_item = validate_DomainGUID(ctx); + if (domain_guid_item != NULL) { + domain_guid = uuid_dup(domain_guid_item->value); + if (auto_discovered != NULL) + *auto_discovered = + (domain_guid_item->state == AD_STATE_AUTO); + } else if (auto_discovered != NULL) + *auto_discovered = B_FALSE; - if (DBG(DISC, 2)) { - int i; - logger(LOG_DEBUG, " Looking for sites for subnets:"); - for (i = 0; subnets[i].subnet[0] != '\0'; i++) { - logger(LOG_DEBUG, " %s", subnets[i].subnet); - } - } + return (domain_guid); +} - dn_subnets = subnets_to_DNs(subnets, config_naming_context); - if (dn_subnets == NULL) - goto out; - - site_object = ldap_lookup_entry_attr( - &ld, domain_controller_item->value, - dn_subnets, "siteobject"); - if (site_object != NULL) { - /* - * The site object should be of the form - * CN=<site>,CN=Sites,CN=Configuration, - * <DN Domain> - */ - if (DBG(DISC, 2)) - logger(LOG_DEBUG, " Site object: %s", site_object); - if (strncasecmp(site_object, "CN=", 3) == 0) { - for (len = 0; site_object[len + 3] != ','; len++) - ; - site_name = malloc(len + 1); - (void) strncpy(site_name, &site_object[3], len); - site_name[len] = '\0'; - if (DBG(DISC, 1)) { - logger(LOG_DEBUG, " Site name \"%s\"", - site_name); - } - update_item(&ctx->site_name, site_name, - AD_STATE_AUTO, 0); - update_version(&ctx->site_name, PARAM1, - domain_controller_item); - } - } - if (ctx->subnets != NULL) { - free(ctx->subnets); - ctx->subnets = NULL; - } - ctx->subnets = subnets; - subnets = NULL; - ctx->subnets_changed = B_FALSE; +/* + * Discover site name (for multi-homed systems the first one found wins) + * This info comes from validate_DomainController() + */ +static ad_item_t * +validate_SiteName(ad_disc_t ctx) +{ + ad_item_t *domain_controller_item; -out: - if (ld != NULL) - (void) ldap_unbind(ld); + if (is_fixed(&ctx->site_name)) + return (&ctx->site_name); - if (dn_subnets != NULL) { - int i; - for (i = 0; dn_subnets[i] != NULL; i++) - free(dn_subnets[i]); - free(dn_subnets); - } - if (config_naming_context != NULL) - free(config_naming_context); - if (site_object != NULL) - free(site_object); + domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL); + if (domain_controller_item == NULL) + return (NULL); - free(subnets); - if (site_name == NULL) + if (!is_valid(&ctx->site_name)) return (NULL); - return (&ctx->site_name); + return (&ctx->site_name); } @@ -1684,69 +1454,25 @@ ad_disc_get_SiteName(ad_disc_t ctx, boolean_t *auto_discovered) -/* Discover forest name */ +/* + * Discover forest name + * This info comes from validate_DomainController() + */ static ad_item_t * validate_ForestName(ad_disc_t ctx) { - LDAP *ld = NULL; - char *config_naming_context; - char *forest_name = NULL; - char *dn_list[2]; ad_item_t *domain_controller_item; if (is_fixed(&ctx->forest_name)) return (&ctx->forest_name); - /* - * We may not have a site name yet, so we won't rely on - * site-specific DCs. (But maybe we could replace - * validate_ForestName() with validate_siteName()?) - */ + domain_controller_item = validate_DomainController(ctx, AD_DISC_GLOBAL); if (domain_controller_item == NULL) return (NULL); - if (!is_valid(&ctx->forest_name) || - is_changed(&ctx->forest_name, PARAM1, domain_controller_item)) { - - dn_list[0] = ""; - dn_list[1] = NULL; - if (DBG(DISC, 1)) - logger(LOG_DEBUG, "Getting forest name"); - config_naming_context = ldap_lookup_entry_attr( - &ld, ctx->domain_controller.value, - dn_list, "configurationNamingContext"); - if (config_naming_context != NULL) { - /* - * The configurationNamingContext should be of - * form: - * CN=Configuration,<DNforestName> - * Remove the first part and convert to DNS form - * (replace ",DC=" with ".") - */ - char *str = "CN=Configuration,"; - int len = strlen(str); - if (strncasecmp(config_naming_context, str, len) == 0) { - forest_name = DN_to_DNS( - config_naming_context + len); - } - free(config_naming_context); - } - if (ld != NULL) - (void) ldap_unbind(ld); - - if (forest_name == NULL) { - if (DBG(DISC, 1)) - logger(LOG_DEBUG, " not found"); - return (NULL); - } - - if (DBG(DISC, 1)) - logger(LOG_DEBUG, " %s", forest_name); + if (!is_valid(&ctx->forest_name)) + return (NULL); - update_item(&ctx->forest_name, forest_name, AD_STATE_AUTO, 0); - update_version(&ctx->forest_name, PARAM1, - domain_controller_item); - } return (&ctx->forest_name); } @@ -1775,12 +1501,15 @@ ad_disc_get_ForestName(ad_disc_t ctx, boolean_t *auto_discovered) static ad_item_t * validate_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req) { - idmap_ad_disc_ds_t *global_catalog = NULL; - uint32_t ttl = 0; + ad_disc_ds_t *gc = NULL; + ad_disc_cds_t *cgc = NULL; boolean_t validate_global = B_FALSE; boolean_t validate_site = B_FALSE; + ad_item_t *dc_item; ad_item_t *forest_name_item; ad_item_t *site_name_item; + char *forest_name; + char *site_name; /* If the values is fixed there will not be a site specific version */ if (is_fixed(&ctx->global_catalog)) @@ -1789,12 +1518,12 @@ validate_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req) forest_name_item = validate_ForestName(ctx); if (forest_name_item == NULL) return (NULL); + forest_name = (char *)forest_name_item->value; if (req == AD_DISC_GLOBAL) validate_global = B_TRUE; else { - site_name_item = validate_SiteName(ctx); - if (site_name_item != NULL) + if (is_fixed(&ctx->site_name)) validate_site = B_TRUE; else if (req == AD_DISC_PREFER_SITE) validate_global = B_TRUE; @@ -1804,40 +1533,63 @@ validate_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req) if (!is_valid(&ctx->global_catalog) || is_changed(&ctx->global_catalog, PARAM1, forest_name_item)) { + /* - * Lookup DNS SRV RR named + * See if our DC is also a GC. + */ + dc_item = validate_DomainController(ctx, req); + if (dc_item != NULL) { + ad_disc_ds_t *ds = dc_item->value; + if ((ds->flags & DS_GC_FLAG) != 0) { + DEBUG1STATUS(ctx, + "DC is also a GC for %s", + forest_name); + gc = ds_dup(ds); + if (gc != NULL) { + gc->port = GC_PORT; + goto update_global; + } + } + } + + /* + * Lookup DNS SRV RR named: * _ldap._tcp.gc._msdcs.<ForestName> */ + DEBUG1STATUS(ctx, "DNS SRV query, forest=%s", + forest_name); DO_RES_NINIT(ctx); - global_catalog = - srv_query(&ctx->res_state, + cgc = srv_query(&ctx->res_state, LDAP_SRV_HEAD GC_SRV_TAIL, - ctx->forest_name.value, NULL, &ttl); + forest_name, NULL); - if (DBG(DISC, 1)) { - logger(LOG_DEBUG, - "GC servers for %s:", - ctx->forest_name.value); - } - if (global_catalog == NULL) { - if (DBG(DISC, 1)) - logger(LOG_DEBUG, " not found"); + if (cgc == NULL) { + DEBUG1STATUS(ctx, "(no DNS response)"); return (NULL); } + log_cds(ctx, cgc); - if (DBG(DISC, 1)) { - int i; - for (i = 0; - global_catalog[i].host[0] != '\0'; - i++) { - logger(LOG_DEBUG, " %s:%d", - global_catalog[i].host, - global_catalog[i].port); - } + /* + * Filter out unresponsive servers, and + * save the domain info we get back. + */ + gc = ldap_ping( + NULL, + cgc, + forest_name, + DS_GC_FLAG); + srv_free(cgc); + cgc = NULL; + + if (gc == NULL) { + DEBUG1STATUS(ctx, "(no LDAP response)"); + return (NULL); } + log_ds(ctx, gc); - update_item(&ctx->global_catalog, global_catalog, - AD_STATE_AUTO, ttl); + update_global: + update_item(&ctx->global_catalog, gc, + AD_STATE_AUTO, gc->ttl); update_version(&ctx->global_catalog, PARAM1, forest_name_item); } @@ -1845,51 +1597,75 @@ validate_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req) } if (validate_site) { + site_name_item = &ctx->site_name; + site_name = (char *)site_name_item->value; + if (!is_valid(&ctx->site_global_catalog) || is_changed(&ctx->site_global_catalog, PARAM1, forest_name_item) || is_changed(&ctx->site_global_catalog, PARAM2, site_name_item)) { - char rr_name[DNS_MAX_NAME]; + char rr_name[DNS_MAX_NAME]; + + /* + * See if our DC is also a GC. + */ + dc_item = validate_DomainController(ctx, req); + if (dc_item != NULL) { + ad_disc_ds_t *ds = dc_item->value; + if ((ds->flags & DS_GC_FLAG) != 0) { + DEBUG1STATUS(ctx, + "DC is also a GC for %s in %s", + forest_name, site_name); + gc = ds_dup(ds); + if (gc != NULL) { + gc->port = GC_PORT; + goto update_site; + } + } + } /* * Lookup DNS SRV RR named: * _ldap._tcp.<siteName>._sites.gc. * _msdcs.<ForestName> */ - (void) snprintf(rr_name, - sizeof (rr_name), + DEBUG1STATUS(ctx, "DNS SRV query, forest=%s, site=%s", + forest_name, site_name); + (void) snprintf(rr_name, sizeof (rr_name), LDAP_SRV_HEAD SITE_SRV_MIDDLE GC_SRV_TAIL, - ctx->site_name.value); + site_name); DO_RES_NINIT(ctx); - global_catalog = srv_query(&ctx->res_state, rr_name, - ctx->forest_name.value, NULL, &ttl); + cgc = srv_query(&ctx->res_state, rr_name, + forest_name, NULL); - if (DBG(DISC, 1)) { - logger(LOG_DEBUG, - "GC servers for %s in %s", - ctx->forest_name.value, - ctx->site_name.value); - } - if (global_catalog == NULL) { - if (DBG(DISC, 1)) - logger(LOG_DEBUG, " not found"); + if (cgc == NULL) { + DEBUG1STATUS(ctx, "(no DNS response)"); return (NULL); } + log_cds(ctx, cgc); - if (DBG(DISC, 1)) { - int i; - for (i = 0; - global_catalog[i].host[0] != '\0'; - i++) { - logger(LOG_DEBUG, " %s:%d", - global_catalog[i].host, - global_catalog[i].port); - } + /* + * Filter out unresponsive servers, and + * save the domain info we get back. + */ + gc = ldap_ping( + NULL, + cgc, + forest_name, + DS_GC_FLAG); + srv_free(cgc); + cgc = NULL; + + if (gc == NULL) { + DEBUG1STATUS(ctx, "(no LDAP response)"); + return (NULL); } + log_ds(ctx, gc); - update_item(&ctx->site_global_catalog, global_catalog, - AD_STATE_AUTO, ttl); + update_site: + update_item(&ctx->site_global_catalog, gc, + AD_STATE_AUTO, gc->ttl); update_version(&ctx->site_global_catalog, PARAM1, forest_name_item); update_version(&ctx->site_global_catalog, PARAM2, @@ -1901,11 +1677,11 @@ validate_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req) } -idmap_ad_disc_ds_t * +ad_disc_ds_t * ad_disc_get_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req req, boolean_t *auto_discovered) { - idmap_ad_disc_ds_t *global_catalog = NULL; + ad_disc_ds_t *global_catalog = NULL; ad_item_t *global_catalog_item; global_catalog_item = validate_GlobalCatalog(ctx, req); @@ -2059,6 +1835,33 @@ ad_disc_get_DomainsInForest(ad_disc_t ctx, boolean_t *auto_discovered) return (domains_in_forest); } +static ad_item_t * +validate_PreferredDC(ad_disc_t ctx) +{ + if (is_valid(&ctx->preferred_dc)) + return (&ctx->preferred_dc); + + return (NULL); +} + +ad_disc_ds_t * +ad_disc_get_PreferredDC(ad_disc_t ctx, boolean_t *auto_discovered) +{ + ad_disc_ds_t *preferred_dc = NULL; + ad_item_t *preferred_dc_item; + + preferred_dc_item = validate_PreferredDC(ctx); + + if (preferred_dc_item != NULL) { + preferred_dc = ds_dup(preferred_dc_item->value); + if (auto_discovered != NULL) + *auto_discovered = + (preferred_dc_item->state == AD_STATE_AUTO); + } else if (auto_discovered != NULL) + *auto_discovered = B_FALSE; + + return (preferred_dc); +} @@ -2077,12 +1880,40 @@ ad_disc_set_DomainName(ad_disc_t ctx, const char *domainName) return (0); } +int +ad_disc_set_DomainGUID(ad_disc_t ctx, uchar_t *u) +{ + char *domain_guid = NULL; + if (u != NULL) { + domain_guid = uuid_dup(u); + if (domain_guid == NULL) + return (-1); + update_item(&ctx->domain_guid, domain_guid, + AD_STATE_FIXED, 0); + } else if (ctx->domain_guid.state == AD_STATE_FIXED) + ctx->domain_guid.state = AD_STATE_INVALID; + return (0); +} + +void +auto_set_DomainGUID(ad_disc_t ctx, uchar_t *u) +{ + char *domain_guid = NULL; + + if (is_fixed(&ctx->domain_guid)) + return; + + domain_guid = uuid_dup(u); + if (domain_guid == NULL) + return; + update_item(&ctx->domain_guid, domain_guid, AD_STATE_AUTO, 0); +} int ad_disc_set_DomainController(ad_disc_t ctx, - const idmap_ad_disc_ds_t *domainController) + const ad_disc_ds_t *domainController) { - idmap_ad_disc_ds_t *domain_controller = NULL; + ad_disc_ds_t *domain_controller = NULL; if (domainController != NULL) { domain_controller = ds_dup(domainController); if (domain_controller == NULL) @@ -2094,7 +1925,6 @@ ad_disc_set_DomainController(ad_disc_t ctx, return (0); } - int ad_disc_set_SiteName(ad_disc_t ctx, const char *siteName) { @@ -2109,6 +1939,20 @@ ad_disc_set_SiteName(ad_disc_t ctx, const char *siteName) return (0); } +void +auto_set_SiteName(ad_disc_t ctx, char *siteName) +{ + char *site_name = NULL; + + if (is_fixed(&ctx->site_name)) + return; + + site_name = strdup(siteName); + if (site_name == NULL) + return; + update_item(&ctx->site_name, site_name, AD_STATE_AUTO, 0); +} + int ad_disc_set_ForestName(ad_disc_t ctx, const char *forestName) { @@ -2124,11 +1968,25 @@ ad_disc_set_ForestName(ad_disc_t ctx, const char *forestName) return (0); } +void +auto_set_ForestName(ad_disc_t ctx, char *forestName) +{ + char *forest_name = NULL; + + if (is_fixed(&ctx->forest_name)) + return; + + forest_name = strdup(forestName); + if (forest_name == NULL) + return; + update_item(&ctx->forest_name, forest_name, AD_STATE_AUTO, 0); +} + int ad_disc_set_GlobalCatalog(ad_disc_t ctx, - const idmap_ad_disc_ds_t *globalCatalog) + const ad_disc_ds_t *globalCatalog) { - idmap_ad_disc_ds_t *global_catalog = NULL; + ad_disc_ds_t *global_catalog = NULL; if (globalCatalog != NULL) { global_catalog = ds_dup(globalCatalog); if (global_catalog == NULL) @@ -2140,6 +1998,27 @@ ad_disc_set_GlobalCatalog(ad_disc_t ctx, return (0); } +int +ad_disc_set_PreferredDC(ad_disc_t ctx, const ad_disc_ds_t *pref_dc) +{ + ad_disc_ds_t *new_pref_dc = NULL; + if (pref_dc != NULL) { + new_pref_dc = ds_dup(pref_dc); + if (new_pref_dc == NULL) + return (-1); + update_item(&ctx->preferred_dc, new_pref_dc, + AD_STATE_FIXED, 0); + } else if (ctx->preferred_dc.state == AD_STATE_FIXED) + ctx->preferred_dc.state = AD_STATE_INVALID; + return (0); +} + +void +ad_disc_set_StatusFP(ad_disc_t ctx, struct __FILE_TAG *fp) +{ + ctx->status_fp = fp; +} + int ad_disc_unset(ad_disc_t ctx) @@ -2150,6 +2029,9 @@ ad_disc_unset(ad_disc_t ctx) if (ctx->domain_controller.state == AD_STATE_FIXED) ctx->domain_controller.state = AD_STATE_INVALID; + if (ctx->preferred_dc.state == AD_STATE_FIXED) + ctx->preferred_dc.state = AD_STATE_INVALID; + if (ctx->site_name.state == AD_STATE_FIXED) ctx->site_name.state = AD_STATE_INVALID; diff --git a/usr/src/lib/libadutils/common/addisc.h b/usr/src/lib/libadutils/common/addisc.h index 786e79a66d..b3a0b44cc3 100644 --- a/usr/src/lib/libadutils/common/addisc.h +++ b/usr/src/lib/libadutils/common/addisc.h @@ -21,12 +21,14 @@ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #ifndef _ADINFO_H #define _ADINFO_H -#include <rpcsvc/idmap_prot.h> +#include <sys/socket.h> +#include <sys/uuid.h> #include "libadutils.h" @@ -41,6 +43,7 @@ extern "C" { */ #define MAXSTRSID 185 #define MAXDOMAINNAME 256 +#define AD_DISC_MAXHOSTNAME 256 typedef struct ad_disc *ad_disc_t; @@ -66,6 +69,24 @@ enum ad_disc_req { AD_DISC_GLOBAL /* Request global version */ }; +/* + * First four members of this are like idmap_ad_disc_ds_t + * (for compatiblity) until that can be eliminated. + * See PROP_DOMAIN_CONTROLLER in idmapd/server.c + */ +typedef struct ad_disc_ds { + /* Keep these first four in sync with idmap_ad_disc_ds_t */ + int port; + int priority; + int weight; + char host[AD_DISC_MAXHOSTNAME]; + /* Members after this are private and free to change. */ + char site[AD_DISC_MAXHOSTNAME]; + struct sockaddr_storage addr; + uint32_t flags; + uint32_t ttl; +} ad_disc_ds_t; + ad_disc_t ad_disc_init(void); void ad_disc_fini(ad_disc_t); @@ -76,17 +97,23 @@ void ad_disc_fini(ad_disc_t); char * ad_disc_get_DomainName(ad_disc_t ctx, boolean_t *auto_discovered); -idmap_ad_disc_ds_t * +uchar_t * +ad_disc_get_DomainGUID(ad_disc_t ctx, boolean_t *auto_discovered); + +ad_disc_ds_t * ad_disc_get_DomainController(ad_disc_t ctx, enum ad_disc_req req, boolean_t *auto_discovered); +ad_disc_ds_t * +ad_disc_get_PreferredDC(ad_disc_t ctx, boolean_t *auto_discovered); + char * ad_disc_get_SiteName(ad_disc_t ctx, boolean_t *auto_discovered); char * ad_disc_get_ForestName(ad_disc_t ctx, boolean_t *auto_discovered); -idmap_ad_disc_ds_t * +ad_disc_ds_t * ad_disc_get_GlobalCatalog(ad_disc_t ctx, enum ad_disc_req, boolean_t *auto_discovered); @@ -105,8 +132,13 @@ int ad_disc_set_DomainName(ad_disc_t ctx, const char *domainName); int +ad_disc_set_DomainGUID(ad_disc_t ctx, uchar_t *u); + +int ad_disc_set_DomainController(ad_disc_t ctx, - const idmap_ad_disc_ds_t *domainController); + const ad_disc_ds_t *domainController); +int +ad_disc_set_PreferredDC(ad_disc_t ctx, const ad_disc_ds_t *dc); int ad_disc_set_SiteName(ad_disc_t ctx, const char *siteName); @@ -116,8 +148,17 @@ ad_disc_set_ForestName(ad_disc_t ctx, const char *forestName); int ad_disc_set_GlobalCatalog(ad_disc_t ctx, - const idmap_ad_disc_ds_t *globalCatalog); + const ad_disc_ds_t *globalCatalog); + +/* + * This function sets a FILE * on which this library will write + * progress information during DC Location. + */ +void +ad_disc_set_StatusFP(ad_disc_t ctx, struct __FILE_TAG *); +int +ad_disc_getnameinfo(char *, int, struct sockaddr_storage *); /* * This routine forces all auto discovery item to be recomputed @@ -140,7 +181,9 @@ boolean_t ad_disc_SubnetChanged(ad_disc_t); /* This routine returns the Time To Live for auto discovered items */ int ad_disc_get_TTL(ad_disc_t); -int ad_disc_compare_ds(idmap_ad_disc_ds_t *ds1, idmap_ad_disc_ds_t *ds2); +int ad_disc_compare_uuid(uuid_t *u1, uuid_t *u2); + +int ad_disc_compare_ds(ad_disc_ds_t *ds1, ad_disc_ds_t *ds2); int ad_disc_compare_trusteddomains(ad_disc_trusteddomains_t *td1, ad_disc_trusteddomains_t *td2); diff --git a/usr/src/lib/libadutils/common/addisc_impl.h b/usr/src/lib/libadutils/common/addisc_impl.h new file mode 100644 index 0000000000..ce26ce256b --- /dev/null +++ b/usr/src/lib/libadutils/common/addisc_impl.h @@ -0,0 +1,130 @@ +/* + * 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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + */ + +#ifndef _ADDISC_IMPL_H +#define _ADDISC_IMPL_H + +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <resolv.h> +#include <ldap.h> +#include <pthread.h> +#include "addisc.h" +#include "libadutils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum ad_item_state { + AD_STATE_INVALID = 0, /* The value is not valid */ + AD_STATE_FIXED, /* The value was fixed by caller */ + AD_STATE_AUTO /* The value is auto discovered */ + }; + +enum ad_data_type { + AD_STRING = 123, + AD_UUID, + AD_DIRECTORY, + AD_DOMAINS_IN_FOREST, + AD_TRUSTED_DOMAINS + }; + + +typedef struct ad_subnet { + char subnet[24]; +} ad_subnet_t; + + +typedef struct ad_item { + enum ad_item_state state; + enum ad_data_type type; + void *value; + time_t expires; + unsigned int version; /* Version is only changed */ + /* if the value changes */ +#define PARAM1 0 +#define PARAM2 1 + int param_version[2]; + /* These holds the version of */ + /* dependents so that a dependent */ + /* change can be detected */ +} ad_item_t; + +typedef struct ad_disc { + struct __res_state res_state; + int res_ninitted; + ad_subnet_t *subnets; + boolean_t subnets_changed; + time_t subnets_last_check; + time_t expires_not_before; + time_t expires_not_after; + ad_item_t domain_name; /* DNS hostname string */ + ad_item_t domain_guid; /* Domain UUID (binary) */ + ad_item_t domain_controller; /* Directory hostname and */ + /* port array */ + ad_item_t preferred_dc; + ad_item_t site_name; /* String */ + ad_item_t forest_name; /* DNS forestname string */ + ad_item_t global_catalog; /* Directory hostname and */ + /* port array */ + ad_item_t domains_in_forest; /* DNS domainname and SID */ + /* array */ + ad_item_t trusted_domains; /* DNS domainname and trust */ + /* direction array */ + /* Site specfic versions */ + ad_item_t site_domain_controller; /* Directory hostname and */ + /* port array */ + ad_item_t site_global_catalog; /* Directory hostname and */ + /* port array */ + /* Optional FILE * for DC Location status. */ + struct __FILE_TAG *status_fp; + + int debug[AD_DEBUG_MAX+1]; /* Debug levels */ +} ad_disc; + +/* Candidate Directory Servers (CDS) */ +typedef struct ad_disc_cds { + struct ad_disc_ds cds_ds; + struct addrinfo *cds_ai; +} ad_disc_cds_t; + +ad_disc_ds_t *ldap_ping(ad_disc_t, ad_disc_cds_t *, char *, int); + +int srv_getdom(res_state, const char *, char **); +ad_disc_cds_t *srv_query(res_state, const char *, const char *, + ad_disc_ds_t *); +void srv_free(ad_disc_cds_t *); + +void auto_set_DomainGUID(ad_disc_t, uchar_t *); +void auto_set_ForestName(ad_disc_t, char *); +void auto_set_SiteName(ad_disc_t, char *); + +#ifdef __cplusplus +} +#endif + +#endif /* _ADDISC_IMPL_H */ diff --git a/usr/src/lib/libadutils/common/adutils_impl.h b/usr/src/lib/libadutils/common/adutils_impl.h index f82282ff97..d5d41a6768 100644 --- a/usr/src/lib/libadutils/common/adutils_impl.h +++ b/usr/src/lib/libadutils/common/adutils_impl.h @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #ifndef _ADUTILS_IMPL_H @@ -31,7 +32,6 @@ #include <ldap.h> #include <pthread.h> #include "addisc.h" -#include <rpcsvc/idmap_prot.h> #include "libadutils.h" #ifdef __cplusplus diff --git a/usr/src/lib/libadutils/common/ldap_ping.c b/usr/src/lib/libadutils/common/ldap_ping.c new file mode 100644 index 0000000000..5008372d74 --- /dev/null +++ b/usr/src/lib/libadutils/common/ldap_ping.c @@ -0,0 +1,677 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + */ + +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> +#include <assert.h> +#include <stdlib.h> +#include <net/if.h> +#include <net/if.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> +#include <resolv.h> +#include <netdb.h> +#include <ctype.h> +#include <errno.h> +#include <ldap.h> +#include <lber.h> +#include <syslog.h> +#include "adutils_impl.h" +#include "addisc_impl.h" + +#define LDAP_PORT 389 + +#define NETLOGON_ATTR_NAME "NetLogon" +#define NETLOGON_NT_VERSION_1 0x00000001 +#define NETLOGON_NT_VERSION_5 0x00000002 +#define NETLOGON_NT_VERSION_5EX 0x00000004 +#define NETLOGON_NT_VERSION_5EX_WITH_IP 0x00000008 +#define NETLOGON_NT_VERSION_WITH_CLOSEST_SITE 0x00000010 +#define NETLOGON_NT_VERSION_AVOID_NT4EMUL 0x01000000 + +typedef enum { + OPCODE = 0, + SBZ, + FLAGS, + DOMAIN_GUID, + FOREST_NAME, + DNS_DOMAIN_NAME, + DNS_HOST_NAME, + NET_DOMAIN_NAME, + NET_COMP_NAME, + USER_NAME, + DC_SITE_NAME, + CLIENT_SITE_NAME, + SOCKADDR_SIZE, + SOCKADDR, + NEXT_CLOSEST_SITE_NAME, + NTVER, + LM_NT_TOKEN, + LM_20_TOKEN +} field_5ex_t; + +struct _berelement { + char *ber_buf; + char *ber_ptr; + char *ber_end; +}; + +extern int ldap_put_filter(BerElement *ber, char *); +static void send_to_cds(ad_disc_cds_t *, char *, size_t, int); +static ad_disc_cds_t *find_cds_by_addr(ad_disc_cds_t *, struct sockaddr_in6 *); +static boolean_t addrmatch(struct addrinfo *, struct sockaddr_in6 *); +static void save_ai(ad_disc_cds_t *, struct addrinfo *); + +static void +cldap_escape_le64(char *buf, uint64_t val, int bytes) +{ + char *p = buf; + + while (bytes != 0) { + p += sprintf(p, "\\%.2x", (uint8_t)(val & 0xff)); + val >>= 8; + bytes--; + } + *p = '\0'; +} + +/* + * Construct CLDAPMessage PDU for NetLogon search request. + * + * CLDAPMessage ::= SEQUENCE { + * messageID MessageID, + * protocolOp searchRequest SearchRequest; + * } + * + * SearchRequest ::= + * [APPLICATION 3] SEQUENCE { + * baseObject LDAPDN, + * scope ENUMERATED { + * baseObject (0), + * singleLevel (1), + * wholeSubtree (2) + * }, + * derefAliases ENUMERATED { + * neverDerefAliases (0), + * derefInSearching (1), + * derefFindingBaseObj (2), + * derefAlways (3) + * }, + * sizeLimit INTEGER (0 .. MaxInt), + * timeLimit INTEGER (0 .. MaxInt), + * attrsOnly BOOLEAN, + * filter Filter, + * attributes SEQUENCE OF AttributeType + * } + */ +BerElement * +cldap_build_request(const char *dname, + const char *host, uint32_t ntver, uint16_t msgid) +{ + BerElement *ber; + int len = 0; + char *basedn = ""; + int scope = LDAP_SCOPE_BASE, deref = LDAP_DEREF_NEVER, + sizelimit = 0, timelimit = 0, attrsonly = 0; + char filter[512]; + char ntver_esc[13]; + char *p, *pend; + + /* + * Construct search filter in LDAP format. + */ + p = filter; + pend = p + sizeof (filter); + + len = snprintf(p, pend - p, "(&(DnsDomain=%s)", dname); + if (len >= (pend - p)) + goto fail; + p += len; + + if (host != NULL) { + len = snprintf(p, (pend - p), "(Host=%s)", host); + if (len >= (pend - p)) + goto fail; + p += len; + } + + if (ntver != 0) { + /* + * Format NtVer as little-endian with LDAPv3 escapes. + */ + cldap_escape_le64(ntver_esc, ntver, sizeof (ntver)); + len = snprintf(p, (pend - p), "(NtVer=%s)", ntver_esc); + if (len >= (pend - p)) + goto fail; + p += len; + } + + len = snprintf(p, pend - p, ")"); + if (len >= (pend - p)) + goto fail; + p += len; + + /* + * Encode CLDAPMessage and beginning of SearchRequest sequence. + */ + + if ((ber = ber_alloc()) == NULL) + goto fail; + + if (ber_printf(ber, "{it{seeiib", msgid, + LDAP_REQ_SEARCH, basedn, scope, deref, + sizelimit, timelimit, attrsonly) < 0) + goto fail; + + /* + * Encode Filter sequence. + */ + if (ldap_put_filter(ber, filter) < 0) + goto fail; + /* + * Encode attribute and close Filter and SearchRequest sequences. + */ + if (ber_printf(ber, "{s}}}", NETLOGON_ATTR_NAME) < 0) + goto fail; + + /* + * Success + */ + return (ber); + +fail: + if (ber != NULL) + ber_free(ber, 1); + return (NULL); +} + +/* + * Parse incoming search responses and attribute to correct hosts. + * + * CLDAPMessage ::= SEQUENCE { + * messageID MessageID, + * searchResponse SEQUENCE OF + * SearchResponse; + * } + * + * SearchResponse ::= + * CHOICE { + * entry [APPLICATION 4] SEQUENCE { + * objectName LDAPDN, + * attributes SEQUENCE OF SEQUENCE { + * AttributeType, + * SET OF + * AttributeValue + * } + * }, + * resultCode [APPLICATION 5] LDAPResult + * } + */ + +static int +decode_name(uchar_t *base, uchar_t *cp, char *str) +{ + uchar_t *tmp = NULL, *st = cp; + uint8_t len; + + /* + * there should probably be some boundary checks on str && cp + * maybe pass in strlen && msglen ? + */ + while (*cp != 0) { + if (*cp == 0xc0) { + if (tmp == NULL) + tmp = cp + 2; + cp = base + *(cp + 1); + } + for (len = *cp++; len > 0; len--) + *str++ = *cp++; + *str++ = '.'; + } + if (cp != st) + *(str-1) = '\0'; + else + *str = '\0'; + + return ((tmp == NULL ? cp + 1 : tmp) - st); +} + +static int +cldap_parse(ad_disc_t ctx, ad_disc_cds_t *cds, BerElement *ber) +{ + ad_disc_ds_t *dc = &cds->cds_ds; + uchar_t *base = NULL, *cp = NULL; + char val[512]; /* how big should val be? */ + int l, msgid, rc = 0; + uint16_t opcode; + field_5ex_t f = OPCODE; + + /* + * Later, compare msgid's/some validation? + */ + + if (ber_scanf(ber, "{i{x{{x[la", &msgid, &l, &cp) == LBER_ERROR) { + rc = 1; + goto out; + } + + for (base = cp; ((cp - base) < l) && (f <= LM_20_TOKEN); f++) { + val[0] = '\0'; + switch (f) { + case OPCODE: + /* opcode = *(uint16_t *)cp; */ + /* cp +=2; */ + opcode = *cp++; + opcode |= (*cp++ << 8); + break; + case SBZ: + cp += 2; + break; + case FLAGS: + /* dci->Flags = *(uint32_t *)cp; */ + /* cp +=4; */ + dc->flags = *cp++; + dc->flags |= (*cp++ << 8); + dc->flags |= (*cp++ << 16); + dc->flags |= (*cp++ << 26); + break; + case DOMAIN_GUID: + if (ctx != NULL) + auto_set_DomainGUID(ctx, cp); + cp += 16; + break; + case FOREST_NAME: + cp += decode_name(base, cp, val); + if (ctx != NULL) + auto_set_ForestName(ctx, val); + break; + case DNS_DOMAIN_NAME: + /* + * We always have this already. + * (Could validate it here.) + */ + cp += decode_name(base, cp, val); + break; + case DNS_HOST_NAME: + cp += decode_name(base, cp, val); + if (0 != strcasecmp(val, dc->host)) { + logger(LOG_ERR, "DC name %s != %s?", + val, dc->host); + } + break; + case NET_DOMAIN_NAME: + /* + * This is the "Flat" domain name. + * (i.e. the NetBIOS name) + * ignore for now. + */ + cp += decode_name(base, cp, val); + break; + case NET_COMP_NAME: + /* not needed */ + cp += decode_name(base, cp, val); + break; + case USER_NAME: + /* not needed */ + cp += decode_name(base, cp, val); + break; + case DC_SITE_NAME: + cp += decode_name(base, cp, val); + (void) strlcpy(dc->site, val, sizeof (dc->site)); + break; + case CLIENT_SITE_NAME: + cp += decode_name(base, cp, val); + if (ctx != NULL) + auto_set_SiteName(ctx, val); + break; + /* + * These are all possible, but we don't really care about them. + * Sockaddr_size && sockaddr might be useful at some point + */ + case SOCKADDR_SIZE: + case SOCKADDR: + case NEXT_CLOSEST_SITE_NAME: + case NTVER: + case LM_NT_TOKEN: + case LM_20_TOKEN: + break; + default: + rc = 3; + goto out; + } + } + +out: + if (base) + free(base); + else if (cp) + free(cp); + return (rc); +} + + +/* + * Filter out unresponsive servers, and save the domain info + * returned by the "LDAP ping" in the returned object. + * If ctx != NULL, this is a query for a DC, in which case we + * also save the Domain GUID, Site name, and Forest name as + * "auto" (discovered) values in the ctx. + * + * Only return the "winner". (We only want one DC/GC) + */ +ad_disc_ds_t * +ldap_ping(ad_disc_t ctx, ad_disc_cds_t *dclist, char *dname, int reqflags) +{ + struct sockaddr_in6 addr6; + socklen_t addrlen; + struct pollfd pingchk; + ad_disc_cds_t *send_ds; + ad_disc_cds_t *recv_ds = NULL; + ad_disc_ds_t *ret_ds = NULL; + BerElement *req = NULL; + BerElement *res = NULL; + struct _berelement *be, *rbe; + size_t be_len, rbe_len; + int fd = -1; + int tries = 3; + int waitsec; + int r; + uint16_t msgid; + + /* One plus a null entry. */ + ret_ds = calloc(2, sizeof (ad_disc_ds_t)); + if (ret_ds == NULL) + goto fail; + + if ((fd = socket(PF_INET6, SOCK_DGRAM, 0)) < 0) + goto fail; + + (void) memset(&addr6, 0, sizeof (addr6)); + addr6.sin6_family = AF_INET6; + addr6.sin6_addr = in6addr_any; + if (bind(fd, (struct sockaddr *)&addr6, sizeof (addr6)) < 0) + goto fail; + + /* + * semi-unique msgid... + */ + msgid = gethrtime() & 0xffff; + + /* + * Is ntver right? It certainly works on w2k8... If others are needed, + * that might require changes to cldap_parse + */ + req = cldap_build_request(dname, NULL, + NETLOGON_NT_VERSION_5EX, msgid); + if (req == NULL) + goto fail; + be = (struct _berelement *)req; + be_len = be->ber_end - be->ber_buf; + + if ((res = ber_alloc()) == NULL) + goto fail; + rbe = (struct _berelement *)res; + rbe_len = rbe->ber_end - rbe->ber_buf; + + pingchk.fd = fd; + pingchk.events = POLLIN; + pingchk.revents = 0; + +try_again: + send_ds = dclist; + waitsec = 5; + while (recv_ds == NULL && waitsec > 0) { + + /* + * If there is another candidate, send to it. + */ + if (send_ds->cds_ds.host[0] != '\0') { + send_to_cds(send_ds, be->ber_buf, be_len, fd); + send_ds++; + + /* + * Wait 1/10 sec. before the next send. + */ + r = poll(&pingchk, 1, 100); +#if 0 /* DEBUG */ + /* Drop all responses 1st pass. */ + if (waitsec == 5) + r = 0; +#endif + } else { + /* + * No more candidates to "ping", so + * just wait a sec for responses. + */ + r = poll(&pingchk, 1, 1000); + if (r == 0) + --waitsec; + } + + if (r > 0) { + /* + * Got a response. + */ + (void) memset(&addr6, 0, addrlen = sizeof (addr6)); + r = recvfrom(fd, rbe->ber_buf, rbe_len, 0, + (struct sockaddr *)&addr6, &addrlen); + + recv_ds = find_cds_by_addr(dclist, &addr6); + if (recv_ds == NULL) + continue; + + (void) cldap_parse(ctx, recv_ds, res); + if ((recv_ds->cds_ds.flags & reqflags) != reqflags) { + logger(LOG_ERR, "Skip %s" + "due to flags 0x%X", + recv_ds->cds_ds.host, + recv_ds->cds_ds.flags); + recv_ds = NULL; + } + } + } + + if (recv_ds == NULL) { + if (--tries <= 0) + goto fail; + goto try_again; + } + + (void) memcpy(ret_ds, recv_ds, sizeof (*ret_ds)); + + ber_free(res, 1); + ber_free(req, 1); + (void) close(fd); + return (ret_ds); + +fail: + ber_free(res, 1); + ber_free(req, 1); + (void) close(fd); + free(ret_ds); + return (NULL); +} + +/* + * Attempt a send of the LDAP request to all known addresses + * for this candidate server. + */ +static void +send_to_cds(ad_disc_cds_t *send_cds, char *ber_buf, size_t be_len, int fd) +{ + struct sockaddr_in6 addr6; + struct addrinfo *ai; + int err; + + if (DBG(DISC, 2)) { + logger(LOG_DEBUG, "send to: %s", send_cds->cds_ds.host); + } + + for (ai = send_cds->cds_ai; ai != NULL; ai = ai->ai_next) { + + /* + * Build the "to" address. + */ + (void) memset(&addr6, 0, sizeof (addr6)); + if (ai->ai_family == AF_INET6) { + (void) memcpy(&addr6, ai->ai_addr, sizeof (addr6)); + } else if (ai->ai_family == AF_INET) { + struct sockaddr_in *sin = + (void *)ai->ai_addr; + addr6.sin6_family = AF_INET6; + IN6_INADDR_TO_V4MAPPED(&sin->sin_addr, + &addr6.sin6_addr); + } else { + continue; + } + addr6.sin6_port = htons(LDAP_PORT); + + /* + * Send the "ping" to this address. + */ + err = sendto(fd, ber_buf, be_len, 0, + (struct sockaddr *)&addr6, sizeof (addr6)); + err = (err < 0) ? errno : 0; + + if (DBG(DISC, 2)) { + char abuf[INET6_ADDRSTRLEN]; + const char *pa; + + pa = inet_ntop(AF_INET6, + &addr6.sin6_addr, + abuf, sizeof (abuf)); + logger(LOG_ERR, " > %s rc=%d", + pa ? pa : "?", err); + } + } +} + +/* + * We have a response from some address. Find the candidate with + * this address. In case a candidate had multiple addresses, we + * keep track of which the response came from. + */ +static ad_disc_cds_t * +find_cds_by_addr(ad_disc_cds_t *dclist, struct sockaddr_in6 *sin6from) +{ + char abuf[INET6_ADDRSTRLEN]; + ad_disc_cds_t *ds; + struct addrinfo *ai; + int eai; + + if (DBG(DISC, 1)) { + eai = getnameinfo((void *)sin6from, sizeof (*sin6from), + abuf, sizeof (abuf), NULL, 0, NI_NUMERICHOST); + if (eai != 0) + (void) strlcpy(abuf, "?", sizeof (abuf)); + logger(LOG_DEBUG, "LDAP ping resp: addr=%s", abuf); + } + + /* + * Find the DS this response came from. + * (don't accept unexpected responses) + */ + for (ds = dclist; ds->cds_ds.host[0] != '\0'; ds++) { + ai = ds->cds_ai; + while (ai != NULL) { + if (addrmatch(ai, sin6from)) + goto found; + ai = ai->ai_next; + } + } + if (DBG(DISC, 1)) { + logger(LOG_DEBUG, " (unexpected)"); + } + return (NULL); + +found: + if (DBG(DISC, 2)) { + logger(LOG_DEBUG, " from %s", ds->cds_ds.host); + } + save_ai(ds, ai); + return (ds); +} + +static boolean_t +addrmatch(struct addrinfo *ai, struct sockaddr_in6 *sin6from) +{ + + /* + * Note: on a GC query, the ds->addr port numbers are + * the GC port, and our from addr has the LDAP port. + * Just compare the IP addresses. + */ + + if (ai->ai_family == AF_INET6) { + struct sockaddr_in6 *sin6p = (void *)ai->ai_addr; + + if (!memcmp(&sin6from->sin6_addr, &sin6p->sin6_addr, + sizeof (struct in6_addr))) + return (B_TRUE); + } + + if (ai->ai_family == AF_INET) { + struct in6_addr in6; + struct sockaddr_in *sin4p = (void *)ai->ai_addr; + + IN6_INADDR_TO_V4MAPPED(&sin4p->sin_addr, &in6); + if (!memcmp(&sin6from->sin6_addr, &in6, + sizeof (struct in6_addr))) + return (B_TRUE); + } + + return (B_FALSE); +} + +static void +save_ai(ad_disc_cds_t *cds, struct addrinfo *ai) +{ + ad_disc_ds_t *ds = &cds->cds_ds; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + + /* + * If this DS already saw a response, keep the first + * address from which we received a response. + */ + if (ds->addr.ss_family != 0) { + if (DBG(DISC, 2)) + logger(LOG_DEBUG, "already have an address"); + return; + } + + switch (ai->ai_family) { + case AF_INET: + sin = (void *)&ds->addr; + (void) memcpy(sin, ai->ai_addr, sizeof (*sin)); + sin->sin_port = htons(ds->port); + break; + + case AF_INET6: + sin6 = (void *)&ds->addr; + (void) memcpy(sin6, ai->ai_addr, sizeof (*sin6)); + sin6->sin6_port = htons(ds->port); + break; + + default: + logger(LOG_ERR, "bad AF %d", ai->ai_family); + } +} diff --git a/usr/src/lib/libadutils/common/mapfile-vers b/usr/src/lib/libadutils/common/mapfile-vers index a05bcb4aff..2d029e3ce5 100644 --- a/usr/src/lib/libadutils/common/mapfile-vers +++ b/usr/src/lib/libadutils/common/mapfile-vers @@ -20,6 +20,7 @@ # # # Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2014 Nexenta Systems, Inc. All rights reserved. # # @@ -66,6 +67,8 @@ SYMBOL_VERSION SUNWprivate { ad_disc_init; ad_disc_get_DomainName; ad_disc_set_DomainName; + ad_disc_get_DomainGUID; + ad_disc_set_DomainGUID; ad_disc_compare_ds; ad_disc_compare_trusteddomains; ad_disc_compare_domainsinforest; @@ -78,9 +81,14 @@ SYMBOL_VERSION SUNWprivate { ad_disc_get_ForestName; ad_disc_get_DomainController; ad_disc_set_DomainController; + ad_disc_get_PreferredDC; + ad_disc_set_PreferredDC; + ad_disc_get_SiteName; ad_disc_set_SiteName; ad_disc_refresh; - ad_disc_get_SiteName; + ad_disc_unset; + ad_disc_getnameinfo; + ad_disc_set_StatusFP; ad_disc_get_TrustedDomains; ad_disc_get_DomainsInForest; domain_eq; diff --git a/usr/src/lib/libadutils/common/srv_query.c b/usr/src/lib/libadutils/common/srv_query.c new file mode 100644 index 0000000000..aa7a833421 --- /dev/null +++ b/usr/src/lib/libadutils/common/srv_query.c @@ -0,0 +1,612 @@ +/* + * 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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + */ + +/* + * DNS query helper functions for addisc.c + */ + +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> +#include <assert.h> +#include <stdlib.h> +#include <net/if.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> +#include <resolv.h> +#include <netdb.h> +#include <ctype.h> +#include <errno.h> +#include <ldap.h> +#include <sasl/sasl.h> +#include <sys/u8_textprep.h> +#include <syslog.h> +#include <uuid/uuid.h> +#include <ads/dsgetdc.h> +#include "adutils_impl.h" +#include "addisc_impl.h" + +static void save_addr(ad_disc_cds_t *, sa_family_t, uchar_t *, size_t); +static struct addrinfo *make_addrinfo(sa_family_t, uchar_t *, size_t); + +static void do_getaddrinfo(ad_disc_cds_t *); +static ad_disc_cds_t *srv_parse(uchar_t *, int, int *, int *); +static void add_preferred(ad_disc_cds_t *, ad_disc_ds_t *, int *, int); +static void get_addresses(ad_disc_cds_t *, int); + +/* + * Simplified version of srv_query() for domain auto-discovery. + */ +int +srv_getdom(res_state state, const char *svc_name, char **rrname) +{ + union { + HEADER hdr; + uchar_t buf[NS_MAXMSG]; + } msg; + int len, qdcount, ancount; + uchar_t *ptr, *eom; + char namebuf[NS_MAXDNAME]; + + /* query necessary resource records */ + + *rrname = NULL; + if (DBG(DNS, 1)) { + logger(LOG_DEBUG, "Looking for SRV RRs '%s.*'", svc_name); + } + len = res_nsearch(state, svc_name, C_IN, T_SRV, + msg.buf, sizeof (msg.buf)); + if (len < 0) { + if (DBG(DNS, 0)) { + logger(LOG_DEBUG, + "DNS search for '%s' failed (%s)", + svc_name, hstrerror(state->res_h_errno)); + } + return (-1); + } + + if (len > sizeof (msg.buf)) { + logger(LOG_WARNING, + "DNS query %ib message doesn't fit into %ib buffer", + len, sizeof (msg.buf)); + len = sizeof (msg.buf); + } + + /* parse the reply header */ + + ptr = msg.buf + sizeof (msg.hdr); + eom = msg.buf + len; + qdcount = ntohs(msg.hdr.qdcount); + ancount = ntohs(msg.hdr.ancount); + + /* skip the question section */ + + len = ns_skiprr(ptr, eom, ns_s_qd, qdcount); + if (len < 0) { + logger(LOG_ERR, "DNS query invalid message format"); + return (-1); + } + ptr += len; + + /* parse the answer section */ + if (ancount < 1) { + logger(LOG_ERR, "DNS query - no answers"); + return (-1); + } + + len = dn_expand(msg.buf, eom, ptr, namebuf, sizeof (namebuf)); + if (len < 0) { + logger(LOG_ERR, "DNS query invalid message format"); + return (-1); + } + *rrname = strdup(namebuf); + if (*rrname == NULL) { + logger(LOG_ERR, "Out of memory"); + return (-1); + } + + return (0); +} + + +/* + * Compare SRC RRs; used with qsort(). Sort order: + * "Earliest" (lowest number) priority first, + * then weight highest to lowest. + */ +static int +srvcmp(ad_disc_ds_t *s1, ad_disc_ds_t *s2) +{ + if (s1->priority < s2->priority) + return (-1); + else if (s1->priority > s2->priority) + return (1); + + if (s1->weight < s2->weight) + return (1); + else if (s1->weight > s2->weight) + return (-1); + + return (0); +} + +/* + * Query or search the SRV RRs for a given name. + * + * If dname == NULL then search (as in res_nsearch(3RESOLV), honoring any + * search list/option), else query (as in res_nquery(3RESOLV)). + * + * The output TTL will be the one of the SRV RR with the lowest TTL. + */ +ad_disc_cds_t * +srv_query(res_state state, const char *svc_name, const char *dname, + ad_disc_ds_t *prefer) +{ + ad_disc_cds_t *cds_res = NULL; + uchar_t *msg = NULL; + int len, scnt, maxcnt; + + msg = malloc(NS_MAXMSG); + if (msg == NULL) { + logger(LOG_ERR, "Out of memory"); + return (NULL); + } + + /* query necessary resource records */ + + /* Search, querydomain or query */ + if (dname == NULL) { + dname = "*"; + if (DBG(DNS, 1)) { + logger(LOG_DEBUG, "Looking for SRV RRs '%s.*'", + svc_name); + } + len = res_nsearch(state, svc_name, C_IN, T_SRV, + msg, NS_MAXMSG); + if (len < 0) { + if (DBG(DNS, 0)) { + logger(LOG_DEBUG, + "DNS search for '%s' failed (%s)", + svc_name, hstrerror(state->res_h_errno)); + } + goto errout; + } + } else { /* dname != NULL */ + if (DBG(DNS, 1)) { + logger(LOG_DEBUG, "Looking for SRV RRs '%s.%s' ", + svc_name, dname); + } + + len = res_nquerydomain(state, svc_name, dname, C_IN, T_SRV, + msg, NS_MAXMSG); + + if (len < 0) { + if (DBG(DNS, 0)) { + logger(LOG_DEBUG, "DNS: %s.%s: %s", + svc_name, dname, + hstrerror(state->res_h_errno)); + } + goto errout; + } + } + + if (len > NS_MAXMSG) { + logger(LOG_WARNING, + "DNS query %ib message doesn't fit into %ib buffer", + len, NS_MAXMSG); + len = NS_MAXMSG; + } + + + /* parse the reply header */ + + cds_res = srv_parse(msg, len, &scnt, &maxcnt); + if (cds_res == NULL) + goto errout; + + if (prefer != NULL) + add_preferred(cds_res, prefer, &scnt, maxcnt); + + get_addresses(cds_res, scnt); + + /* sort list of candidates */ + if (scnt > 1) + qsort(cds_res, scnt, sizeof (*cds_res), + (int (*)(const void *, const void *))srvcmp); + + free(msg); + return (cds_res); + +errout: + free(msg); + return (NULL); +} + +static ad_disc_cds_t * +srv_parse(uchar_t *msg, int len, int *scnt, int *maxcnt) +{ + ad_disc_cds_t *cds; + ad_disc_cds_t *cds_res = NULL; + HEADER *hdr; + int i, qdcount, ancount, nscount, arcount; + uchar_t *ptr, *eom; + uchar_t *end; + uint16_t type; + /* LINTED E_FUNC_SET_NOT_USED */ + uint16_t class; + uint32_t rttl; + uint16_t size; + char namebuf[NS_MAXDNAME]; + + eom = msg + len; + hdr = (void *)msg; + ptr = msg + sizeof (HEADER); + + qdcount = ntohs(hdr->qdcount); + ancount = ntohs(hdr->ancount); + nscount = ntohs(hdr->nscount); + arcount = ntohs(hdr->arcount); + + /* skip the question section */ + + len = ns_skiprr(ptr, eom, ns_s_qd, qdcount); + if (len < 0) { + logger(LOG_ERR, "DNS query invalid message format"); + return (NULL); + } + ptr += len; + + /* + * Walk through the answer section, building the result array. + * The array size is +2 because we (possibly) add the preferred + * DC if it was not there, and an empty one (null termination). + */ + + *maxcnt = ancount + 2; + cds_res = calloc(*maxcnt, sizeof (*cds_res)); + if (cds_res == NULL) { + logger(LOG_ERR, "Out of memory"); + return (NULL); + } + + cds = cds_res; + for (i = 0; i < ancount; i++) { + + len = dn_expand(msg, eom, ptr, namebuf, + sizeof (namebuf)); + if (len < 0) { + logger(LOG_ERR, "DNS query invalid message format"); + goto err; + } + ptr += len; + NS_GET16(type, ptr); + NS_GET16(class, ptr); + NS_GET32(rttl, ptr); + NS_GET16(size, ptr); + if ((end = ptr + size) > eom) { + logger(LOG_ERR, "DNS query invalid message format"); + goto err; + } + + if (type != T_SRV) { + ptr = end; + continue; + } + + NS_GET16(cds->cds_ds.priority, ptr); + NS_GET16(cds->cds_ds.weight, ptr); + NS_GET16(cds->cds_ds.port, ptr); + len = dn_expand(msg, eom, ptr, cds->cds_ds.host, + sizeof (cds->cds_ds.host)); + if (len < 0) { + logger(LOG_ERR, "DNS query invalid SRV record"); + goto err; + } + + cds->cds_ds.ttl = rttl; + + if (DBG(DNS, 2)) { + logger(LOG_DEBUG, " %s", namebuf); + logger(LOG_DEBUG, + " ttl=%d pri=%d weight=%d %s:%d", + rttl, cds->cds_ds.priority, cds->cds_ds.weight, + cds->cds_ds.host, cds->cds_ds.port); + } + cds++; + + /* move ptr to the end of current record */ + ptr = end; + } + *scnt = (cds - cds_res); + + /* skip the nameservers section (if any) */ + + len = ns_skiprr(ptr, eom, ns_s_ns, nscount); + if (len < 0) { + logger(LOG_ERR, "DNS query invalid message format"); + goto err; + } + ptr += len; + + /* walk through the additional records */ + for (i = 0; i < arcount; i++) { + sa_family_t af; + + len = dn_expand(msg, eom, ptr, namebuf, + sizeof (namebuf)); + if (len < 0) { + logger(LOG_ERR, "DNS query invalid message format"); + goto err; + } + ptr += len; + NS_GET16(type, ptr); + NS_GET16(class, ptr); + NS_GET32(rttl, ptr); + NS_GET16(size, ptr); + if ((end = ptr + size) > eom) { + logger(LOG_ERR, "DNS query invalid message format"); + goto err; + } + switch (type) { + case ns_t_a: + af = AF_INET; + break; + case ns_t_aaaa: + af = AF_INET6; + break; + default: + continue; + } + + if (DBG(DNS, 2)) { + char abuf[INET6_ADDRSTRLEN]; + const char *ap; + + ap = inet_ntop(af, ptr, abuf, sizeof (abuf)); + logger(LOG_DEBUG, " %s %s %s", + (af == AF_INET) ? "A " : "AAAA", + (ap) ? ap : "?", namebuf); + } + + /* Find the server, add to its address list. */ + for (cds = cds_res; cds->cds_ds.host[0] != '\0'; cds++) + if (0 == strcmp(namebuf, cds->cds_ds.host)) + save_addr(cds, af, ptr, size); + + /* move ptr to the end of current record */ + ptr = end; + } + + return (cds_res); + +err: + free(cds_res); + return (NULL); +} + +/* + * Save this address on the server, if not already there. + */ +static void +save_addr(ad_disc_cds_t *cds, sa_family_t af, uchar_t *addr, size_t alen) +{ + struct addrinfo *ai, *new_ai, *last_ai; + + new_ai = make_addrinfo(af, addr, alen); + if (new_ai == NULL) + return; + + last_ai = NULL; + for (ai = cds->cds_ai; ai != NULL; ai = ai->ai_next) { + last_ai = ai; + + if (new_ai->ai_family == ai->ai_family && + new_ai->ai_addrlen == ai->ai_addrlen && + 0 == memcmp(new_ai->ai_addr, ai->ai_addr, + ai->ai_addrlen)) { + /* it's already there */ + freeaddrinfo(new_ai); + return; + } + } + + /* Not found. Append. */ + if (last_ai != NULL) { + last_ai->ai_next = new_ai; + } else { + cds->cds_ai = new_ai; + } +} + +static struct addrinfo * +make_addrinfo(sa_family_t af, uchar_t *addr, size_t alen) +{ + struct addrinfo *ai; + struct sockaddr *sa; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + int slen; + + ai = calloc(1, sizeof (*ai)); + sa = calloc(1, sizeof (struct sockaddr_in6)); + + if (ai == NULL || sa == NULL) { + logger(LOG_ERR, "Out of memory"); + goto errout; + } + + switch (af) { + case AF_INET: + sin = (void *)sa; + if (alen < sizeof (in_addr_t)) { + logger(LOG_ERR, "bad IPv4 addr len"); + goto errout; + } + alen = sizeof (in_addr_t); + sin->sin_family = af; + (void) memcpy(&sin->sin_addr, addr, alen); + slen = sizeof (*sin); + break; + + case AF_INET6: + sin6 = (void *)sa; + if (alen < sizeof (in6_addr_t)) { + logger(LOG_ERR, "bad IPv6 addr len"); + goto errout; + } + alen = sizeof (in6_addr_t); + sin6->sin6_family = af; + (void) memcpy(&sin6->sin6_addr, addr, alen); + slen = sizeof (*sin6); + break; + + default: + goto errout; + } + + ai->ai_family = af; + ai->ai_addrlen = slen; + ai->ai_addr = sa; + sa->sa_family = af; + return (ai); + +errout: + free(ai); + free(sa); + return (NULL); +} + +/* + * Set a preferred candidate, which may already be in the list, + * in which case we just bump its priority, or else add it. + */ +static void +add_preferred(ad_disc_cds_t *cds, ad_disc_ds_t *prefer, int *nds, int maxds) +{ + ad_disc_ds_t *ds; + int i; + + assert(*nds < maxds); + for (i = 0; i < *nds; i++) { + ds = &cds[i].cds_ds; + + if (strcasecmp(ds->host, prefer->host) == 0) { + /* Force this element to be sorted first. */ + ds->priority = 0; + ds->weight = 200; + return; + } + } + + /* + * The preferred DC was not found in this DNS response, + * so add it. Again arrange for it to be sorted first. + * Address info. is added later. + */ + ds = &cds[i].cds_ds; + (void) memcpy(ds, prefer, sizeof (*ds)); + ds->priority = 0; + ds->weight = 200; + *nds = i + 1; +} + +/* + * Do another pass over the array to check for missing addresses and + * try resolving the names. Normally, the DNS response from AD will + * have supplied additional address records for all the SRV records. + */ +static void +get_addresses(ad_disc_cds_t *cds, int cnt) +{ + int i; + + for (i = 0; i < cnt; i++) { + if (cds[i].cds_ai == NULL) { + do_getaddrinfo(&cds[i]); + } + } +} + +static void +do_getaddrinfo(ad_disc_cds_t *cds) +{ + struct addrinfo hints; + struct addrinfo *ai; + ad_disc_ds_t *ds; + time_t t0, t1; + int err; + + (void) memset(&hints, 0, sizeof (hints)); + hints.ai_protocol = IPPROTO_TCP; + hints.ai_socktype = SOCK_STREAM; + ds = &cds->cds_ds; + + /* + * This getaddrinfo call may take a LONG time, i.e. if our + * DNS servers are misconfigured or not responding. + * We need something like getaddrinfo_a(), with a timeout. + * For now, just log when this happens so we'll know + * if these calls are taking a long time. + */ + if (DBG(DNS, 2)) + logger(LOG_DEBUG, "getaddrinfo %s ...", ds->host); + t0 = time(NULL); + err = getaddrinfo(cds->cds_ds.host, NULL, &hints, &ai); + t1 = time(NULL); + if (DBG(DNS, 2)) + logger(LOG_DEBUG, "getaddrinfo %s rc=%d", ds->host, err); + if (t1 > (t0 + 5)) { + logger(LOG_WARNING, "Lookup host (%s) took %u sec. " + "(Check DNS settings)", ds->host, (int)(t1 - t0)); + } + if (err != 0) { + logger(LOG_ERR, "No address for host: %s (%s)", + ds->host, gai_strerror(err)); + /* Make this sort at the end. */ + ds->priority = 1 << 16; + return; + } + + cds->cds_ai = ai; +} + +void +srv_free(ad_disc_cds_t *cds_vec) +{ + ad_disc_cds_t *cds; + + for (cds = cds_vec; cds->cds_ds.host[0] != '\0'; cds++) { + if (cds->cds_ai != NULL) { + freeaddrinfo(cds->cds_ai); + } + } + free(cds_vec); +} diff --git a/usr/src/lib/libkrb5/Makefile b/usr/src/lib/libkrb5/Makefile index 68feb56d99..07a5fcc0e5 100644 --- a/usr/src/lib/libkrb5/Makefile +++ b/usr/src/lib/libkrb5/Makefile @@ -22,19 +22,23 @@ # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright 2014 Nexenta Systems, Inc. All rights reserved. +# include ../Makefile.lib SUBDIRS = $(MACH) $(BUILD64) $(MACH64) INSTALLED_HDRS = \ - com_err.h krb5.h + com_err.h krb5.h locate_plugin.h COMERRH = $(SRC)/lib/gss_mechs/mech_krb5/include/com_err.h +LOCPLUGH = $(SRC)/lib/gss_mechs/mech_krb5/include/locate_plugin.h KRB5H = $(SRC)/uts/common/gssapi/mechs/krb5/include/krb5.h KRB5INCDIR= $(ROOT)/usr/include/kerberosv5 COMERRHINST = $(KRB5INCDIR)/com_err.h +LOCPLUGHINST = $(KRB5INCDIR)/locate_plugin.h KRB5HINST = $(KRB5INCDIR)/krb5.h KRB5HDRS = $(INSTALLED_HDRS:%=$(KRB5INCDIR)/%) @@ -59,5 +63,7 @@ $(COMERRHINST): $(COMERRH) install -s -m 644 -f $(KRB5INCDIR) $(COMERRH) $(KRB5HINST): $(KRB5H) install -s -m 644 -f $(KRB5INCDIR) $(KRB5H) +$(LOCPLUGHINST): $(LOCPLUGH) + install -s -m 644 -f $(KRB5INCDIR) $(LOCPLUGH) FRC: diff --git a/usr/src/lib/libldap5/mapfile-vers b/usr/src/lib/libldap5/mapfile-vers index fc89fa2e4c..0da199128f 100644 --- a/usr/src/lib/libldap5/mapfile-vers +++ b/usr/src/lib/libldap5/mapfile-vers @@ -20,7 +20,7 @@ # # # Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. -# Copyright 2011 Nexenta Systems, Inc. All rights reserved. +# Copyright 2014 Nexenta Systems, Inc. All rights reserved. # # @@ -179,6 +179,7 @@ SYMBOL_VERSION SUNW_5.1 { ldap_parse_sort_control; ldap_parse_virtuallist_control; ldap_perror; + ldap_put_filter; ldap_rename; ldap_rename_s; ldap_result; diff --git a/usr/src/lib/libresolv2/common/resolv/res_send.c b/usr/src/lib/libresolv2/common/resolv/res_send.c index 8e629a3d96..a043cf2624 100644 --- a/usr/src/lib/libresolv2/common/resolv/res_send.c +++ b/usr/src/lib/libresolv2/common/resolv/res_send.c @@ -1,6 +1,8 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ @@ -94,6 +96,7 @@ static const char rcsid[] = "$Id: res_send.c,v 1.22 2009/01/22 23:49:23 tbox Exp #include <sys/uio.h> #include <netinet/in.h> +#include <netinet/tcp.h> #include <arpa/nameser.h> #include <arpa/inet.h> @@ -672,6 +675,29 @@ send_vc(res_state statp, (void)setsockopt(statp->_vcsock, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on)); #endif +#ifdef TCP_CONN_ABORT_THRESHOLD + /* + * The default connection timeout is over two minutes. + * We need something more reasonable here. The default + * retrans value is 5 sec., then 10, 20, 40, on retries. + * TCP connect does its own retries, so we want just one + * reasonable timeout value. Using 2X retrans, which + * gives us a 10 sec. connect timeout. If we're waiting + * that long to connect, we probably want to give up and + * try the next DNS server in our list. + * + * It might be reasonable to do this for all callers, + * but for now do it only when we see MS_INTEROP in the + * environment (set in smbd and idmapd) + */ + if (getenv("MS_INTEROP") != NULL) { + int conn_tmo; + conn_tmo = statp->retrans * 2000; /* mSec */ + (void)setsockopt(statp->_vcsock, IPPROTO_TCP, + TCP_CONN_ABORT_THRESHOLD, &conn_tmo, + sizeof(conn_tmo)); + } +#endif errno = 0; if (connect(statp->_vcsock, nsap, nsaplen) < 0) { *terrno = errno; diff --git a/usr/src/lib/libshare/smb/Makefile.com b/usr/src/lib/libshare/smb/Makefile.com index 0fc5f66142..ea69f59b22 100644 --- a/usr/src/lib/libshare/smb/Makefile.com +++ b/usr/src/lib/libshare/smb/Makefile.com @@ -22,7 +22,7 @@ # Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# Copyright 2012 Nexenta Systems, Inc. All rights reserved. +# Copyright 2013 Nexenta Systems, Inc. All rights reserved. # LIBRARY = libshare_smb.a @@ -45,8 +45,7 @@ LIBSRCS = $(LIBOBJS:%.o=$(SRCDIR)/%.c) lintcheck := SRCS = $(LIBSRCS) LIBS = $(DYNLIB) -LDLIBS += -lshare -ldlpi -lnsl -lnvpair -lscf -lumem -lc -all install := LDLIBS += -lxml2 +LDLIBS += -lshare -lscf -luuid -ldlpi -lnsl -lnvpair -lxml2 -lumem -lc CFLAGS += $(CCVERBOSE) CERRWARN += -_gcc=-Wno-char-subscripts diff --git a/usr/src/lib/libsmbfs/Makefile b/usr/src/lib/libsmbfs/Makefile index 47c14c6bfa..b708bd0571 100644 --- a/usr/src/lib/libsmbfs/Makefile +++ b/usr/src/lib/libsmbfs/Makefile @@ -19,10 +19,11 @@ # CDDL HEADER END # # -# Copyright 2011 Nexenta Systems, Inc. All rights reserved. # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright 2013 Nexenta Systems, Inc. All rights reserved. +# # # lib/libsmbfs/Makefile @@ -30,7 +31,7 @@ include $(SRC)/lib/Makefile.lib -HDRS= smbfs_acl.h smbfs_api.h smb_keychain.h +HDRS= ntlmssp.h smbfs_acl.h smbfs_api.h smb_keychain.h spnego.h HDRDIR= netsmb ROOTHDRDIR= $(ROOT)/usr/include/netsmb diff --git a/usr/src/lib/libsmbfs/Makefile.com b/usr/src/lib/libsmbfs/Makefile.com index a3ec0fb397..f5801a18db 100644 --- a/usr/src/lib/libsmbfs/Makefile.com +++ b/usr/src/lib/libsmbfs/Makefile.com @@ -23,6 +23,8 @@ # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright 2013 Nexenta Systems, Inc. All rights reserved. +# # # lib/libsmbfs/Makefile.com @@ -101,7 +103,7 @@ CERRWARN += -_gcc=-Wno-uninitialized CERRWARN += -_gcc=-Wno-unused-variable CPPFLAGS += -D__EXTENSIONS__ -D_REENTRANT -DMIA \ - -I$(SRCDIR) -I.. \ + -I$(SRCDIR) -I.. -I../netsmb \ -I$(SRC)/uts/common \ -I$(SRC)/common/smbclnt diff --git a/usr/src/lib/libsmbfs/smb/ntlmssp.h b/usr/src/lib/libsmbfs/netsmb/ntlmssp.h index 5f3e09ac0d..f4cfc34783 100644 --- a/usr/src/lib/libsmbfs/smb/ntlmssp.h +++ b/usr/src/lib/libsmbfs/netsmb/ntlmssp.h @@ -22,6 +22,7 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ @@ -37,6 +38,10 @@ * http://msdn.microsoft.com/en-us/library/cc236621(PROT.10).aspx */ +#ifdef __cplusplus +extern "C" { +#endif + /* * NTLMSSP Message Types * [MS-NLMP] sec. 2.2.1 @@ -82,4 +87,28 @@ #define NTLMSSP_NEGOTIATE_KEY_EXCH 0x40000000 #define NTLMSSP_NEGOTIATE_56 0x80000000 +/* + * NTLMSSP AV_PAIR types + * [MS-NLMP] sec. 2.2.2.1 + * + * The names are all LE-Unicode. + */ +typedef enum ntlmssp_AvId { + MsvAvEOL = 0, /* End Of List */ + MsvAvNbComputerName, /* server's NetBIOS name */ + MsvAvNbDomainName, /* server's NetBIOS domain */ + MsvAvDnsComputerName, /* server's DNS name */ + MsvAvDnsDomainName, /* server's DNS domain */ + MsvAvDnsTreeName, /* server's Forest name */ + MsvAvFlags, /* 32-bit (LE) flags */ + MsvAvTimestamp, /* 64-bit time, [MS-DTYP] sec. 2.3.1 */ + MsvAvRestrictions, /* struct, [MS-NLMP] sec. 2.2.2.2 */ + MsvAvTargetName, /* SPN of the server */ + MsvChannelBindings, /* MD5 hash of GSS challen bindings */ +} ntlmssp_AvId_t; + +#ifdef __cplusplus +} +#endif + #endif /* _NTLMSSP_H */ diff --git a/usr/src/lib/libsmbfs/netsmb/spnego.h b/usr/src/lib/libsmbfs/netsmb/spnego.h new file mode 100644 index 0000000000..1a60436740 --- /dev/null +++ b/usr/src/lib/libsmbfs/netsmb/spnego.h @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2002 Microsoft Corporation + * All rights reserved. + * + * THIS CODE AND INFORMATION IS PROVIDED "AS IS" + * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED + * OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY + * AND/OR FITNESS FOR A PARTICULAR PURPOSE. + * + * Date - 10/08/2002 + * Author - Sanj Surati + */ + +/* + * Copyright 2012 Nexenta Systems, Inc. All rights reserved. + */ + +/* + * spnego.h + * + * SPNEGO Token Handler Header File + * + * Contains the definitions required to interpret and create + * SPNEGO tokens so that Kerberos GSS tokens can be + * Unpackaged/packaged. + */ + +#ifndef _SPNEGO_H +#define _SPNEGO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Type Definitions + */ + +/* + * Users of SPNEGO Token Handler API will request + * these as well as free them, + */ +typedef void* SPNEGO_TOKEN_HANDLE; + +/* + * Defines the element types that are found + * in each of the tokens. + */ + +typedef enum spnego_element_type +{ + spnego_element_min, /* Lower bound */ + + /* Init token elements */ + spnego_init_mechtypes, + spnego_init_reqFlags, + spnego_init_mechToken, + spnego_init_mechListMIC, + + /* Targ token elements */ + spnego_targ_negResult, + spnego_targ_supportedMech, + spnego_targ_responseToken, + spnego_targ_mechListMIC, + + spnego_element_max /* Upper bound */ + +} SPNEGO_ELEMENT_TYPE; + +/* + * Token Element Availability. Elements in both + * token types are optional. Since there are only + * 4 elements in each Token, we will allocate space + * to hold the information, but we need a way to + * indicate whether or not an element is available + */ + +#define SPNEGO_TOKEN_ELEMENT_UNAVAILABLE 0 +#define SPNEGO_TOKEN_ELEMENT_AVAILABLE 1 + +/* + * Token type values. SPNEGO has 2 token types: + * NegTokenInit and NegTokenTarg + */ + +#define SPNEGO_TOKEN_INIT 0 +#define SPNEGO_TOKEN_TARG 1 + +/* + * GSS Mechanism OID enumeration. We only really handle + * 3 different OIDs. These are stored in an array structure + * defined in the parsing code. + */ + +typedef enum spnego_mech_oid +{ + /* Init token elements */ + spnego_mech_oid_Kerberos_V5_Legacy, /* Really V5, but OID off by 1 */ + spnego_mech_oid_Kerberos_V5, + spnego_mech_oid_Spnego, + spnego_mech_oid_NTLMSSP, + spnego_mech_oid_NotUsed = -1 + +} SPNEGO_MECH_OID; + +/* + * Defines the negResult values. + */ + +typedef enum spnego_negResult +{ + spnego_negresult_success, + spnego_negresult_incomplete, + spnego_negresult_rejected, + spnego_negresult_NotUsed = -1 +} SPNEGO_NEGRESULT; + +/* + * Context Flags in NegTokenInit + */ + +/* + * ContextFlags values MUST be zero or a combination + * of the below + */ + +#define SPNEGO_NEGINIT_CONTEXT_DELEG_FLAG 0x80 +#define SPNEGO_NEGINIT_CONTEXT_MUTUAL_FLAG 0x40 +#define SPNEGO_NEGINIT_CONTEXT_REPLAY_FLAG 0x20 +#define SPNEGO_NEGINIT_CONTEXT_SEQUENCE_FLAG 0x10 +#define SPNEGO_NEGINIT_CONTEXT_ANON_FLAG 0x8 +#define SPNEGO_NEGINIT_CONTEXT_CONF_FLAG 0x4 +#define SPNEGO_NEGINIT_CONTEXT_INTEG_FLAG 0x2 + +/* + * Mask to retrieve valid values. + */ + +#define SPNEGO_NEGINIT_CONTEXT_MASK 0xFE + +/* + * SPNEGO API return codes. + */ + +/* API function was successful */ +#define SPNEGO_E_SUCCESS 0 + +/* The supplied Token was invalid */ +#define SPNEGO_E_INVALID_TOKEN -1 + +/* An invalid length was encountered */ +#define SPNEGO_E_INVALID_LENGTH -2 + +/* The Token Parse failed */ +#define SPNEGO_E_PARSE_FAILED -3 + +/* The requested value was not found */ +#define SPNEGO_E_NOT_FOUND -4 + +/* The requested element is not available */ +#define SPNEGO_E_ELEMENT_UNAVAILABLE -5 + +/* Out of Memory */ +#define SPNEGO_E_OUT_OF_MEMORY -6 + +/* Not Implemented */ +#define SPNEGO_E_NOT_IMPLEMENTED -7 + +/* Invalid Parameter */ +#define SPNEGO_E_INVALID_PARAMETER -8 + +/* Token Handler encountered an unexpected OID */ +#define SPNEGO_E_UNEXPECTED_OID -9 + +/* The requested token was not found */ +#define SPNEGO_E_TOKEN_NOT_FOUND -10 + +/* An unexpected type was encountered in the encoding */ +#define SPNEGO_E_UNEXPECTED_TYPE -11 + +/* The buffer was too small */ +#define SPNEGO_E_BUFFER_TOO_SMALL -12 + +/* A Token Element was invalid (e.g. improper length or value) */ +#define SPNEGO_E_INVALID_ELEMENT -13 + +/* Miscelaneous API Functions */ + +/* Frees opaque data */ +void spnegoFreeData(SPNEGO_TOKEN_HANDLE hSpnegoToken); + +/* Initializes SPNEGO_TOKEN structure from DER encoded binary data */ +int spnegoInitFromBinary(unsigned char *pbTokenData, unsigned long ulLength, + SPNEGO_TOKEN_HANDLE* phSpnegoToken); + +/* Initializes SPNEGO_TOKEN structure for a NegTokenInit type */ +int spnegoCreateNegTokenHint(SPNEGO_MECH_OID *pMechTypeList, int MechTypeCnt, + unsigned char *pbPrincipal, SPNEGO_TOKEN_HANDLE* phSpnegoToken); + +/* Initializes SPNEGO_TOKEN structure for a NegTokenInit type */ +int spnegoCreateNegTokenInit(SPNEGO_MECH_OID MechType, + unsigned char ucContextFlags, unsigned char *pbMechToken, + unsigned long ulMechTokenLen, unsigned char *pbMechTokenMIC, + unsigned long ulMechTokenMIC, SPNEGO_TOKEN_HANDLE *phSpnegoToken); + +/* Initializes SPNEGO_TOKEN structure for a NegTokenTarg type */ +int spnegoCreateNegTokenTarg(SPNEGO_MECH_OID MechType, + SPNEGO_NEGRESULT spnegoNegResult, unsigned char *pbMechToken, + unsigned long ulMechTokenLen, unsigned char *pbMechListMIC, + unsigned long ulMechListMICLen, SPNEGO_TOKEN_HANDLE* phSpnegoToken); + +/* Copies binary representation of SPNEGO Data into user supplied buffer */ +int spnegoTokenGetBinary(SPNEGO_TOKEN_HANDLE hSpnegoToken, + unsigned char *pbTokenData, unsigned long *pulDataLen); + +/* Returns SPNEGO Token Type */ +int spnegoGetTokenType(SPNEGO_TOKEN_HANDLE hSpnegoToken, int *piTokenType); + +/* Reading an Init Token */ + +/* Returns the Initial Mech Type in the MechList element in the NegInitToken. */ +int spnegoIsMechTypeAvailable(SPNEGO_TOKEN_HANDLE hSpnegoToken, + SPNEGO_MECH_OID MechOID, int *piMechTypeIndex); + +/* Returns the value from the context flags element in the NegInitToken */ +int spnegoGetContextFlags(SPNEGO_TOKEN_HANDLE hSpnegoToken, + unsigned char *pucContextFlags); + +/* Reading a Response Token */ + +/* + * Returns the value from the negResult element + * (Status code of GSS call - 0,1,2) + */ +int spnegoGetNegotiationResult(SPNEGO_TOKEN_HANDLE hSpnegoToken, + SPNEGO_NEGRESULT* pnegResult); + +/* Returns the Supported Mech Type from the NegTokenTarg. */ +int spnegoGetSupportedMechType(SPNEGO_TOKEN_HANDLE hSpnegoToken, + SPNEGO_MECH_OID* pMechOID); + +/* Reading either Token Type */ + +/* + * Returns the actual Mechanism data from the token + * (this is what is passed into GSS-API functions + */ +int spnegoGetMechToken(SPNEGO_TOKEN_HANDLE hSpnegoToken, + unsigned char *pbTokenData, unsigned long *pulDataLen); + +/* Returns the Message Integrity BLOB in the token */ +int spnegoGetMechListMIC(SPNEGO_TOKEN_HANDLE hSpnegoToken, + unsigned char *pbMICData, unsigned long *pulDataLen); + +#ifdef __cplusplus +} +#endif + +#endif /* _SPNEGO_H */ diff --git a/usr/src/lib/libsmbfs/smb/derparse.c b/usr/src/lib/libsmbfs/smb/derparse.c index f92da913c2..f21a277d5a 100644 --- a/usr/src/lib/libsmbfs/smb/derparse.c +++ b/usr/src/lib/libsmbfs/smb/derparse.c @@ -1,3 +1,4 @@ +// Copyright 2012 Nexenta Systems, Inc. All rights reserved. // Copyright (C) 2002 Microsoft Corporation // All rights reserved. // @@ -451,29 +452,37 @@ long ASNDerCalcElementLength( long nDataLength, long* pnInternalLength ) // Comments : // Helper function to calculate a MechList length. A mechlist consists // of a NegTokenInit sequence token, a sequence token for the MechList -// and finally a list of OIDs. In our case, we only really have one -// OID. +// and finally a list of OIDs. // //////////////////////////////////////////////////////////////////////////// -long ASNDerCalcMechListLength( SPNEGO_MECH_OID mechoid, long* pnInternalLength ) +long ASNDerCalcMechListLength( SPNEGO_MECH_OID *mechOidLst, int mechOidCnt, + long* pnInternalLength ) { - // First the OID - long nTotalLength = g_stcMechOIDList[mechoid].iLen; - - // Next add in a sequence token - nTotalLength += ASNDerCalcTokenLength( nTotalLength, 0L ); - - // Internal length is the length without the element sequence token - if ( NULL != pnInternalLength ) - { - *pnInternalLength = nTotalLength; - } - - // Finally add in the element's sequence token - nTotalLength += ASNDerCalcTokenLength( nTotalLength, 0L ); - - return nTotalLength; + // First the OID + SPNEGO_MECH_OID oid_idx; + long nTotalLength; + int i; + + nTotalLength = 0; + for (i = 0; i < mechOidCnt; i++) { + oid_idx = mechOidLst[i]; + nTotalLength += g_stcMechOIDList[oid_idx].iLen; + } + + // Next add in a sequence token + nTotalLength += ASNDerCalcTokenLength( nTotalLength, 0L ); + + // Internal length is the length without the element sequence token + if ( NULL != pnInternalLength ) + { + *pnInternalLength = nTotalLength; + } + + // Finally add in the element's sequence token + nTotalLength += ASNDerCalcTokenLength( nTotalLength, 0L ); + + return nTotalLength; } @@ -646,9 +655,12 @@ int ASNDerWriteToken( unsigned char* pbData, unsigned char ucType, int ASNDerWriteOID( unsigned char* pbData, SPNEGO_MECH_OID eMechOID ) { - memcpy( pbData, g_stcMechOIDList[eMechOID].ucOid, g_stcMechOIDList[eMechOID].iLen ); + if (pbData != NULL) { + memcpy( pbData, g_stcMechOIDList[eMechOID].ucOid, + g_stcMechOIDList[eMechOID].iLen ); + } - return g_stcMechOIDList[eMechOID].iLen; + return g_stcMechOIDList[eMechOID].iLen; } @@ -671,27 +683,35 @@ int ASNDerWriteOID( unsigned char* pbData, SPNEGO_MECH_OID eMechOID ) // //////////////////////////////////////////////////////////////////////////// -long ASNDerWriteMechList( unsigned char* pbData, SPNEGO_MECH_OID mechoid ) +long ASNDerWriteMechList( unsigned char* pbData, SPNEGO_MECH_OID *mechOidLst, int mechOidCnt ) { - // First get the length - long nInternalLength = 0L; - long nMechListLength = ASNDerCalcMechListLength( mechoid, &nInternalLength ); - long nTempLength = 0L; - - nTempLength = ASNDerWriteToken( pbData, SPNEGO_NEGINIT_ELEMENT_MECHTYPES, + // First get the length + long nInternalLength = 0L; + long nMechListLength; + long nTempLength = 0L; + int i; + + nMechListLength = ASNDerCalcMechListLength(mechOidLst, mechOidCnt, &nInternalLength); + nTempLength = ASNDerWriteToken( pbData, SPNEGO_NEGINIT_ELEMENT_MECHTYPES, NULL, nInternalLength ); - // Adjust the data pointer - pbData += nTempLength; + // Adjust the data pointer + pbData += nTempLength; + nInternalLength -= nTempLength; + + // Now write the Sequence token and the OID (the OID is a BLOB in the global + // structure. - // Now write the Sequence token and the OID (the OID is a BLOB in the global - // structure. + nTempLength = ASNDerWriteToken( pbData, SPNEGO_CONSTRUCTED_SEQUENCE, + NULL, nInternalLength); + pbData += nTempLength; - nTempLength = ASNDerWriteToken( pbData, SPNEGO_CONSTRUCTED_SEQUENCE, - g_stcMechOIDList[mechoid].ucOid, - g_stcMechOIDList[mechoid].iLen ); + for (i = 0; i < mechOidCnt; i++) { + nTempLength = ASNDerWriteOID( pbData, mechOidLst[i] ); + pbData += nTempLength; + } - return nMechListLength; + return nMechListLength; } diff --git a/usr/src/lib/libsmbfs/smb/derparse.h b/usr/src/lib/libsmbfs/smb/derparse.h index dcdf5828dc..b1801c8e1c 100644 --- a/usr/src/lib/libsmbfs/smb/derparse.h +++ b/usr/src/lib/libsmbfs/smb/derparse.h @@ -1,3 +1,4 @@ +// Copyright 2012 Nexenta Systems, Inc. All rights reserved. // Copyright (C) 2002 Microsoft Corporation // All rights reserved. // @@ -21,8 +22,6 @@ // ///////////////////////////////////////////////////////////// -#pragma ident "%Z%%M% %I% %E% SMI" - #ifndef __DERPARSE_H__ #define __DERPARSE_H__ @@ -178,12 +177,13 @@ int ASNDerCheckOID( unsigned char* pbTokenData, SPNEGO_MECH_OID nMechOID, long n int ASNDerCalcNumLengthBytes( long nLength ); long ASNDerCalcTokenLength( long nLength, long nDataLength ); long ASNDerCalcElementLength( long nDataLength, long* pnInternalLength ); -long ASNDerCalcMechListLength( SPNEGO_MECH_OID mechoid, long* pnInternalLength ); +long ASNDerCalcMechListLength( SPNEGO_MECH_OID *mechOidLst, int mechOidCnt, + long* pnInternalLength ); int ASNDerWriteLength( unsigned char* pbData, long nLength ); int ASNDerWriteToken( unsigned char* pbData, unsigned char ucType, unsigned char* pbTokenValue, long nLength ); int ASNDerWriteOID( unsigned char* pbData, SPNEGO_MECH_OID eMechOID ); -long ASNDerWriteMechList( unsigned char* pbData, SPNEGO_MECH_OID mechoid ); +long ASNDerWriteMechList( unsigned char* pbData, SPNEGO_MECH_OID *mechOidLst, int mechOidCnt ); int ASNDerWriteElement( unsigned char* pbData, unsigned char ucElementSequence, unsigned char ucType, unsigned char* pbTokenValue, long nLength ); diff --git a/usr/src/lib/libsmbfs/smb/llib-lsmbfs b/usr/src/lib/libsmbfs/smb/llib-lsmbfs index e8e05e4272..1096482541 100644 --- a/usr/src/lib/libsmbfs/smb/llib-lsmbfs +++ b/usr/src/lib/libsmbfs/smb/llib-lsmbfs @@ -22,6 +22,8 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ /*LINTLIBRARY*/ @@ -34,4 +36,5 @@ #include <netsmb/smb_keychain.h> #include <netsmb/smb_netshareenum.h> #include <netsmb/smb_rap.h> +#include <netsmb/spnego.h> diff --git a/usr/src/lib/libsmbfs/smb/mapfile-vers b/usr/src/lib/libsmbfs/smb/mapfile-vers index 1c2a5d6fa9..24bffec63d 100644 --- a/usr/src/lib/libsmbfs/smb/mapfile-vers +++ b/usr/src/lib/libsmbfs/smb/mapfile-vers @@ -18,8 +18,8 @@ # CDDL HEADER END # # -# Copyright 2011 Nexenta Systems, Inc. All rights reserved. # Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2013 Nexenta Systems, Inc. All rights reserved. # # @@ -151,6 +151,20 @@ SYMBOL_VERSION SUNWprivate { smbfs_set_default_user; smbutil_std_opts; + + spnegoCreateNegTokenHint; + spnegoCreateNegTokenInit; + spnegoCreateNegTokenTarg; + spnegoFreeData; + spnegoGetContextFlags; + spnegoGetMechListMIC; + spnegoGetMechToken; + spnegoGetNegotiationResult; + spnegoGetSupportedMechType; + spnegoGetTokenType; + spnegoInitFromBinary; + spnegoIsMechTypeAvailable; + spnegoTokenGetBinary; local: *; }; diff --git a/usr/src/lib/libsmbfs/smb/spnego.c b/usr/src/lib/libsmbfs/smb/spnego.c index 3e300cd606..a15303da30 100644 --- a/usr/src/lib/libsmbfs/smb/spnego.c +++ b/usr/src/lib/libsmbfs/smb/spnego.c @@ -1,3 +1,4 @@ +// Copyright 2012 Nexenta Systems, Inc. All rights reserved. // Copyright (C) 2002 Microsoft Corporation // All rights reserved. // @@ -21,10 +22,9 @@ // ///////////////////////////////////////////////////////////// -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stdlib.h> #include <stdio.h> +#include <string.h> #include <memory.h> #include "spnego.h" #include "derparse.h" @@ -89,6 +89,111 @@ int spnegoInitFromBinary( unsigned char* pbTokenData, unsigned long ulLength, SP ///////////////////////////////////////////////////////////////////////////// // // Function: +// spnegoCreateNegTokenHint +// +// Parameters: +// [in] pMechTypeList - List of MechTypes (OIDs) to include +// [in] MechTypeCnt - Length of MechTypes array +// [in] pbPrincipal - Principal name for MechListMIC +// [out] phSpnegoToken - SPNEGO_TOKEN_HANDLE pointer +// +// Returns: +// int Success - SPNEGO_E_SUCCESS +// Failure - SPNEGO API Error code +// +// Comments : +// Initializes a SPNEGO_TOKEN_HANDLE for a NegTokenInit type token +// from the supplied parameters. The token created is the "hint" +// used (for example) in the response to an SMB negotiate protocol. +// Returned data structure must be freed by calling spnegoFreeData(). +// +// The "hint" tells the client what authentication methods this +// server supports (the ones in the MechTypeList). The Principal +// name historically was the server's own SPN, but recent versions +// of windows only supply: "not_defined_in_RFC4178@please_ignore" +// So if you want to be nice to your clients, provide the host SPN, +// otherwise provide the bogus SPN string like recent windows. +// +//////////////////////////////////////////////////////////////////////////// + +int spnegoCreateNegTokenHint( SPNEGO_MECH_OID *pMechTypeList, int MechTypeCnt, + unsigned char *pbPrincipal, SPNEGO_TOKEN_HANDLE* phSpnegoToken ) +{ + int nReturn; + long nTokenLength = 0L; + long nInternalTokenLength = 0L; + unsigned long ulPrincipalLen; + unsigned char* pbMechListMIC; + unsigned long ulMechListMICLen; + unsigned char* pbTokenData = NULL; + SPNEGO_TOKEN** ppSpnegoToken = (SPNEGO_TOKEN**) phSpnegoToken; + + if ( NULL == ppSpnegoToken || NULL == pbPrincipal ) + return (SPNEGO_E_INVALID_PARAMETER); + + /* + * Get the actual token size + */ + ulPrincipalLen = strlen((char *)pbPrincipal); + ulMechListMICLen = ASNDerCalcElementLength( ulPrincipalLen, NULL ); + nReturn = CalculateMinSpnegoInitTokenSize( + 0, /* ulMechTokenLen */ + ulMechListMICLen, + pMechTypeList, + MechTypeCnt, + 0, /* nReqFlagsAvailable */ + &nTokenLength, + &nInternalTokenLength ); + if ( nReturn != SPNEGO_E_SUCCESS ) + return (nReturn); + + // Allocate a buffer to hold the data. + pbTokenData = calloc( 1, nTokenLength ); + + if ( NULL == pbTokenData ) + return ( SPNEGO_E_OUT_OF_MEMORY ); + + /* + * Construct the MechListMIC + */ + pbMechListMIC = pbTokenData + (nTokenLength - ulMechListMICLen); + (void) ASNDerWriteElement( pbMechListMIC, SPNEGO_NEGINIT_ELEMENT_MECHTYPES, + GENERALSTR, pbPrincipal, ulPrincipalLen ); + + // Now write the token + nReturn = CreateSpnegoInitToken( + pMechTypeList, + MechTypeCnt, + 0, /* ContextFlags */ + NULL, 0, /* MechToken, len */ + pbMechListMIC, + ulMechListMICLen, + pbTokenData, + nTokenLength, + nInternalTokenLength ); + if ( nReturn != SPNEGO_E_SUCCESS ) { + free( pbTokenData ); + return (nReturn); + } + + // This will copy our allocated pointer, and ensure that the sructure cleans + // up the data later + nReturn = InitTokenFromBinary( SPNEGO_TOKEN_INTERNAL_COPYPTR, + SPNEGO_TOKEN_INTERNAL_FLAGS_FREEDATA, + pbTokenData, nTokenLength, ppSpnegoToken ); + + // Cleanup on failure + if ( nReturn != SPNEGO_E_SUCCESS ) { + free( pbTokenData ); + return (nReturn); + } + + return (SPNEGO_E_SUCCESS); +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function: // spnegoCreateNegTokenInit // // Parameters: @@ -131,7 +236,7 @@ int spnegoCreateNegTokenInit( SPNEGO_MECH_OID MechType, // Get the actual token size if ( ( nReturn = CalculateMinSpnegoInitTokenSize( ulMechTokenLen, ulMechListMICLen, - MechType, ( ucContextFlags != 0L ), + &MechType, 1, ( ucContextFlags != 0L ), &nTokenLength, &nInternalTokenLength ) ) == SPNEGO_E_SUCCESS ) { @@ -142,7 +247,7 @@ int spnegoCreateNegTokenInit( SPNEGO_MECH_OID MechType, { // Now write the token - if ( ( nReturn = CreateSpnegoInitToken( MechType, + if ( ( nReturn = CreateSpnegoInitToken( &MechType, 1, ucContextFlags, pbMechToken, ulMechTokenLen, pbMechListMIC, ulMechListMICLen, pbTokenData, @@ -227,11 +332,7 @@ int spnegoCreateNegTokenTarg( SPNEGO_MECH_OID MechType, spnego_mech_oid_NotUsed == MechType ) && ( IsValidNegResult( spnegoNegResult ) || - spnego_negresult_NotUsed == spnegoNegResult ) && - - !( !IsValidMechOid( MechType ) && - ( spnego_negresult_success == spnegoNegResult || - spnego_negresult_incomplete == spnegoNegResult ) ) ) + spnego_negresult_NotUsed == spnegoNegResult ) ) { // Get the actual token size diff --git a/usr/src/lib/libsmbfs/smb/spnego.h b/usr/src/lib/libsmbfs/smb/spnego.h deleted file mode 100644 index 9865fbd85d..0000000000 --- a/usr/src/lib/libsmbfs/smb/spnego.h +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright (C) 2002 Microsoft Corporation -// All rights reserved. -// -// THIS CODE AND INFORMATION IS PROVIDED "AS IS" -// WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -// OR IMPLIED, INCLUDING BUT NOT LIMITED -// TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY -// AND/OR FITNESS FOR A PARTICULAR PURPOSE. -// -// Date - 10/08/2002 -// Author - Sanj Surati - -///////////////////////////////////////////////////////////// -// -// SPNEGO.H -// -// SPNEGO Token Handler Header File -// -// Contains the definitions required to interpret and create -// SPNEGO tokens so that Kerberos GSS tokens can be -// Unpackaged/packaged. -// -///////////////////////////////////////////////////////////// - -#pragma ident "%Z%%M% %I% %E% SMI" - -#ifndef __SPNEGO_H__ -#define __SPNEGO_H__ - -// C++ Specific -#if defined(__cplusplus) -extern "C" -{ -#endif - -// Type Definitions - -// -// Users of SPNEGO Token Handler API will request -// these as well as free them, -// -typedef void* SPNEGO_TOKEN_HANDLE; - -// -// Defines the element types that are found -// in each of the tokens. -// - -typedef enum spnego_element_type -{ - spnego_element_min, // Lower bound - - // Init token elements - spnego_init_mechtypes, - spnego_init_reqFlags, - spnego_init_mechToken, - spnego_init_mechListMIC, - - // Targ token elements - spnego_targ_negResult, - spnego_targ_supportedMech, - spnego_targ_responseToken, - spnego_targ_mechListMIC, - - spnego_element_max // Upper bound - -} SPNEGO_ELEMENT_TYPE; - -// -// Token Element Availability. Elements in both -// token types are optional. Since there are only -// 4 elements in each Token, we will allocate space -// to hold the information, but we need a way to -// indicate whether or not an element is available -// - -#define SPNEGO_TOKEN_ELEMENT_UNAVAILABLE 0 -#define SPNEGO_TOKEN_ELEMENT_AVAILABLE 1 - -// -// Token type values. SPNEGO has 2 token types: -// NegTokenInit and NegTokenTarg -// - -#define SPNEGO_TOKEN_INIT 0 -#define SPNEGO_TOKEN_TARG 1 - -// -// GSS Mechanism OID enumeration. We only really handle -// 3 different OIDs. These are stored in an array structure -// defined in the parsing code. -// - -typedef enum spnego_mech_oid -{ - // Init token elements - spnego_mech_oid_Kerberos_V5_Legacy, // Really V5, but OID off by 1 bit - spnego_mech_oid_Kerberos_V5, - spnego_mech_oid_Spnego, - spnego_mech_oid_NTLMSSP, - spnego_mech_oid_NotUsed = -1 - -} SPNEGO_MECH_OID; - -// -// Defines the negResult values. -// - -typedef enum spnego_negResult -{ - spnego_negresult_success, - spnego_negresult_incomplete, - spnego_negresult_rejected, - spnego_negresult_NotUsed = -1 -} SPNEGO_NEGRESULT; - -// -// Context Flags in NegTokenInit -// - -// -// ContextFlags values MUST be zero or a combination -// of the below -// - -#define SPNEGO_NEGINIT_CONTEXT_DELEG_FLAG 0x80 -#define SPNEGO_NEGINIT_CONTEXT_MUTUAL_FLAG 0x40 -#define SPNEGO_NEGINIT_CONTEXT_REPLAY_FLAG 0x20 -#define SPNEGO_NEGINIT_CONTEXT_SEQUENCE_FLAG 0x10 -#define SPNEGO_NEGINIT_CONTEXT_ANON_FLAG 0x8 -#define SPNEGO_NEGINIT_CONTEXT_CONF_FLAG 0x4 -#define SPNEGO_NEGINIT_CONTEXT_INTEG_FLAG 0x2 - -// -// Mask to retrieve valid values. -// - -#define SPNEGO_NEGINIT_CONTEXT_MASK 0xFE // Logical combination of above flags - -// -// SPNEGO API return codes. -// - -// API function was successful -#define SPNEGO_E_SUCCESS 0 - -// The supplied Token was invalid -#define SPNEGO_E_INVALID_TOKEN -1 - -// An invalid length was encountered -#define SPNEGO_E_INVALID_LENGTH -2 - -// The Token Parse failed -#define SPNEGO_E_PARSE_FAILED -3 - -// The requested value was not found -#define SPNEGO_E_NOT_FOUND -4 - -// The requested element is not available -#define SPNEGO_E_ELEMENT_UNAVAILABLE -5 - -// Out of Memory -#define SPNEGO_E_OUT_OF_MEMORY -6 - -// Not Implemented -#define SPNEGO_E_NOT_IMPLEMENTED -7 - -// Invalid Parameter -#define SPNEGO_E_INVALID_PARAMETER -8 - -// Token Handler encountered an unexpected OID -#define SPNEGO_E_UNEXPECTED_OID -9 - -// The requested token was not found -#define SPNEGO_E_TOKEN_NOT_FOUND -10 - -// An unexpected type was encountered in the encoding -#define SPNEGO_E_UNEXPECTED_TYPE -11 - -// The buffer was too small -#define SPNEGO_E_BUFFER_TOO_SMALL -12 - -// A Token Element was invalid (e.g. improper length or value) -#define SPNEGO_E_INVALID_ELEMENT -13 - -/* Miscelaneous API Functions */ - -// Frees opaque data -void spnegoFreeData( SPNEGO_TOKEN_HANDLE hSpnegoToken ); - -// Initializes SPNEGO_TOKEN structure from DER encoded binary data -int spnegoInitFromBinary( unsigned char* pbTokenData, unsigned long ulLength, SPNEGO_TOKEN_HANDLE* phSpnegoToken ); - -// Initializes SPNEGO_TOKEN structure for a NegTokenInit type using the -// supplied parameters -int spnegoCreateNegTokenInit( SPNEGO_MECH_OID MechType, - unsigned char ucContextFlags, unsigned char* pbMechToken, - unsigned long ulMechTokenLen, unsigned char* pbMechTokenMIC, - unsigned long ulMechTokenMIC, SPNEGO_TOKEN_HANDLE* phSpnegoToken ); - -// Initializes SPNEGO_TOKEN structure for a NegTokenTarg type using the -// supplied parameters -int spnegoCreateNegTokenTarg( SPNEGO_MECH_OID MechType, - SPNEGO_NEGRESULT spnegoNegResult, unsigned char* pbMechToken, - unsigned long ulMechTokenLen, unsigned char* pbMechListMIC, - unsigned long ulMechListMICLen, SPNEGO_TOKEN_HANDLE* phSpnegoToken ); - -// Copies binary representation of SPNEGO Data into user supplied buffer -int spnegoTokenGetBinary( SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pbTokenData, - unsigned long * pulDataLen ); - -// Returns SPNEGO Token Type -int spnegoGetTokenType( SPNEGO_TOKEN_HANDLE hSpnegoToken, int * piTokenType ); - -/* Reading an Init Token */ - -// Returns the Initial Mech Type in the MechList element in the NegInitToken. -int spnegoIsMechTypeAvailable( SPNEGO_TOKEN_HANDLE hSpnegoToken, SPNEGO_MECH_OID MechOID, int * piMechTypeIndex ); - -// Returns the value from the context flags element in the NegInitToken as an unsigned long -int spnegoGetContextFlags( SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pucContextFlags ); - -/* Reading a Response Token */ - -// Returns the value from the negResult element (Status code of GSS call - 0,1,2) -int spnegoGetNegotiationResult( SPNEGO_TOKEN_HANDLE hSpnegoToken, SPNEGO_NEGRESULT* pnegResult ); - -// Returns the Supported Mech Type from the NegTokenTarg. -int spnegoGetSupportedMechType( SPNEGO_TOKEN_HANDLE hSpnegoToken, SPNEGO_MECH_OID* pMechOID ); - -/* Reading either Token Type */ - -// Returns the actual Mechanism data from the token (this is what is passed into GSS-API functions -int spnegoGetMechToken( SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pbTokenData, unsigned long* pulDataLen ); - -// Returns the Message Integrity BLOB in the token -int spnegoGetMechListMIC( SPNEGO_TOKEN_HANDLE hSpnegoToken, unsigned char* pbMICData, unsigned long* pulDataLen ); - -// C++ Specific -#if defined(__cplusplus) -} -#endif - -#endif diff --git a/usr/src/lib/libsmbfs/smb/spnegoparse.c b/usr/src/lib/libsmbfs/smb/spnegoparse.c index e9f1e2781b..ea5fb03a70 100644 --- a/usr/src/lib/libsmbfs/smb/spnegoparse.c +++ b/usr/src/lib/libsmbfs/smb/spnegoparse.c @@ -1,3 +1,4 @@ +// Copyright 2012 Nexenta Systems, Inc. All rights reserved. // Copyright (C) 2002 Microsoft Corporation // All rights reserved. // @@ -54,6 +55,7 @@ extern MECH_OID g_stcMechOIDList []; // Parameters: // [in] nMechTokenLength - Length of the MechToken Element // [in] nMechListMICLength - Length of the MechListMIC Element +// (or negHints, if no MechToken) // [in] mechOID - OID for MechList // [in] nReqFlagsAvailable - Is ContextFlags element available // [out] pnTokenSize - Filled out with total size of token @@ -75,7 +77,7 @@ extern MECH_OID g_stcMechOIDList []; //////////////////////////////////////////////////////////////////////////// int CalculateMinSpnegoInitTokenSize( long nMechTokenLength, - long nMechListMICLength, SPNEGO_MECH_OID mechOid, + long nMechListMICLength, SPNEGO_MECH_OID *mechOidLst, int mechOidCnt, int nReqFlagsAvailable, long* pnTokenSize, long* pnInternalTokenLength ) { @@ -87,7 +89,7 @@ int CalculateMinSpnegoInitTokenSize( long nMechTokenLength, // We will calculate this by walking the token backwards - // Start with MIC Element + // Start with MIC Element (or negHints) if ( nMechListMICLength > 0L ) { nTempLength = ASNDerCalcElementLength( nMechListMICLength, NULL ); @@ -130,7 +132,7 @@ int CalculateMinSpnegoInitTokenSize( long nMechTokenLength, } // Next is the MechList - This is REQUIRED - nTempLength += ASNDerCalcMechListLength( mechOid, NULL ); + nTempLength += ASNDerCalcMechListLength( mechOidLst, mechOidCnt, NULL ); // Check for rollover error if ( nTempLength < nTotalLength ) @@ -205,11 +207,12 @@ xEndTokenInitLength: // CreateSpnegoInitToken // // Parameters: -// [in] MechType - OID in MechList +// [in] pMechTypeList - OID array +// [in] MechTypeCnt - OID array length // [in] ucContextFlags - ContextFlags value // [in] pbMechToken - Mech Token Binary Data // [in] ulMechTokenLen - Length of Mech Token -// [in] pbMechListMIC - MechListMIC Binary Data +// [in] pbMechListMIC - MechListMIC Binary Data (or negHints) // [in] ulMechListMICn - Length of MechListMIC // [out] pbTokenData - Buffer to write token into. // [in] nTokenLength - Length of pbTokenData buffer @@ -227,9 +230,18 @@ xEndTokenInitLength: // backwards, so we always know how many bytes we will potentially be // writing out. // +// This function is also used to create an SPNEGO "hint", as described in +// [MS-SPNG] sec. 2.2.1 negTokenInit2. The "hint" looks almost identical +// to a NegTokenInit, but has a "negHints" field inserted before the MIC. +// A normal SPNEGO negTokenInit2 contains only the mech list and the +// negHints. To avoid a giant copy/paste of this function, we pass the +// negHints as the MIC arg, and pass NULL as the MechToken to indicate +// that we're creating a Hint rather than an Init, and use the correct +// type when writing out the MIC (or negHints) element. +// //////////////////////////////////////////////////////////////////////////// -int CreateSpnegoInitToken( SPNEGO_MECH_OID MechType, +int CreateSpnegoInitToken( SPNEGO_MECH_OID *pMechTypeList, long MechTypeCnt, unsigned char ucContextFlags, unsigned char* pbMechToken, unsigned long ulMechTokenLen, unsigned char* pbMechListMIC, unsigned long ulMechListMICLen, unsigned char* pbTokenData, @@ -251,17 +263,22 @@ int CreateSpnegoInitToken( SPNEGO_MECH_OID MechType, // We will write the token out backwards to properly handle the cases // where the length bytes become adjustable - // Start with MIC Element + // Start with MIC Element (or negHints) if ( ulMechListMICLen > 0L ) { + unsigned char ucType; nTempLength = ASNDerCalcElementLength( ulMechListMICLen, &nInternalLength ); - // Decrease the pbWriteTokenData, now we know the length and - // write it out. + // Decrease the pbWriteTokenData, now we know the length and write it out. + // Note: When MechTokenLen == 0, we're writing a negTokenInit2 and the + // MIC arg is really negHints, written as a constructed sequence. + // Otherwise we're writing a negTokenInit, and the MIC is an OCTETSTRING. + ucType = (ulMechTokenLen == 0) ? + SPNEGO_CONSTRUCTED_SEQUENCE : OCTETSTRING; pbWriteTokenData -= nTempLength; nTempLength = ASNDerWriteElement( pbWriteTokenData, SPNEGO_NEGINIT_ELEMENT_MECHLISTMIC, - OCTETSTRING, pbMechListMIC, ulMechListMICLen ); + ucType, pbMechListMIC, ulMechListMICLen ); // Adjust Values and sanity check nTotalBytesWritten += nTempLength; @@ -325,12 +342,12 @@ int CreateSpnegoInitToken( SPNEGO_MECH_OID MechType, } // IF ContextFlags // Next is the MechList - This is REQUIRED - nTempLength = ASNDerCalcMechListLength( MechType, &nInternalLength ); + nTempLength = ASNDerCalcMechListLength( pMechTypeList, MechTypeCnt, &nInternalLength ); // Decrease the pbWriteTokenData, now we know the length and // write it out. pbWriteTokenData -= nTempLength; - nTempLength = ASNDerWriteMechList( pbWriteTokenData, MechType ); + nTempLength = ASNDerWriteMechList( pbWriteTokenData, pMechTypeList, MechTypeCnt ); // Adjust Values and sanity check nTotalBytesWritten += nTempLength; @@ -1281,8 +1298,6 @@ int InitSpnegoTokenElements( SPNEGO_TOKEN* pSpnegoToken, unsigned char* pbTokenD long nElementLength = 0L; long nActualTokenLength = 0L; unsigned char* pbElements = NULL; - unsigned char * ptok; - long tlen, elen, len; // Point to the correct array switch( pSpnegoToken->ucTokenType ) @@ -1370,37 +1385,32 @@ int InitSpnegoTokenElements( SPNEGO_TOKEN* pSpnegoToken, unsigned char* pbTokenD nReturn = InitSpnegoTokenElementFromBasicType( pbTokenData, nElementLength, OCTETSTRING, spnego_init_mechToken, &pSpnegoToken->aElementArray[nCtr] ); - } + } break; - case SPNEGO_NEGINIT_ELEMENT_MECHLISTMIC: + case SPNEGO_NEGINIT_ELEMENT_MECHLISTMIC: // xA3 { // - // This is an OCTETSTRING which contains a message integrity BLOB. + // Don't yet know if this is a negTokenInit, or negTokenInit2. + // Unfortunately, both have the same type: SPNEGO_TOKEN_INIT + // If it's negTokenInit, this element should be an OCTETSTRING + // containing the MIC. If it's a negTokenInit2, this element + // should be an SPNEGO_CONSTRUCTED_SEQUENCE containing the + // negHints (GENERALSTR, ignored) // nReturn = InitSpnegoTokenElementFromBasicType( pbTokenData, nElementLength, - OCTETSTRING, spnego_init_mechListMIC, - &pSpnegoToken->aElementArray[nCtr] ); - /* - * don't believe everything you read in RFCs (and MS - * sample code)... win2k is sending not an octet string, - * but a "general string", wrapped in a sequence. - */ - if (nReturn != SPNEGO_E_UNEXPECTED_TYPE) - break; - ptok = pbTokenData; - elen = nElementLength; - if ((nReturn = ASNDerCheckToken(ptok, SPNEGO_CONSTRUCTED_SEQUENCE, elen, elen, &len, &tlen)) != SPNEGO_E_SUCCESS) - break; - elen -= tlen; - ptok += tlen; - - if ((nReturn = ASNDerCheckToken(ptok, SEQ_ELM(0), elen, elen, &len, &tlen)) != SPNEGO_E_SUCCESS) - break; - elen -= tlen; - ptok += tlen; - nReturn = InitSpnegoTokenElementFromBasicType(ptok, elen, GENERALSTR, spnego_init_mechListMIC, &pSpnegoToken->aElementArray[nCtr]); + OCTETSTRING, spnego_init_mechListMIC, + &pSpnegoToken->aElementArray[nCtr] ); + + if (nReturn == SPNEGO_E_UNEXPECTED_TYPE) { + // This is really a negHints element. Check the type and length, + // but otherwise just ignore it. + long elen, tlen; + nReturn = ASNDerCheckToken( pbTokenData, SPNEGO_CONSTRUCTED_SEQUENCE, + nElementLength, nElementLength, + &elen, &tlen ); + } } break; @@ -1408,6 +1418,7 @@ int InitSpnegoTokenElements( SPNEGO_TOKEN* pSpnegoToken, unsigned char* pbTokenD } else { + /* pSpnegoToken->ucTokenType == SPNEGO_TOKEN_TARG */ switch( pbElements[nCtr] ) { @@ -1453,12 +1464,13 @@ int InitSpnegoTokenElements( SPNEGO_TOKEN* pSpnegoToken, unsigned char* pbTokenD case SPNEGO_NEGTARG_ELEMENT_MECHLISTMIC: { // - // This is an OCTETSTRING which specifies a message integrity BLOB. + // This is an OCTETSTRING, typically 16 bytes, + // which contains a message integrity BLOB. // nReturn = InitSpnegoTokenElementFromBasicType( pbTokenData, nElementLength, - OCTETSTRING, spnego_targ_mechListMIC, - &pSpnegoToken->aElementArray[nCtr] ); + OCTETSTRING, spnego_targ_mechListMIC, + &pSpnegoToken->aElementArray[nCtr] ); } break; diff --git a/usr/src/lib/libsmbfs/smb/spnegoparse.h b/usr/src/lib/libsmbfs/smb/spnegoparse.h index b874dc453d..1f7fde7486 100644 --- a/usr/src/lib/libsmbfs/smb/spnegoparse.h +++ b/usr/src/lib/libsmbfs/smb/spnegoparse.h @@ -1,3 +1,4 @@ +// Copyright 2012 Nexenta Systems, Inc. All rights reserved. // Copyright (C) 2002 Microsoft Corporation // All rights reserved. // @@ -21,8 +22,6 @@ // ///////////////////////////////////////////////////////////// -#pragma ident "%Z%%M% %I% %E% SMI" - #ifndef __SPNEGOPARSE_H__ #define __SPNEGOPARSE_H__ @@ -136,13 +135,13 @@ int FindMechOIDInMechList( SPNEGO_ELEMENT* pSpnegoElement, SPNEGO_MECH_OID MechO int * piMechTypeIndex ); int ValidateMechList( unsigned char* pbMechListData, long nBoundaryLength ); int CalculateMinSpnegoInitTokenSize( long nMechTokenLength, long nMechListMICLength, - SPNEGO_MECH_OID mechOid, int nReqFlagsAvailable, + SPNEGO_MECH_OID *mechOid, int mechOidCnt, int nReqFlagsAvailable, long* plTokenSize, long* plInternalLength ); int CalculateMinSpnegoTargTokenSize( SPNEGO_MECH_OID MechType, SPNEGO_NEGRESULT spnegoNegResult, long nMechTokenLen, long nMechTokenMIC, long* pnTokenSize, long* pnInternalTokenLength ); -int CreateSpnegoInitToken( SPNEGO_MECH_OID MechType, +int CreateSpnegoInitToken( SPNEGO_MECH_OID *MechTypeList, long nMechTypes, unsigned char ucContextFlags, unsigned char* pbMechToken, unsigned long ulMechTokenLen, unsigned char* pbMechListMIC, unsigned long ulMechListMICLen, unsigned char* pbTokenData, diff --git a/usr/src/lib/nsswitch/ad/common/ad_common.c b/usr/src/lib/nsswitch/ad/common/ad_common.c index 4aab4e7320..aa1bf0424d 100644 --- a/usr/src/lib/nsswitch/ad/common/ad_common.c +++ b/usr/src/lib/nsswitch/ad/common/ad_common.c @@ -21,6 +21,8 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #include <malloc.h> @@ -96,7 +98,7 @@ nssad_cfg_reload_ad(nssad_prop_t *props, adutils_ad_t **ad) static int -update_dirs(idmap_ad_disc_ds_t **value, idmap_ad_disc_ds_t **new) +update_dirs(ad_disc_ds_t **value, ad_disc_ds_t **new) { if (*value == *new) return (0); diff --git a/usr/src/lib/nsswitch/ad/common/ad_common.h b/usr/src/lib/nsswitch/ad/common/ad_common.h index 7592d3613b..be90f2e2a9 100644 --- a/usr/src/lib/nsswitch/ad/common/ad_common.h +++ b/usr/src/lib/nsswitch/ad/common/ad_common.h @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #ifndef _AD_COMMON_H @@ -88,7 +89,7 @@ struct ad_backend { typedef struct nssad_prop { char *domain_name; - idmap_ad_disc_ds_t *domain_controller; + ad_disc_ds_t *domain_controller; } nssad_prop_t; typedef struct nssad_cfg { diff --git a/usr/src/lib/smbsrv/libfksmbsrv/Makefile.com b/usr/src/lib/smbsrv/libfksmbsrv/Makefile.com index d40fa8d629..20c8f74f77 100644 --- a/usr/src/lib/smbsrv/libfksmbsrv/Makefile.com +++ b/usr/src/lib/smbsrv/libfksmbsrv/Makefile.com @@ -51,6 +51,7 @@ OBJS_LOCAL = \ OBJS_FS_SMBSRV = \ smb_acl.o \ smb_alloc.o \ + smb_authenticate.o \ smb_close.o \ smb_common_open.o \ smb_common_transact.o \ diff --git a/usr/src/lib/smbsrv/libmlsvc/Makefile.com b/usr/src/lib/smbsrv/libmlsvc/Makefile.com index 151a1e8337..50e596caa4 100644 --- a/usr/src/lib/smbsrv/libmlsvc/Makefile.com +++ b/usr/src/lib/smbsrv/libmlsvc/Makefile.com @@ -83,7 +83,7 @@ include ../../Makefile.lib INCS += -I$(SRC)/common/smbsrv LDLIBS += $(MACH_LDLIBS) -LDLIBS += -lmlrpc -lsmb -lsmbns -lshare -lsmbfs -lresolv -lnsl -lpkcs11 \ +LDLIBS += -lmlrpc -lsmb -lsmbns -lshare -lsmbfs -lnsl -lpkcs11 \ -lscf -lcmdutils -lsec -lavl -lnvpair -luutil -luuid -lgen -lzfs -lc CPPFLAGS += $(INCS) -D_REENTRANT diff --git a/usr/src/lib/smbsrv/libmlsvc/common/dfs.c b/usr/src/lib/smbsrv/libmlsvc/common/dfs.c index 9acda4430e..7668d433b6 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/dfs.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/dfs.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #include <strings.h> @@ -151,15 +152,13 @@ static uint32_t dfs_modinfo(uint32_t, dfs_info_t *, dfs_info_t *, uint32_t); void dfs_init(void) { - smb_domain_t di; - smb_cache_create(&dfs_nscache, 0, dfs_cache_cmp, free, bcopy, sizeof (dfs_nscnode_t)); - if (!smb_domain_lookup_type(SMB_DOMAIN_LOCAL, &di)) + if (smb_getnetbiosname(dfs_nbname, sizeof (dfs_nbname)) != 0) { + syslog(LOG_ERR, "dfs: can't get machine name"); return; - - (void) strlcpy(dfs_nbname, di.di_nbname, NETBIOS_NAME_SZ); + } bzero((void *)&dfs_intr_ops, sizeof (dfs_intr_ops)); diff --git a/usr/src/lib/smbsrv/libmlsvc/common/dssetup_clnt.c b/usr/src/lib/smbsrv/libmlsvc/common/dssetup_clnt.c index f067583f28..02b57b9328 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/dssetup_clnt.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/dssetup_clnt.c @@ -19,8 +19,8 @@ * CDDL HEADER END */ /* - * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ /* @@ -47,7 +47,7 @@ dssetup_get_domain_info(ds_primary_domain_info_t *ds_info) if (!smb_domain_getinfo(&di)) return (-1); - if (ndr_rpc_bind(&handle, di.d_dc, di.d_primary.di_nbname, + if (ndr_rpc_bind(&handle, di.d_dci.dc_name, di.d_primary.di_nbname, MLSVC_ANON_USER, "DSSETUP") != 0) return (-1); diff --git a/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h b/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h index 881535d2f3..381a2ea98c 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h +++ b/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2011 Nexenta Systems, Inc. All rights reserved. + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ #ifndef _LIBMLSVC_H @@ -59,7 +59,10 @@ uint32_t lsa_lookup_sid(smb_sid_t *, smb_account_t *); * information. */ -extern boolean_t smb_locate_dc(char *, char *, smb_domainex_t *); +extern boolean_t smb_locate_dc(char *, smb_domainex_t *); +uint32_t smb_ddiscover_dns(char *, smb_domainex_t *); +extern void smb_ddiscover_bad_dc(char *); +extern void smb_ddiscover_refresh(void); extern int smb_ddiscover_wait(void); extern int dssetup_check_service(void); @@ -68,7 +71,11 @@ extern void mlsvc_disconnect(const char *); extern int mlsvc_init(void); extern void mlsvc_fini(void); extern DWORD mlsvc_netlogon(char *, char *); -extern DWORD mlsvc_join(smb_domainex_t *, char *, char *); +extern void mlsvc_join(smb_joininfo_t *, smb_joinres_t *); + +extern void smb_logon_domain(smb_logon_t *, smb_token_t *); +extern uint32_t smb_decode_krb5_pac(smb_token_t *, char *, uint_t); +extern boolean_t smb_token_setup_common(smb_token_t *); /* @@ -154,7 +161,7 @@ typedef struct mlsvc_handle { void ndr_rpc_init(void); void ndr_rpc_fini(void); -int ndr_rpc_bind(mlsvc_handle_t *, char *, char *, char *, const char *); +uint32_t ndr_rpc_bind(mlsvc_handle_t *, char *, char *, char *, const char *); void ndr_rpc_unbind(mlsvc_handle_t *); int ndr_rpc_call(mlsvc_handle_t *, int, void *); void ndr_rpc_set_nonull(mlsvc_handle_t *); diff --git a/usr/src/lib/smbsrv/libmlsvc/common/lsalib.c b/usr/src/lib/smbsrv/libmlsvc/common/lsalib.c index 62732106c6..e995cf1c68 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/lsalib.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/lsalib.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ /* @@ -43,7 +44,8 @@ static uint32_t lsa_lookup_name_domain(char *, smb_account_t *); static uint32_t lsa_lookup_sid_builtin(smb_sid_t *, smb_account_t *); static uint32_t lsa_lookup_sid_domain(smb_sid_t *, smb_account_t *); -static int lsa_list_accounts(mlsvc_handle_t *); +static uint32_t lsa_list_accounts(mlsvc_handle_t *); +static uint32_t lsa_map_status(uint32_t); /* * Lookup the given account and returns the account information @@ -133,20 +135,21 @@ lsa_lookup_sid(smb_sid_t *sid, smb_account_t *info) * * The requested information will be returned via 'info' argument. * - * Returns NT status codes. + * Returns NT status codes. (Raw, not LSA-ized) */ DWORD lsa_query_primary_domain_info(char *server, char *domain, smb_domain_t *info) { mlsvc_handle_t domain_handle; - DWORD status; char user[SMB_USERNAME_MAXLEN]; + DWORD status; smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); - if ((lsar_open(server, domain, user, &domain_handle)) != 0) - return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); + status = lsar_open(server, domain, user, &domain_handle); + if (status != 0) + return (status); status = lsar_query_info_policy(&domain_handle, MSLSA_POLICY_PRIMARY_DOMAIN_INFO, info); @@ -161,20 +164,21 @@ lsa_query_primary_domain_info(char *server, char *domain, * * The requested information will be returned via 'info' argument. * - * Returns NT status codes. + * Returns NT status codes. (Raw, not LSA-ized) */ DWORD lsa_query_account_domain_info(char *server, char *domain, smb_domain_t *info) { mlsvc_handle_t domain_handle; - DWORD status; char user[SMB_USERNAME_MAXLEN]; + DWORD status; smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); - if ((lsar_open(server, domain, user, &domain_handle)) != 0) - return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); + status = lsar_open(server, domain, user, &domain_handle); + if (status != 0) + return (status); status = lsar_query_info_policy(&domain_handle, MSLSA_POLICY_ACCOUNT_DOMAIN_INFO, info); @@ -191,19 +195,20 @@ lsa_query_account_domain_info(char *server, char *domain, * * The requested information will be returned via 'info' argument. * - * Returns NT status codes. + * Returns NT status codes. (Raw, not LSA-ized) */ DWORD lsa_query_dns_domain_info(char *server, char *domain, smb_domain_t *info) { mlsvc_handle_t domain_handle; - DWORD status; char user[SMB_USERNAME_MAXLEN]; + DWORD status; smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); - if ((lsar_open(server, domain, user, &domain_handle)) != 0) - return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); + status = lsar_open(server, domain, user, &domain_handle); + if (status != 0) + return (status); status = lsar_query_info_policy(&domain_handle, MSLSA_POLICY_DNS_DOMAIN_INFO, info); @@ -219,21 +224,22 @@ lsa_query_dns_domain_info(char *server, char *domain, smb_domain_t *info) * * The requested information will be returned via 'info' argument. * - * Returns NT status codes. + * Returns NT status codes. (Raw, not LSA-ized) */ DWORD lsa_enum_trusted_domains(char *server, char *domain, smb_trusted_domains_t *info) { mlsvc_handle_t domain_handle; + char user[SMB_USERNAME_MAXLEN]; DWORD enum_context; DWORD status; - char user[SMB_USERNAME_MAXLEN]; smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); - if ((lsar_open(server, domain, user, &domain_handle)) != 0) - return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); + status = lsar_open(server, domain, user, &domain_handle); + if (status != 0) + return (status); enum_context = 0; @@ -258,21 +264,22 @@ lsa_enum_trusted_domains(char *server, char *domain, * * The requested information will be returned via 'info' argument. * - * Returns NT status codes. + * Returns NT status codes. (Raw, not LSA-ized) */ DWORD lsa_enum_trusted_domains_ex(char *server, char *domain, smb_trusted_domains_t *info) { mlsvc_handle_t domain_handle; + char user[SMB_USERNAME_MAXLEN]; DWORD enum_context; DWORD status; - char user[SMB_USERNAME_MAXLEN]; smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); - if ((lsar_open(server, domain, user, &domain_handle)) != 0) - return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); + status = lsar_open(server, domain, user, &domain_handle); + if (status != 0) + return (status); enum_context = 0; @@ -333,28 +340,31 @@ lsa_lookup_name_builtin(char *domain, char *name, smb_account_t *info) } /* - * Lookup the given account in domain. + * Lookup a domain account by its name. * * The information is returned in the user_info structure. * The caller is responsible for allocating and releasing * this structure. + * + * Returns NT status codes. (LSA-ized) */ static uint32_t lsa_lookup_name_domain(char *account_name, smb_account_t *info) { mlsvc_handle_t domain_handle; smb_domainex_t dinfo; - uint32_t status; char user[SMB_USERNAME_MAXLEN]; + uint32_t status; smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); if (!smb_domain_getinfo(&dinfo)) return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); - if (lsar_open(dinfo.d_dc, dinfo.d_primary.di_nbname, user, - &domain_handle) != 0) - return (NT_STATUS_INVALID_PARAMETER); + status = lsar_open(dinfo.d_dci.dc_name, dinfo.d_primary.di_nbname, + user, &domain_handle); + if (status != 0) + return (lsa_map_status(status)); status = lsar_lookup_names(&domain_handle, account_name, info); @@ -373,29 +383,30 @@ lsa_lookup_name_domain(char *account_name, smb_account_t *info) * privileges are returned in the user_info structure. The caller is * responsible for allocating and releasing this structure. * - * On success 0 is returned. Otherwise a -ve error code. + * Returns NT status codes. (LSA-ized) */ /*ARGSUSED*/ -int +DWORD lsa_lookup_privs(char *account_name, char *target_name, smb_account_t *ainfo) { mlsvc_handle_t domain_handle; - int rc; smb_domainex_t dinfo; char user[SMB_USERNAME_MAXLEN]; + DWORD status; smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); if (!smb_domain_getinfo(&dinfo)) - return (-1); + return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); - if ((lsar_open(dinfo.d_dc, dinfo.d_primary.di_nbname, user, - &domain_handle)) != 0) - return (-1); + status = lsar_open(dinfo.d_dci.dc_name, dinfo.d_primary.di_nbname, + user, &domain_handle); + if (status != 0) + return (lsa_map_status(status)); - rc = lsa_list_accounts(&domain_handle); + status = lsa_list_accounts(&domain_handle); (void) lsar_close(&domain_handle); - return (rc); + return (status); } /* @@ -404,7 +415,7 @@ lsa_lookup_privs(char *account_name, char *target_name, smb_account_t *ainfo) * List the privileges supported by the specified server. * This function is only intended for diagnostics. * - * Returns NT status codes. + * Returns NT status codes. (LSA-ized) */ DWORD lsa_list_privs(char *server, char *domain) @@ -412,15 +423,16 @@ lsa_list_privs(char *server, char *domain) static char name[128]; static struct ms_luid luid; mlsvc_handle_t domain_handle; + char user[SMB_USERNAME_MAXLEN]; + DWORD status; int rc; int i; - char user[SMB_USERNAME_MAXLEN]; smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); - rc = lsar_open(server, domain, user, &domain_handle); - if (rc != 0) - return (NT_STATUS_INVALID_PARAMETER); + status = lsar_open(server, domain, user, &domain_handle); + if (status != 0) + return (lsa_map_status(status)); for (i = 0; i < 30; ++i) { luid.low_part = i; @@ -443,9 +455,9 @@ lsa_list_privs(char *server, char *domain) * This function can be used to list the accounts in the specified * domain. For now the SIDs are just listed in the system log. * - * On success 0 is returned. Otherwise a -ve error code. + * Returns NT status */ -static int +static DWORD lsa_list_accounts(mlsvc_handle_t *domain_handle) { mlsvc_handle_t account_handle; @@ -453,16 +465,16 @@ lsa_list_accounts(mlsvc_handle_t *domain_handle) struct mslsa_sid *sid; smb_account_t ainfo; DWORD enum_context = 0; - int rc; + DWORD status; int i; bzero(&accounts, sizeof (struct mslsa_EnumAccountBuf)); do { - rc = lsar_enum_accounts(domain_handle, &enum_context, + status = lsar_enum_accounts(domain_handle, &enum_context, &accounts); - if (rc != 0) - return (rc); + if (status != 0) + return (status); for (i = 0; i < accounts.entries_read; ++i) { sid = accounts.info[i].sid; @@ -479,7 +491,7 @@ lsa_list_accounts(mlsvc_handle_t *domain_handle) if (accounts.info) free(accounts.info); - } while (rc == 0 && accounts.entries_read != 0); + } while (status == 0 && accounts.entries_read != 0); return (0); } @@ -522,25 +534,66 @@ lsa_lookup_sid_builtin(smb_sid_t *sid, smb_account_t *ainfo) return (NT_STATUS_SUCCESS); } +/* + * Lookup a domain account by its SID. + * + * The information is returned in the user_info structure. + * The caller is responsible for allocating and releasing + * this structure. + * + * Returns NT status codes. (LSA-ized) + */ static uint32_t lsa_lookup_sid_domain(smb_sid_t *sid, smb_account_t *ainfo) { mlsvc_handle_t domain_handle; - uint32_t status; smb_domainex_t dinfo; char user[SMB_USERNAME_MAXLEN]; + uint32_t status; smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); if (!smb_domain_getinfo(&dinfo)) return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); - if (lsar_open(dinfo.d_dc, dinfo.d_primary.di_nbname, user, - &domain_handle) != 0) - return (NT_STATUS_INVALID_PARAMETER); + status = lsar_open(dinfo.d_dci.dc_name, dinfo.d_primary.di_nbname, + user, &domain_handle); + if (status != 0) + return (lsa_map_status(status)); status = lsar_lookup_sids(&domain_handle, sid, ainfo); (void) lsar_close(&domain_handle); return (status); } + +/* + * Most functions that call the local security authority expect + * only a limited set of status returns. This function maps the + * status we get from talking to our domain controller into one + * that LSA functions can return. Most common errors become: + * NT_STATUS_CANT_ACCESS_DOMAIN_INFO (when no DC etc.) + */ +static uint32_t +lsa_map_status(uint32_t status) +{ + switch (status) { + case NT_STATUS_SUCCESS: + break; + case NT_STATUS_INVALID_PARAMETER: /* rpc bind */ + break; + case NT_STATUS_NO_MEMORY: + break; + case NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND: + case NT_STATUS_BAD_NETWORK_PATH: /* get server addr */ + case NT_STATUS_NETWORK_ACCESS_DENIED: /* authentication */ + case NT_STATUS_BAD_NETWORK_NAME: /* tree connect */ + case NT_STATUS_ACCESS_DENIED: /* open pipe */ + status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + break; + default: + status = NT_STATUS_UNSUCCESSFUL; + break; + } + return (status); +} diff --git a/usr/src/lib/smbsrv/libmlsvc/common/lsalib.h b/usr/src/lib/smbsrv/libmlsvc/common/lsalib.h index f6f3f11fcd..c26eab4a13 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/lsalib.h +++ b/usr/src/lib/smbsrv/libmlsvc/common/lsalib.h @@ -21,6 +21,8 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ #ifndef _LSALIB_H @@ -61,8 +63,8 @@ DWORD lsa_enum_trusted_domains_ex(char *, char *, smb_trusted_domains_t *); /* * lsar_open.c */ -int lsar_open(char *, char *, char *, mlsvc_handle_t *); -int lsar_open_policy2(char *, char *, char *, mlsvc_handle_t *); +DWORD lsar_open(char *, char *, char *, mlsvc_handle_t *); +DWORD lsar_open_policy2(char *, char *, char *, mlsvc_handle_t *); int lsar_open_account(mlsvc_handle_t *, struct mslsa_sid *, mlsvc_handle_t *); int lsar_close(mlsvc_handle_t *); @@ -74,7 +76,7 @@ DWORD lsar_query_info_policy(mlsvc_handle_t *, WORD, smb_domain_t *); uint32_t lsar_lookup_names(mlsvc_handle_t *, char *, smb_account_t *); uint32_t lsar_lookup_sids(mlsvc_handle_t *, smb_sid_t *, smb_account_t *); -int lsar_enum_accounts(mlsvc_handle_t *, DWORD *, +DWORD lsar_enum_accounts(mlsvc_handle_t *, DWORD *, struct mslsa_EnumAccountBuf *); DWORD lsar_enum_trusted_domains(mlsvc_handle_t *, DWORD *, smb_trusted_domains_t *); diff --git a/usr/src/lib/smbsrv/libmlsvc/common/lsar_clnt.c b/usr/src/lib/smbsrv/libmlsvc/common/lsar_clnt.c index 043ee29549..eeab7745f2 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/lsar_clnt.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/lsar_clnt.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012 Nexenta Systems, Inc. All rights reserved. + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ /* @@ -87,19 +87,23 @@ static void lsar_set_trusted_domains(struct mslsa_EnumTrustedDomainBuf *, * If username argument is NULL, an anonymous connection will be established. * Otherwise, an authenticated connection will be established. * - * On success 0 is returned. Otherwise a -ve error code. + * Returns 0 or NT status (Raw, not LSA-ized) */ -int +DWORD lsar_open(char *server, char *domain, char *username, mlsvc_handle_t *domain_handle) { + DWORD status; + if (server == NULL || domain == NULL) - return (-1); + return (NT_STATUS_INTERNAL_ERROR); if (username == NULL) username = MLSVC_ANON_USER; - return (lsar_open_policy2(server, domain, username, domain_handle)); + status = lsar_open_policy2(server, domain, username, domain_handle); + + return (status); } /* @@ -111,20 +115,20 @@ lsar_open(char *server, char *domain, char *username, * function via lsar_open to ensure that the appropriate connection is * in place. * - * Returns 0 on success. Otherwise non-zero to indicate a failure. + * Returns 0 or NT status (Raw, not LSA-ized) */ -int -lsar_open_policy2(char *server, char *domain, char *username, +DWORD +lsar_open_policy2(char *server, char *domain, char *user, mlsvc_handle_t *lsa_handle) { struct mslsa_OpenPolicy2 arg; + DWORD status; int opnum; int len; - int rc; - rc = ndr_rpc_bind(lsa_handle, server, domain, username, "LSARPC"); - if (rc != 0) - return (-1); + status = ndr_rpc_bind(lsa_handle, server, domain, user, "LSARPC"); + if (status != 0) + return (status); opnum = LSARPC_OPNUM_OpenPolicy2; bzero(&arg, sizeof (struct mslsa_OpenPolicy2)); @@ -132,34 +136,33 @@ lsar_open_policy2(char *server, char *domain, char *username, len = strlen(server) + 4; arg.servername = ndr_rpc_malloc(lsa_handle, len); if (arg.servername == NULL) { - ndr_rpc_unbind(lsa_handle); - return (-1); + status = NT_STATUS_NO_MEMORY; + goto out; } (void) snprintf((char *)arg.servername, len, "\\\\%s", server); arg.attributes.length = sizeof (struct mslsa_object_attributes); arg.desiredAccess = MAXIMUM_ALLOWED; - if ((rc = ndr_rpc_call(lsa_handle, opnum, &arg)) != 0) { - ndr_rpc_unbind(lsa_handle); - return (-1); + if (ndr_rpc_call(lsa_handle, opnum, &arg) != 0) { + status = RPC_NT_CALL_FAILED; + goto out; } - - if (arg.status != 0) { - rc = -1; - } else { + status = arg.status; + if (status == NT_STATUS_SUCCESS) { (void) memcpy(&lsa_handle->handle, &arg.domain_handle, sizeof (ndr_hdid_t)); if (ndr_is_null_handle(lsa_handle)) - rc = -1; + status = NT_STATUS_INVALID_PARAMETER; } ndr_rpc_release(lsa_handle); - if (rc != 0) +out: + if (status != NT_STATUS_SUCCESS) ndr_rpc_unbind(lsa_handle); - return (rc); + return (status); } /* @@ -946,7 +949,7 @@ lsar_lookup_sids3(mlsvc_handle_t *lsa_handle, lsa_sid_t *sid, * This list is dynamically allocated using malloc, it should be freed * by the caller when it is no longer required. */ -int +DWORD lsar_enum_accounts(mlsvc_handle_t *lsa_handle, DWORD *enum_context, struct mslsa_EnumAccountBuf *accounts) { @@ -954,12 +957,13 @@ lsar_enum_accounts(mlsvc_handle_t *lsa_handle, DWORD *enum_context, struct mslsa_AccountInfo *info; int opnum; int rc; + DWORD status; DWORD n_entries; DWORD i; int nbytes; if (lsa_handle == NULL || enum_context == NULL || accounts == NULL) - return (-1); + return (NT_STATUS_INTERNAL_ERROR); accounts->entries_read = 0; accounts->info = 0; @@ -973,12 +977,12 @@ lsar_enum_accounts(mlsvc_handle_t *lsa_handle, DWORD *enum_context, rc = ndr_rpc_call(lsa_handle, opnum, &arg); if (rc == 0) { + status = arg.status; if (arg.status != 0) { if (arg.status == NT_STATUS_NO_MORE_ENTRIES) { *enum_context = arg.enum_context; } else { ndr_rpc_status(lsa_handle, opnum, arg.status); - rc = -1; } } else if (arg.enum_buf->entries_read != 0) { n_entries = arg.enum_buf->entries_read; @@ -986,7 +990,7 @@ lsar_enum_accounts(mlsvc_handle_t *lsa_handle, DWORD *enum_context, if ((info = malloc(nbytes)) == NULL) { ndr_rpc_release(lsa_handle); - return (-1); + return (NT_STATUS_NO_MEMORY); } for (i = 0; i < n_entries; ++i) @@ -997,10 +1001,12 @@ lsar_enum_accounts(mlsvc_handle_t *lsa_handle, DWORD *enum_context, accounts->info = info; *enum_context = arg.enum_context; } + } else { + status = NT_STATUS_INVALID_PARAMETER; } ndr_rpc_release(lsa_handle); - return (rc); + return (status); } /* diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mapfile-vers b/usr/src/lib/smbsrv/libmlsvc/common/mapfile-vers index 29af98f2ac..4354bbc7d4 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/mapfile-vers +++ b/usr/src/lib/smbsrv/libmlsvc/common/mapfile-vers @@ -20,6 +20,7 @@ # # # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2014 Nexenta Systems, Inc. All rights reserved. # # @@ -53,6 +54,9 @@ SYMBOL_VERSION SUNWprivate { mlsvc_netlogon; smb_autohome_add; smb_autohome_remove; + smb_ddiscover_bad_dc; + smb_ddiscover_refresh; + smb_decode_krb5_pac; smb_locate_dc; smb_logon; smb_logon_abort; @@ -76,6 +80,7 @@ SYMBOL_VERSION SUNWprivate { smb_shr_stop; smb_token_destroy; smb_token_log; + smb_token_setup_common; spoolss_register_copyfile; local: *; diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h index d693d8825c..dcf2c5f0e7 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012 Nexenta Systems, Inc. All rights reserved. + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ #ifndef _SMBSRV_MLSVC_H @@ -33,6 +33,8 @@ extern "C" { #endif +struct netr_info; + int smb_dclocator_init(void); void smbrdr_initialize(void); void dssetup_initialize(void); @@ -54,12 +56,13 @@ void svcctl_finalize(void); void spoolss_finalize(void); void netdfs_finalize(void); -int netr_open(char *, char *, mlsvc_handle_t *); +/* netr_auth.c */ +DWORD netr_open(char *, char *, mlsvc_handle_t *); int netr_close(mlsvc_handle_t *); DWORD netlogon_auth(char *, mlsvc_handle_t *, DWORD); -int netr_setup_authenticator(netr_info_t *, struct netr_authenticator *, +int netr_setup_authenticator(struct netr_info *, struct netr_authenticator *, struct netr_authenticator *); -DWORD netr_validate_chain(netr_info_t *, struct netr_authenticator *); +DWORD netr_validate_chain(struct netr_info *, struct netr_authenticator *); void ndr_srvsvc_timecheck(char *, char *); @@ -81,9 +84,9 @@ void smb_quota_fini(void); void smb_quota_add_fs(const char *); void smb_quota_remove_fs(const char *); +uint32_t smb_ddiscover_main(char *, smb_domainex_t *); + #ifdef __cplusplus } #endif - - #endif /* _SMBSRV_MLSVC_H */ diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c index 0a52a43e65..ef3ca34bcd 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012 Nexenta Systems, Inc. All rights reserved. + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ /* @@ -43,6 +43,7 @@ #include <netsmb/smbfs_api.h> #include <smbsrv/libsmb.h> +#include <smbsrv/libsmbns.h> #include <smbsrv/libmlrpc.h> #include <smbsrv/libmlsvc.h> #include <smbsrv/ndl/srvsvc.ndl> @@ -69,8 +70,18 @@ static void ndr_xa_release(ndr_client_t *); * The client points to this top-level handle so that we know when to * unbind and teardown the connection. As each handle is initialized it * will inherit a reference to the client context. + * + * Returns 0 or an NT_STATUS: + * NT_STATUS_BAD_NETWORK_PATH (get server addr) + * NT_STATUS_NETWORK_ACCESS_DENIED (connect, auth) + * NT_STATUS_BAD_NETWORK_NAME (tcon, open) + * NT_STATUS_ACCESS_DENIED (open pipe) + * NT_STATUS_INVALID_PARAMETER (rpc bind) + * + * NT_STATUS_INTERNAL_ERROR (bad args etc) + * NT_STATUS_NO_MEMORY */ -int +DWORD ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain, char *username, const char *service) { @@ -78,15 +89,17 @@ ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain, ndr_client_t *clnt = NULL; ndr_service_t *svc; srvsvc_server_info_t svinfo; + DWORD status; int fd = -1; int rc; - if (handle == NULL || server == NULL || + if (handle == NULL || server == NULL || server[0] == '\0' || domain == NULL || username == NULL) - return (-1); + return (NT_STATUS_INTERNAL_ERROR); + /* In case the service was not registered... */ if ((svc = ndr_svc_lookup_name(service)) == NULL) - return (-1); + return (NT_STATUS_INTERNAL_ERROR); /* * Set the default based on the assumption that most @@ -112,13 +125,19 @@ ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain, * Setup smbfs library handle, authenticate, connect to * the IPC$ share. This will reuse an existing connection * if the driver already has one for this combination of - * server, user, domain. + * server, user, domain. It may return any of: + * NT_STATUS_BAD_NETWORK_PATH (get server addr) + * NT_STATUS_NETWORK_ACCESS_DENIED (connect, auth) + * NT_STATUS_BAD_NETWORK_NAME (tcon) */ - if ((rc = smbrdr_ctx_new(&ctx, server, domain, username)) != 0) { + status = smbrdr_ctx_new(&ctx, server, domain, username); + if (status != NT_STATUS_SUCCESS) { syslog(LOG_ERR, "ndr_rpc_bind: smbrdr_ctx_new" "(Srv=%s Dom=%s User=%s), %s (0x%x)", server, domain, username, - xlate_nt_status(rc), rc); + xlate_nt_status(status), status); + /* Tell the DC Locator this DC failed. */ + smb_ddiscover_bad_dc(server); goto errout; } @@ -127,16 +146,28 @@ ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain, */ fd = smb_fh_open(ctx, svc->endpoint, O_RDWR); if (fd < 0) { + rc = errno; syslog(LOG_DEBUG, "ndr_rpc_bind: " - "smb_fh_open, err=%d", errno); + "smb_fh_open (%s) err=%d", + svc->endpoint, rc); + switch (rc) { + case EACCES: + status = NT_STATUS_ACCESS_DENIED; + break; + default: + status = NT_STATUS_BAD_NETWORK_NAME; + break; + } goto errout; } /* * Setup the RPC client handle. */ - if ((clnt = malloc(sizeof (ndr_client_t))) == NULL) + if ((clnt = malloc(sizeof (ndr_client_t))) == NULL) { + status = NT_STATUS_NO_MEMORY; goto errout; + } bzero(clnt, sizeof (ndr_client_t)); clnt->handle = &handle->handle; @@ -152,8 +183,10 @@ ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain, ndr_svc_binding_pool_init(&clnt->binding_list, clnt->binding_pool, NDR_N_BINDING_POOL); - if ((clnt->heap = ndr_heap_create()) == NULL) + if ((clnt->heap = ndr_heap_create()) == NULL) { + status = NT_STATUS_NO_MEMORY; goto errout; + } /* * Fill in the caller's handle. @@ -166,14 +199,26 @@ ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain, * Do the OtW RPC bind. */ rc = ndr_clnt_bind(clnt, service, &clnt->binding); - if (NDR_DRC_IS_FAULT(rc)) { - syslog(LOG_DEBUG, "ndr_rpc_bind: " - "ndr_clnt_bind, rc=0x%x", rc); - goto errout; + switch (rc) { + case NDR_DRC_FAULT_OUT_OF_MEMORY: + status = NT_STATUS_NO_MEMORY; + break; + case NDR_DRC_FAULT_API_SERVICE_INVALID: /* not registered */ + status = NT_STATUS_INTERNAL_ERROR; + break; + default: + if (NDR_DRC_IS_FAULT(rc)) { + status = NT_STATUS_INVALID_PARAMETER; + break; + } + /* FALLTHROUGH */ + case NDR_DRC_OK: + return (NT_STATUS_SUCCESS); } - /* Success! */ - return (0); + syslog(LOG_DEBUG, "ndr_rpc_bind: " + "ndr_clnt_bind, %s (0x%x)", + xlate_nt_status(status), status); errout: handle->clnt = NULL; @@ -187,7 +232,7 @@ errout: smbrdr_ctx_free(ctx); } - return (-1); + return (status); } /* diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_domain.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_domain.c index 00040f5482..74da88b2d5 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_domain.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_domain.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012 Nexenta Systems, Inc. All rights reserved. + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ #include <syslog.h> @@ -44,6 +44,7 @@ #include <smbsrv/smbinfo.h> #include <lsalib.h> +#include <mlsvc.h> /* * DC Locator @@ -52,9 +53,11 @@ #define SMB_IS_FQDN(domain) (strchr(domain, '.') != NULL) typedef struct smb_dclocator { + smb_dcinfo_t sdl_dci; /* .dc_name .dc_addr */ char sdl_domain[SMB_PI_MAX_DOMAIN]; - char sdl_dc[MAXHOSTNAMELEN]; boolean_t sdl_locate; + boolean_t sdl_bad_dc; + boolean_t sdl_cfg_chg; mutex_t sdl_mtx; cond_t sdl_cv; uint32_t sdl_status; @@ -64,14 +67,11 @@ static smb_dclocator_t smb_dclocator; static pthread_t smb_dclocator_thr; static void *smb_ddiscover_service(void *); -static void smb_ddiscover_main(char *, char *); -static uint32_t smb_ddiscover_dns(char *, char *, smb_domainex_t *); -static boolean_t smb_ddiscover_nbt(char *, char *, smb_domainex_t *); -static boolean_t smb_ddiscover_domain_match(char *, char *, uint32_t); static uint32_t smb_ddiscover_qinfo(char *, char *, smb_domainex_t *); static void smb_ddiscover_enum_trusted(char *, char *, smb_domainex_t *); static uint32_t smb_ddiscover_use_config(char *, smb_domainex_t *); static void smb_domainex_free(smb_domainex_t *); +static void smb_set_krb5_realm(char *); /* * =================================================================== @@ -93,45 +93,54 @@ smb_dclocator_init(void) (void) pthread_attr_init(&tattr); (void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); rc = pthread_create(&smb_dclocator_thr, &tattr, - smb_ddiscover_service, 0); + smb_ddiscover_service, &smb_dclocator); (void) pthread_attr_destroy(&tattr); return (rc); } /* * This is the entry point for discovering a domain controller for the - * specified domain. + * specified domain. Called during join domain, and then periodically + * by smbd_dc_update (the "DC monitor" thread). * * The actual work of discovering a DC is handled by DC locator thread. * All we do here is signal the request and wait for a DC or a timeout. * * Input parameters: * domain - domain to be discovered (can either be NetBIOS or DNS domain) - * dc - preferred DC. If the preferred DC is set to empty string, it - * will attempt to discover any DC in the specified domain. * * Output parameter: * dp - on success, dp will be filled with the discovered DC and domain * information. + * * Returns B_TRUE if the DC/domain info is available. */ boolean_t -smb_locate_dc(char *domain, char *dc, smb_domainex_t *dp) +smb_locate_dc(char *domain, smb_domainex_t *dp) { int rc; + boolean_t rv; timestruc_t to; smb_domainex_t domain_info; - if (domain == NULL || *domain == '\0') + if (domain == NULL || *domain == '\0') { + syslog(LOG_DEBUG, "smb_locate_dc NULL dom"); + smb_set_krb5_realm(NULL); return (B_FALSE); + } (void) mutex_lock(&smb_dclocator.sdl_mtx); + if (strcmp(smb_dclocator.sdl_domain, domain)) { + (void) strlcpy(smb_dclocator.sdl_domain, domain, + sizeof (smb_dclocator.sdl_domain)); + smb_dclocator.sdl_cfg_chg = B_TRUE; + syslog(LOG_DEBUG, "smb_locate_dc new dom=%s", domain); + smb_set_krb5_realm(domain); + } + if (!smb_dclocator.sdl_locate) { smb_dclocator.sdl_locate = B_TRUE; - (void) strlcpy(smb_dclocator.sdl_domain, domain, - SMB_PI_MAX_DOMAIN); - (void) strlcpy(smb_dclocator.sdl_dc, dc, MAXHOSTNAMELEN); (void) cond_broadcast(&smb_dclocator.sdl_cv); } @@ -141,17 +150,106 @@ smb_locate_dc(char *domain, char *dc, smb_domainex_t *dp) rc = cond_reltimedwait(&smb_dclocator.sdl_cv, &smb_dclocator.sdl_mtx, &to); - if (rc == ETIME) - break; + if (rc == ETIME) { + syslog(LOG_NOTICE, "smb_locate_dc timeout"); + rv = B_FALSE; + goto out; + } + } + if (smb_dclocator.sdl_status != 0) { + syslog(LOG_NOTICE, "smb_locate_dc status 0x%x", + smb_dclocator.sdl_status); + rv = B_FALSE; + goto out; } if (dp == NULL) dp = &domain_info; - rc = smb_domain_getinfo(dp); + rv = smb_domain_getinfo(dp); +out: (void) mutex_unlock(&smb_dclocator.sdl_mtx); - return (rc); + return (rv); +} + +/* + * Tell the domain discovery service to run again now, + * and assume changed configuration (i.e. a new DC). + * Like the first part of smb_locate_dc(). + * + * Note: This is called from the service refresh handler + * and the door handler to tell the ddiscover thread to + * request the new DC from idmap. Therefore, we must not + * trigger a new idmap discovery run from here, or that + * would start a ping-pong match. + */ +/* ARGSUSED */ +void +smb_ddiscover_refresh() +{ + + (void) mutex_lock(&smb_dclocator.sdl_mtx); + + if (smb_dclocator.sdl_cfg_chg == B_FALSE) { + smb_dclocator.sdl_cfg_chg = B_TRUE; + syslog(LOG_DEBUG, "smb_ddiscover_refresh set cfg changed"); + } + if (!smb_dclocator.sdl_locate) { + smb_dclocator.sdl_locate = B_TRUE; + (void) cond_broadcast(&smb_dclocator.sdl_cv); + } + + (void) mutex_unlock(&smb_dclocator.sdl_mtx); +} + +/* + * Called by our client-side threads after they fail to connect to + * the DC given to them by smb_locate_dc(). This is often called + * after some delay, because the connection timeout delays these + * threads for a while, so it's quite common that the DC locator + * service has already started looking for a new DC. These late + * notifications should not continually restart the DC locator. + */ +void +smb_ddiscover_bad_dc(char *bad_dc) +{ + + assert(bad_dc[0] != '\0'); + + (void) mutex_lock(&smb_dclocator.sdl_mtx); + + syslog(LOG_DEBUG, "smb_ddiscover_bad_dc, cur=%s, bad=%s", + smb_dclocator.sdl_dci.dc_name, bad_dc); + + if (strcmp(smb_dclocator.sdl_dci.dc_name, bad_dc)) { + /* + * The "bad" DC is no longer the current one. + * Probably a late "bad DC" report. + */ + goto out; + } + if (smb_dclocator.sdl_bad_dc) { + /* Someone already marked the current DC as "bad". */ + syslog(LOG_DEBUG, "smb_ddiscover_bad_dc repeat"); + goto out; + } + + /* + * Mark the current DC as "bad" and let the DC Locator + * run again if it's not already. + */ + syslog(LOG_INFO, "smb_ddiscover, bad DC: %s", bad_dc); + smb_dclocator.sdl_bad_dc = B_TRUE; + + /* In-line smb_ddiscover_kick */ + if (!smb_dclocator.sdl_locate) { + smb_dclocator.sdl_locate = B_TRUE; + (void) cond_broadcast(&smb_dclocator.sdl_cv); + } + +out: + (void) mutex_unlock(&smb_dclocator.sdl_mtx); } /* @@ -195,27 +293,94 @@ smb_ddiscover_wait(void) static void * smb_ddiscover_service(void *arg) { - char domain[SMB_PI_MAX_DOMAIN]; - char sought_dc[MAXHOSTNAMELEN]; + smb_domainex_t dxi; + smb_dclocator_t *sdl = arg; + uint32_t status; + boolean_t bad_dc; + boolean_t cfg_chg; for (;;) { - (void) mutex_lock(&smb_dclocator.sdl_mtx); - - while (!smb_dclocator.sdl_locate) - (void) cond_wait(&smb_dclocator.sdl_cv, - &smb_dclocator.sdl_mtx); - - (void) strlcpy(domain, smb_dclocator.sdl_domain, - SMB_PI_MAX_DOMAIN); - (void) strlcpy(sought_dc, smb_dclocator.sdl_dc, MAXHOSTNAMELEN); - (void) mutex_unlock(&smb_dclocator.sdl_mtx); + /* + * Wait to be signaled for work by one of: + * smb_locate_dc(), smb_ddiscover_refresh(), + * smb_ddiscover_bad_dc() + */ + syslog(LOG_DEBUG, "smb_ddiscover_service waiting"); + + (void) mutex_lock(&sdl->sdl_mtx); + while (!sdl->sdl_locate) + (void) cond_wait(&sdl->sdl_cv, + &sdl->sdl_mtx); + + if (!smb_config_getbool(SMB_CI_DOMAIN_MEMB)) { + sdl->sdl_status = NT_STATUS_INVALID_SERVER_STATE; + syslog(LOG_DEBUG, "smb_ddiscover_service: " + "not a domain member"); + goto wait_again; + } - smb_ddiscover_main(domain, sought_dc); + /* + * Want to know if these change below. + * Note: mutex held here + */ + find_again: + bad_dc = sdl->sdl_bad_dc; + sdl->sdl_bad_dc = B_FALSE; + if (bad_dc) { + /* + * Need to clear the current DC name or + * ddiscover_bad_dc will keep setting bad_dc + */ + sdl->sdl_dci.dc_name[0] = '\0'; + } + cfg_chg = sdl->sdl_cfg_chg; + sdl->sdl_cfg_chg = B_FALSE; + + (void) mutex_unlock(&sdl->sdl_mtx); + + syslog(LOG_DEBUG, "smb_ddiscover_service running " + "cfg_chg=%d bad_dc=%d", (int)cfg_chg, (int)bad_dc); + + /* + * Clear the cached DC now so that we'll ask idmap again. + * If our current DC gave us errors, force rediscovery. + */ + smb_ads_refresh(bad_dc); + + /* + * Search for the DC, save the result. + */ + bzero(&dxi, sizeof (dxi)); + status = smb_ddiscover_main(sdl->sdl_domain, &dxi); + if (status == 0) + smb_domain_save(); + (void) mutex_lock(&sdl->sdl_mtx); + sdl->sdl_status = status; + if (status == 0) + sdl->sdl_dci = dxi.d_dci; + + /* + * Run again if either of cfg_chg or bad_dc + * was turned on during smb_ddiscover_main(). + * Note: mutex held here. + */ + if (sdl->sdl_bad_dc) { + syslog(LOG_DEBUG, "smb_ddiscover_service " + "restart because bad_dc was set"); + goto find_again; + } + if (sdl->sdl_cfg_chg) { + syslog(LOG_DEBUG, "smb_ddiscover_service " + "restart because cfg_chg was set"); + goto find_again; + } - (void) mutex_lock(&smb_dclocator.sdl_mtx); - smb_dclocator.sdl_locate = B_FALSE; - (void) cond_broadcast(&smb_dclocator.sdl_cv); - (void) mutex_unlock(&smb_dclocator.sdl_mtx); + wait_again: + sdl->sdl_locate = B_FALSE; + sdl->sdl_bad_dc = B_FALSE; + sdl->sdl_cfg_chg = B_FALSE; + (void) cond_broadcast(&sdl->sdl_cv); + (void) mutex_unlock(&sdl->sdl_mtx); } /*NOTREACHED*/ @@ -223,186 +388,81 @@ smb_ddiscover_service(void *arg) } /* - * Discovers a domain controller for the specified domain either via - * DNS or NetBIOS. After the domain controller is discovered successfully - * primary and trusted domain infromation will be queried using RPC queries. - * If the RPC queries fail, the domain information stored in SMF might be used - * if the the discovered domain is the same as the previously joined domain. - * If everything is successful domain cache will be updated with all the - * obtained information. - */ -static void -smb_ddiscover_main(char *domain, char *server) -{ - smb_domainex_t dxi; - uint32_t status; - - bzero(&dxi, sizeof (smb_domainex_t)); - - if (smb_domain_start_update() != SMB_DOMAIN_SUCCESS) - return; - - status = smb_ddiscover_dns(domain, server, &dxi); - if (status != 0 && !SMB_IS_FQDN(domain)) { - if (smb_ddiscover_nbt(domain, server, &dxi)) - status = 0; - } - - if (status == 0) - smb_domain_update(&dxi); - - smb_domain_end_update(); - - smb_domainex_free(&dxi); - - if (status == 0) - smb_domain_save(); -} - -/* - * Discovers a DC for the specified domain via DNS. If a DC is found - * primary and trusted domains information will be queried. - */ -static uint32_t -smb_ddiscover_dns(char *domain, char *server, smb_domainex_t *dxi) -{ - uint32_t status; - - if (!smb_ads_lookup_msdcs(domain, server, dxi->d_dc, MAXHOSTNAMELEN)) - return (NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND); - - status = smb_ddiscover_qinfo(domain, dxi->d_dc, dxi); - return (status); -} - -/* - * Discovers a DC for the specified domain using NETLOGON protocol. - * If a DC cannot be found using NETLOGON then it will - * try to resolve it via DNS, i.e. find out if it is the first label - * of a DNS domain name. If the corresponding DNS name is found, DC - * discovery will be done via DNS query. + * Discovers a domain controller for the specified domain via DNS. + * After the domain controller is discovered successfully primary and + * trusted domain infromation will be queried using RPC queries. * - * If the fully-qualified domain name is derived from the DNS config - * file, the NetBIOS domain name specified by the user will be compared - * against the NetBIOS domain name obtained via LSA query. If there is - * a mismatch, the DC discovery will fail since the discovered DC is - * actually for another domain, whose first label of its FQDN somehow - * matches with the NetBIOS name of the domain we're interested in. + * Caller should zero out *dxi before calling, and after a + * successful return should call: smb_domain_save() */ -static boolean_t -smb_ddiscover_nbt(char *domain, char *server, smb_domainex_t *dxi) +uint32_t +smb_ddiscover_main(char *domain, smb_domainex_t *dxi) { - char dnsdomain[MAXHOSTNAMELEN]; uint32_t status; - *dnsdomain = '\0'; - - if (!smb_browser_netlogon(domain, dxi->d_dc, MAXHOSTNAMELEN)) { - if (!smb_ddiscover_domain_match(domain, dnsdomain, - MAXHOSTNAMELEN)) - return (B_FALSE); - - if (!smb_ads_lookup_msdcs(dnsdomain, server, dxi->d_dc, - MAXHOSTNAMELEN)) - return (B_FALSE); + if (domain[0] == '\0') { + syslog(LOG_DEBUG, "smb_ddiscover_main NULL domain"); + return (NT_STATUS_INTERNAL_ERROR); } - status = smb_ddiscover_qinfo(domain, dxi->d_dc, dxi); - if (status != NT_STATUS_SUCCESS) - return (B_FALSE); - - if ((*dnsdomain != '\0') && - smb_strcasecmp(domain, dxi->d_primary.di_nbname, 0)) - return (B_FALSE); - - /* - * Now that we get the fully-qualified DNS name of the - * domain via LSA query. Verifies ADS configuration - * if we previously locate a DC via NetBIOS. On success, - * ADS cache will be populated. - */ - if (smb_ads_lookup_msdcs(dxi->d_primary.di_fqname, server, - dxi->d_dc, MAXHOSTNAMELEN) == 0) - return (B_FALSE); - - return (B_TRUE); -} - -/* - * Tries to find a matching DNS domain for the given NetBIOS domain - * name by checking the first label of system's configured DNS domains. - * If a match is found, it'll be returned in the passed buffer. - */ -static boolean_t -smb_ddiscover_domain_match(char *nb_domain, char *buf, uint32_t len) -{ - struct __res_state res_state; - int i; - char *entry, *p; - char first_label[MAXHOSTNAMELEN]; - boolean_t found; - - if (!nb_domain || !buf) - return (B_FALSE); + if (smb_domain_start_update() != SMB_DOMAIN_SUCCESS) { + syslog(LOG_DEBUG, "smb_ddiscover_main can't get lock"); + return (NT_STATUS_INTERNAL_ERROR); + } - *buf = '\0'; - bzero(&res_state, sizeof (struct __res_state)); - if (res_ninit(&res_state)) - return (B_FALSE); + status = smb_ads_lookup_msdcs(domain, &dxi->d_dci); + if (status != 0) { + syslog(LOG_DEBUG, "smb_ddiscover_main can't find DC (%s)", + xlate_nt_status(status)); + goto out; + } - found = B_FALSE; - entry = res_state.defdname; - for (i = 0; entry != NULL; i++) { - (void) strlcpy(first_label, entry, MAXHOSTNAMELEN); - if ((p = strchr(first_label, '.')) != NULL) { - *p = '\0'; - if (strlen(first_label) > 15) - first_label[15] = '\0'; - } + status = smb_ddiscover_qinfo(domain, dxi->d_dci.dc_name, dxi); + if (status != 0) { + syslog(LOG_DEBUG, + "smb_ddiscover_main can't get domain info (%s)", + xlate_nt_status(status)); + goto out; + } - if (smb_strcasecmp(nb_domain, first_label, 0) == 0) { - found = B_TRUE; - (void) strlcpy(buf, entry, len); - break; - } + smb_domain_update(dxi); - entry = res_state.dnsrch[i]; - } +out: + smb_domain_end_update(); + /* Don't need the trusted domain list anymore. */ + smb_domainex_free(dxi); - res_ndestroy(&res_state); - return (found); + return (status); } /* * Obtain primary and trusted domain information using LSA queries. * - * Disconnect any existing connection with the domain controller. - * This will ensure that no stale connection will be used, it will - * also pickup any configuration changes in either side by trying - * to establish a new connection. - * * domain - either NetBIOS or fully-qualified domain name */ static uint32_t smb_ddiscover_qinfo(char *domain, char *server, smb_domainex_t *dxi) { - uint32_t status; - - mlsvc_disconnect(server); - - status = lsa_query_dns_domain_info(server, domain, &dxi->d_primary); - if (status != NT_STATUS_SUCCESS) { - status = smb_ddiscover_use_config(domain, dxi); - if (status != NT_STATUS_SUCCESS) - status = lsa_query_primary_domain_info(server, domain, - &dxi->d_primary); - } - - if (status == NT_STATUS_SUCCESS) - smb_ddiscover_enum_trusted(domain, server, dxi); - - return (status); + uint32_t ret, tmp; + + /* If we must return failure, use this first one. */ + ret = lsa_query_dns_domain_info(server, domain, &dxi->d_primary); + if (ret == NT_STATUS_SUCCESS) + goto success; + tmp = smb_ddiscover_use_config(domain, dxi); + if (tmp == NT_STATUS_SUCCESS) + goto success; + tmp = lsa_query_primary_domain_info(server, domain, &dxi->d_primary); + if (tmp == NT_STATUS_SUCCESS) + goto success; + + /* All of the above failed. */ + return (ret); + +success: + smb_ddiscover_enum_trusted(domain, server, dxi); + return (NT_STATUS_SUCCESS); } /* @@ -459,4 +519,22 @@ static void smb_domainex_free(smb_domainex_t *dxi) { free(dxi->d_trusted.td_domains); + dxi->d_trusted.td_domains = NULL; +} + +static void +smb_set_krb5_realm(char *domain) +{ + static char realm[MAXHOSTNAMELEN]; + + if (domain == NULL || domain[0] == '\0') { + (void) unsetenv("KRB5_DEFAULT_REALM"); + return; + } + + /* In case krb5.conf is not configured, set the default realm. */ + (void) strlcpy(realm, domain, sizeof (realm)); + (void) smb_strupr(realm); + + (void) setenv("KRB5_DEFAULT_REALM", realm, 1); } diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_init.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_init.c index 0fbe2a6890..794e6c9576 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_init.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_init.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012 Nexenta Systems, Inc. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #include <sys/errno.h> @@ -111,7 +111,8 @@ mlsvc_timecheck(void *arg) if (!smb_domain_getinfo(&di)) continue; - ndr_srvsvc_timecheck(di.d_dc, di.d_primary.di_nbname); + ndr_srvsvc_timecheck(di.d_dci.dc_name, + di.d_primary.di_nbname); } /*NOTREACHED*/ diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c index 18d5bd18b0..ad3565699b 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ /* @@ -43,14 +43,11 @@ #include <smbsrv/libmlsvc.h> #include <smbsrv/ntaccess.h> #include <smbsrv/smbinfo.h> +#include <smbsrv/netrauth.h> #include <libsmbrdr.h> #include <lsalib.h> #include <samlib.h> -#include <smbsrv/netrauth.h> - -extern int netr_open(char *, char *, mlsvc_handle_t *); -extern int netr_close(mlsvc_handle_t *); -extern DWORD netlogon_auth(char *, mlsvc_handle_t *, DWORD); +#include <mlsvc.h> static DWORD mlsvc_join_rpc(smb_domainex_t *dxi, @@ -67,18 +64,25 @@ mlsvc_netlogon(char *server, char *domain) mlsvc_handle_t netr_handle; DWORD status; - if (netr_open(server, domain, &netr_handle) == 0) { - if ((status = netlogon_auth(server, &netr_handle, - NETR_FLG_INIT)) != NT_STATUS_SUCCESS) - syslog(LOG_NOTICE, "Failed to establish NETLOGON " - "credential chain"); - (void) netr_close(&netr_handle); - } else { + status = netr_open(server, domain, &netr_handle); + if (status != 0) { syslog(LOG_NOTICE, "Failed to connect to %s " - "for domain %s", server, domain); - status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + "for domain %s (%s)", server, domain, + xlate_nt_status(status)); + return (status); } + status = netlogon_auth(server, &netr_handle, NETR_FLG_INIT); + if (status != NT_STATUS_SUCCESS) { + syslog(LOG_NOTICE, "Failed to establish NETLOGON " + "credential chain with DC: %s (%s)", server, + xlate_nt_status(status)); + syslog(LOG_NOTICE, "The machine account information on the " + "domain controller does not match the local storage."); + syslog(LOG_NOTICE, "To correct this, use 'smbadm join'"); + } + (void) netr_close(&netr_handle); + return (status); } @@ -90,69 +94,133 @@ mlsvc_netlogon(char *server, char *domain) * machine attempts to join, and we just change the password from the * (weak) default password for a new machine account to a random one. * - * Note that the caller has already done "DC discovery" and passes the - * domain info. in the first arg. - * * Returns NT status codes. */ -DWORD -mlsvc_join(smb_domainex_t *dxi, char *admin_user, char *admin_pw) +void +mlsvc_join(smb_joininfo_t *info, smb_joinres_t *res) { + static unsigned char zero_hash[SMBAUTH_HASH_SZ]; char machine_name[SMB_SAMACCT_MAXLEN]; char machine_pw[NETR_MACHINE_ACCT_PASSWD_MAX]; unsigned char passwd_hash[SMBAUTH_HASH_SZ]; - smb_domain_t *di = &dxi->d_primary; + smb_domainex_t dxi; + smb_domain_t *di = &dxi.d_primary; DWORD status; int rc; + bzero(&dxi, sizeof (dxi)); + /* * Domain join support: AD (Kerberos+LDAP) or MS-RPC? - * Leave the AD code path disabled until it can be - * fixed up so that the SMB server is in complete - * control of which AD server we talk to. See: - * NX 12427 (Re-enable Kerberos+LDAP with...) */ boolean_t ads_enabled = smb_config_get_ads_enable(); - if (smb_getsamaccount(machine_name, sizeof (machine_name)) != 0) - return (NT_STATUS_INTERNAL_ERROR); + if (smb_getsamaccount(machine_name, sizeof (machine_name)) != 0) { + res->status = NT_STATUS_INVALID_COMPUTER_NAME; + return; + } (void) smb_gen_random_passwd(machine_pw, sizeof (machine_pw)); /* - * A non-null user means we do "secure join". + * Ensure that any previous membership of this domain has + * been cleared from the environment before we start. This + * will ensure that we don't attempt a NETLOGON_SAMLOGON + * when attempting to find the PDC. */ - if (admin_user != NULL && admin_user[0] != '\0') { - /* - * Doing "secure join", so authenticate as the - * specified user (with admin. rights). - */ - (void) smb_auth_ntlm_hash(admin_pw, passwd_hash); - smb_ipc_set(admin_user, passwd_hash); + (void) smb_config_setbool(SMB_CI_DOMAIN_MEMB, B_FALSE); + + if (info->domain_username[0] != '\0') { + (void) smb_auth_ntlm_hash(info->domain_passwd, passwd_hash); + smb_ipc_set(info->domain_username, passwd_hash); + } else { + smb_ipc_set(MLSVC_ANON_USER, zero_hash); + } + + /* + * Tentatively set the idmap domain to the one we're joining, + * so that the DC locator in idmap knows what to look for. + * Ditto the SMB server domain. + */ + if (smb_config_set_idmap_domain(info->domain_name) != 0) + syslog(LOG_NOTICE, "Failed to set idmap domain name"); + if (smb_config_refresh_idmap() != 0) + syslog(LOG_NOTICE, "Failed to refresh idmap service"); + + /* Clear DNS local (ADS) lookup cache. */ + smb_ads_refresh(B_FALSE); + + /* + * Locate a DC for this domain. Intentionally bypass the + * ddiscover service here because we're still joining. + * This also allows better reporting of any failures. + */ + status = smb_ads_lookup_msdcs(info->domain_name, &dxi.d_dci); + if (status != NT_STATUS_SUCCESS) { + syslog(LOG_ERR, + "smbd: failed to locate AD server for domain %s (%s)", + info->domain_name, xlate_nt_status(status)); + goto out; + } + + /* + * Found a DC. Report what we found along with the return status + * so that admin will know which AD server we were talking to. + */ + (void) strlcpy(res->dc_name, dxi.d_dci.dc_name, MAXHOSTNAMELEN); + syslog(LOG_INFO, "smbd: found AD server %s", dxi.d_dci.dc_name); + /* + * Domain discovery needs to authenticate with the AD server. + * Disconnect any existing connection with the domain controller + * to make sure we won't use any prior authentication context + * our redirector might have. + */ + mlsvc_disconnect(dxi.d_dci.dc_name); + + /* + * Get the domain policy info (domain SID etc). + * Here too, bypass the smb_ddiscover_service. + */ + status = smb_ddiscover_main(info->domain_name, &dxi); + if (status != NT_STATUS_SUCCESS) { + syslog(LOG_ERR, + "smbd: failed getting domain info for %s (%s)", + info->domain_name, xlate_nt_status(status)); + goto out; + } + /* + * After a successful smbd_ddiscover_main() call + * we should call smb_domain_save() to update the + * data shown by smbadm list. Do that at the end, + * only if all goes well with joining the domain. + */ + + /* + * Create or update our machine account on the DC. + * A non-null user means we do "secure join". + */ + if (info->domain_username[0] != '\0') { /* * If enabled, try to join using AD Services. - * The ADS code needs work. Not enabled yet. */ status = NT_STATUS_UNSUCCESSFUL; if (ads_enabled) { - smb_adjoin_status_t err; - err = smb_ads_join(di->di_fqname, - admin_user, admin_pw, machine_pw); - if (err != SMB_ADJOIN_SUCCESS) { - smb_ads_join_errmsg(err); - } else { + res->join_err = smb_ads_join(di->di_fqname, + info->domain_username, info->domain_passwd, + machine_pw); + if (res->join_err == SMB_ADS_SUCCESS) { status = NT_STATUS_SUCCESS; } - } - - /* - * If ADS was disabled or gave an error, - * fall-back and try to join using RPC. - */ - if (status != NT_STATUS_SUCCESS) { - status = mlsvc_join_rpc(dxi, - admin_user, admin_pw, + } else { + syslog(LOG_DEBUG, "use_ads=false (do RPC join)"); + + /* + * If ADS was disabled, join using RPC. + */ + status = mlsvc_join_rpc(&dxi, + info->domain_username, + info->domain_passwd, machine_name, machine_pw); } @@ -160,10 +228,7 @@ mlsvc_join(smb_domainex_t *dxi, char *admin_user, char *admin_pw) /* * Doing "Unsecure join" (pre-created account) */ - bzero(passwd_hash, sizeof (passwd_hash)); - smb_ipc_set(MLSVC_ANON_USER, passwd_hash); - - status = mlsvc_join_noauth(dxi, machine_name, machine_pw); + status = mlsvc_join_noauth(&dxi, machine_name, machine_pw); } if (status != NT_STATUS_SUCCESS) @@ -175,47 +240,63 @@ mlsvc_join(smb_domainex_t *dxi, char *admin_user, char *admin_pw) */ (void) smb_auth_ntlm_hash(machine_pw, passwd_hash); smb_ipc_set(machine_name, passwd_hash); - rc = smbrdr_logon(dxi->d_dc, di->di_nbname, machine_name); + rc = smbrdr_logon(dxi.d_dci.dc_name, di->di_nbname, machine_name); if (rc != 0) { syslog(LOG_NOTICE, "Authenticate with " "new/updated machine account: %s", strerror(rc)); + res->join_err = SMB_ADJOIN_ERR_AUTH_NETLOGON; status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; goto out; } /* - * Store the new machine account password. + * Store the new machine account password, and + * SMB_CI_DOMAIN_MEMB etc. */ - rc = smb_setdomainprops(NULL, dxi->d_dc, machine_pw); + rc = smb_setdomainprops(NULL, dxi.d_dci.dc_name, machine_pw); if (rc != 0) { syslog(LOG_NOTICE, "Failed to save machine account password"); + res->join_err = SMB_ADJOIN_ERR_STORE_PROPS; status = NT_STATUS_INTERNAL_DB_ERROR; goto out; } /* - * Update idmap config + * Update idmap config? + * Already set the domain_name above. */ - if (smb_config_set_idmap_domain(di->di_fqname) != 0) - syslog(LOG_NOTICE, "Failed to set idmap domain name"); - if (smb_config_refresh_idmap() != 0) - syslog(LOG_NOTICE, "Failed to refresh idmap service"); /* - * Note: The caller (smbd) saves the "secmode" and - * domain info (via smb_config_setdomaininfo) and - * and does smb_ipc_commit (or rollback). + * Save the SMB server config. Sets: SMB_CI_DOMAIN_* + * Should unify SMB vs idmap configs. */ + smb_config_setdomaininfo(di->di_nbname, di->di_fqname, + di->di_sid, + di->di_u.di_dns.ddi_forest, + di->di_u.di_dns.ddi_guid); + smb_ipc_commit(); + smb_domain_save(); + status = 0; out: + + if (status != 0) { + /* + * Undo the tentative domain settings. + */ + (void) smb_config_set_idmap_domain(""); + (void) smb_config_refresh_idmap(); + smb_ipc_rollback(); + } + /* Avoid leaving cleartext passwords around. */ bzero(machine_pw, sizeof (machine_pw)); bzero(passwd_hash, sizeof (passwd_hash)); - return (status); + res->status = status; } static DWORD @@ -227,7 +308,7 @@ mlsvc_join_rpc(smb_domainex_t *dxi, mlsvc_handle_t domain_handle; mlsvc_handle_t user_handle; smb_account_t ainfo; - char *server = dxi->d_dc; + char *server = dxi->d_dci.dc_name; smb_domain_t *di = &dxi->d_primary; DWORD account_flags; DWORD rid; @@ -264,7 +345,7 @@ mlsvc_join_rpc(smb_domainex_t *dxi, } if (status != NT_STATUS_SUCCESS) { syslog(LOG_NOTICE, - "Create or open machine account: %s", + "smbd: failed to open machine account (%s)", xlate_nt_status(status)); goto out_domain_handle; } @@ -277,7 +358,7 @@ mlsvc_join_rpc(smb_domainex_t *dxi, status = netr_set_user_password(&user_handle, machine_pw); if (status != NT_STATUS_SUCCESS) { syslog(LOG_NOTICE, - "Set machine account password: %s", + "smbd: failed to set machine account password (%s)", xlate_nt_status(status)); goto out_user_handle; } @@ -329,7 +410,7 @@ mlsvc_join_noauth(smb_domainex_t *dxi, return (NT_STATUS_INTERNAL_ERROR); old_pw[14] = '\0'; - status = netr_change_password(dxi->d_dc, machine_name, + status = netr_change_password(dxi->d_dci.dc_name, machine_name, old_pw, machine_pw); if (status != NT_STATUS_SUCCESS) { syslog(LOG_NOTICE, diff --git a/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c b/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c index 4b0224261b..ea31c935ed 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012 Nexenta Systems, Inc. All rights reserved. + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ /* @@ -94,6 +94,7 @@ netlogon_auth(char *server, mlsvc_handle_t *netr_handle, DWORD flags) if (rc != 0) return (NT_STATUS_UNSUCCESSFUL); + /* server is our DC. Note: normally an FQDN. */ (void) snprintf(netr_info->server, sizeof (netr_info->server), "\\\\%s", server); @@ -128,18 +129,20 @@ netlogon_auth(char *server, mlsvc_handle_t *netr_handle, DWORD flags) * * We store the remote server information, which is used to drive Windows * version specific behavior. + * + * Returns 0 or NT status */ -int +DWORD netr_open(char *server, char *domain, mlsvc_handle_t *netr_handle) { char user[SMB_USERNAME_MAXLEN]; + DWORD status; smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); - if (ndr_rpc_bind(netr_handle, server, domain, user, "NETR") < 0) - return (-1); + status = ndr_rpc_bind(netr_handle, server, domain, user, "NETR"); - return (0); + return (status); } /* @@ -195,9 +198,10 @@ static int netr_server_authenticate2(mlsvc_handle_t *netr_handle, netr_info_t *netr_info) { struct netr_ServerAuthenticate2 arg; + /* sizeof netr_info->hostname, + 1 for the '$' */ + char account_name[(NETBIOS_NAME_SZ * 2) + 1]; int opnum; int rc; - char account_name[NETBIOS_NAME_SZ * 2]; bzero(&arg, sizeof (struct netr_ServerAuthenticate2)); opnum = NETR_OPNUM_ServerAuthenticate2; diff --git a/usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c b/usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c index 566837fe2d..ab99db75f8 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012 Nexenta Systems, Inc. All rights reserved. + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ /* @@ -61,6 +61,8 @@ static void netr_setup_identity(ndr_heap_t *, smb_logon_t *, static boolean_t netr_isadmin(struct netr_validation_info3 *); static uint32_t netr_setup_domain_groups(struct netr_validation_info3 *, smb_ids_t *); +static uint32_t netr_setup_token_info3(struct netr_validation_info3 *, + smb_token_t *); static uint32_t netr_setup_token_wingrps(struct netr_validation_info3 *, smb_token_t *); @@ -75,6 +77,83 @@ static boolean_t netlogon_busy = B_FALSE; static boolean_t netlogon_abort = B_FALSE; /* + * Helper for Kerberos authentication + */ +uint32_t +smb_decode_krb5_pac(smb_token_t *token, char *data, uint_t len) +{ + struct krb5_validation_info info; + ndr_buf_t *nbuf; + uint32_t status = NT_STATUS_NO_MEMORY; + int rc; + + bzero(&info, sizeof (info)); + + /* Need to keep this until we're done with &info */ + nbuf = ndr_buf_init(&TYPEINFO(netr_interface)); + if (nbuf == NULL) + goto out; + + rc = ndr_buf_decode(nbuf, NDR_PTYPE_PAC, + NETR_OPNUM_decode_krb5_pac, data, len, &info); + if (rc != NDR_DRC_OK) { + status = RPC_NT_PROTOCOL_ERROR; + goto out; + } + + status = netr_setup_token_info3(&info.info3, token); + + /* Deal with the "resource groups"? */ + + +out: + if (nbuf != NULL) + ndr_buf_fini(nbuf); + + return (status); +} + +/* + * Code factored out of netr_setup_token() + */ +static uint32_t +netr_setup_token_info3(struct netr_validation_info3 *info3, + smb_token_t *token) +{ + smb_sid_t *domsid; + + domsid = (smb_sid_t *)info3->LogonDomainId; + + token->tkn_user.i_sid = smb_sid_splice(domsid, + info3->UserId); + if (token->tkn_user.i_sid == NULL) + goto errout; + + token->tkn_primary_grp.i_sid = smb_sid_splice(domsid, + info3->PrimaryGroupId); + if (token->tkn_primary_grp.i_sid == NULL) + goto errout; + + if (info3->EffectiveName.str) { + token->tkn_account_name = + strdup((char *)info3->EffectiveName.str); + if (token->tkn_account_name == NULL) + goto errout; + } + + if (info3->LogonDomainName.str) { + token->tkn_domain_name = + strdup((char *)info3->LogonDomainName.str); + if (token->tkn_domain_name == NULL) + goto errout; + } + + return (netr_setup_token_wingrps(info3, token)); +errout: + return (NT_STATUS_INSUFF_SERVER_RESOURCES); +} + +/* * Abort impending domain logon requests. */ void @@ -148,7 +227,7 @@ static uint32_t netlogon_logon(smb_logon_t *user_info, smb_token_t *token) { char resource_domain[SMB_PI_MAX_DOMAIN]; - char server[NETBIOS_NAME_SZ * 2]; + char server[MAXHOSTNAMELEN]; mlsvc_handle_t netr_handle; smb_domainex_t di; uint32_t status; @@ -164,13 +243,14 @@ netlogon_logon(smb_logon_t *user_info, smb_token_t *token) } do { - if (netr_open(di.d_dc, di.d_primary.di_nbname, &netr_handle) - != 0) + if (netr_open(di.d_dci.dc_name, di.d_primary.di_nbname, + &netr_handle) != 0) return (NT_STATUS_OPEN_FAILED); - if (di.d_dc && (*netr_global_info.server != '\0')) { + if (di.d_dci.dc_name[0] != '\0' && + (*netr_global_info.server != '\0')) { (void) snprintf(server, sizeof (server), - "\\\\%s", di.d_dc); + "\\\\%s", di.d_dci.dc_name); if (strncasecmp(netr_global_info.server, server, strlen(server)) != 0) netr_invalidate_chain(); @@ -178,7 +258,7 @@ netlogon_logon(smb_logon_t *user_info, smb_token_t *token) if ((netr_global_info.flags & NETR_FLG_VALID) == 0 || !smb_match_netlogon_seqnum()) { - status = netlogon_auth(di.d_dc, &netr_handle, + status = netlogon_auth(di.d_dci.dc_name, &netr_handle, NETR_FLG_NULL); if (status != 0) { @@ -190,7 +270,7 @@ netlogon_logon(smb_logon_t *user_info, smb_token_t *token) } status = netr_server_samlogon(&netr_handle, - &netr_global_info, di.d_dc, user_info, token); + &netr_global_info, di.d_dci.dc_name, user_info, token); (void) netr_close(&netr_handle); } while (status == NT_STATUS_INSUFFICIENT_LOGON_INFO && retries++ < 3); @@ -254,13 +334,14 @@ netr_setup_token(struct netr_validation_info3 *info3, smb_logon_t *user_info, * exclusively ored with the 16 byte UserSessionKey to recover * the the clear form. */ - if ((token->tkn_session_key = malloc(SMBAUTH_SESSION_KEY_SZ)) == NULL) + if ((token->tkn_ssnkey.val = malloc(SMBAUTH_SESSION_KEY_SZ)) == NULL) return (NT_STATUS_NO_MEMORY); + token->tkn_ssnkey.len = SMBAUTH_SESSION_KEY_SZ; bzero(rc4key, SMBAUTH_SESSION_KEY_SZ); bcopy(netr_info->session_key.key, rc4key, netr_info->session_key.len); - bcopy(info3->UserSessionKey.data, token->tkn_session_key, + bcopy(info3->UserSessionKey.data, token->tkn_ssnkey.val, SMBAUTH_SESSION_KEY_SZ); - rand_hash((unsigned char *)token->tkn_session_key, + rand_hash((unsigned char *)token->tkn_ssnkey.val, SMBAUTH_SESSION_KEY_SZ, rc4key, SMBAUTH_SESSION_KEY_SZ); return (NT_STATUS_SUCCESS); @@ -603,7 +684,14 @@ netr_setup_identity(ndr_heap_t *heap, smb_logon_t *user_info, (void) mutex_unlock(&logon_id_mutex); - identity->parameter_control = 0; + /* + * [MS-APDS] 3.1.5.2 "NTLM Network Logon" says to set + * ParameterControl to the 'E' + 'K' bits. Those are: + * (1 << 5) | (1 << 11), a.k.a + */ + identity->parameter_control = + MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT | + MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT; identity->logon_id.LowPart = logon_id; identity->logon_id.HighPart = 0; diff --git a/usr/src/lib/smbsrv/libmlsvc/common/samlib.c b/usr/src/lib/smbsrv/libmlsvc/common/samlib.c index 1a2233ce39..8b0c4e3fbd 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/samlib.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/samlib.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012 Nexenta Systems, Inc. All rights reserved. + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ /* @@ -293,14 +293,14 @@ netr_change_password( uint8_t old_nt_hash[SAMR_PWHASH_LEN]; uint8_t new_nt_hash[SAMR_PWHASH_LEN]; mlsvc_handle_t handle; - DWORD rc; + DWORD status; /* * Create an RPC handle to this server, bound to SAMR. */ - rc = ndr_rpc_bind(&handle, server, "", "", "SAMR"); - if (rc) - return (RPC_NT_SERVER_UNAVAILABLE); + status = ndr_rpc_bind(&handle, server, "", "", "SAMR"); + if (status != NT_STATUS_SUCCESS) + return (status); /* * Encrypt the new p/w (plus random filler) with the @@ -319,7 +319,7 @@ netr_change_password( /* * Finally, ready to try the OtW call. */ - rc = samr_change_password( + status = samr_change_password( &handle, server, account, &epw, &old_hash); @@ -328,7 +328,7 @@ netr_change_password( (void) memset(new_nt_hash, 0, sizeof (new_nt_hash)); ndr_rpc_unbind(&handle); - return (rc); + return (status); } /* diff --git a/usr/src/lib/smbsrv/libmlsvc/common/samr_clnt.c b/usr/src/lib/smbsrv/libmlsvc/common/samr_clnt.c index fc2956f226..289e8470cf 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/samr_clnt.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/samr_clnt.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012 Nexenta Systems, Inc. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ /* @@ -84,7 +84,7 @@ samr_open(char *server, char *domain, char *username, DWORD access_mask, if (server == NULL || domain == NULL) { if (!smb_domain_getinfo(&di)) return (NT_STATUS_INTERNAL_ERROR); - server = di.d_dc; + server = di.d_dci.dc_name; domain = di.d_primary.di_nbname; } diff --git a/usr/src/lib/smbsrv/libmlsvc/common/smb_autohome.c b/usr/src/lib/smbsrv/libmlsvc/common/smb_autohome.c index 8ffc4f6f28..c9e634f515 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/smb_autohome.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/smb_autohome.c @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ #include <sys/param.h> @@ -82,6 +83,9 @@ smb_autohome_add(const smb_token_t *token) uid_t uid; gid_t gid; + if (token->tkn_flags & SMB_ATF_ANON) + return; + uid = token->tkn_user.i_id; gid = token->tkn_primary_grp.i_id; diff --git a/usr/src/lib/smbsrv/libmlsvc/common/smb_logon.c b/usr/src/lib/smbsrv/libmlsvc/common/smb_logon.c index 4b7a1cd178..24baa848e7 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/smb_logon.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/smb_logon.c @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ #include <unistd.h> @@ -44,7 +45,6 @@ static rwlock_t smb_logoninit_rwl; typedef void (*smb_logonop_t)(smb_logon_t *, smb_token_t *); -extern void smb_logon_domain(smb_logon_t *, smb_token_t *); static void smb_logon_local(smb_logon_t *, smb_token_t *); static void smb_logon_guest(smb_logon_t *, smb_token_t *); static void smb_logon_anon(smb_logon_t *, smb_token_t *); @@ -158,8 +158,8 @@ smb_token_sids2ids(smb_token_t *token) } stat = smb_idmap_batch_getmappings(&sib); - smb_idmap_batch_destroy(&sib); smb_idmap_check("smb_idmap_batch_getmappings", stat); + smb_idmap_batch_destroy(&sib); return (stat == IDMAP_SUCCESS ? 0 : -1); } @@ -253,7 +253,7 @@ smb_token_destroy(smb_token_t *token) free(token->tkn_posix_grps); free(token->tkn_account_name); free(token->tkn_domain_name); - free(token->tkn_session_key); + free(token->tkn_ssnkey.val); bzero(token, sizeof (smb_token_t)); free(token); } @@ -344,8 +344,10 @@ smb_token_set_flags(smb_token_t *token) * has been done. * * Note that the order of calls in this function are important. + * + * Returns B_TRUE for success. */ -static boolean_t +boolean_t smb_token_setup_common(smb_token_t *token) { smb_token_set_flags(token); @@ -475,7 +477,6 @@ smb_logon_local(smb_logon_t *user_info, smb_token_t *token) char guest[SMB_USERNAME_MAXLEN]; smb_passwd_t smbpw; uint32_t status; - boolean_t isguest; if (user_info->lg_secmode == SMB_SECMODE_DOMAIN) { if ((user_info->lg_domain_type != SMB_DOMAIN_LOCAL) && @@ -483,16 +484,18 @@ smb_logon_local(smb_logon_t *user_info, smb_token_t *token) return; } + /* + * If the requested account name is "guest" (or whatever + * our guest account is named) then don't handle it here. + * Let this request fall through to smb_logon_guest(). + */ smb_guest_account(guest, SMB_USERNAME_MAXLEN); - isguest = (smb_strcasecmp(guest, user_info->lg_e_username, 0) == 0); + if (smb_strcasecmp(guest, user_info->lg_e_username, 0) == 0) + return; status = smb_token_auth_local(user_info, token, &smbpw); - if (status == NT_STATUS_SUCCESS) { - if (isguest) - status = smb_token_setup_guest(user_info, token); - else - status = smb_token_setup_local(&smbpw, token); - } + if (status == NT_STATUS_SUCCESS) + status = smb_token_setup_local(&smbpw, token); user_info->lg_status = status; } @@ -514,23 +517,31 @@ smb_logon_guest(smb_logon_t *user_info, smb_token_t *token) char guest[SMB_USERNAME_MAXLEN]; smb_passwd_t smbpw; char *temp; - uint32_t status; if (user_info->lg_status != NT_STATUS_NO_SUCH_USER) return; + /* Get the name of the guest account. */ smb_guest_account(guest, SMB_USERNAME_MAXLEN); - temp = user_info->lg_e_username; - user_info->lg_e_username = guest; - status = smb_token_auth_local(user_info, token, &smbpw); - if ((status == NT_STATUS_SUCCESS) || - (status == NT_STATUS_NO_SUCH_USER)) { - status = smb_token_setup_guest(user_info, token); - } + /* Does the guest account exist? */ + if (smb_pwd_getpwnam(guest, &smbpw) == NULL) + return; + /* Is it enabled? (empty p/w is OK) */ + if (smbpw.pw_flags & SMB_PWF_DISABLE) + return; + + /* + * OK, give the client a guest logon. Note that on entry, + * lg_e_username is typically something other than "guest" + * so we need to set the effective username when createing + * the guest token. + */ + temp = user_info->lg_e_username; + user_info->lg_e_username = guest; + user_info->lg_status = smb_token_setup_guest(user_info, token); user_info->lg_e_username = temp; - user_info->lg_status = status; } /* @@ -552,7 +563,7 @@ static uint32_t smb_token_auth_local(smb_logon_t *user_info, smb_token_t *token, smb_passwd_t *smbpw) { - boolean_t lm_ok, nt_ok; + boolean_t ok; uint32_t status = NT_STATUS_SUCCESS; if (smb_pwd_getpwnam(user_info->lg_e_username, smbpw) == NULL) @@ -561,41 +572,42 @@ smb_token_auth_local(smb_logon_t *user_info, smb_token_t *token, if (smbpw->pw_flags & SMB_PWF_DISABLE) return (NT_STATUS_ACCOUNT_DISABLED); - nt_ok = lm_ok = B_FALSE; - if ((smbpw->pw_flags & SMB_PWF_LM) && - (user_info->lg_lm_password.len != 0)) { - lm_ok = smb_auth_validate_lm( - user_info->lg_challenge_key.val, - user_info->lg_challenge_key.len, - smbpw, - user_info->lg_lm_password.val, - user_info->lg_lm_password.len, - user_info->lg_domain, - user_info->lg_username); - token->tkn_session_key = NULL; - } - - if (!lm_ok && (user_info->lg_nt_password.len != 0)) { - token->tkn_session_key = malloc(SMBAUTH_SESSION_KEY_SZ); - if (token->tkn_session_key == NULL) - return (NT_STATUS_NO_MEMORY); - nt_ok = smb_auth_validate_nt( - user_info->lg_challenge_key.val, - user_info->lg_challenge_key.len, - smbpw, - user_info->lg_nt_password.val, - user_info->lg_nt_password.len, - user_info->lg_domain, - user_info->lg_username, - (uchar_t *)token->tkn_session_key); + if ((smbpw->pw_flags & (SMB_PWF_LM | SMB_PWF_NT)) == 0) { + /* + * The SMB passwords have not been set. + * Return an error that suggests the + * password needs to be set. + */ + return (NT_STATUS_PASSWORD_EXPIRED); } - if (!nt_ok && !lm_ok) { - status = NT_STATUS_WRONG_PASSWORD; - syslog(LOG_NOTICE, "logon[%s\\%s]: %s", - user_info->lg_e_domain, user_info->lg_e_username, - xlate_nt_status(status)); - } + token->tkn_ssnkey.val = malloc(SMBAUTH_SESSION_KEY_SZ); + if (token->tkn_ssnkey.val == NULL) + return (NT_STATUS_NO_MEMORY); + token->tkn_ssnkey.len = SMBAUTH_SESSION_KEY_SZ; + + ok = smb_auth_validate( + smbpw, + user_info->lg_domain, + user_info->lg_username, + user_info->lg_challenge_key.val, + user_info->lg_challenge_key.len, + user_info->lg_nt_password.val, + user_info->lg_nt_password.len, + user_info->lg_lm_password.val, + user_info->lg_lm_password.len, + token->tkn_ssnkey.val); + if (ok) + return (NT_STATUS_SUCCESS); + + free(token->tkn_ssnkey.val); + token->tkn_ssnkey.val = NULL; + token->tkn_ssnkey.len = 0; + + status = NT_STATUS_WRONG_PASSWORD; + syslog(LOG_NOTICE, "logon[%s\\%s]: %s", + user_info->lg_e_domain, user_info->lg_e_username, + xlate_nt_status(status)); return (status); } diff --git a/usr/src/lib/smbsrv/libmlsvc/common/smbrdr_glue.c b/usr/src/lib/smbsrv/libmlsvc/common/smbrdr_glue.c index ab127b6150..724c509d8d 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/smbrdr_glue.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/smbrdr_glue.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012 Nexenta Systems, Inc. All rights reserved. + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ /* @@ -115,6 +115,9 @@ smbrdr_ctx_new(struct smb_ctx **ctx_p, char *server, assert(domain != NULL); assert(user != NULL); + if (server[0] == '\0') + return (NT_STATUS_INTERNAL_ERROR); + if ((err = smb_ctx_alloc(&ctx)) != 0) return (NT_STATUS_NO_MEMORY); diff --git a/usr/src/lib/smbsrv/libmlsvc/common/srvsvc_clnt.c b/usr/src/lib/smbsrv/libmlsvc/common/srvsvc_clnt.c index 819f5f1aa7..def8ced54a 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/srvsvc_clnt.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/srvsvc_clnt.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ /* @@ -62,14 +63,14 @@ srvsvc_open(char *server, char *domain, char *username, mlsvc_handle_t *handle) if (!smb_domain_getinfo(&di)) return (-1); - server = di.d_dc; + server = di.d_dci.dc_name; domain = di.d_primary.di_nbname; } if (username == NULL) username = MLSVC_ANON_USER; - if (ndr_rpc_bind(handle, server, domain, username, "SRVSVC") < 0) + if (ndr_rpc_bind(handle, server, domain, username, "SRVSVC") != 0) return (-1); return (0); @@ -422,8 +423,8 @@ srvsvc_timesync(void) if (!smb_domain_getinfo(&di)) return; - if (srvsvc_net_remote_tod(di.d_dc, di.d_primary.di_nbname, &tv, &tm) - != 0) + if (srvsvc_net_remote_tod(di.d_dci.dc_name, di.d_primary.di_nbname, + &tv, &tm) != 0) return; if (settimeofday(&tv, 0)) @@ -447,8 +448,8 @@ srvsvc_gettime(unsigned long *t) if (!smb_domain_getinfo(&di)) return (-1); - if (srvsvc_net_remote_tod(di.d_dc, di.d_primary.di_nbname, &tv, &tm) - != 0) + if (srvsvc_net_remote_tod(di.d_dci.dc_name, di.d_primary.di_nbname, + &tv, &tm) != 0) return (-1); *t = tv.tv_sec; @@ -553,7 +554,7 @@ srvsvc_net_test(char *server, char *domain, char *netname) (void) smb_tracef("%s %s %s", server, domain, netname); if (smb_domain_getinfo(&di)) { - server = di.d_dc; + server = di.d_dci.dc_name; domain = di.d_primary.di_nbname; } diff --git a/usr/src/lib/smbsrv/libsmb/Makefile.com b/usr/src/lib/smbsrv/libsmb/Makefile.com index 0c972a66f0..78dc4fdcfa 100644 --- a/usr/src/lib/smbsrv/libsmb/Makefile.com +++ b/usr/src/lib/smbsrv/libsmb/Makefile.com @@ -54,7 +54,6 @@ OBJS_COMMON = \ smb_info.o \ smb_kmod.o \ smb_lgrp.o \ - smb_mac.o \ smb_nic.o \ smb_pwdutil.o \ smb_privilege.o \ @@ -73,14 +72,16 @@ include ../../../Makefile.lib include ../../Makefile.lib INCS += -I$(SRC)/common/smbsrv +INCS += -I$(SRC)/lib/libsmbfs/smb LINTCHECKFLAGS += -erroff=E_INCONS_ARG_DECL2 LINTCHECKFLAGS += -erroff=E_BAD_FORMAT_STR2 LDLIBS += $(MACH_LDLIBS) +# perfer to keep libs ordered by dependence LDLIBS += -lscf -lmd -luuid -lpkcs11 -lcryptoutil -LDLIBS += -lsec -lidmap -lnsl -lsocket -lresolv -LDLIBS += -lreparse -lnvpair -lcmdutils -lavl -lc +LDLIBS += -lsec -lidmap -lreparse -lcmdutils -lavl +LDLIBS += -lnvpair -lresolv -lsocket -lnsl -lc CPPFLAGS += $(INCS) -D_REENTRANT CPPFLAGS += -Dsyslog=smb_syslog CERRWARN += -_gcc=-Wno-uninitialized diff --git a/usr/src/lib/smbsrv/libsmb/common/libsmb.h b/usr/src/lib/smbsrv/libsmb/common/libsmb.h index 7c5f88976a..6932e5d3a4 100644 --- a/usr/src/lib/smbsrv/libsmb/common/libsmb.h +++ b/usr/src/lib/smbsrv/libsmb/common/libsmb.h @@ -21,7 +21,7 @@ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ #ifndef _LIBSMB_H @@ -139,6 +139,7 @@ typedef enum { SMB_CI_DYNDNS_ENABLE, SMB_CI_MACHINE_PASSWD, + SMB_CI_MACHINE_UUID, SMB_CI_KPASSWD_SRV, SMB_CI_KPASSWD_DOMAIN, SMB_CI_KPASSWD_SEQNUM, @@ -193,6 +194,7 @@ extern boolean_t smb_config_get_ads_enable(void); extern int smb_config_get_debug(void); extern uint8_t smb_config_get_fg_flag(void); extern char *smb_config_get_localsid(void); +extern int smb_config_get_localuuid(uuid_t); extern int smb_config_secmode_fromstr(char *); extern char *smb_config_secmode_tostr(int); extern int smb_config_get_secmode(void); @@ -202,7 +204,7 @@ extern int smb_config_refresh_idmap(void); extern int smb_config_getip(smb_cfg_id_t, smb_inaddr_t *); extern void smb_config_get_version(smb_version_t *); uint32_t smb_config_get_execinfo(char *, char *, size_t); - +extern void smb_config_get_negtok(uchar_t *, uint32_t *); extern void smb_load_kconfig(smb_kmod_cfg_t *kcfg); extern uint32_t smb_crc_gen(uint8_t *, size_t); @@ -215,6 +217,7 @@ extern void smb_update_netlogon_seqnum(void); #define SMB_PASSWD_MAXLEN 127 #define SMB_USERNAME_MAXLEN 40 +/* See also: smb_joininfo_xdr() */ typedef struct smb_joininfo { char domain_name[MAXHOSTNAMELEN]; char domain_username[SMB_USERNAME_MAXLEN + 1]; @@ -222,10 +225,19 @@ typedef struct smb_joininfo { uint32_t mode; } smb_joininfo_t; +/* See also: smb_joinres_xdr() */ +typedef struct smb_joinres { + uint32_t status; + int join_err; + char dc_name[MAXHOSTNAMELEN]; +} smb_joinres_t; + /* APIs to communicate with SMB daemon via door calls */ -uint32_t smb_join(smb_joininfo_t *info); +int smb_join(smb_joininfo_t *, smb_joinres_t *info); bool_t smb_joininfo_xdr(XDR *, smb_joininfo_t *); +bool_t smb_joinres_xdr(XDR *, smb_joinres_t *); boolean_t smb_find_ads_server(char *, char *, int); +void smb_notify_dc_changed(void); extern void smb_config_getdomaininfo(char *, char *, char *, char *, char *); extern void smb_config_setdomaininfo(char *, char *, char *, char *, char *); @@ -303,7 +315,7 @@ void libsmb_redirect_syslog(__FILE_TAG *fp, int priority); #define SMBAUTH_HASH_SZ 16 /* also LM/NTLM/NTLMv2 Hash size */ #define SMBAUTH_LM_RESP_SZ 24 /* also NTLM Response size */ #define SMBAUTH_LM_PWD_SZ 14 /* LM password size */ -#define SMBAUTH_V2_CLNT_CHALLENGE_SZ 8 /* both LMv2 and NTLMv2 */ +#define SMBAUTH_CHAL_SZ 8 /* both LMv2 and NTLMv2 */ #define SMBAUTH_SESSION_KEY_SZ SMBAUTH_HASH_SZ #define SMBAUTH_HEXHASH_SZ (SMBAUTH_HASH_SZ * 2) @@ -364,7 +376,7 @@ typedef struct smb_auth_data_blob { unsigned char ndb_signature[4]; unsigned char ndb_reserved[4]; uint64_t ndb_timestamp; - unsigned char ndb_clnt_challenge[SMBAUTH_V2_CLNT_CHALLENGE_SZ]; + unsigned char ndb_clnt_challenge[SMBAUTH_CHAL_SZ]; unsigned char ndb_unknown[4]; smb_auth_name_entry_t ndb_names[2]; unsigned char ndb_unknown2[4]; @@ -488,6 +500,9 @@ extern int smb_auth_RC4(unsigned char *, int, unsigned char *, int, extern int smb_auth_md4(unsigned char *, unsigned char *, int); extern int smb_auth_lm_hash(const char *, unsigned char *); extern int smb_auth_ntlm_hash(const char *, unsigned char *); +extern void smb_auth_ntlm2_mkchallenge(char *, const char *, const char *); +extern void smb_auth_ntlm2_kxkey(unsigned char *, const char *, const char *, + unsigned char *); extern int smb_auth_set_info(char *, char *, unsigned char *, char *, unsigned char *, @@ -496,12 +511,8 @@ extern int smb_auth_set_info(char *, char *, extern int smb_auth_ntlmv2_hash(unsigned char *, char *, char *, unsigned char *); -extern int smb_auth_gen_session_key(smb_auth_info_t *, unsigned char *); - -boolean_t smb_auth_validate_lm(unsigned char *, uint32_t, smb_passwd_t *, - unsigned char *, int, char *, char *); -boolean_t smb_auth_validate_nt(unsigned char *, uint32_t, smb_passwd_t *, - unsigned char *, int, char *, char *, uchar_t *); +boolean_t smb_auth_validate(smb_passwd_t *, char *, char *, + uchar_t *, uint_t, uchar_t *, uint_t, uchar_t *, uint_t, uchar_t *); int smb_gen_random_passwd(char *passwd, size_t bufsz); @@ -516,14 +527,6 @@ extern void smb_ipc_rollback(void); extern void smb_ipc_set(char *, uint8_t *); /* - * SMB MAC Signing - */ - -#define SMB_MAC_KEY_SZ (SMBAUTH_SESSION_KEY_SZ + SMBAUTH_CS_MAXLEN) -#define SMB_SIG_OFFS 14 /* signature field offset within header */ -#define SMB_SIG_SIZE 8 /* SMB signature size */ - -/* * Signing flags: * * SMB_SCF_ENABLE Signing is enabled. @@ -547,38 +550,6 @@ extern void smb_ipc_set(char *, uint8_t *); #define SMB_SCF_KEY_ISSET_THIS_LOGON 0x08 /* - * smb_sign_ctx - * - * SMB signing context. - * - * ssc_seqnum sequence number - * ssc_keylen mac key length - * ssc_mid multiplex id - reserved - * ssc_flags flags - * ssc_mackey mac key - * ssc_sign mac signature - * - */ -typedef struct smb_sign_ctx { - unsigned int ssc_seqnum; - unsigned short ssc_keylen; - unsigned short ssc_mid; - unsigned int ssc_flags; - unsigned char ssc_mackey[SMB_MAC_KEY_SZ]; - unsigned char ssc_sign[SMB_SIG_SIZE]; -} smb_sign_ctx_t; - -extern int smb_mac_init(smb_sign_ctx_t *sign_ctx, smb_auth_info_t *auth); -extern int smb_mac_calc(smb_sign_ctx_t *sign_ctx, - const unsigned char *buf, size_t buf_len, unsigned char *mac_sign); -extern int smb_mac_chk(smb_sign_ctx_t *sign_ctx, - const unsigned char *buf, size_t buf_len); -extern int smb_mac_sign(smb_sign_ctx_t *sign_ctx, - unsigned char *buf, size_t buf_len); -extern void smb_mac_inc_seqnum(smb_sign_ctx_t *sign_ctx); -extern void smb_mac_dec_seqnum(smb_sign_ctx_t *sign_ctx); - -/* * Each domain is categorized using the enum values below. * The local domain refers to the local machine and is named * after the local hostname. The primary domain is the domain @@ -645,6 +616,11 @@ typedef struct smb_trusted_domains { #define SMB_DOMAIN_NO_MEMORY 6 #define SMB_DOMAIN_NO_CACHE 7 +typedef struct smb_dcinfo { + char dc_name[MAXHOSTNAMELEN]; + smb_inaddr_t dc_addr; +} smb_dcinfo_t; + /* * This structure could contain information about * the primary domain the name of selected domain controller @@ -654,7 +630,7 @@ typedef struct smb_trusted_domains { * which only contains information about a single domain. */ typedef struct smb_domainex { - char d_dc[MAXHOSTNAMELEN]; + smb_dcinfo_t d_dci; smb_domain_t d_primary; smb_trusted_domains_t d_trusted; } smb_domainex_t; @@ -675,7 +651,7 @@ void smb_domain_set_dns_info(char *, char *, char *, char *, char *, smb_domain_t *); void smb_domain_set_trust_info(char *, char *, char *, uint32_t, uint32_t, uint32_t, smb_domain_t *); -void smb_domain_current_dc(char *buf, size_t len); +void smb_domain_current_dc(smb_dcinfo_t *); typedef struct smb_gsid { smb_sid_t *gs_sid; diff --git a/usr/src/lib/smbsrv/libsmb/common/mapfile-vers b/usr/src/lib/smbsrv/libsmb/common/mapfile-vers index 3570d05ba4..5c88aa1d76 100644 --- a/usr/src/lib/smbsrv/libsmb/common/mapfile-vers +++ b/usr/src/lib/smbsrv/libsmb/common/mapfile-vers @@ -75,13 +75,13 @@ SYMBOL_VERSION SUNWprivate { smb_acl_to_zfs; smb_auth_DES; smb_auth_RC4; - smb_auth_gen_session_key; smb_auth_hmac_md5; smb_auth_ntlm_hash; + smb_auth_ntlm2_kxkey; + smb_auth_ntlm2_mkchallenge; smb_auth_ntlmv2_hash; smb_auth_qnd_unicode; - smb_auth_validate_lm; - smb_auth_validate_nt; + smb_auth_validate; smb_buf32_xdr; smb_cache_add; smb_cache_create; @@ -196,6 +196,7 @@ SYMBOL_VERSION SUNWprivate { smb_gmttoken_response_xdr; smb_gmttoken_snapname_xdr; smb_joininfo_xdr; + smb_joinres_xdr; smb_idmap_batch_create; smb_idmap_batch_destroy; smb_idmap_batch_getid; @@ -265,11 +266,6 @@ SYMBOL_VERSION SUNWprivate { smb_logon_xdr; smb_lookup_name; smb_lookup_sid; - smb_mac_chk; - smb_mac_dec_seqnum; - smb_mac_inc_seqnum; - smb_mac_init; - smb_mac_sign; smb_match_netlogon_seqnum; smb_mbstos; smb_mbstowcs; @@ -309,6 +305,7 @@ SYMBOL_VERSION SUNWprivate { smb_nic_getnext; smb_nic_getnum; smb_nic_init; + smb_notify_dc_changed; smb_priv_getbyname; smb_priv_getbyvalue; smb_priv_presentable_ids; diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_auth.c b/usr/src/lib/smbsrv/libsmb/common/smb_auth.c index 9f4100e805..29918639e7 100644 --- a/usr/src/lib/smbsrv/libsmb/common/smb_auth.c +++ b/usr/src/lib/smbsrv/libsmb/common/smb_auth.c @@ -21,15 +21,70 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #include <strings.h> #include <stdlib.h> +#include <syslog.h> +#include <sys/md5.h> #include <smbsrv/string.h> #include <smbsrv/libsmb.h> +#include <netsmb/spnego.h> /* libsmbfs */ #include <assert.h> +#define NTLM_CHAL_SZ SMBAUTH_CHAL_SZ /* challenge size */ + +/* + * Compute the combined (server+client) challenge per. [MS-NLMP 3.3.1] + * MD5(concat(ServerChallenge,ClientChallenge)) + */ +void +smb_auth_ntlm2_mkchallenge(char *result, + const char *srv_chal, const char *clnt_chal) +{ + MD5_CTX context; + uchar_t challenges[2 * NTLM_CHAL_SZ]; + uchar_t digest[SMBAUTH_HASH_SZ]; + + /* + * challenges = ConcatenationOf(ServerChallenge, ClientChallenge) + */ + (void) memcpy(challenges, srv_chal, NTLM_CHAL_SZ); + (void) memcpy(challenges + NTLM_CHAL_SZ, clnt_chal, NTLM_CHAL_SZ); + + /* + * digest = MD5(challenges) + */ + MD5Init(&context); + MD5Update(&context, challenges, sizeof (challenges)); + MD5Final(digest, &context); + + /* + * result = digest[0..7] + */ + (void) memcpy(result, digest, NTLM_CHAL_SZ); +} + +void +smb_auth_ntlm2_kxkey(unsigned char *result, const char *srv_chal, + const char *clnt_chal, unsigned char *ssn_base_key) +{ + uchar_t challenges[2 * NTLM_CHAL_SZ]; + + /* + * challenges = ConcatenationOf(ServerChallenge, ClientChallenge) + */ + (void) memcpy(challenges, srv_chal, NTLM_CHAL_SZ); + (void) memcpy(challenges + NTLM_CHAL_SZ, clnt_chal, NTLM_CHAL_SZ); + + /* HMAC_MD5(SessionBaseKey, concat(...)) */ + /* SMBAUTH_HMACT64 args: D, Dsz, K, Ksz, digest */ + (void) SMBAUTH_HMACT64(challenges, sizeof (challenges), + ssn_base_key, SMBAUTH_HASH_SZ, result); +} + /* * smb_auth_qnd_unicode * @@ -117,7 +172,7 @@ smb_auth_lm_hash(const char *password, unsigned char *lm_hash) */ static int smb_auth_lm_response(unsigned char *hash, - unsigned char *challenge, int clen, + unsigned char *challenge, /* NTLM_CHAL_SZ */ unsigned char *lm_rsp) { unsigned char S21[21]; @@ -131,7 +186,7 @@ smb_auth_lm_response(unsigned char *hash, /* padded LM Hash -> LM Response */ return (smb_auth_DES(lm_rsp, SMBAUTH_LM_RESP_SZ, S21, 21, - challenge, clen)); + challenge, NTLM_CHAL_SZ)); } /* @@ -174,7 +229,7 @@ smb_auth_ntlm_hash(const char *password, unsigned char *hash) */ static int smb_auth_ntlm_response(unsigned char *hash, - unsigned char *challenge, int clen, + unsigned char *challenge, /* NTLM_CHAL_SZ */ unsigned char *ntlm_rsp) { unsigned char S21[21]; @@ -182,7 +237,7 @@ smb_auth_ntlm_response(unsigned char *hash, bcopy(hash, S21, SMBAUTH_HASH_SZ); bzero(&S21[SMBAUTH_HASH_SZ], 5); if (smb_auth_DES((unsigned char *)ntlm_rsp, SMBAUTH_LM_RESP_SZ, - S21, 21, challenge, clen) == SMBAUTH_FAILURE) + S21, 21, challenge, NTLM_CHAL_SZ) == SMBAUTH_FAILURE) return (0); return (SMBAUTH_LM_RESP_SZ); } @@ -253,13 +308,14 @@ smb_auth_ntlmv2_hash(unsigned char *ntlm_hash, static int smb_auth_v2_response( unsigned char *hash, - unsigned char *srv_challenge, int slen, + unsigned char *srv_challenge, /* NTLM_CHAL_SZ */ unsigned char *clnt_data, int clen, unsigned char *v2_rsp) { unsigned char *hmac_data; + int slen = NTLM_CHAL_SZ; - hmac_data = (unsigned char *)malloc((slen + clen) * sizeof (char)); + hmac_data = malloc(NTLM_CHAL_SZ + clen); if (!hmac_data) { return (-1); } @@ -275,70 +331,39 @@ smb_auth_v2_response( return (SMBAUTH_HASH_SZ + clen); } -/* - * smb_auth_gen_session_key - * - * Generate the NTLM user session key if LMCompatibilityLevel is 2 or - * NTLMv2 user session key if LMCompatibilityLevel is 3 or above. - * - * NTLM_Session_Key = MD4(NTLM_Hash); - * - * NTLMv2_Session_Key = HMAC_MD5(NTLMv2Hash, 16, NTLMv2_HMAC, 16) - * - * Prior to calling this function, the auth instance should be set - * via smb_auth_set_info(). - * - * Returns the appropriate session key. - */ -int -smb_auth_gen_session_key(smb_auth_info_t *auth, unsigned char *session_key) -{ - int rc; - - if (auth->lmcompatibility_lvl == 2) - rc = smb_auth_md4(session_key, auth->hash, SMBAUTH_HASH_SZ); - else - rc = SMBAUTH_HMACT64((unsigned char *)auth->cs, - SMBAUTH_HASH_SZ, (unsigned char *)auth->hash_v2, - SMBAUTH_SESSION_KEY_SZ, session_key); - - return (rc); -} static boolean_t smb_lm_password_ok( unsigned char *challenge, - uint32_t clen, unsigned char *lm_hash, - unsigned char *passwd) + unsigned char *lm_resp) { - unsigned char lm_resp[SMBAUTH_LM_RESP_SZ]; + unsigned char ok_resp[SMBAUTH_LM_RESP_SZ]; int rc; - rc = smb_auth_lm_response(lm_hash, challenge, clen, lm_resp); + rc = smb_auth_lm_response(lm_hash, challenge, ok_resp); if (rc != SMBAUTH_SUCCESS) return (B_FALSE); - return (bcmp(lm_resp, passwd, SMBAUTH_LM_RESP_SZ) == 0); + return (bcmp(ok_resp, lm_resp, SMBAUTH_LM_RESP_SZ) == 0); } static boolean_t smb_ntlm_password_ok( unsigned char *challenge, - uint32_t clen, unsigned char *ntlm_hash, - unsigned char *passwd, + unsigned char *nt_resp, unsigned char *session_key) { - unsigned char ntlm_resp[SMBAUTH_LM_RESP_SZ]; + unsigned char ok_resp[SMBAUTH_LM_RESP_SZ]; int rc; boolean_t ok; - rc = smb_auth_ntlm_response(ntlm_hash, challenge, clen, ntlm_resp); + rc = smb_auth_ntlm_response(ntlm_hash, challenge, ok_resp); if (rc != SMBAUTH_LM_RESP_SZ) return (B_FALSE); - ok = (bcmp(ntlm_resp, passwd, SMBAUTH_LM_RESP_SZ) == 0); + ok = (bcmp(ok_resp, nt_resp, SMBAUTH_LM_RESP_SZ) == 0); if (ok && (session_key)) { rc = smb_auth_md4(session_key, ntlm_hash, SMBAUTH_HASH_SZ); if (rc != SMBAUTH_SUCCESS) @@ -350,7 +375,6 @@ smb_ntlm_password_ok( static boolean_t smb_ntlmv2_password_ok( unsigned char *challenge, - uint32_t clen, unsigned char *ntlm_hash, unsigned char *passwd, int pwdlen, @@ -404,7 +428,7 @@ smb_ntlmv2_password_ok( break; if (smb_auth_v2_response(ntlmv2_hash, challenge, - clen, clnt_blob, clnt_blob_len, ntlmv2_resp) < 0) + clnt_blob, clnt_blob_len, ntlmv2_resp) < 0) break; ok = (bcmp(passwd, ntlmv2_resp, pwdlen) == 0); @@ -426,8 +450,7 @@ smb_ntlmv2_password_ok( static boolean_t smb_lmv2_password_ok( - unsigned char *challenge, - uint32_t clen, + unsigned char *srv_challenge, unsigned char *ntlm_hash, unsigned char *passwd, char *domain, @@ -469,8 +492,8 @@ smb_lmv2_password_ok( ntlmv2_hash) != SMBAUTH_SUCCESS) break; - if (smb_auth_v2_response(ntlmv2_hash, challenge, - clen, clnt_challenge, SMBAUTH_V2_CLNT_CHALLENGE_SZ, + if (smb_auth_v2_response(ntlmv2_hash, srv_challenge, + clnt_challenge, SMBAUTH_CHAL_SZ, lmv2_resp) < 0) break; @@ -484,81 +507,83 @@ smb_lmv2_password_ok( } /* - * smb_auth_validate_lm + * smb_auth_validate * - * Validates given LM/LMv2 client response, passed in passwd arg, against - * stored user's password, passed in smbpw - * - * If LM level <=3 server accepts LM responses, otherwise LMv2 + * Validates given NTLMv2 (or NTLM, LMv2, LM) client responses against + * the stored user's password, passed in smbpw. Try those in the order + * strongest to weakest, stopping at a point determined by the configured + * lmauth_level (LM Compatibility Level). */ boolean_t -smb_auth_validate_lm( - unsigned char *challenge, - uint32_t clen, +smb_auth_validate( smb_passwd_t *smbpw, - unsigned char *passwd, - int pwdlen, char *domain, - char *username) + char *username, + unsigned char *challenge, + uint_t clen, + unsigned char *nt_resp, + uint_t nt_len, + unsigned char *lm_resp, + uint_t lm_len, + uchar_t *session_key) { - boolean_t ok = B_FALSE; int64_t lmlevel; - - if (pwdlen != SMBAUTH_LM_RESP_SZ) - return (B_FALSE); + boolean_t ok = B_FALSE; if (smb_config_getnum(SMB_CI_LM_LEVEL, &lmlevel) != SMBD_SMF_OK) return (B_FALSE); - if (lmlevel <= 3) { - ok = smb_lm_password_ok(challenge, clen, smbpw->pw_lmhash, - passwd); - } - - if (!ok) - ok = smb_lmv2_password_ok(challenge, clen, smbpw->pw_nthash, - passwd, domain, username); + if (lmlevel > 5) + return (B_FALSE); - return (ok); -} + if (clen != NTLM_CHAL_SZ) + return (B_FALSE); -/* - * smb_auth_validate_nt - * - * Validates given NTLM/NTLMv2 client response, passed in passwd arg, against - * stored user's password, passed in smbpw - * - * If LM level <=4 server accepts NTLM/NTLMv2 responses, otherwise only NTLMv2 - */ -boolean_t -smb_auth_validate_nt( - unsigned char *challenge, - uint32_t clen, - smb_passwd_t *smbpw, - unsigned char *passwd, - int pwdlen, - char *domain, - char *username, - uchar_t *session_key) -{ - int64_t lmlevel; - boolean_t ok; + /* + * Accept NTLMv2 at any LM level (0-5). + */ + if (nt_len > SMBAUTH_LM_RESP_SZ) { + ok = smb_ntlmv2_password_ok(challenge, + smbpw->pw_nthash, nt_resp, nt_len, + domain, username, session_key); + if (ok) + return (ok); + } - if (smb_config_getnum(SMB_CI_LM_LEVEL, &lmlevel) != SMBD_SMF_OK) + if (lmlevel == 5) return (B_FALSE); - if ((lmlevel == 5) && (pwdlen <= SMBAUTH_LM_RESP_SZ)) + /* + * Accept NTLM at levels 0-4 + */ + if (nt_len == SMBAUTH_LM_RESP_SZ) { + ok = smb_ntlm_password_ok(challenge, smbpw->pw_nthash, + nt_resp, session_key); + if (ok) + return (ok); + } + + if (lmlevel == 4) return (B_FALSE); - if (pwdlen > SMBAUTH_LM_RESP_SZ) - ok = smb_ntlmv2_password_ok(challenge, clen, - smbpw->pw_nthash, passwd, pwdlen, - domain, username, session_key); - else - ok = smb_ntlm_password_ok(challenge, clen, - smbpw->pw_nthash, passwd, session_key); - return (ok); + /* + * Accept LM/LMv2 auth at levels 0-3 + */ + if (lm_len != SMBAUTH_LM_RESP_SZ) + return (B_FALSE); + if (session_key) + (void) smb_auth_md4(session_key, smbpw->pw_nthash, + SMBAUTH_HASH_SZ); + ok = smb_lmv2_password_ok(challenge, smbpw->pw_nthash, + lm_resp, domain, username); + if (ok) + return (ok); + ok = smb_lm_password_ok(challenge, smbpw->pw_lmhash, lm_resp); + if (ok) + return (ok); + + return (B_FALSE); } /* diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_cfg.c b/usr/src/lib/smbsrv/libsmb/common/smb_cfg.c index 08ab3376be..83a7cae7bc 100644 --- a/usr/src/lib/smbsrv/libsmb/common/smb_cfg.c +++ b/usr/src/lib/smbsrv/libsmb/common/smb_cfg.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ /* @@ -58,7 +58,9 @@ typedef struct smb_cfg_param { /* idmap SMF fmri and Property Group */ #define IDMAP_FMRI_PREFIX "system/idmap" #define MACHINE_SID "machine_sid" +#define MACHINE_UUID "machine_uuid" #define IDMAP_DOMAIN "domain_name" +#define IDMAP_PREF_DC "preferred_dc" #define IDMAP_PG_NAME "config" #define SMB_SECMODE_WORKGRP_STR "workgroup" @@ -121,14 +123,12 @@ static smb_cfg_param_t smb_cfg_table[] = {SMB_CI_MACHINE_PASSWD, "machine_passwd", SCF_TYPE_ASTRING, SMB_CF_PROTECTED}, - {SMB_CI_KPASSWD_SRV, "kpasswd_server", SCF_TYPE_ASTRING, - 0}, - {SMB_CI_KPASSWD_DOMAIN, "kpasswd_domain", SCF_TYPE_ASTRING, - 0}, - {SMB_CI_KPASSWD_SEQNUM, "kpasswd_seqnum", SCF_TYPE_INTEGER, - 0}, - {SMB_CI_NETLOGON_SEQNUM, "netlogon_seqnum", SCF_TYPE_INTEGER, - 0}, + + {SMB_CI_MACHINE_UUID, "machine_uuid", SCF_TYPE_ASTRING, 0}, + {SMB_CI_KPASSWD_SRV, "kpasswd_server", SCF_TYPE_ASTRING, 0}, + {SMB_CI_KPASSWD_DOMAIN, "kpasswd_domain", SCF_TYPE_ASTRING, 0}, + {SMB_CI_KPASSWD_SEQNUM, "kpasswd_seqnum", SCF_TYPE_INTEGER, 0}, + {SMB_CI_NETLOGON_SEQNUM, "netlogon_seqnum", SCF_TYPE_INTEGER, 0}, {SMB_CI_IPV6_ENABLE, "ipv6_enable", SCF_TYPE_BOOLEAN, 0}, {SMB_CI_PRINT_ENABLE, "print_enable", SCF_TYPE_BOOLEAN, 0}, {SMB_CI_MAP, "map", SCF_TYPE_ASTRING, SMB_CF_EXEC}, @@ -136,6 +136,7 @@ static smb_cfg_param_t smb_cfg_table[] = {SMB_CI_DISPOSITION, "disposition", SCF_TYPE_ASTRING, SMB_CF_EXEC}, {SMB_CI_DFS_STDROOT_NUM, "dfs_stdroot_num", SCF_TYPE_INTEGER, 0}, {SMB_CI_TRAVERSE_MOUNTS, "traverse_mounts", SCF_TYPE_BOOLEAN, 0}, + /* SMB_CI_MAX */ }; @@ -144,6 +145,8 @@ static smb_cfg_param_t *smb_config_getent(smb_cfg_id_t); static boolean_t smb_is_base64(unsigned char c); static char *smb_base64_encode(char *str_to_encode); static char *smb_base64_decode(char *encoded_str); +static int smb_config_get_idmap_preferred_dc(char *, int); +static int smb_config_set_idmap_preferred_dc(char *); char * smb_config_getname(smb_cfg_id_t id) @@ -365,6 +368,9 @@ smb_config_getstr(smb_cfg_id_t id, char *cbuf, int bufsz) cfg = smb_config_getent(id); assert(cfg->sc_type == SCF_TYPE_ASTRING); + if (id == SMB_CI_DOMAIN_SRV) + return (smb_config_get_idmap_preferred_dc(cbuf, bufsz)); + handle = smb_smf_scf_init(SMBD_FMRI_PREFIX); if (handle == NULL) return (SMBD_SMF_SYSTEM_ERR); @@ -568,6 +574,9 @@ smb_config_setstr(smb_cfg_id_t id, char *value) cfg = smb_config_getent(id); assert(cfg->sc_type == SCF_TYPE_ASTRING); + if (id == SMB_CI_DOMAIN_SRV) + return (smb_config_set_idmap_preferred_dc(value)); + protected = B_FALSE; switch (cfg->sc_flags) { @@ -792,7 +801,7 @@ smb_config_get_ads_enable(void) rc = smb_smf_get_boolean_property(handle, "use_ads", &vbool); smb_smf_scf_fini(handle); - return ((rc == SMBD_SMF_OK) ? (vbool == 1) : B_FALSE); + return ((rc == SMBD_SMF_OK) ? (vbool == 1) : B_TRUE); } /* @@ -800,7 +809,7 @@ smb_config_get_ads_enable(void) * * Returns value of the "config/machine_sid" parameter * from the IDMAP SMF configuration repository. - * + * Result is allocated; caller should free. */ char * smb_config_get_localsid(void) @@ -810,6 +819,56 @@ smb_config_get_localsid(void) } /* + * smb_config_get_localuuid + * + * Returns value of the "config/machine_uuid" parameter + * from the IDMAP SMF configuration repository. + * + */ +int +smb_config_get_localuuid(uuid_t uu) +{ + char *s; + + uuid_clear(uu); + s = smb_config_getenv_generic(MACHINE_UUID, IDMAP_FMRI_PREFIX, + IDMAP_PG_NAME); + if (s == NULL) + return (-1); + + if (uuid_parse(s, uu) < 0) { + free(s); + return (-1); + } + + return (0); +} + +static int +smb_config_get_idmap_preferred_dc(char *cbuf, int bufsz) +{ + char *s; + int len, rc = -1; + + s = smb_config_getenv_generic(IDMAP_PREF_DC, + IDMAP_FMRI_PREFIX, IDMAP_PG_NAME); + if (s != NULL) { + len = strlcpy(cbuf, s, bufsz); + if (len < bufsz) + rc = 0; + free(s); + } + return (rc); +} + +static int +smb_config_set_idmap_preferred_dc(char *value) +{ + return (smb_config_setenv_generic(IDMAP_FMRI_PREFIX, IDMAP_PG_NAME, + IDMAP_PREF_DC, value)); +} + +/* * smb_config_set_idmap_domain * * Set the "config/domain_name" parameter from IDMAP SMF repository. diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_domain.c b/usr/src/lib/smbsrv/libsmb/common/smb_domain.c index e3974dba8d..3d673d2d4c 100644 --- a/usr/src/lib/smbsrv/libsmb/common/smb_domain.c +++ b/usr/src/lib/smbsrv/libsmb/common/smb_domain.c @@ -22,7 +22,7 @@ * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * - * Copyright 2011 Nexenta Systems, Inc. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ /* @@ -74,7 +74,7 @@ typedef struct smb_domain_cache { cond_t dc_cv; uint32_t dc_state; uint32_t dc_nops; - char dc_server[MAXHOSTNAMELEN]; + smb_dcinfo_t dc_dci; } smb_domain_cache_t; static smb_domain_cache_t smb_dcache; @@ -90,8 +90,8 @@ static uint32_t smb_dcache_lock(int); static void smb_dcache_unlock(void); static void smb_dcache_remove(smb_domain_t *); static uint32_t smb_dcache_add(smb_domain_t *); -static void smb_dcache_getdc(char *, size_t); -static void smb_dcache_setdc(char *); +static boolean_t smb_dcache_getdc(smb_dcinfo_t *); +static void smb_dcache_setdc(const smb_dcinfo_t *); static boolean_t smb_dcache_wait(void); static uint32_t smb_dcache_updating(void); static void smb_dcache_ready(void); @@ -293,13 +293,14 @@ smb_domain_lookup_type(smb_domain_type_t type, smb_domain_t *di) boolean_t smb_domain_getinfo(smb_domainex_t *dxi) { - boolean_t success; + boolean_t rv; - success = smb_domain_lookup_type(SMB_DOMAIN_PRIMARY, &dxi->d_primary); - if (success) - smb_dcache_getdc(dxi->d_dc, sizeof (dxi->d_dc)); + /* Note: this waits for the dcache lock. */ + rv = smb_domain_lookup_type(SMB_DOMAIN_PRIMARY, &dxi->d_primary); + if (rv) + rv = smb_dcache_getdc(&dxi->d_dci); - return (success); + return (rv); } /* @@ -307,9 +308,9 @@ smb_domain_getinfo(smb_domainex_t *dxi) * Does NOT block. */ void -smb_domain_current_dc(char *buf, size_t len) +smb_domain_current_dc(smb_dcinfo_t *dci) { - smb_dcache_getdc(buf, len); + (void) smb_dcache_getdc(dci); } /* @@ -364,7 +365,7 @@ smb_domain_update(smb_domainex_t *dxi) for (i = 0; i < dxi->d_trusted.td_num; i++) (void) smb_dcache_add(&dxi->d_trusted.td_domains[i]); - smb_dcache_setdc(dxi->d_dc); + smb_dcache_setdc(&dxi->d_dci); } smb_dcache_unlock(); @@ -594,7 +595,7 @@ smb_dcache_create(void) offsetof(smb_domain_t, di_lnd)); smb_dcache.dc_nops = 0; - *smb_dcache.dc_server = '\0'; + bzero(&smb_dcache.dc_dci, sizeof (smb_dcache.dc_dci)); smb_dcache.dc_state = SMB_DCACHE_STATE_READY; (void) mutex_unlock(&smb_dcache.dc_mtx); } @@ -727,19 +728,23 @@ smb_dcache_remove(smb_domain_t *di) } static void -smb_dcache_setdc(char *dc) +smb_dcache_setdc(const smb_dcinfo_t *dci) { (void) mutex_lock(&smb_dcache.dc_mtx); - (void) strlcpy(smb_dcache.dc_server, dc, sizeof (smb_dcache.dc_server)); + smb_dcache.dc_dci = *dci; /* struct assignment! */ (void) mutex_unlock(&smb_dcache.dc_mtx); } -static void -smb_dcache_getdc(char *buf, size_t buflen) +/* + * Return B_TRUE if we have DC information. + */ +static boolean_t +smb_dcache_getdc(smb_dcinfo_t *dci) { (void) mutex_lock(&smb_dcache.dc_mtx); - (void) strlcpy(buf, smb_dcache.dc_server, buflen); + *dci = smb_dcache.dc_dci; /* struct assignment! */ (void) mutex_unlock(&smb_dcache.dc_mtx); + return (dci->dc_name[0] != '\0'); } /* diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_doorclnt.c b/usr/src/lib/smbsrv/libsmb/common/smb_doorclnt.c index 97b4bef60e..e51f8bbca1 100644 --- a/usr/src/lib/smbsrv/libsmb/common/smb_doorclnt.c +++ b/usr/src/lib/smbsrv/libsmb/common/smb_doorclnt.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ #include <assert.h> @@ -112,27 +112,24 @@ smb_lookup_name(const char *name, sid_type_t sidtype, lsa_account_t *acct) return (rc); } -uint32_t -smb_join(smb_joininfo_t *jdi) +int +smb_join(smb_joininfo_t *jdi, smb_joinres_t *jres) { - uint32_t status; int rc; - if (jdi == NULL) - return (NT_STATUS_INVALID_PARAMETER); - rc = smb_door_call(SMB_DR_JOIN, jdi, smb_joininfo_xdr, - &status, xdr_uint32_t); + jres, smb_joinres_xdr); if (rc != 0) { /* * This usually means the SMB service is not running. */ syslog(LOG_DEBUG, "smb_join: %m"); - status = NT_STATUS_SERVER_DISABLED; + jres->status = NT_STATUS_SERVER_DISABLED; + return (rc); } - return (status); + return (0); } /* @@ -198,6 +195,23 @@ smb_joininfo_xdr(XDR *xdrs, smb_joininfo_t *objp) return (TRUE); } +bool_t +smb_joinres_xdr(XDR *xdrs, smb_joinres_t *objp) +{ + + if (!xdr_uint32_t(xdrs, &objp->status)) + return (FALSE); + + if (!xdr_int(xdrs, &objp->join_err)) + return (FALSE); + + if (!xdr_vector(xdrs, (char *)objp->dc_name, MAXHOSTNAMELEN, + sizeof (char), (xdrproc_t)xdr_char)) + return (FALSE); + + return (TRUE); +} + /* * Parameters: * fqdn (input) - fully-qualified domain name @@ -249,6 +263,19 @@ smb_find_ads_server(char *fqdn, char *buf, int buflen) return (found); } +void +smb_notify_dc_changed(void) +{ + int rc; + + rc = smb_door_call(SMB_DR_NOTIFY_DC_CHANGED, + NULL, NULL, NULL, NULL); + + if (rc != 0) + syslog(LOG_DEBUG, "smb_notify_dc_changed: %m"); +} + + /* * After a successful door call the local door_arg->data_ptr is assigned * to the caller's arg->rbuf so that arg has references to both input and diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_info.c b/usr/src/lib/smbsrv/libsmb/common/smb_info.c index ea6707951e..0dcae43179 100644 --- a/usr/src/lib/smbsrv/libsmb/common/smb_info.c +++ b/usr/src/lib/smbsrv/libsmb/common/smb_info.c @@ -20,11 +20,14 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ -#include <assert.h> #include <sys/types.h> +#include <sys/sockio.h> +#include <sys/socket.h> +#include <sys/utsname.h> + #include <stdarg.h> #include <unistd.h> #include <stdlib.h> @@ -39,11 +42,11 @@ #include <netinet/in.h> #include <arpa/nameser.h> #include <resolv.h> -#include <sys/sockio.h> -#include <sys/socket.h> + #include <smbsrv/smbinfo.h> #include <smbsrv/netbios.h> #include <smbsrv/libsmb.h> +#include <assert.h> static mutex_t seqnum_mtx; @@ -67,6 +70,7 @@ static rwlock_t smb_ipc_lock; void smb_load_kconfig(smb_kmod_cfg_t *kcfg) { + struct utsname uts; int64_t citem; bzero(kcfg, sizeof (smb_kmod_cfg_t)); @@ -106,6 +110,18 @@ smb_load_kconfig(smb_kmod_cfg_t *kcfg) 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)); } /* diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_kmod.c b/usr/src/lib/smbsrv/libsmb/common/smb_kmod.c index 145d8da21c..8b7b32fb19 100644 --- a/usr/src/lib/smbsrv/libsmb/common/smb_kmod.c +++ b/usr/src/lib/smbsrv/libsmb/common/smb_kmod.c @@ -69,6 +69,7 @@ smb_kmod_isbound(void) return ((smbdrv_fd == -1) ? B_FALSE : B_TRUE); } +/* See also: smbsrv smb_server_store_cfg */ int smb_kmod_setcfg(smb_kmod_cfg_t *cfg) { @@ -83,13 +84,21 @@ smb_kmod_setcfg(smb_kmod_cfg_t *cfg) ioc.oplock_enable = cfg->skc_oplock_enable; ioc.sync_enable = cfg->skc_sync_enable; ioc.secmode = cfg->skc_secmode; - ioc.ipv6_enable = cfg->skc_ipv6_enable; ioc.netbios_enable = cfg->skc_netbios_enable; + ioc.ipv6_enable = cfg->skc_ipv6_enable; ioc.print_enable = cfg->skc_print_enable; ioc.traverse_mounts = cfg->skc_traverse_mounts; ioc.exec_flags = cfg->skc_execflags; + ioc.negtok_len = cfg->skc_negtok_len; ioc.version = cfg->skc_version; + (void) memcpy(ioc.machine_uuid, cfg->skc_machine_uuid, sizeof (uuid_t)); + (void) memcpy(ioc.negtok, cfg->skc_negtok, sizeof (ioc.negtok)); + (void) memcpy(ioc.native_os, cfg->skc_native_os, + sizeof (ioc.native_os)); + (void) memcpy(ioc.native_lm, cfg->skc_native_lm, + sizeof (ioc.native_lm)); + (void) strlcpy(ioc.nbdomain, cfg->skc_nbdomain, sizeof (ioc.nbdomain)); (void) strlcpy(ioc.fqdn, cfg->skc_fqdn, sizeof (ioc.fqdn)); (void) strlcpy(ioc.hostname, cfg->skc_hostname, sizeof (ioc.hostname)); diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_mac.c b/usr/src/lib/smbsrv/libsmb/common/smb_mac.c deleted file mode 100644 index 57fb74530c..0000000000 --- a/usr/src/lib/smbsrv/libsmb/common/smb_mac.c +++ /dev/null @@ -1,207 +0,0 @@ -/* - * 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 2007 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#pragma ident "%Z%%M% %I% %E% SMI" - -/* - * SMB MAC Signing support. - */ - -#include <strings.h> -#include <security/cryptoki.h> -#include <security/pkcs11.h> - -#include <smbsrv/libsmb.h> - -#include <smbsrv/smb.h> - -/* - * smb_mac_init - * - * Calculates the MAC key using the specified user session - * key (NTLM or NTLMv2). - * - * Returns SMBAUTH_SUCCESS if key generation was successful, - * SMBAUTH_FAILURE if not. - */ -int -smb_mac_init(smb_sign_ctx_t *sign_ctx, smb_auth_info_t *auth) -{ - unsigned char S16[SMBAUTH_SESSION_KEY_SZ]; - - if (smb_auth_gen_session_key(auth, S16) != SMBAUTH_SUCCESS) - return (SMBAUTH_FAILURE); - bcopy(S16, sign_ctx->ssc_mackey, SMBAUTH_SESSION_KEY_SZ); - bcopy(auth->cs, &(sign_ctx->ssc_mackey[SMBAUTH_SESSION_KEY_SZ]), - auth->cs_len); - sign_ctx->ssc_keylen = SMBAUTH_SESSION_KEY_SZ + auth->cs_len; - return (SMBAUTH_SUCCESS); -} - -/* - * smb_mac_calc - * - * Calculates MAC signature for the given buffer and returns - * it in the mac_sign parameter. - * - * The MAC signature is calculated as follows: - * - * data = concat(MAC_Key, MAC_Key_Len, SMB_Msg, SMB_Msg_Len); - * hash = MD5(data); - * MAC = head(hash, 8); - * - * The tricky part is that a sequence number should be used - * in calculation instead of the signature field in the - * SMB header. - * - * Returns SMBAUTH_SUCCESS if cryptology framework use was successful, - * SMBAUTH_FAILURE if not. - */ -int -smb_mac_calc(smb_sign_ctx_t *sign_ctx, const unsigned char *buf, - size_t buf_len, unsigned char *mac_sign) -{ - CK_RV rv; - CK_MECHANISM mechanism; - CK_SESSION_HANDLE hSession; - unsigned long diglen = MD_DIGEST_LEN; - int rc = SMBAUTH_FAILURE; - - int offset_end_of_sig = (SMB_SIG_OFFS + SMB_SIG_SIZE); - unsigned char seq_buf[SMB_SIG_SIZE]; - unsigned char mac[16]; - - /* - * put seq_num into the first 4 bytes and - * zero out the next 4 bytes - */ - bcopy(&sign_ctx->ssc_seqnum, seq_buf, 4); - bzero(seq_buf + 4, 4); - - mechanism.mechanism = CKM_MD5; - mechanism.pParameter = 0; - mechanism.ulParameterLen = 0; - - rv = SUNW_C_GetMechSession(mechanism.mechanism, &hSession); - if (rv != CKR_OK) - return (SMBAUTH_FAILURE); - - /* Initialize the digest operation in the session */ - rv = C_DigestInit(hSession, &mechanism); - if (rv != CKR_OK) - goto smbmacdone; - - /* init with the MAC key */ - rv = C_DigestUpdate(hSession, sign_ctx->ssc_mackey, - sign_ctx->ssc_keylen); - if (rv != CKR_OK) - goto smbmacdone; - - /* copy in SMB packet info till signature field */ - rv = C_DigestUpdate(hSession, (CK_BYTE_PTR)buf, SMB_SIG_OFFS); - if (rv != CKR_OK) - goto smbmacdone; - - /* copy in the seq_buf instead of the signature */ - rv = C_DigestUpdate(hSession, seq_buf, sizeof (seq_buf)); - if (rv != CKR_OK) - goto smbmacdone; - - /* copy in the rest of the packet, skipping the signature */ - rv = C_DigestUpdate(hSession, (CK_BYTE_PTR)buf + offset_end_of_sig, - buf_len - offset_end_of_sig); - if (rv != CKR_OK) - goto smbmacdone; - - rv = C_DigestFinal(hSession, mac, &diglen); - if (rv != CKR_OK) - goto smbmacdone; - - bcopy(mac, mac_sign, SMB_SIG_SIZE); - rc = SMBAUTH_SUCCESS; - -smbmacdone: - (void) C_CloseSession(hSession); - return (rc); -} - -/* - * smb_mac_chk - * - * Calculates MAC signature for the given buffer - * and compares it to the signature in the given context. - * Return 1 if the signature are match, otherwise, return (0); - */ -int -smb_mac_chk(smb_sign_ctx_t *sign_ctx, - const unsigned char *buf, size_t buf_len) -{ - unsigned char mac_sign[SMB_SIG_SIZE]; - - /* calculate mac signature */ - if (smb_mac_calc(sign_ctx, buf, buf_len, mac_sign) != SMBAUTH_SUCCESS) - return (0); - - /* compare the signatures */ - if (memcmp(sign_ctx->ssc_sign, mac_sign, SMB_SIG_SIZE) == 0) - return (1); - - return (0); -} - -/* - * smb_mac_sign - * - * Calculates MAC signature for the given buffer, - * and write it to the buffer's signature field. - * - * Returns SMBAUTH_SUCCESS if cryptology framework use was successful, - * SMBAUTH_FAILURE if not. - */ -int -smb_mac_sign(smb_sign_ctx_t *sign_ctx, unsigned char *buf, size_t buf_len) -{ - unsigned char mac_sign[SMB_SIG_SIZE]; - - /* calculate mac signature */ - if (smb_mac_calc(sign_ctx, buf, buf_len, mac_sign) != SMBAUTH_SUCCESS) - return (SMBAUTH_FAILURE); - - /* put mac signature in the header's signature field */ - (void) memcpy(buf + SMB_SIG_OFFS, mac_sign, SMB_SIG_SIZE); - return (SMBAUTH_SUCCESS); -} - -void -smb_mac_inc_seqnum(smb_sign_ctx_t *sign_ctx) -{ - sign_ctx->ssc_seqnum++; -} - -void -smb_mac_dec_seqnum(smb_sign_ctx_t *sign_ctx) -{ - sign_ctx->ssc_seqnum--; -} diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_pwdutil.c b/usr/src/lib/smbsrv/libsmb/common/smb_pwdutil.c index b359d32d5d..19cb5166a1 100644 --- a/usr/src/lib/smbsrv/libsmb/common/smb_pwdutil.c +++ b/usr/src/lib/smbsrv/libsmb/common/smb_pwdutil.c @@ -21,6 +21,8 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #include <syslog.h> @@ -262,10 +264,13 @@ smb_pwd_getpwnam(const char *name, smb_passwd_t *smbpw) return (smb_pwd_ops.pwop_getpwnam(name, smbpw)); err = smb_pwd_lock(); - if (err != SMB_PWE_SUCCESS) + if (err != SMB_PWE_SUCCESS) { + syslog(LOG_WARNING, "smb_pwdutil: lock failed, err=%d", err); return (NULL); + } if ((fp = fopen(SMB_PASSWD, "rF")) == NULL) { + syslog(LOG_WARNING, "smb_pwdutil: open failed, %m"); (void) smb_pwd_unlock(); return (NULL); } @@ -274,8 +279,7 @@ smb_pwd_getpwnam(const char *name, smb_passwd_t *smbpw) while (smb_pwd_fgetent(fp, &pwbuf, SMB_PWD_GETF_ALL) != NULL) { if (strcmp(name, smbpw->pw_name) == 0) { - if ((smbpw->pw_flags & (SMB_PWF_LM | SMB_PWF_NT))) - found = B_TRUE; + found = B_TRUE; break; } } @@ -311,10 +315,13 @@ smb_pwd_getpwuid(uid_t uid, smb_passwd_t *smbpw) return (smb_pwd_ops.pwop_getpwuid(uid, smbpw)); err = smb_pwd_lock(); - if (err != SMB_PWE_SUCCESS) + if (err != SMB_PWE_SUCCESS) { + syslog(LOG_WARNING, "smb_pwdutil: lock failed, err=%d", err); return (NULL); + } if ((fp = fopen(SMB_PASSWD, "rF")) == NULL) { + syslog(LOG_WARNING, "smb_pwdutil: open failed, %m"); (void) smb_pwd_unlock(); return (NULL); } @@ -323,8 +330,7 @@ smb_pwd_getpwuid(uid_t uid, smb_passwd_t *smbpw) while (smb_pwd_fgetent(fp, &pwbuf, SMB_PWD_GETF_ALL) != NULL) { if (uid == smbpw->pw_uid) { - if ((smbpw->pw_flags & (SMB_PWF_LM | SMB_PWF_NT))) - found = B_TRUE; + found = B_TRUE; break; } } @@ -1002,10 +1008,13 @@ smb_lucache_do_update(void) void *cookie = NULL; FILE *fp; - if ((rc = smb_pwd_lock()) != SMB_PWE_SUCCESS) + if ((rc = smb_pwd_lock()) != SMB_PWE_SUCCESS) { + syslog(LOG_WARNING, "smb_pwdutil: lock failed, err=%d", rc); return (rc); + } if ((fp = fopen(SMB_PASSWD, "rF")) == NULL) { + syslog(LOG_WARNING, "smb_pwdutil: open failed, %m"); (void) smb_pwd_unlock(); return (SMB_PWE_OPEN_FAILED); } diff --git a/usr/src/lib/smbsrv/libsmbns/Makefile.com b/usr/src/lib/smbsrv/libsmbns/Makefile.com index 31d30cb358..e0dd1beec7 100644 --- a/usr/src/lib/smbsrv/libsmbns/Makefile.com +++ b/usr/src/lib/smbsrv/libsmbns/Makefile.com @@ -49,7 +49,8 @@ SRCS= $(OBJS_COMMON:%.o=$(SRCDIR)/%.c) \ $(OBJS_SHARED:%.o=$(SRC)/common/smbsrv/%.c) LDLIBS += $(MACH_LDLIBS) -LDLIBS += -lsmb -lgss -lcmdutils -lldap -lresolv -lnsl -lsocket -lc +LDLIBS += -lsmb -lads -lgss -lcmdutils -lldap \ + -lsocket -lnsl -lc CPPFLAGS += -D_REENTRANT CPPFLAGS += -Dsyslog=smb_syslog CERRWARN += -_gcc=-Wno-unused-function diff --git a/usr/src/lib/smbsrv/libsmbns/common/libsmbns.h b/usr/src/lib/smbsrv/libsmbns/common/libsmbns.h index 95d16b104e..11396695d2 100644 --- a/usr/src/lib/smbsrv/libsmbns/common/libsmbns.h +++ b/usr/src/lib/smbsrv/libsmbns/common/libsmbns.h @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012 Nexenta Systems, Inc. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #ifndef _LIBSMBNS_H @@ -54,11 +54,24 @@ typedef struct smb_ads_host_info { } smb_ads_host_info_t; /* - * The possible return status of the adjoin routine. + * Return status codes for the ads functions. */ -typedef enum smb_adjoin_status { - SMB_ADJOIN_SUCCESS = 0, - SMB_ADJOIN_ERR_GET_HANDLE, +typedef enum smb_ads_status { + SMB_ADS_SUCCESS = 0, + /* errno values... */ + SMB_ADS_ERRNO_GAP = 200, + SMB_ADS_KRB5_INIT_CTX, + SMB_ADS_KRB5_CC_DEFAULT, + SMB_ADS_KRB5_PARSE_PRINCIPAL, + SMB_ADS_KRB5_GET_INIT_CREDS_PW, + SMB_ADS_KRB5_CC_INITIALIZE, + SMB_ADS_KRB5_CC_STORE_CRED, + SMB_ADS_CANT_LOCATE_DC, + SMB_ADS_LDAP_INIT, + SMB_ADS_LDAP_SETOPT, + SMB_ADS_LDAP_SET_DOM, + SMB_ADS_LDAP_SASL_BIND, + SMB_ADJOIN_ERR_GEN_PWD, SMB_ADJOIN_ERR_GET_DCLEVEL, SMB_ADJOIN_ERR_ADD_TRUST_ACCT, @@ -72,13 +85,15 @@ typedef enum smb_adjoin_status { SMB_ADJOIN_ERR_WRITE_KEYTAB, SMB_ADJOIN_ERR_IDMAP_SET_DOMAIN, SMB_ADJOIN_ERR_IDMAP_REFRESH, - SMB_ADJOIN_ERR_COMMIT_KEYTAB -} smb_adjoin_status_t; + SMB_ADJOIN_ERR_COMMIT_KEYTAB, + SMB_ADJOIN_ERR_AUTH_NETLOGON, + SMB_ADJOIN_ERR_STORE_PROPS, +} smb_ads_status_t; /* ADS functions */ extern void smb_ads_init(void); extern void smb_ads_fini(void); -extern void smb_ads_refresh(void); +extern void smb_ads_refresh(boolean_t); extern smb_ads_handle_t *smb_ads_open(void); extern void smb_ads_close(smb_ads_handle_t *); extern int smb_ads_publish_share(smb_ads_handle_t *, const char *, const char *, @@ -90,10 +105,11 @@ extern int smb_ads_lookup_share(smb_ads_handle_t *, const char *, const char *, char *); extern int smb_ads_add_share(smb_ads_handle_t *, const char *, const char *, const char *); -extern smb_adjoin_status_t smb_ads_join(char *, char *, char *, char *); -extern void smb_ads_join_errmsg(smb_adjoin_status_t); -extern boolean_t smb_ads_lookup_msdcs(char *, char *, char *, uint32_t); -extern smb_ads_host_info_t *smb_ads_find_host(char *, char *); +extern smb_ads_status_t smb_ads_join(char *, char *, char *, char *); +extern void smb_ads_log_errmsg(smb_ads_status_t); +extern const char *smb_ads_strerror(int); +extern uint32_t smb_ads_lookup_msdcs(char *, smb_dcinfo_t *); +extern smb_ads_host_info_t *smb_ads_find_host(char *); /* DYNDNS functions */ extern void *dyndns_publisher(void *); diff --git a/usr/src/lib/smbsrv/libsmbns/common/mapfile-vers b/usr/src/lib/smbsrv/libsmbns/common/mapfile-vers index 48af7b02ea..9b9ae24e54 100644 --- a/usr/src/lib/smbsrv/libsmbns/common/mapfile-vers +++ b/usr/src/lib/smbsrv/libsmbns/common/mapfile-vers @@ -20,6 +20,7 @@ # # # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2014 Nexenta Systems, Inc. All rights reserved. # # @@ -53,13 +54,14 @@ SYMBOL_VERSION SUNWprivate { smb_ads_fini; smb_ads_init; smb_ads_join; - smb_ads_join_errmsg; + smb_ads_log_errmsg; smb_ads_lookup_msdcs; smb_ads_lookup_share; smb_ads_open; smb_ads_publish_share; smb_ads_refresh; smb_ads_remove_share; + smb_ads_strerror; smb_browser_netlogon; smb_browser_reconfig; smb_ccache_init; diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_ads.c b/usr/src/lib/smbsrv/libsmbns/common/smbns_ads.c index c88ab69cec..4c574786d5 100644 --- a/usr/src/lib/smbsrv/libsmbns/common/smbns_ads.c +++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_ads.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #include <sys/param.h> @@ -47,6 +47,7 @@ #include <note.h> #include <errno.h> #include <cryptoutil.h> +#include <ads/dsgetdc.h> #include <smbsrv/libsmbns.h> #include <smbns_dyndns.h> @@ -169,7 +170,6 @@ static mutex_t smb_ads_cached_host_mtx; */ typedef struct smb_ads_config { char c_site[SMB_ADS_SITE_MAX]; - smb_inaddr_t c_pdc; mutex_t c_mtx; } smb_ads_config_t; @@ -195,7 +195,7 @@ typedef struct smb_ads_host_list { smb_ads_host_info_t *ah_list; } smb_ads_host_list_t; -static smb_ads_handle_t *smb_ads_open_main(char *, char *, char *); +static int smb_ads_open_main(smb_ads_handle_t **, char *, char *, char *); static int smb_ads_add_computer(smb_ads_handle_t *, int, char *); static int smb_ads_modify_computer(smb_ads_handle_t *, int, char *); static int smb_ads_computer_op(smb_ads_handle_t *, int, int, char *); @@ -207,16 +207,12 @@ static void smb_ads_free_cached_host(void); static int smb_ads_alloc_attr(LDAPMod **, int); static void smb_ads_free_attr(LDAPMod **); static int smb_ads_get_dc_level(smb_ads_handle_t *); -static smb_ads_host_info_t *smb_ads_select_dc(smb_ads_host_list_t *); static smb_ads_qstat_t smb_ads_find_computer(smb_ads_handle_t *, char *); static smb_ads_qstat_t smb_ads_getattr(LDAP *, LDAPMessage *, smb_ads_avpair_t *); static smb_ads_qstat_t smb_ads_get_qstat(smb_ads_handle_t *, LDAPMessage *, smb_ads_avpair_t *); -static boolean_t smb_ads_match_pdc(smb_ads_host_info_t *); -static boolean_t smb_ads_is_sought_host(smb_ads_host_info_t *, char *); static boolean_t smb_ads_is_same_domain(char *, char *); -static boolean_t smb_ads_is_pdc_configured(void); static smb_ads_host_info_t *smb_ads_dup_host_info(smb_ads_host_info_t *); static char *smb_ads_get_sharedn(const char *, const char *, const char *); static krb5_enctype *smb_ads_get_enctypes(int, int *); @@ -232,8 +228,10 @@ smb_ads_init(void) (void) mutex_lock(&smb_ads_cfg.c_mtx); (void) smb_config_getstr(SMB_CI_ADS_SITE, smb_ads_cfg.c_site, SMB_ADS_SITE_MAX); - (void) smb_config_getip(SMB_CI_DOMAIN_SRV, &smb_ads_cfg.c_pdc); (void) mutex_unlock(&smb_ads_cfg.c_mtx); + + /* Force -lads to load, for dtrace. */ + DsFreeDcInfo(NULL); } void @@ -246,52 +244,29 @@ smb_ads_fini(void) * smb_ads_refresh * * This function will be called when smb/server SMF service is refreshed. + * (See smbd_join.c) + * * Clearing the smb_ads_cached_host_info would allow the next DC * discovery process to pick up an AD based on the new AD configuration. */ void -smb_ads_refresh(void) +smb_ads_refresh(boolean_t force_rediscovery) { char new_site[SMB_ADS_SITE_MAX]; - smb_inaddr_t new_pdc; - boolean_t purge = B_FALSE; (void) smb_config_getstr(SMB_CI_ADS_SITE, new_site, SMB_ADS_SITE_MAX); - (void) smb_config_getip(SMB_CI_DOMAIN_SRV, &new_pdc); (void) mutex_lock(&smb_ads_cfg.c_mtx); - if (smb_strcasecmp(smb_ads_cfg.c_site, new_site, 0)) { - (void) strlcpy(smb_ads_cfg.c_site, new_site, SMB_ADS_SITE_MAX); - purge = B_TRUE; - } - - smb_ads_cfg.c_pdc = new_pdc; + (void) strlcpy(smb_ads_cfg.c_site, new_site, SMB_ADS_SITE_MAX); (void) mutex_unlock(&smb_ads_cfg.c_mtx); - (void) mutex_lock(&smb_ads_cached_host_mtx); - if (smb_ads_cached_host_info && - smb_ads_is_pdc_configured() && - !smb_ads_match_pdc(smb_ads_cached_host_info)) - purge = B_TRUE; - (void) mutex_unlock(&smb_ads_cached_host_mtx); + smb_ads_free_cached_host(); - if (purge) - smb_ads_free_cached_host(); + if (force_rediscovery) { + (void) _DsForceRediscovery(NULL, 0); + } } - -static boolean_t -smb_ads_is_pdc_configured(void) -{ - boolean_t configured; - - (void) mutex_lock(&smb_ads_cfg.c_mtx); - configured = !smb_inet_iszero(&smb_ads_cfg.c_pdc); - (void) mutex_unlock(&smb_ads_cfg.c_mtx); - - return (configured); -} - /* * smb_ads_build_unc_name * @@ -315,44 +290,6 @@ smb_ads_build_unc_name(char *unc_name, int maxlen, } /* - * smb_ads_ldap_ping - * - * This is used to bind to an ADS server to see - * if it is still alive. - * - * Returns: - * -1: error - * 0: successful - */ -/*ARGSUSED*/ -static int -smb_ads_ldap_ping(smb_ads_host_info_t *ads_host) -{ - int ldversion = LDAP_VERSION3, status, timeoutms = 5 * 1000; - LDAP *ld = NULL; - - ld = ldap_init(ads_host->name, ads_host->port); - if (ld == NULL) - return (-1); - - ldversion = LDAP_VERSION3; - (void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldversion); - /* setup TCP/IP connect timeout */ - (void) ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT, &timeoutms); - - status = ldap_bind_s(ld, "", NULL, LDAP_AUTH_SIMPLE); - - if (status != LDAP_SUCCESS) { - (void) ldap_unbind(ld); - return (-1); - } - - (void) ldap_unbind(ld); - - return (0); -} - -/* * The cached ADS host is no longer valid if one of the following criteria * is satisfied: * @@ -366,7 +303,7 @@ smb_ads_ldap_ping(smb_ads_host_info_t *ads_host) * Return B_TRUE if the cache host is still valid. Otherwise, return B_FALSE. */ static boolean_t -smb_ads_validate_cache_host(char *domain, char *srv) +smb_ads_validate_cache_host(char *domain) { if (!smb_ads_cached_host_info) return (B_FALSE); @@ -374,33 +311,6 @@ smb_ads_validate_cache_host(char *domain, char *srv) if (!smb_ads_is_same_domain(smb_ads_cached_host_info->name, domain)) return (B_FALSE); - if (smb_ads_ldap_ping(smb_ads_cached_host_info) == 0) { - if (!srv) - return (B_TRUE); - - if (smb_ads_is_sought_host(smb_ads_cached_host_info, srv)) - return (B_TRUE); - } - - return (B_FALSE); -} - -/* - * smb_ads_is_sought_host - * - * Returns true, if the sought host name matches the input host (host) name. - * The sought host is expected to be in Fully Qualified Domain Name (FQDN) - * format. - */ -static boolean_t -smb_ads_is_sought_host(smb_ads_host_info_t *host, char *sought_host_name) -{ - if ((host == NULL) || (sought_host_name == NULL)) - return (B_FALSE); - - if (smb_strcasecmp(host->name, sought_host_name, 0)) - return (B_FALSE); - return (B_TRUE); } @@ -430,163 +340,6 @@ smb_ads_is_same_domain(char *cached_host_name, char *current_domain) } /* - * smb_ads_skip_ques_sec - * Skips the question section. - */ -static int -smb_ads_skip_ques_sec(int qcnt, uchar_t **ptr, uchar_t *eom) -{ - int i, len; - - for (i = 0; i < qcnt; i++) { - if ((len = dn_skipname(*ptr, eom)) < 0) - return (-1); - - *ptr += len + QFIXEDSZ; - } - - return (0); -} - -/* - * smb_ads_decode_host_ans_sec - * Decodes ADS hosts, priority, weight and port number from the answer - * section based on the current buffer pointer. - */ -static int -smb_ads_decode_host_ans_sec(int ans_cnt, uchar_t **ptr, uchar_t *eom, - uchar_t *buf, smb_ads_host_info_t *ads_host_list) -{ - int i, len; - smb_ads_host_info_t *ads_host; - - for (i = 0; i < ans_cnt; i++) { - ads_host = &ads_host_list[i]; - - if ((len = dn_skipname(*ptr, eom)) < 0) - return (-1); - - - *ptr += len; - - /* skip type, class, ttl */ - *ptr += 8; - /* data size */ - *ptr += 2; - - /* Get priority, weight */ - /* LINTED: E_CONSTANT_CONDITION */ - NS_GET16(ads_host->priority, *ptr); - /* LINTED: E_CONSTANT_CONDITION */ - NS_GET16(ads_host->weight, *ptr); - - /* port */ - /* LINTED: E_CONSTANT_CONDITION */ - NS_GET16(ads_host->port, *ptr); - /* domain name */ - len = dn_expand(buf, eom, *ptr, ads_host->name, MAXHOSTNAMELEN); - if (len < 0) - return (-1); - - *ptr += len; - } - - return (0); -} - -/* - * smb_ads_skip_auth_sec - * Skips the authority section. - */ -static int -smb_ads_skip_auth_sec(int ns_cnt, uchar_t **ptr, uchar_t *eom) -{ - int i, len; - uint16_t size; - - for (i = 0; i < ns_cnt; i++) { - if ((len = dn_skipname(*ptr, eom)) < 0) - return (-1); - - *ptr += len; - /* skip type, class, ttl */ - *ptr += 8; - /* get len of data */ - /* LINTED: E_CONSTANT_CONDITION */ - NS_GET16(size, *ptr); - if ((*ptr + size) > eom) - return (-1); - - *ptr += size; - } - - return (0); -} - -/* - * smb_ads_decode_host_ip - * - * Decodes ADS hosts and IP Addresses from the additional section based - * on the current buffer pointer. - */ -static int -smb_ads_decode_host_ip(int addit_cnt, int ans_cnt, uchar_t **ptr, - uchar_t *eom, uchar_t *buf, smb_ads_host_info_t *ads_host_list) -{ - int i, j, len; - smb_inaddr_t ipaddr; - char hostname[MAXHOSTNAMELEN]; - char *name; - uint16_t size = 0; - - for (i = 0; i < addit_cnt; i++) { - - /* domain name */ - len = dn_expand(buf, eom, *ptr, hostname, MAXHOSTNAMELEN); - if (len < 0) - return (-1); - - *ptr += len; - - /* skip type, class, TTL, data len */ - *ptr += 8; - /* LINTED: E_CONSTANT_CONDITION */ - NS_GET16(size, *ptr); - - if (size == NS_INADDRSZ) { - /* LINTED: E_CONSTANT_CONDITION */ - NS_GET32(ipaddr.a_ipv4, *ptr); - ipaddr.a_ipv4 = htonl(ipaddr.a_ipv4); - ipaddr.a_family = AF_INET; - } else if (size == NS_IN6ADDRSZ) { -#ifdef BIG_ENDIAN - bcopy(*ptr, &ipaddr.a_ipv6, NS_IN6ADDRSZ); -#else - for (i = 0; i < NS_IN6ADDRSZ; i++) - (uint8_t *)(ipaddr.a_ipv6) - [NS_IN6ADDRSZ-1-i] = *(*ptr+i); -#endif - ipaddr.a_family = AF_INET6; - *ptr += size; - } - - /* - * find the host in the list of DC records from - * the answer section, that matches the host in the - * additional section, and set its IP address. - */ - for (j = 0; j < ans_cnt; j++) { - if ((name = ads_host_list[j].name) == NULL) - continue; - if (smb_strcasecmp(name, hostname, 0) == 0) { - ads_host_list[j].ipaddr = ipaddr; - } - } - } - return (0); -} - -/* * smb_ads_dup_host_info * * Duplicates the passed smb_ads_host_info_t structure. @@ -612,240 +365,6 @@ smb_ads_dup_host_info(smb_ads_host_info_t *ads_host) } /* - * smb_ads_hlist_alloc - */ -static smb_ads_host_list_t * -smb_ads_hlist_alloc(int count) -{ - int size; - smb_ads_host_list_t *hlist; - - if (count == 0) - return (NULL); - - size = sizeof (smb_ads_host_info_t) * count; - hlist = (smb_ads_host_list_t *)malloc(sizeof (smb_ads_host_list_t)); - if (hlist == NULL) - return (NULL); - - hlist->ah_cnt = count; - hlist->ah_list = (smb_ads_host_info_t *)malloc(size); - if (hlist->ah_list == NULL) { - free(hlist); - return (NULL); - } - - bzero(hlist->ah_list, size); - return (hlist); -} - -/* - * smb_ads_hlist_free - */ -static void -smb_ads_hlist_free(smb_ads_host_list_t *host_list) -{ - if (host_list == NULL) - return; - - free(host_list->ah_list); - free(host_list); -} - -/* - * smb_ads_query_dns_server - * - * This routine sends a DNS service location (SRV) query message to the - * DNS server via TCP to query it for a list of ADS server(s). Once a reply - * is received, the reply message is parsed to get the hostname. If there are IP - * addresses populated in the additional section then the additional section - * is parsed to obtain the IP addresses. - * - * The service location of _ldap._tcp.dc.msdcs.<ADS domain> is used to - * guarantee that Microsoft domain controllers are returned. Microsoft domain - * controllers are also ADS servers. - * - * The ADS hostnames are stored in the answer section of the DNS reply message. - * The IP addresses are stored in the additional section. - * - * The DNS reply message may be in compress formed. The compression is done - * on repeating domain name label in the message. i.e hostname. - * - * Upon successful completion, host list of ADS server(s) is returned. - */ -static smb_ads_host_list_t * -smb_ads_query_dns_server(char *domain, char *msdcs_svc_name) -{ - smb_ads_host_list_t *hlist = NULL; - int len, qcnt, ans_cnt, ns_cnt, addit_cnt; - uchar_t *ptr, *eom; - struct __res_state res_state; - union { - HEADER hdr; - uchar_t buf[NS_MAXMSG]; - } msg; - - bzero(&res_state, sizeof (struct __res_state)); - if (res_ninit(&res_state) < 0) - return (NULL); - - /* use TCP */ - res_state.options |= RES_USEVC; - - len = res_nquerydomain(&res_state, msdcs_svc_name, domain, - C_IN, T_SRV, msg.buf, sizeof (msg.buf)); - - if (len < 0) { - syslog(LOG_NOTICE, "DNS query for %s failed: %s", - msdcs_svc_name, hstrerror(res_state.res_h_errno)); - res_ndestroy(&res_state); - return (NULL); - } - - if (len > sizeof (msg.buf)) { - syslog(LOG_NOTICE, - "DNS query for %s failed: too big", msdcs_svc_name); - res_ndestroy(&res_state); - return (NULL); - } - - /* parse the reply, skip header and question sections */ - ptr = msg.buf + sizeof (msg.hdr); - eom = msg.buf + len; - - /* check truncated message bit */ - if (msg.hdr.tc) - syslog(LOG_NOTICE, - "DNS query for %s failed: truncated", msdcs_svc_name); - - qcnt = ntohs(msg.hdr.qdcount); - ans_cnt = ntohs(msg.hdr.ancount); - ns_cnt = ntohs(msg.hdr.nscount); - addit_cnt = ntohs(msg.hdr.arcount); - - if (smb_ads_skip_ques_sec(qcnt, &ptr, eom) != 0) { - res_ndestroy(&res_state); - return (NULL); - } - - hlist = smb_ads_hlist_alloc(ans_cnt); - if (hlist == NULL) { - res_ndestroy(&res_state); - return (NULL); - } - - /* walk through the answer section */ - if (smb_ads_decode_host_ans_sec(ans_cnt, &ptr, eom, msg.buf, - hlist->ah_list) != 0) { - smb_ads_hlist_free(hlist); - res_ndestroy(&res_state); - return (NULL); - } - - /* check authority section */ - if (ns_cnt > 0) { - if (smb_ads_skip_auth_sec(ns_cnt, &ptr, eom) != 0) { - smb_ads_hlist_free(hlist); - res_ndestroy(&res_state); - return (NULL); - } - } - - /* - * Check additional section to get IP address of ADS host. - */ - if (addit_cnt > 0) { - if (smb_ads_decode_host_ip(addit_cnt, ans_cnt, - &ptr, eom, msg.buf, hlist->ah_list) != 0) { - smb_ads_hlist_free(hlist); - res_ndestroy(&res_state); - return (NULL); - } - } - - res_ndestroy(&res_state); - return (hlist); -} - -/* - * smb_ads_get_site_service - * - * Gets the msdcs SRV RR for the specified site. - */ -static void -smb_ads_get_site_service(char *site_service, size_t len) -{ - (void) mutex_lock(&smb_ads_cfg.c_mtx); - if (*smb_ads_cfg.c_site == '\0') - *site_service = '\0'; - else - (void) snprintf(site_service, len, - SMB_ADS_MSDCS_SRV_SITE_RR, smb_ads_cfg.c_site); - - (void) mutex_unlock(&smb_ads_cfg.c_mtx); -} - -/* - * smb_ads_getipnodebyname - * - * This method gets the IP address by doing a host name lookup. - */ -static int -smb_ads_getipnodebyname(smb_ads_host_info_t *hentry) -{ - struct hostent *h; - int error; - - switch (hentry->ipaddr.a_family) { - case AF_INET6: - h = getipnodebyname(hentry->name, hentry->ipaddr.a_family, - AI_DEFAULT, &error); - if (h == NULL || h->h_length != NS_IN6ADDRSZ) - return (-1); - break; - - case AF_INET: - h = getipnodebyname(hentry->name, hentry->ipaddr.a_family, - 0, &error); - if (h == NULL || h->h_length != NS_INADDRSZ) - return (-1); - break; - - default: - return (-1); - } - bcopy(*(h->h_addr_list), &hentry->ipaddr.a_ip, h->h_length); - freehostent(h); - return (0); -} - -/* - * Checks the IP address to see if it is zero. If so, then do a host - * lookup by hostname to get the IP address based on the IP family. - * - * If the family is unknown then do a lookup by hostame based on the - * setting of the SMB_CI_IPV6_ENABLE property. - */ -static int -smb_ads_set_ipaddr(smb_ads_host_info_t *hentry) -{ - if (smb_inet_iszero(&hentry->ipaddr)) { - if (smb_ads_getipnodebyname(hentry) < 0) - return (-1); - } else if (SMB_ADS_AF_UNKNOWN(hentry)) { - hentry->ipaddr.a_family = - smb_config_getbool(SMB_CI_IPV6_ENABLE) ? AF_INET6 : AF_INET; - - if (smb_ads_getipnodebyname(hentry) < 0) { - hentry->ipaddr.a_family = 0; - return (-1); - } - } - - return (0); -} - -/* * smb_ads_find_host * * Finds an ADS host in a given domain. @@ -867,7 +386,6 @@ smb_ads_set_ipaddr(smb_ads_host_info_t *hentry) * * Parameters: * domain: fully-qualified domain name. - * kpasswd_srv: fully-quailifed hostname of the kpasswd server. * * Returns: * A copy of the cached host info is returned. The caller is responsible @@ -875,20 +393,17 @@ smb_ads_set_ipaddr(smb_ads_host_info_t *hentry) */ /*ARGSUSED*/ smb_ads_host_info_t * -smb_ads_find_host(char *domain, char *kpasswd_srv) +smb_ads_find_host(char *domain) { - int i; - char site_service[MAXHOSTNAMELEN]; - smb_ads_host_list_t *hlist, *hlist2; - smb_ads_host_info_t *hlistp = NULL, *host = NULL; - smb_ads_host_info_t *found_kpasswd_srv = NULL; - smb_ads_host_info_t *found_pdc = NULL; - - if ((kpasswd_srv) && (*kpasswd_srv == '\0')) - kpasswd_srv = NULL; + smb_ads_host_info_t *host = NULL; + DOMAIN_CONTROLLER_INFO *dci = NULL; + struct sockaddr_storage *ss; + uint32_t flags = DS_DS_FLAG; + uint32_t status; + int tries; (void) mutex_lock(&smb_ads_cached_host_mtx); - if (smb_ads_validate_cache_host(domain, kpasswd_srv)) { + if (smb_ads_validate_cache_host(domain)) { host = smb_ads_dup_host_info(smb_ads_cached_host_info); (void) mutex_unlock(&smb_ads_cached_host_mtx); return (host); @@ -898,79 +413,77 @@ smb_ads_find_host(char *domain, char *kpasswd_srv) smb_ads_free_cached_host(); /* - * First look for ADS hosts in ADS site if configured. Then try - * without ADS site info. + * The _real_ DC Locator is over in idmapd. + * Door call over there to get it. */ - hlist = NULL; - smb_ads_get_site_service(site_service, MAXHOSTNAMELEN); - + tries = 15; +again: + status = _DsGetDcName( + NULL, /* ComputerName */ + domain, + NULL, /* DomainGuid */ + NULL, /* SiteName */ + flags, + &dci); + switch (status) { + case 0: + break; /* - * If we're given an AD, the DNS SRV RR lookup should not be restricted - * to the specified site since there is no guarantee that the specified - * AD is in the specified site. + * We can see these errors when joining a domain, if we race + * asking idmap for the DC before it knows the new domain. */ - if (*site_service != '\0' && !kpasswd_srv && - !smb_ads_is_pdc_configured()) - hlist = smb_ads_query_dns_server(domain, site_service); - - if (!hlist) - hlist = smb_ads_query_dns_server(domain, - SMB_ADS_MSDCS_SRV_DC_RR); - - if ((hlist == NULL) || (hlist->ah_list == NULL) || (hlist->ah_cnt == 0)) + case NT_STATUS_NO_SUCH_DOMAIN: /* Specified domain unknown */ + case NT_STATUS_INVALID_SERVER_STATE: /* not in domain mode. */ + if (--tries > 0) { + (void) sleep(1); + goto again; + } + /* FALLTHROUGH */ + case NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND: + case NT_STATUS_CANT_WAIT: /* timeout over in idmap */ + default: return (NULL); - - for (i = 0, hlistp = hlist->ah_list; i < hlist->ah_cnt; i++) { - if (smb_ads_set_ipaddr(&hlistp[i]) < 0) - continue; - - if (smb_ads_is_sought_host(&hlistp[i], kpasswd_srv)) - found_kpasswd_srv = &hlistp[i]; - - if (smb_ads_match_pdc(&hlistp[i])) - found_pdc = &hlistp[i]; } - if (found_kpasswd_srv && smb_ads_ldap_ping(found_kpasswd_srv) == 0) { - host = found_kpasswd_srv; - goto update_cache; - } + host = calloc(1, sizeof (*host)); + if (host == NULL) + goto out; - if (found_pdc && smb_ads_ldap_ping(found_pdc) == 0) { - host = found_pdc; - goto update_cache; + (void) strlcpy(host->name, dci->DomainControllerName, MAXHOSTNAMELEN); + ss = (void *)dci->_sockaddr; + switch (ss->ss_family) { + case AF_INET: { + struct sockaddr_in *sin = (void *)ss; + host->port = ntohs(sin->sin_port); + host->ipaddr.a_family = AF_INET; + (void) memcpy(&host->ipaddr.a_ipv4, &sin->sin_addr, + sizeof (in_addr_t)); + break; } - - /* - * If the specified DC (kpasswd_srv or pdc) is not found, fallback - * to find a DC in the specified AD site. - */ - if (*site_service != '\0' && - (kpasswd_srv || smb_ads_is_pdc_configured())) { - hlist2 = smb_ads_query_dns_server(domain, site_service); - if (hlist2 && hlist2->ah_list && hlist2->ah_cnt != 0) { - smb_ads_hlist_free(hlist); - hlist = hlist2; - hlistp = hlist->ah_list; - - for (i = 0; i < hlist->ah_cnt; i++) - (void) smb_ads_set_ipaddr(&hlistp[i]); - } + case AF_INET6: { + struct sockaddr_in6 *sin6 = (void *)ss; + host->port = ntohs(sin6->sin6_port); + host->ipaddr.a_family = AF_INET6; + (void) memcpy(&host->ipaddr.a_ipv6, &sin6->sin6_addr, + sizeof (in6_addr_t)); + break; } - - /* Select DC from DC list */ - host = smb_ads_select_dc(hlist); - -update_cache: - if (host) { - (void) mutex_lock(&smb_ads_cached_host_mtx); - if (!smb_ads_cached_host_info) - smb_ads_cached_host_info = smb_ads_dup_host_info(host); - host = smb_ads_dup_host_info(smb_ads_cached_host_info); - (void) mutex_unlock(&smb_ads_cached_host_mtx); + default: + syslog(LOG_ERR, "no addr for DC %s", + dci->DomainControllerName); + free(host); + host = NULL; + goto out; } - smb_ads_hlist_free(hlist); + (void) mutex_lock(&smb_ads_cached_host_mtx); + if (!smb_ads_cached_host_info) + smb_ads_cached_host_info = smb_ads_dup_host_info(host); + host = smb_ads_dup_host_info(smb_ads_cached_host_info); + (void) mutex_unlock(&smb_ads_cached_host_mtx); + +out: + DsFreeDcInfo(dci); return (host); } @@ -1063,6 +576,8 @@ smb_ads_handle_t * smb_ads_open(void) { char domain[MAXHOSTNAMELEN]; + smb_ads_handle_t *h; + smb_ads_status_t err; if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN) return (NULL); @@ -1070,7 +585,13 @@ smb_ads_open(void) if (smb_getfqdomainname(domain, MAXHOSTNAMELEN) != 0) return (NULL); - return (smb_ads_open_main(domain, NULL, NULL)); + err = smb_ads_open_main(&h, domain, NULL, NULL); + if (err != 0) { + smb_ads_log_errmsg(err); + return (NULL); + } + + return (h); } static int @@ -1116,39 +637,44 @@ smb_ads_saslcallback(LDAP *ld, unsigned flags, void *defaults, void *prompts) * NULL : can't connect to ADS server or other errors * smb_ads_handle_t* : handle to ADS server */ -static smb_ads_handle_t * -smb_ads_open_main(char *domain, char *user, char *password) +static int +smb_ads_open_main(smb_ads_handle_t **hp, char *domain, char *user, + char *password) { smb_ads_handle_t *ah; LDAP *ld; int version = 3; smb_ads_host_info_t *ads_host = NULL; - int rc; + int err, rc; + + *hp = NULL; if (user != NULL) { - if (smb_kinit(user, password) == 0) - return (NULL); + err = smb_kinit(domain, user, password); + if (err != 0) + return (err); user = NULL; password = NULL; } - ads_host = smb_ads_find_host(domain, NULL); + ads_host = smb_ads_find_host(domain); if (ads_host == NULL) - return (NULL); + return (SMB_ADS_CANT_LOCATE_DC); ah = (smb_ads_handle_t *)malloc(sizeof (smb_ads_handle_t)); if (ah == NULL) { free(ads_host); - return (NULL); + return (ENOMEM); } (void) memset(ah, 0, sizeof (smb_ads_handle_t)); if ((ld = ldap_init(ads_host->name, ads_host->port)) == NULL) { + syslog(LOG_ERR, "smbns: ldap_init failed"); smb_ads_free_cached_host(); free(ah); free(ads_host); - return (NULL); + return (SMB_ADS_LDAP_INIT); } if (ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version) @@ -1157,7 +683,7 @@ smb_ads_open_main(char *domain, char *user, char *password) free(ah); free(ads_host); (void) ldap_unbind(ld); - return (NULL); + return (SMB_ADS_LDAP_SETOPT); } (void) ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); @@ -1167,7 +693,7 @@ smb_ads_open_main(char *domain, char *user, char *password) if (ah->domain == NULL) { smb_ads_close(ah); free(ads_host); - return (NULL); + return (SMB_ADS_LDAP_SETOPT); } /* @@ -1179,14 +705,14 @@ smb_ads_open_main(char *domain, char *user, char *password) if (ah->domain_dn == NULL) { smb_ads_close(ah); free(ads_host); - return (NULL); + return (SMB_ADS_LDAP_SET_DOM); } ah->hostname = strdup(ads_host->name); if (ah->hostname == NULL) { smb_ads_close(ah); free(ads_host); - return (NULL); + return (ENOMEM); } (void) mutex_lock(&smb_ads_cfg.c_mtx); if (*smb_ads_cfg.c_site != '\0') { @@ -1194,7 +720,7 @@ smb_ads_open_main(char *domain, char *user, char *password) smb_ads_close(ah); (void) mutex_unlock(&smb_ads_cfg.c_mtx); free(ads_host); - return (NULL); + return (ENOMEM); } } else { ah->site = NULL; @@ -1204,15 +730,17 @@ smb_ads_open_main(char *domain, char *user, char *password) rc = ldap_sasl_interactive_bind_s(ah->ld, "", "GSSAPI", NULL, NULL, LDAP_SASL_INTERACTIVE, &smb_ads_saslcallback, NULL); if (rc != LDAP_SUCCESS) { - syslog(LOG_ERR, "ldal_sasl_interactive_bind_s failed (%s)", + syslog(LOG_ERR, "smbns: ldap_sasl_..._bind_s failed (%s)", ldap_err2string(rc)); smb_ads_close(ah); free(ads_host); - return (NULL); + return (SMB_ADS_LDAP_SASL_BIND); } free(ads_host); - return (ah); + *hp = ah; + + return (SMB_ADS_SUCCESS); } /* @@ -2163,28 +1691,28 @@ smb_ads_lookup_computer_attr_kvno(smb_ads_handle_t *ah, char *dn) * That would be needed while acquiring Kerberos TGT ticket for the host * principal after the domain join operation. */ -smb_adjoin_status_t +smb_ads_status_t smb_ads_join(char *domain, char *user, char *usr_passwd, char *machine_passwd) { smb_ads_handle_t *ah = NULL; krb5_context ctx = NULL; krb5_principal *krb5princs = NULL; krb5_kvno kvno; - boolean_t des_only, delete = B_TRUE; - smb_adjoin_status_t rc = SMB_ADJOIN_SUCCESS; + boolean_t delete = B_TRUE; + smb_ads_status_t rc; boolean_t new_acct; int dclevel, num, usrctl_flags = 0; smb_ads_qstat_t qstat; char dn[SMB_ADS_DN_MAX]; char tmpfile[] = SMBNS_KRB5_KEYTAB_TMP; - int cnt; + int cnt, x; smb_krb5_pn_set_t spns; - krb5_enctype *encptr; - if ((ah = smb_ads_open_main(domain, user, usr_passwd)) == NULL) { + rc = smb_ads_open_main(&ah, domain, user, usr_passwd); + if (rc != 0) { smb_ccache_remove(SMB_CCACHE_PATH); - return (SMB_ADJOIN_ERR_GET_HANDLE); + return (rc); } if ((dclevel = smb_ads_get_dc_level(ah)) == -1) { @@ -2224,8 +1752,6 @@ smb_ads_join(char *domain, char *user, char *usr_passwd, char *machine_passwd) return (rc); } - des_only = B_FALSE; - if (smb_krb5_ctx_init(&ctx) != 0) { rc = SMB_ADJOIN_ERR_INIT_KRB_CTX; goto adjoin_cleanup; @@ -2257,30 +1783,26 @@ smb_ads_join(char *domain, char *user, char *usr_passwd, char *machine_passwd) /* * Only members of Domain Admins and Enterprise Admins can set * the TRUSTED_FOR_DELEGATION userAccountControl flag. + * Try to set this, but don't fail the join if we can't. + * Look into just removing this... */ - if (smb_ads_update_computer_cntrl_attr(ah, + usrctl_flags = ( SMB_ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT | - SMB_ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION, dn) - == LDAP_INSUFFICIENT_ACCESS) { - usrctl_flags |= (SMB_ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT | - SMB_ADS_USER_ACCT_CTL_DONT_EXPIRE_PASSWD); - + SMB_ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION | + SMB_ADS_USER_ACCT_CTL_DONT_EXPIRE_PASSWD); +set_ctl_again: + x = smb_ads_update_computer_cntrl_attr(ah, usrctl_flags, dn); + if (x != LDAP_SUCCESS && (usrctl_flags & + SMB_ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION) != 0) { syslog(LOG_NOTICE, "Unable to set the " - "TRUSTED_FOR_DELEGATION userAccountControl flag on " - "the machine account in Active Directory. Please refer " - "to the Troubleshooting guide for more information."); - - } else { - usrctl_flags |= (SMB_ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT | - SMB_ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION | - SMB_ADS_USER_ACCT_CTL_DONT_EXPIRE_PASSWD); - } - - if (des_only) - usrctl_flags |= SMB_ADS_USER_ACCT_CTL_USE_DES_KEY_ONLY; - - if (smb_ads_update_computer_cntrl_attr(ah, usrctl_flags, dn) - != 0) { +"TRUSTED_FOR_DELEGATION userAccountControl flag on the " +"machine account in Active Directory. It may be necessary " +"to set that via Active Directory administration."); + usrctl_flags &= + ~SMB_ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION; + goto set_ctl_again; + } + if (x != LDAP_SUCCESS) { rc = SMB_ADJOIN_ERR_UPDATE_CNTRL_ATTR; goto adjoin_cleanup; } @@ -2298,6 +1820,8 @@ smb_ads_join(char *domain, char *user, char *usr_passwd, char *machine_passwd) } delete = B_FALSE; + rc = SMB_ADS_SUCCESS; + adjoin_cleanup: if (new_acct && delete) smb_ads_del_computer(ah, dn); @@ -2309,7 +1833,7 @@ adjoin_cleanup: } /* commit keytab file */ - if (rc == SMB_ADJOIN_SUCCESS) { + if (rc == SMB_ADS_SUCCESS) { if (rename(tmpfile, SMBNS_KRB5_KEYTAB) != 0) { (void) unlink(tmpfile); rc = SMB_ADJOIN_ERR_COMMIT_KEYTAB; @@ -2323,221 +1847,103 @@ adjoin_cleanup: return (rc); } -/* - * smb_ads_join_errmsg - * - * Display error message for the specific adjoin error code. - */ -void -smb_ads_join_errmsg(smb_adjoin_status_t status) -{ - int i; - struct xlate_table { - smb_adjoin_status_t status; - char *msg; - } adjoin_table[] = { - { SMB_ADJOIN_ERR_GET_HANDLE, "Failed to connect to an " - "Active Directory server." }, - { SMB_ADJOIN_ERR_GEN_PWD, "Failed to generate machine " - "password." }, - { SMB_ADJOIN_ERR_GET_DCLEVEL, "Unknown functional level of " - "the domain controller. The rootDSE attribute named " - "\"domainControllerFunctionality\" is missing from the " - "Active Directory." }, - { SMB_ADJOIN_ERR_ADD_TRUST_ACCT, "Failed to create the " - "workstation trust account." }, - { SMB_ADJOIN_ERR_MOD_TRUST_ACCT, "Failed to modify the " - "workstation trust account." }, - { SMB_ADJOIN_ERR_DUP_TRUST_ACCT, "Failed to create the " - "workstation trust account because its name is already " - "in use." }, - { SMB_ADJOIN_ERR_TRUST_ACCT, "Error in querying the " - "workstation trust account" }, - { SMB_ADJOIN_ERR_INIT_KRB_CTX, "Failed to initialize Kerberos " - "context." }, - { SMB_ADJOIN_ERR_GET_SPNS, "Failed to get Kerberos " - "principals." }, - { SMB_ADJOIN_ERR_KSETPWD, "Failed to set machine password." }, - { SMB_ADJOIN_ERR_UPDATE_CNTRL_ATTR, "Failed to modify " - "userAccountControl attribute of the workstation trust " - "account." }, - { SMB_ADJOIN_ERR_WRITE_KEYTAB, "Error in writing to local " - "keytab file (i.e /etc/krb5/krb5.keytab)." }, - { SMB_ADJOIN_ERR_IDMAP_SET_DOMAIN, "Failed to update idmap " - "configuration." }, - { SMB_ADJOIN_ERR_IDMAP_REFRESH, "Failed to refresh idmap " - "service." }, - { SMB_ADJOIN_ERR_COMMIT_KEYTAB, "Failed to commit changes to " - "local keytab file (i.e. /etc/krb5/krb5.keytab)." } - }; - - for (i = 0; i < sizeof (adjoin_table) / sizeof (adjoin_table[0]); i++) { - if (adjoin_table[i].status == status) - syslog(LOG_NOTICE, "%s", adjoin_table[i].msg); - } -} - -/* - * smb_ads_match_pdc - * - * Returns B_TRUE if the given host's IP address matches the preferred DC's - * IP address. Otherwise, returns B_FALSE. - */ -static boolean_t -smb_ads_match_pdc(smb_ads_host_info_t *host) -{ - boolean_t match = B_FALSE; - - if (!host) - return (match); - - (void) mutex_lock(&smb_ads_cfg.c_mtx); - if (smb_inet_equal(&host->ipaddr, &smb_ads_cfg.c_pdc)) - match = B_TRUE; - (void) mutex_unlock(&smb_ads_cfg.c_mtx); +struct xlate_table { + int err; + const char const *msg; +}; - return (match); -} +static const struct xlate_table +adjoin_table[] = { + { SMB_ADS_SUCCESS, "Success" }, + { SMB_ADS_KRB5_INIT_CTX, + "Failed creating a Kerberos context." }, + { SMB_ADS_KRB5_CC_DEFAULT, + "Failed to resolve default credential cache." }, + { SMB_ADS_KRB5_PARSE_PRINCIPAL, + "Failed parsing the user principal name." }, + { SMB_ADS_KRB5_GET_INIT_CREDS_PW, + "Failed getting initial credentials. (Wrong password?)" }, + { SMB_ADS_KRB5_CC_INITIALIZE, + "Failed initializing the credential cache." }, + { SMB_ADS_KRB5_CC_STORE_CRED, + "Failed to update the credential cache." }, + { SMB_ADS_CANT_LOCATE_DC, + "Failed to locate a domain controller." }, + { SMB_ADS_LDAP_INIT, + "Failed to create an LDAP handle." }, + { SMB_ADS_LDAP_SETOPT, + "Failed to set an LDAP option." }, + { SMB_ADS_LDAP_SET_DOM, + "Failed to set the LDAP handle DN." }, + { SMB_ADS_LDAP_SASL_BIND, + "Failed to bind the LDAP handle. " + "Usually indicates an authentication problem." }, + + { SMB_ADJOIN_ERR_GEN_PWD, + "Failed to generate machine password." }, + { SMB_ADJOIN_ERR_GET_DCLEVEL, "Unknown functional level of " + "the domain controller. The rootDSE attribute named " + "\"domainControllerFunctionality\" is missing from the " + "Active Directory." }, + { SMB_ADJOIN_ERR_ADD_TRUST_ACCT, "Failed to create the " + "workstation trust account." }, + { SMB_ADJOIN_ERR_MOD_TRUST_ACCT, "Failed to modify the " + "workstation trust account." }, + { SMB_ADJOIN_ERR_DUP_TRUST_ACCT, "Failed to create the " + "workstation trust account because its name is already " + "in use." }, + { SMB_ADJOIN_ERR_TRUST_ACCT, "Error in querying the " + "workstation trust account" }, + { SMB_ADJOIN_ERR_INIT_KRB_CTX, "Failed to initialize Kerberos " + "context." }, + { SMB_ADJOIN_ERR_GET_SPNS, "Failed to get Kerberos " + "principals." }, + { SMB_ADJOIN_ERR_KSETPWD, "Failed to set machine password." }, + { SMB_ADJOIN_ERR_UPDATE_CNTRL_ATTR, "Failed to modify " + "userAccountControl attribute of the workstation trust " + "account." }, + { SMB_ADJOIN_ERR_WRITE_KEYTAB, "Error in writing to local " + "keytab file (i.e /etc/krb5/krb5.keytab)." }, + { SMB_ADJOIN_ERR_IDMAP_SET_DOMAIN, "Failed to update idmap " + "configuration." }, + { SMB_ADJOIN_ERR_IDMAP_REFRESH, "Failed to refresh idmap " + "service." }, + { SMB_ADJOIN_ERR_COMMIT_KEYTAB, "Failed to commit changes to " + "local keytab file (i.e. /etc/krb5/krb5.keytab)." }, + { SMB_ADJOIN_ERR_AUTH_NETLOGON, + "Failed to authenticate using the new computer account." }, + { SMB_ADJOIN_ERR_STORE_PROPS, + "Failed to store computer account information locally." }, + { 0, NULL } +}; /* - * smb_ads_select_dcfromsubnet - * - * This method walks the list of DCs and returns the first DC record that - * responds to ldap ping and is in the same subnet as the host. + * smb_ads_strerror * - * Returns a pointer to the found DC record. - * Returns NULL, on error or if no DC record is found. + * Lookup an error message for the specific adjoin error code. */ -static smb_ads_host_info_t * -smb_ads_select_dcfromsubnet(smb_ads_host_list_t *hlist) +const char * +smb_ads_strerror(int err) { - smb_ads_host_info_t *hentry; - smb_nic_t *lnic; - smb_niciter_t ni; - size_t cnt; - int i; - - if (smb_nic_getfirst(&ni) != SMB_NIC_SUCCESS) - return (NULL); - do { - lnic = &ni.ni_nic; - cnt = hlist->ah_cnt; - - for (i = 0; i < cnt; i++) { - hentry = &hlist->ah_list[i]; - if ((hentry->ipaddr.a_family == AF_INET) && - (lnic->nic_ip.a_family == AF_INET)) { - if ((hentry->ipaddr.a_ipv4 & - lnic->nic_mask) == - (lnic->nic_ip.a_ipv4 & - lnic->nic_mask)) - if (smb_ads_ldap_ping(hentry) == 0) - return (hentry); - } - } - } while (smb_nic_getnext(&ni) == SMB_NIC_SUCCESS); - - return (NULL); -} + const struct xlate_table *xt; -/* - * smb_ads_select_dcfromlist - * - * This method walks the list of DCs and returns the first DC that - * responds to ldap ping. - * - * Returns a pointer to the found DC record. - * Returns NULL if no DC record is found. - */ -static smb_ads_host_info_t * -smb_ads_select_dcfromlist(smb_ads_host_list_t *hlist) -{ - smb_ads_host_info_t *hentry; - size_t cnt; - int i; + if (err > 0 && err < SMB_ADS_ERRNO_GAP) + return (strerror(err)); - cnt = hlist->ah_cnt; - for (i = 0; i < cnt; i++) { - hentry = &hlist->ah_list[i]; - if (smb_ads_ldap_ping(hentry) == 0) - return (hentry); - } + for (xt = adjoin_table; xt->msg; xt++) + if (xt->err == err) + return (xt->msg); - return (NULL); + return ("Unknown error code."); } -/* - * smb_ads_dc_compare - * - * Comparision function for sorting host entries (SRV records of DC) via qsort. - * RFC 2052/2782 are taken as reference, while implementing this algorithm. - * - * Domain Controllers(DCs) with lowest priority in their SRV DNS records - * are selected first. If they have equal priorities, then DC with highest - * weight in its SRV DNS record is selected. If the priority and weight are - * both equal, then the DC at the top of the list is selected. - */ -static int -smb_ads_dc_compare(const void *p, const void *q) +void +smb_ads_log_errmsg(smb_ads_status_t err) { - smb_ads_host_info_t *h1 = (smb_ads_host_info_t *)p; - smb_ads_host_info_t *h2 = (smb_ads_host_info_t *)q; - - if (h1->priority < h2->priority) - return (-1); - if (h1->priority > h2->priority) - return (1); - - /* Priorities are equal */ - if (h1->weight < h2->weight) - return (1); - if (h1->weight > h2->weight) - return (-1); - - return (0); + const char *s = smb_ads_strerror(err); + syslog(LOG_NOTICE, "%s", s); } -/* - * smb_ads_select_dc - * - * The list of ADS hosts returned by ADS lookup, is sorted by lowest priority - * and highest weight. On this sorted list, following additional rules are - * applied, to select a DC. - * - * - If there is a DC in the same subnet, then return the DC, - * if it responds to ldap ping. - * - Else, return first DC that responds to ldap ping. - * - * A reference to the host entry from input host list is returned. - * - * Returns NULL on error. - */ -static smb_ads_host_info_t * -smb_ads_select_dc(smb_ads_host_list_t *hlist) -{ - smb_ads_host_info_t *hentry = NULL; - - if (hlist->ah_cnt == 0) - return (NULL); - - if (hlist->ah_cnt == 1) { - hentry = hlist->ah_list; - if (smb_ads_ldap_ping(hentry) == 0) - return (hentry); - } - - /* Sort the list by priority and weight */ - qsort(hlist->ah_list, hlist->ah_cnt, - sizeof (smb_ads_host_info_t), smb_ads_dc_compare); - - if ((hentry = smb_ads_select_dcfromsubnet(hlist)) != NULL) - return (hentry); - - if ((hentry = smb_ads_select_dcfromlist(hlist)) != NULL) - return (hentry); - - return (NULL); -} /* * smb_ads_lookup_msdcs @@ -2547,39 +1953,30 @@ smb_ads_select_dc(smb_ads_host_list_t *hlist) * Returns the discovered DC via buf. * * fqdn - fully-qualified domain name - * server - fully-qualifed hostname of a DC - * buf - the hostname of the discovered DC + * dci - the name and address of the found DC */ -boolean_t -smb_ads_lookup_msdcs(char *fqdn, char *server, char *buf, uint32_t buflen) +uint32_t +smb_ads_lookup_msdcs(char *fqdn, smb_dcinfo_t *dci) { smb_ads_host_info_t *hinfo = NULL; - char *p; - char *sought_host; char ipstr[INET6_ADDRSTRLEN]; - if (!fqdn || !buf) - return (B_FALSE); + if (!fqdn || !dci) + return (NT_STATUS_INTERNAL_ERROR); ipstr[0] = '\0'; - *buf = '\0'; - sought_host = (*server == 0 ? NULL : server); - if ((hinfo = smb_ads_find_host(fqdn, sought_host)) == NULL) - return (B_FALSE); + if ((hinfo = smb_ads_find_host(fqdn)) == NULL) + return (NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND); (void) smb_inet_ntop(&hinfo->ipaddr, ipstr, SMB_IPSTRLEN(hinfo->ipaddr.a_family)); smb_tracef("msdcsLookupADS: %s [%s]", hinfo->name, ipstr); - (void) strlcpy(buf, hinfo->name, buflen); - /* - * Remove the domain extension - */ - if ((p = strchr(buf, '.')) != 0) - *p = '\0'; + (void) strlcpy(dci->dc_name, hinfo->name, sizeof (dci->dc_name)); + dci->dc_addr = hinfo->ipaddr; free(hinfo); - return (B_TRUE); + return (NT_STATUS_SUCCESS); } static krb5_enctype * diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_krb.c b/usr/src/lib/smbsrv/libsmbns/common/smbns_krb.c index 4bb8ef49d6..aebc6f8c06 100644 --- a/usr/src/lib/smbsrv/libsmbns/common/smbns_krb.c +++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_krb.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ /* * Copyright 1990 by the Massachusetts Institute of Technology. @@ -59,8 +60,9 @@ #include <smbns_krb.h> int -smb_kinit(char *principal_name, char *principal_passwd) +smb_kinit(char *domain_name, char *principal_name, char *principal_passwd) { + char default_realm[MAXHOSTNAMELEN]; krb5_context ctx = NULL; krb5_ccache cc = NULL; krb5_principal me = NULL; @@ -68,6 +70,7 @@ smb_kinit(char *principal_name, char *principal_passwd) krb5_error_code code; const char *errmsg = NULL; const char *doing = NULL; + smb_ads_status_t err; assert(principal_name != NULL); assert(principal_passwd != NULL); @@ -81,12 +84,21 @@ smb_kinit(char *principal_name, char *principal_passwd) code = krb5_init_context(&ctx); if (code) { + err = SMB_ADS_KRB5_INIT_CTX; doing = "smbns_krb: initializing context"; goto cleanup; } + /* + * In case krb5.conf is not configured, set the default realm. + */ + (void) strlcpy(default_realm, domain_name, sizeof (default_realm)); + (void) smb_strupr(default_realm); + (void) krb5_set_default_realm(ctx, default_realm); + code = krb5_cc_default(ctx, &cc); if (code != 0) { + err = SMB_ADS_KRB5_CC_DEFAULT; doing = "smbns_krb: resolve default credentials cache"; goto cleanup; } @@ -94,6 +106,7 @@ smb_kinit(char *principal_name, char *principal_passwd) /* Use specified name */ code = krb5_parse_name(ctx, principal_name, &me); if (code != 0) { + err = SMB_ADS_KRB5_PARSE_PRINCIPAL; doing = "smbns_krb: parsing principal name"; goto cleanup; } @@ -102,6 +115,7 @@ smb_kinit(char *principal_name, char *principal_passwd) principal_passwd, NULL, 0, (krb5_deltat)0, NULL, NULL); if (code != 0) { + err = SMB_ADS_KRB5_GET_INIT_CREDS_PW; doing = "smbns_krb: getting initial credentials"; if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) { @@ -113,17 +127,20 @@ smb_kinit(char *principal_name, char *principal_passwd) code = krb5_cc_initialize(ctx, cc, me); if (code != 0) { + err = SMB_ADS_KRB5_CC_INITIALIZE; doing = "smbns_krb: initializing cache"; goto cleanup; } code = krb5_cc_store_cred(ctx, cc, &my_creds); if (code != 0) { + err = SMB_ADS_KRB5_CC_STORE_CRED; doing = "smbns_krb: storing credentials"; goto cleanup; } /* SUCCESS! */ + err = SMB_ADS_SUCCESS; cleanup: if (code != 0) { @@ -145,7 +162,7 @@ cleanup: if (ctx) krb5_free_context(ctx); - return (code == 0); + return (err); } /* diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_krb.h b/usr/src/lib/smbsrv/libsmbns/common/smbns_krb.h index 279634afc2..50c3985564 100644 --- a/usr/src/lib/smbsrv/libsmbns/common/smbns_krb.h +++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_krb.h @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #ifndef _SMBSRV_SMB_KRB_H @@ -40,6 +41,7 @@ extern "C" { #define SMB_PN_SALT 0x0008 /* w/ REALM */ #define SMB_PN_SVC_HOST "host" +#define SMB_PN_SVC_CIFS "cifs" #define SMB_PN_SVC_NFS "nfs" #define SMB_PN_SVC_HTTP "HTTP" #define SMB_PN_SVC_ROOT "root" @@ -47,7 +49,11 @@ extern "C" { /* Assign an identifier for each principal name format */ typedef enum smb_krb5_pn_id { SMB_KRB5_PN_ID_SALT, - SMB_KRB5_PN_ID_HOST_FQHN, + SMB_KRB5_PN_ID_HOST_FQHN, /* fully qualified name */ + SMB_KRB5_PN_ID_HOST_SHORT, /* short name */ + SMB_KRB5_PN_ID_CIFS_FQHN, + SMB_KRB5_PN_ID_CIFS_SHORT, + SMB_KRB5_PN_ID_MACHINE, /* the machine account */ SMB_KRB5_PN_ID_NFS_FQHN, SMB_KRB5_PN_ID_HTTP_FQHN, SMB_KRB5_PN_ID_ROOT_FQHN, @@ -78,7 +84,7 @@ typedef struct smb_krb5_pn_set { char **s_pns; } smb_krb5_pn_set_t; -int smb_kinit(char *, char *); +int smb_kinit(char *, char *, char *); int smb_krb5_ctx_init(krb5_context *); void smb_krb5_ctx_fini(krb5_context); int smb_krb5_get_kprincs(krb5_context, char **, size_t, krb5_principal **); diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_ksetpwd.c b/usr/src/lib/smbsrv/libsmbns/common/smbns_ksetpwd.c index 946ca6461a..9478b77291 100644 --- a/usr/src/lib/smbsrv/libsmbns/common/smbns_ksetpwd.c +++ b/usr/src/lib/smbsrv/libsmbns/common/smbns_ksetpwd.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #include <stdio.h> @@ -49,9 +50,17 @@ static smb_krb5_pn_t smb_krb5_pn_tab[] = { */ {SMB_KRB5_PN_ID_SALT, SMB_PN_SVC_HOST, SMB_PN_SALT}, - /* HOST */ + /* CIFS SPNs. (HOST, CIFS, ...) */ {SMB_KRB5_PN_ID_HOST_FQHN, SMB_PN_SVC_HOST, SMB_PN_KEYTAB_ENTRY | SMB_PN_SPN_ATTR | SMB_PN_UPN_ATTR}, + {SMB_KRB5_PN_ID_HOST_SHORT, SMB_PN_SVC_HOST, + SMB_PN_KEYTAB_ENTRY | SMB_PN_SPN_ATTR}, + {SMB_KRB5_PN_ID_CIFS_FQHN, SMB_PN_SVC_CIFS, + SMB_PN_KEYTAB_ENTRY | SMB_PN_SPN_ATTR}, + {SMB_KRB5_PN_ID_CIFS_SHORT, SMB_PN_SVC_CIFS, + SMB_PN_KEYTAB_ENTRY | SMB_PN_SPN_ATTR}, + {SMB_KRB5_PN_ID_MACHINE, NULL, + SMB_PN_KEYTAB_ENTRY}, /* NFS */ {SMB_KRB5_PN_ID_NFS_FQHN, SMB_PN_SVC_NFS, @@ -246,15 +255,20 @@ smb_krb5_setpwd(krb5_context ctx, const char *fqdn, char *passwd) code = krb5_set_password_using_ccache(ctx, cc, passwd, princ, &result_code, &result_code_string, &result_string); + (void) krb5_cc_close(ctx, cc); + if (code != 0) smb_krb5_log_errmsg(ctx, "smbns_ksetpwd: KPASSWD protocol " "exchange failed", code); - (void) krb5_cc_close(ctx, cc); - - if (result_code != 0) - syslog(LOG_ERR, "smbns_ksetpwd: KPASSWD failed: %s", + if (result_code != 0) { + syslog(LOG_ERR, "smbns_ksetpwd: KPASSWD failed: rc=%d %.*s", + result_code, + result_code_string.length, result_code_string.data); + if (code == 0) + code = EACCES; + } krb5_free_principal(ctx, princ); free(result_code_string.data); @@ -529,12 +543,30 @@ smb_krb5_get_pn_by_id(smb_krb5_pn_id_t id, uint32_t type, break; case SMB_KRB5_PN_ID_HOST_FQHN: + case SMB_KRB5_PN_ID_CIFS_FQHN: case SMB_KRB5_PN_ID_NFS_FQHN: case SMB_KRB5_PN_ID_HTTP_FQHN: case SMB_KRB5_PN_ID_ROOT_FQHN: (void) asprintf(&buf, "%s/%s.%s", pn->p_svc, hostname, fqdn); break; + + case SMB_KRB5_PN_ID_HOST_SHORT: + case SMB_KRB5_PN_ID_CIFS_SHORT: + (void) asprintf(&buf, "%s/%s", + pn->p_svc, nbname); + break; + + /* + * SPN for the machine account, which is simply the + * (short) machine name with a dollar sign appended. + */ + case SMB_KRB5_PN_ID_MACHINE: + (void) asprintf(&buf, "%s$", nbname); + break; + + default: + return (NULL); } /* diff --git a/usr/src/man/man1m/smbadm.1m b/usr/src/man/man1m/smbadm.1m index b1e6e2de98..3ce3ecf923 100644 --- a/usr/src/man/man1m/smbadm.1m +++ b/usr/src/man/man1m/smbadm.1m @@ -1,10 +1,10 @@ '\" te -.\" Copyright 2011 Nexenta Systems, Inc. All rights reserved. +.\" Copyright 2014 Nexenta Systems, Inc. All rights reserved. .\" Copyright (c) 2009, Sun Microsystems, Inc. All Rights Reserved. .\" 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] -.TH SMBADM 1M "Jan 8, 2009" +.TH SMBADM 1M "Feb 19, 2014" .SH NAME smbadm \- configure and manage CIFS local groups and users, and manage domain membership @@ -41,12 +41,12 @@ membership .LP .nf -\fBsmbadm join\fR -u \fIusername\fR \fIdomain\fR +\fBsmbadm join\fR [-y] -u \fIusername\fR \fIdomain\fR .fi .LP .nf -\fBsmbadm join\fR -w \fIworkgroup\fR +\fBsmbadm join\fR [-y] -w \fIworkgroup\fR .fi .LP @@ -80,7 +80,6 @@ membership .fi .SH DESCRIPTION -.sp .LP The \fBsmbadm\fR command is used to configure \fBCIFS\fR local groups and to manage domain membership. You can also use the \fBsmbadm\fR command to enable @@ -161,7 +160,6 @@ then reset the user's password by using the \fBpasswd\fR command. The \fBpam_smb_passwd.so.1\fR module must be added to the system's PAM configuration to generate an SMB password. .SS "Escaping Backslash Character" -.sp .LP For the \fBadd-member\fR, \fBremove-member\fR, and \fBjoin\fR (with \fB-u\fR) subcommands, the backslash character (\fB\e\fR) is a valid separator between @@ -171,7 +169,6 @@ backslash character with another backslash character: \fIdomain\fR\fB\e\e\fR\fIusername\fR. For more information about handling shell special characters, see the man page for your shell. .SH OPERANDS -.sp .LP The \fBsmbadm\fR command uses the following operands: .sp @@ -205,7 +202,6 @@ Specifies the name of a Solaris local user. .RE .SH SUB-COMMANDS -.sp .LP The \fBsmbadm\fR command includes these subcommands: .sp @@ -301,7 +297,7 @@ all property values are shown. .sp .ne 2 .na -\fB\fBjoin\fR \fB-u\fR \fIusername\fR \fIdomain\fR\fR +\fB\fBjoin\fR \fB[-y] -u\fR \fIusername\fR \fIdomain\fR\fR .ad .sp .6 .RS 4n @@ -335,12 +331,13 @@ controller, any authenticated user account can be used when joining the domain. However, if the machine trust account does \fBnot\fR already exist, an account that has administrative privileges on the domain is required to join the domain. +Specifying \fB-y\fR will bypass the smb service restart prompt. .RE .sp .ne 2 .na -\fB\fBjoin\fR \fB-w\fR \fIworkgroup\fR\fR +\fB\fBjoin\fR \fB[-y] -w\fR \fIworkgroup\fR\fR .ad .sp .6 .RS 4n @@ -348,6 +345,7 @@ Joins a Windows domain or a workgroup. .sp The \fB-w\fR \fIworkgroup\fR option specifies the name of the workgroup to join when using the \fBjoin\fR subcommand. +Specifying \fB-y\fR will bypass the smb service restart prompt. .RE .sp @@ -528,7 +526,6 @@ specified, the group privileges are also shown. .RE .SH EXIT STATUS -.sp .LP The following exit values are returned: .sp @@ -550,7 +547,6 @@ An error occurred. .RE .SH ATTRIBUTES -.sp .LP See the \fBattributes\fR(5) man page for descriptions of the following attributes: @@ -571,7 +567,6 @@ _ .TE .SH SEE ALSO -.sp .LP \fBpasswd\fR(1), \fBgroupadd\fR(1M), \fBidmap\fR(1M), \fBidmapd\fR(1M), \fBkclient\fR(1M), \fBshare\fR(1M), \fBsharectl\fR(1M), \fBsharemgr\fR(1M), diff --git a/usr/src/pkg/manifests/SUNWcs.mf b/usr/src/pkg/manifests/SUNWcs.mf index 3c9a69cc84..cf7d4230d6 100644 --- a/usr/src/pkg/manifests/SUNWcs.mf +++ b/usr/src/pkg/manifests/SUNWcs.mf @@ -21,9 +21,9 @@ # # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. -# Copyright 2015 Nexenta Systems, Inc. All rights reserved. # Copyright (c) 2013 Gary Mills # Copyright 2014 Garrett D'Amore <garrett@damore.org> +# Copyright 2015 Nexenta Systems, Inc. All rights reserved. # <include SUNWcs.man1.inc> @@ -1351,6 +1351,7 @@ file path=usr/sbin/mvdir mode=0555 file path=usr/sbin/ndd mode=0555 file path=usr/sbin/ndp mode=0555 file path=usr/sbin/nlsadmin group=adm mode=0755 +file path=usr/sbin/nltest mode=0555 file path=usr/sbin/nscd mode=0555 file path=usr/sbin/nwamadm mode=0555 file path=usr/sbin/nwamcfg mode=0555 diff --git a/usr/src/pkg/manifests/developer-library-lint.mf b/usr/src/pkg/manifests/developer-library-lint.mf index f9397a1c45..b513373f6e 100644 --- a/usr/src/pkg/manifests/developer-library-lint.mf +++ b/usr/src/pkg/manifests/developer-library-lint.mf @@ -20,9 +20,9 @@ # # -# Copyright 2011 Nexenta Systems, Inc. All rights reserved. # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2012 OmniTI Computer Consulting, Inc. All rights reserved. +# Copyright 2014 Nexenta Systems, Inc. All rights reserved. # set name=pkg.fmri value=pkg:/developer/library/lint@$(PKGVERS) @@ -198,6 +198,7 @@ $(i386_ONLY)file path=usr/lib/$(ARCH64)/crt1.o file path=usr/lib/$(ARCH64)/crti.o file path=usr/lib/$(ARCH64)/crtn.o $(i386_ONLY)file path=usr/lib/$(ARCH64)/gcrt1.o +file path=usr/lib/$(ARCH64)/llib-lads.ln file path=usr/lib/$(ARCH64)/llib-ladutils.ln file path=usr/lib/$(ARCH64)/llib-lbsdmalloc.ln file path=usr/lib/$(ARCH64)/llib-lcfgadm.ln @@ -246,6 +247,8 @@ $(i386_ONLY)file path=usr/lib/crt1.o file path=usr/lib/crti.o file path=usr/lib/crtn.o $(i386_ONLY)file path=usr/lib/gcrt1.o +file path=usr/lib/llib-lads +file path=usr/lib/llib-lads.ln file path=usr/lib/llib-ladutils file path=usr/lib/llib-ladutils.ln file path=usr/lib/llib-lbsdmalloc diff --git a/usr/src/pkg/manifests/service-file-system-smb.mf b/usr/src/pkg/manifests/service-file-system-smb.mf index 41f836c1b0..2cc02917e3 100644 --- a/usr/src/pkg/manifests/service-file-system-smb.mf +++ b/usr/src/pkg/manifests/service-file-system-smb.mf @@ -20,9 +20,8 @@ # # -# Copyright 2011 Nexenta Systems, Inc. All rights reserved. # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. -# Copyright 2011 Nexenta Systems, Inc. All rights reserved. +# Copyright 2014 Nexenta Systems, Inc. All rights reserved. # set name=pkg.fmri value=pkg:/service/file-system/smb@$(PKGVERS) @@ -53,6 +52,7 @@ dir path=usr/lib/mdb/kvm/$(ARCH64) group=sys dir path=usr/lib/reparse dir path=usr/lib/security dir path=usr/lib/smbsrv +dir path=usr/lib/smbsrv/dtrace dir path=usr/sbin dir path=usr/share/man dir path=usr/share/man/man1m @@ -79,6 +79,13 @@ file path=usr/lib/mdb/kvm/$(ARCH64)/smbsrv.so group=sys mode=0555 $(i386_ONLY)file path=usr/lib/mdb/kvm/smbsrv.so group=sys mode=0555 file path=usr/lib/reparse/libreparse_smb.so.1 file path=usr/lib/security/pam_smb_passwd.so.1 +file path=usr/lib/smbsrv/dtrace/smbd-all.d mode=0555 +file path=usr/lib/smbsrv/dtrace/smbd-authsvc.d mode=0555 +file path=usr/lib/smbsrv/dtrace/smbd-doorsvc.d mode=0555 +file path=usr/lib/smbsrv/dtrace/smbd-pipesvc.d mode=0555 +file path=usr/lib/smbsrv/dtrace/smbnode.d mode=0555 +file path=usr/lib/smbsrv/dtrace/smbsrv.d mode=0555 +file path=usr/lib/smbsrv/dtrace/smbvfs.d mode=0555 file path=usr/lib/smbsrv/libmlrpc.so.1 file path=usr/lib/smbsrv/libmlsvc.so.1 file path=usr/lib/smbsrv/libsmb.so.1 diff --git a/usr/src/pkg/manifests/system-header.mf b/usr/src/pkg/manifests/system-header.mf index 08f0b19416..4551ca095c 100644 --- a/usr/src/pkg/manifests/system-header.mf +++ b/usr/src/pkg/manifests/system-header.mf @@ -22,9 +22,9 @@ # # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. # Copyright (c) 2012 by Delphix. All rights reserved. -# Copyright 2015 Nexenta Systems, Inc. All rights reserved. -# Copyright 2014 Garrett D'Amore <garrett@damore.org> # Copyright 2013 Saso Kiselkov. All rights reserved. +# Copyright 2014 Garrett D'Amore <garrett@damore.org> +# Copyright 2015 Nexenta Systems, Inc. All rights reserved. # set name=pkg.fmri value=pkg:/system/header@$(PKGVERS) @@ -37,6 +37,7 @@ dir path=usr group=sys dir path=usr/include $(i386_ONLY)dir path=usr/include/$(ARCH64) $(i386_ONLY)dir path=usr/include/$(ARCH64)/sys +dir path=usr/include/ads dir path=usr/include/arpa dir path=usr/include/asm dir path=usr/include/ast @@ -192,6 +193,7 @@ dir path=usr/xpg4/include $(i386_ONLY)file path=usr/include/$(ARCH64)/sys/kdi_regs.h $(i386_ONLY)file path=usr/include/$(ARCH64)/sys/privmregs.h $(i386_ONLY)file path=usr/include/$(ARCH64)/sys/privregs.h +file path=usr/include/ads/dsgetdc.h file path=usr/include/aio.h file path=usr/include/alloca.h file path=usr/include/apptrace.h @@ -461,6 +463,7 @@ file path=usr/include/iso/wctype_iso.h file path=usr/include/iso646.h file path=usr/include/kerberosv5/com_err.h file path=usr/include/kerberosv5/krb5.h +file path=usr/include/kerberosv5/locate_plugin.h file path=usr/include/kerberosv5/mit-sipb-copyright.h file path=usr/include/kerberosv5/mit_copyright.h file path=usr/include/klpd.h diff --git a/usr/src/pkg/manifests/system-library.mf b/usr/src/pkg/manifests/system-library.mf index e50eb582df..e468a18091 100644 --- a/usr/src/pkg/manifests/system-library.mf +++ b/usr/src/pkg/manifests/system-library.mf @@ -20,10 +20,10 @@ # # -# Copyright 2014 Nexenta Systems, Inc. # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2012 OmniTI Computer Consulting, Inc. All rights reserved. # Copyright (c) 2013 Gary Mills +# Copyright 2014 Nexenta Systems, Inc. All rights reserved. # <include system-library.man3.inc> @@ -321,6 +321,7 @@ file path=lib/nss_nis.so.1 file path=lib/nss_user.so.1 file path=usr/lib/$(ARCH64)/0@0.so.1 file path=usr/lib/$(ARCH64)/getloginx.so.1 +file path=usr/lib/$(ARCH64)/libads.so.1 file path=usr/lib/$(ARCH64)/libadutils.so.1 file path=usr/lib/$(ARCH64)/libast.so.1 file path=usr/lib/$(ARCH64)/libbsdmalloc.so.1 @@ -392,6 +393,7 @@ file path=usr/lib/cfgadm/usb.so.1 file path=usr/lib/extendedFILE.so.1 file path=usr/lib/getloginx.so.1 file path=usr/lib/lib.b mode=0444 +file path=usr/lib/libads.so.1 file path=usr/lib/libadutils.so.1 file path=usr/lib/libast.so.1 file path=usr/lib/libbsdmalloc.so.1 @@ -757,6 +759,7 @@ link path=usr/lib/$(ARCH64)/libadm.so \ target=../../../lib/$(ARCH64)/libadm.so.1 link path=usr/lib/$(ARCH64)/libadm.so.1 \ target=../../../lib/$(ARCH64)/libadm.so.1 +link path=usr/lib/$(ARCH64)/libads.so target=./libads.so.1 link path=usr/lib/$(ARCH64)/libadutils.so target=./libadutils.so.1 link path=usr/lib/$(ARCH64)/libaio.so \ target=../../../lib/$(ARCH64)/libaio.so.1 @@ -1039,6 +1042,7 @@ link path=usr/lib/cfgadm/shp.so target=./shp.so.1 link path=usr/lib/cfgadm/usb.so target=./usb.so.1 link path=usr/lib/libadm.so target=../../lib/libadm.so.1 link path=usr/lib/libadm.so.1 target=../../lib/libadm.so.1 +link path=usr/lib/libads.so target=./libads.so.1 link path=usr/lib/libadutils.so target=./libadutils.so.1 link path=usr/lib/libaio.so target=../../lib/libaio.so.1 link path=usr/lib/libaio.so.1 target=../../lib/libaio.so.1 diff --git a/usr/src/tools/quick/make-idmap b/usr/src/tools/quick/make-idmap index b587d2c5bc..a69ab3a1a2 100755 --- a/usr/src/tools/quick/make-idmap +++ b/usr/src/tools/quick/make-idmap @@ -97,6 +97,7 @@ fi # Need some library headers too... for lib in \ + libads \ libbsm \ libcmdutils \ libcryptoutil \ @@ -135,6 +136,7 @@ for lib in \ libadutils \ libuutil \ libidmap \ + libads \ libsmbfs \ libsqlite \ nsswitch/ad @@ -186,6 +188,8 @@ do_tar() { files=" lib/svc/manifest/system/idmap.xml usr/lib/idmapd +usr/lib/libads.so.1 +usr/lib/$arch64/libads.so.1 usr/lib/libadutils.so.1 usr/lib/$arch64/libadutils.so.1 usr/lib/libidmap.so.1 diff --git a/usr/src/tools/quick/make-smbsrv b/usr/src/tools/quick/make-smbsrv index 3eca17ff83..31c76379ca 100755 --- a/usr/src/tools/quick/make-smbsrv +++ b/usr/src/tools/quick/make-smbsrv @@ -20,7 +20,8 @@ make=${MAKE:-dmake} CLOSED_IS_PRESENT=no export CLOSED_IS_PRESENT -export SOURCEDEBUG=yes +# Do this if you want to use dbx or gdb +# export SOURCEDEBUG=yes [ -n "$SRC" ] || { echo "SRC not set. Run 'ws' or 'bldenv' first." @@ -88,20 +89,23 @@ then (cd $SRC/head && $make install_h) # always update the smbsrv headers to be safe - # test -f $ROOT/usr/include/smbsrv/wintypes.h || - (cd $SRC/uts/common/sys && $make -k install_h) - (cd $SRC/uts/common/smb && $make -k install_h) - (cd $SRC/uts/common/smbsrv && $make -k install_h) + (cd $SRC/uts/common/gssapi && $make -k install_h) + (cd $SRC/uts/common/sys && $make -k install_h) + (cd $SRC/uts/common/smb && $make -k install_h) + (cd $SRC/uts/common/smbsrv && $make -k install_h) fi # Need some library headers too... for lib in \ + libads \ libbsm \ libcmdutils \ libcryptoutil \ libdevid \ libfakekernel \ + libgss \ + libkrb5 \ libidmap \ libpam \ libsec \ @@ -140,6 +144,8 @@ do_lib1() { for lib in \ libavl \ + libgss \ + libkrb5 \ libcmdutils \ libsqlite \ libuutil @@ -155,6 +161,7 @@ do_lib2() { for lib in \ libfakekernel \ + libads \ libsmbfs do (cd $SRC/lib/$lib && $make $1) @@ -241,8 +248,12 @@ do_tags() { find uts/common/smbsrv -name '*.ndl' -print |sort find uts/common/smbsrv -name '*.[ch]' -print |sort find uts/common/fs/smbsrv -name '*.[ch]' -print |sort - find lib/libsmbfs -name '*.[ch]' -print |sort + find uts/common/gssapi -name '*.[ch]' -print |sort + find head -name '*.h' -print |sort find lib/smbsrv -name '*.[ch]' -print |sort + find lib/libsmbfs -name '*.[ch]' -print |sort + find lib/libads -name '*.[ch]' -print |sort + find lib/libgss -name '*.[ch]' -print |sort find cmd/smbsrv -name '*.[ch]' -print |sort find common/smbsrv -name '*.[ch]' -print |sort ) > $SRC/cscope.files @@ -256,7 +267,9 @@ do_tags() { # This creates a tarfile one can use to update a test machine. do_tar() { + git_rev=`git rev-parse --short=8 HEAD` files=" +lib/svc/manifest/network/smb/server.xml usr/kernel/drv/$arch64/smbsrv usr/kernel/drv/smbsrv usr/kernel/kmdb/$arch64/smbsrv @@ -268,6 +281,7 @@ usr/lib/mdb/kvm/$arch64/smbsrv.so usr/lib/mdb/kvm/smbsrv.so usr/lib/reparse/libreparse_smb.so.1 usr/lib/security/pam_smb_passwd.so.1 +usr/lib/smbsrv/dtrace usr/lib/smbsrv/libmlrpc.so.1 usr/lib/smbsrv/libmlsvc.so.1 usr/lib/smbsrv/libsmb.so.1 @@ -278,7 +292,7 @@ usr/sbin/smbadm usr/sbin/smbstat " - (cd $ROOT && tar cfj ../../smbsrv.tar.bz2 $files) + (cd $ROOT && tar cfj ../../smbsrv-${git_rev}.tar.bz2 $files) } ################################################################ diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files index 6090baab7e..f1c0de1b21 100644 --- a/usr/src/uts/common/Makefile.files +++ b/usr/src/uts/common/Makefile.files @@ -1215,10 +1215,12 @@ SMBSRV_SHARED_OBJS += \ SMBSRV_OBJS += $(SMBSRV_SHARED_OBJS) \ smb_acl.o \ smb_alloc.o \ + smb_authenticate.o \ smb_close.o \ smb_common_open.o \ smb_common_transact.o \ smb_create.o \ + smb_cred.o \ smb_delete.o \ smb_directory.o \ smb_dispatch.o \ diff --git a/usr/src/uts/common/fs/smbsrv/smb_authenticate.c b/usr/src/uts/common/fs/smbsrv/smb_authenticate.c new file mode 100644 index 0000000000..ffc553cada --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_authenticate.c @@ -0,0 +1,706 @@ +/* + * 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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + */ + +/* + * Authentication support for SMB session setup + */ + +#include <sys/types.h> +#include <sys/sid.h> +#include <sys/priv_names.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <netinet/in.h> +#include <smbsrv/smb_idmap.h> +#include <smbsrv/smb_kproto.h> +#include <smbsrv/smb_token.h> + +static uint32_t smb_authsock_open(smb_user_t *); +static int smb_authsock_send(ksocket_t, void *, size_t); +static int smb_authsock_recv(ksocket_t, void *, size_t); +static uint32_t smb_authsock_sendrecv(smb_user_t *, smb_lsa_msg_hdr_t *hdr, + void *sndbuf, void **recvbuf); +/* void smb_authsock_close(smb_user_t *); kproto.h */ + +static uint32_t smb_auth_do_clinfo(smb_request_t *); +static uint32_t smb_auth_do_oldreq(smb_request_t *); +static uint32_t smb_auth_get_token(smb_request_t *); +static uint32_t smb_priv_xlate(smb_token_t *); + +/* + * Handle old-style session setup (non-extended security) + * + * The user information is passed to smbd for authentication. + * If smbd can authenticate the user an access token is returned and we + * generate a cred and new user based on the token. + */ +int +smb_authenticate_old(smb_request_t *sr) +{ + smb_user_t *user = NULL; + uint32_t status; + + user = smb_user_new(sr->session); + if (user == NULL) + return (NT_STATUS_TOO_MANY_SESSIONS); + + /* user cleanup in smb_request_free */ + sr->uid_user = user; + sr->smb_uid = user->u_uid; + + /* + * Open a connection to the local logon service. + * If we can't, it may be busy, or not running. + * Don't log here - this may be frequent. + */ + if ((status = smb_authsock_open(user)) != 0) + goto errout; + + /* + * Tell the auth. svc who this client is. + */ + if ((status = smb_auth_do_clinfo(sr)) != 0) + goto errout; + + /* + * Authentication proper + */ + if ((status = smb_auth_do_oldreq(sr)) != 0) + goto errout; + + /* + * Get the final auth. token. + */ + if ((status = smb_auth_get_token(sr)) != 0) + goto errout; + + return (0); + +errout: + smb_user_logoff(user); + return (status); +} + +/* + * Build an authentication request message and + * send it to the local logon service. + */ +static uint32_t +smb_auth_do_oldreq(smb_request_t *sr) +{ + smb_lsa_msg_hdr_t msg_hdr; + smb_logon_t user_info; + XDR xdrs; + smb_arg_sessionsetup_t *sinfo = sr->sr_ssetup; + smb_user_t *user = sr->uid_user; + void *sbuf = NULL; + void *rbuf = NULL; + uint32_t slen = 0; + uint32_t rlen = 0; + uint32_t status; + bool_t ok; + + bzero(&user_info, sizeof (smb_logon_t)); + + user_info.lg_level = NETR_NETWORK_LOGON; + user_info.lg_username = sinfo->ssi_user; + user_info.lg_domain = sinfo->ssi_domain; + user_info.lg_workstation = sr->session->workstation; + user_info.lg_clnt_ipaddr = sr->session->ipaddr; + user_info.lg_local_ipaddr = sr->session->local_ipaddr; + user_info.lg_local_port = sr->session->s_local_port; + user_info.lg_challenge_key.val = sr->session->challenge_key; + user_info.lg_challenge_key.len = sr->session->challenge_len; + user_info.lg_nt_password.val = sinfo->ssi_ntpwd; + user_info.lg_nt_password.len = sinfo->ssi_ntpwlen; + user_info.lg_lm_password.val = sinfo->ssi_lmpwd; + user_info.lg_lm_password.len = sinfo->ssi_lmpwlen; + user_info.lg_native_os = sr->session->native_os; + user_info.lg_native_lm = sr->session->native_lm; + /* lg_flags? */ + + slen = xdr_sizeof(smb_logon_xdr, &user_info); + sbuf = kmem_alloc(slen, KM_SLEEP); + xdrmem_create(&xdrs, sbuf, slen, XDR_ENCODE); + ok = smb_logon_xdr(&xdrs, &user_info); + xdr_destroy(&xdrs); + if (!ok) { + status = RPC_NT_BAD_STUB_DATA; + goto out; + } + + msg_hdr.lmh_msgtype = LSA_MTYPE_OLDREQ; + msg_hdr.lmh_msglen = slen; + status = smb_authsock_sendrecv(user, &msg_hdr, sbuf, &rbuf); + if (status != 0) + goto out; + rlen = msg_hdr.lmh_msglen; + kmem_free(sbuf, slen); + sbuf = NULL; + + /* + * Decode the response message. + */ + switch (msg_hdr.lmh_msgtype) { + + case LSA_MTYPE_OK: + status = 0; + break; + + case LSA_MTYPE_ERROR: + if (rlen == sizeof (smb_lsa_eresp_t)) { + smb_lsa_eresp_t *ler = rbuf; + status = ler->ler_ntstatus; + break; + } + /* FALLTHROUGH */ + + default: /* Bogus message type */ + status = NT_STATUS_INTERNAL_ERROR; + break; + } + +out: + if (rbuf != NULL) + kmem_free(rbuf, rlen); + if (sbuf != NULL) + kmem_free(sbuf, slen); + + return (status); +} + +/* + * Handle new-style (extended security) session setup. + * Returns zero: success, non-zero: error (value not used) + * + * Note that this style uses a sequence of session setup requests, + * where the first has SMB UID=0, and subsequent requests in the + * same authentication sequence have the SMB UID returned for that + * first request. We allocate a USER object when the first request + * in the sequence arrives (SMB_USER_STATE_LOGGING_ON) and use that + * to maintain state between requests in this sequence. The state + * for one sequence includes an AF_UNIX "authsock" connection to the + * user-space smbd. The neat part of this is: in smbd, the handler + * for the server-side of one authsock gets only request specific to + * one authentication sequence, simplifying it's work immensely. + * When the authentication sequence is finished, with either success + * or failure, the local side of the authsock is closed. + * + * As with the old-style authentication, if we succeed, then the + * last message from smbd will be an smb_token_t encoding the + * information about the new user. + * + * Outline: + * (a) On the first request (UID==0) create a USER object, + * and on subsequent requests, find USER by SMB UID. + * (b) Send message / recv. response as above, + * (c) If response says "we're done", close authsock + * (both success and failure must close authsock) + */ +int +smb_authenticate_ext(smb_request_t *sr) +{ + smb_lsa_msg_hdr_t msg_hdr; + smb_arg_sessionsetup_t *sinfo = sr->sr_ssetup; + smb_user_t *user = NULL; + void *rbuf = NULL; + uint32_t rlen = 0; + uint32_t status; + + ASSERT(sr->uid_user == NULL); + + /* + * On the first request (UID==0) create a USER object. + * On subsequent requests (UID!=0) find the USER object. + * Either way, sr->uid_user is set, so our ref. on the + * user object is dropped during normal cleanup work + * for the smb_request (sr). Ditto u_authsock. + */ + if (sr->smb_uid == 0) { + user = smb_user_new(sr->session); + if (user == NULL) + return (NT_STATUS_TOO_MANY_SESSIONS); + + /* user cleanup in smb_request_free */ + sr->uid_user = user; + sr->smb_uid = user->u_uid; + + /* + * Open a connection to the local logon service. + * If we can't, it may be busy, or not running. + * Don't log here - this may be frequent. + */ + if ((status = smb_authsock_open(user)) != 0) + goto errout; + + /* + * Tell the auth. svc who this client is. + */ + if ((status = smb_auth_do_clinfo(sr)) != 0) + goto errout; + + msg_hdr.lmh_msgtype = LSA_MTYPE_ESFIRST; + } else { + user = smb_session_lookup_uid_st(sr->session, + sr->smb_uid, SMB_USER_STATE_LOGGING_ON); + if (user == NULL) + return (NT_STATUS_USER_SESSION_DELETED); + + /* user cleanup in smb_request_free */ + sr->uid_user = user; + + msg_hdr.lmh_msgtype = LSA_MTYPE_ESNEXT; + } + + /* + * Wrap the "security blob" with our header + * (LSA_MTYPE_ESFIRST or LSA_MTYPE_ESNEXT) + * and send it up the authsock with either + */ + msg_hdr.lmh_msglen = sinfo->ssi_iseclen; + status = smb_authsock_sendrecv(user, &msg_hdr, + sinfo->ssi_isecblob, &rbuf); + if (status != 0) + goto errout; + rlen = msg_hdr.lmh_msglen; + + /* + * Decode the response message. + * Note: allocated rbuf + */ + switch (msg_hdr.lmh_msgtype) { + + case LSA_MTYPE_ES_CONT: + sinfo->ssi_oseclen = (uint16_t)rlen; + sinfo->ssi_osecblob = smb_srm_alloc(sr, sinfo->ssi_oseclen); + bcopy(rbuf, sinfo->ssi_osecblob, sinfo->ssi_oseclen); + /* + * This is not really an error, but tells the client + * it should send another session setup request. + */ + status = NT_STATUS_MORE_PROCESSING_REQUIRED; + break; + + case LSA_MTYPE_ES_DONE: + sinfo->ssi_oseclen = (uint16_t)rlen; + sinfo->ssi_osecblob = smb_srm_alloc(sr, sinfo->ssi_oseclen); + bcopy(rbuf, sinfo->ssi_osecblob, sinfo->ssi_oseclen); + sinfo->ssi_ntpwlen = 0; + /* + * Get the final auth. token. + */ + status = smb_auth_get_token(sr); + break; + + case LSA_MTYPE_ERROR: + /* + * Authentication failed. Return the error + * provided in the reply message. + */ + if (rlen == sizeof (smb_lsa_eresp_t)) { + smb_lsa_eresp_t *ler = rbuf; + status = ler->ler_ntstatus; + goto errout; + } + /* FALLTHROUGH */ + + default: /* Bogus message type */ + status = NT_STATUS_INTERNAL_ERROR; + goto errout; + } + + if (status != 0 && status != NT_STATUS_MORE_PROCESSING_REQUIRED) { + errout: + smb_user_logoff(user); + } + + if (rbuf != NULL) + kmem_free(rbuf, rlen); + + return (status); +} + +/* + * Send the "client info" up to the auth service. + */ +static uint32_t +smb_auth_do_clinfo(smb_request_t *sr) +{ + smb_lsa_msg_hdr_t msg_hdr; + smb_lsa_clinfo_t clinfo; + smb_user_t *user = sr->uid_user; + void *rbuf = NULL; + uint32_t status; + + /* + * Send a message with info. about the client + * (IP address, etc) and wait for an ACK. + */ + msg_hdr.lmh_msgtype = LSA_MTYPE_CLINFO; + msg_hdr.lmh_msglen = sizeof (clinfo); + clinfo.lci_clnt_ipaddr = sr->session->ipaddr; + (void) memcpy(clinfo.lci_challenge_key, + sr->session->challenge_key, + sizeof (clinfo.lci_challenge_key)); + status = smb_authsock_sendrecv(user, &msg_hdr, &clinfo, &rbuf); + /* We don't use this response. */ + if (rbuf != NULL) { + kmem_free(rbuf, msg_hdr.lmh_msglen); + rbuf = NULL; + } + + return (status); +} + +/* + * After a successful authentication, ask the authsvc to + * send us the authentication token. + */ +static uint32_t +smb_auth_get_token(smb_request_t *sr) +{ + smb_lsa_msg_hdr_t msg_hdr; + XDR xdrs; + smb_user_t *user = sr->uid_user; + smb_token_t *token = NULL; + cred_t *cr = NULL; + void *rbuf = NULL; + uint32_t rlen = 0; + uint32_t privileges; + uint32_t status; + bool_t ok; + + msg_hdr.lmh_msgtype = LSA_MTYPE_GETTOK; + msg_hdr.lmh_msglen = 0; + + status = smb_authsock_sendrecv(user, &msg_hdr, NULL, &rbuf); + if (status != 0) + goto errout; + + rlen = msg_hdr.lmh_msglen; + switch (msg_hdr.lmh_msgtype) { + + case LSA_MTYPE_TOKEN: + status = 0; + break; + + case LSA_MTYPE_ERROR: + if (rlen == sizeof (smb_lsa_eresp_t)) { + smb_lsa_eresp_t *ler = rbuf; + status = ler->ler_ntstatus; + goto errout; + } + /* FALLTHROUGH */ + + default: + status = NT_STATUS_INTERNAL_ERROR; + goto errout; + } + + /* + * Authenticated. Decode the LSA_MTYPE_TOKEN. + */ + xdrmem_create(&xdrs, rbuf, rlen, XDR_DECODE); + token = kmem_zalloc(sizeof (smb_token_t), KM_SLEEP); + ok = smb_token_xdr(&xdrs, token); + xdr_destroy(&xdrs); + if (!ok) { + status = RPC_NT_BAD_STUB_DATA; + goto errout; + } + kmem_free(rbuf, rlen); + rbuf = NULL; + + /* + * Setup the logon object. + */ + cr = smb_cred_create(token); + if (cr == NULL) + goto errout; + privileges = smb_priv_xlate(token); + (void) smb_user_logon(user, cr, + token->tkn_domain_name, token->tkn_account_name, + token->tkn_flags, privileges, token->tkn_audit_sid); + crfree(cr); + + /* + * Save the session key, and (maybe) enable signing, + * but only for real logon (not ANON or GUEST). + */ + if ((token->tkn_flags & (SMB_ATF_GUEST | SMB_ATF_ANON)) == 0) { + if (smb_sign_begin(sr, token) != 0) { + status = NT_STATUS_INTERNAL_ERROR; + goto errout; + } + } + + smb_token_free(token); + + sr->user_cr = user->u_cred; + return (0); + +errout: + if (rbuf != NULL) + kmem_free(rbuf, rlen); + if (token != NULL) + smb_token_free(token); + return (status); +} + +/* + * Tokens are allocated in the kernel via XDR. + * Call xdr_free before freeing the token structure. + */ +void +smb_token_free(smb_token_t *token) +{ + if (token != NULL) { + xdr_free(smb_token_xdr, (char *)token); + kmem_free(token, sizeof (smb_token_t)); + } +} + +/* + * Convert access token privileges to local definitions. + */ +static uint32_t +smb_priv_xlate(smb_token_t *token) +{ + uint32_t privileges = 0; + + if (smb_token_query_privilege(token, SE_BACKUP_LUID)) + privileges |= SMB_USER_PRIV_BACKUP; + + if (smb_token_query_privilege(token, SE_RESTORE_LUID)) + privileges |= SMB_USER_PRIV_RESTORE; + + if (smb_token_query_privilege(token, SE_TAKE_OWNERSHIP_LUID)) + privileges |= SMB_USER_PRIV_TAKE_OWNERSHIP; + + if (smb_token_query_privilege(token, SE_SECURITY_LUID)) + privileges |= SMB_USER_PRIV_SECURITY; + + return (privileges); +} + +/* + * Send/recv a request/reply sequence on the auth socket. + * Returns zero or an NT status. + * + * Errors here mean we can't communicate with the smbd_authsvc. + * With limited authsock instances, this should be rare. + */ +static uint32_t +smb_authsock_sendrecv(smb_user_t *user, smb_lsa_msg_hdr_t *hdr, + void *sndbuf, void **recvbuf) +{ + ksocket_t so; + uint32_t status; + int rc; + + /* + * Get a hold on the auth socket. + */ + mutex_enter(&user->u_mutex); + so = user->u_authsock; + if (so == NULL) { + mutex_exit(&user->u_mutex); + return (NT_STATUS_INTERNAL_ERROR); + } + ksocket_hold(so); + mutex_exit(&user->u_mutex); + + rc = smb_authsock_send(so, hdr, sizeof (*hdr)); + if (rc == 0 && hdr->lmh_msglen != 0) { + rc = smb_authsock_send(so, sndbuf, hdr->lmh_msglen); + } + if (rc) + goto out; + + rc = smb_authsock_recv(so, hdr, sizeof (*hdr)); + if (rc == 0 && hdr->lmh_msglen != 0) { + *recvbuf = kmem_alloc(hdr->lmh_msglen, KM_SLEEP); + rc = smb_authsock_recv(so, *recvbuf, hdr->lmh_msglen); + if (rc) { + kmem_free(*recvbuf, hdr->lmh_msglen); + *recvbuf = NULL; + } + } + +out: + ksocket_rele(so); + switch (rc) { + case 0: + status = 0; + break; + case EIO: + status = RPC_NT_COMM_FAILURE; + break; + case ENOTCONN: + status = RPC_NT_PIPE_CLOSED; + break; + default: + status = RPC_NT_CALL_FAILED; + break; + } + + return (status); +} + +/* + * Hope this is interpreted per-zone... + */ +static struct sockaddr_un smbauth_sockname = { + AF_UNIX, SMB_AUTHSVC_SOCKNAME }; + +/* + * Limit how long smb_authsock_sendrecv() will wait for a + * response from the local authentication service. + */ +struct timeval smb_auth_recv_tmo = { 45, 0 }; + +/* + * Also limit the time smb_authsock_sendrecv() will wait + * trying to send a request to the authentication service. + */ +struct timeval smb_auth_send_tmo = { 15, 0 }; + +static uint32_t +smb_authsock_open(smb_user_t *user) +{ + smb_server_t *sv = user->u_server; + ksocket_t so = NULL; + uint32_t status; + int rc; + + /* + * If the auth. service is busy, wait our turn. + * This may be frequent, so don't log. + */ + if ((rc = smb_threshold_enter(&sv->sv_ssetup_ct)) != 0) + return (NT_STATUS_NO_LOGON_SERVERS); + + rc = ksocket_socket(&so, AF_UNIX, SOCK_STREAM, 0, + KSOCKET_SLEEP, CRED()); + if (rc != 0) { + cmn_err(CE_NOTE, "smb_authsock_open: socket, rc=%d", rc); + status = NT_STATUS_INSUFF_SERVER_RESOURCES; + goto errout; + } + + /* + * Set the send/recv timeouts. + */ + (void) ksocket_setsockopt(so, SOL_SOCKET, SO_SNDTIMEO, + &smb_auth_send_tmo, sizeof (smb_auth_send_tmo), CRED()); + (void) ksocket_setsockopt(so, SOL_SOCKET, SO_RCVTIMEO, + &smb_auth_recv_tmo, sizeof (smb_auth_recv_tmo), CRED()); + + /* + * Connect to the smbd auth. service. + * + * Would like to set the connect timeout too, but there's + * apparently no easy way to do that for AF_UNIX. + */ + rc = ksocket_connect(so, (struct sockaddr *)&smbauth_sockname, + sizeof (smbauth_sockname), CRED()); + if (rc != 0) { + DTRACE_PROBE1(error, int, rc); + status = NT_STATUS_NETLOGON_NOT_STARTED; + goto errout; + } + + /* Note: u_authsock cleanup in smb_authsock_close() */ + mutex_enter(&user->u_mutex); + if (user->u_authsock != NULL) { + mutex_exit(&user->u_mutex); + status = NT_STATUS_INTERNAL_ERROR; + goto errout; + } + user->u_authsock = so; + mutex_exit(&user->u_mutex); + return (0); + +errout: + if (so != NULL) + (void) ksocket_close(so, CRED()); + smb_threshold_exit(&sv->sv_ssetup_ct); + + return (status); +} + +static int +smb_authsock_send(ksocket_t so, void *buf, size_t len) +{ + int rc; + size_t iocnt = 0; + + rc = ksocket_send(so, buf, len, 0, &iocnt, CRED()); + if (rc == 0 && iocnt != len) { + DTRACE_PROBE1(short, size_t, iocnt); + rc = EIO; + } + if (rc != 0) { + DTRACE_PROBE1(error, int, rc); + } + + return (rc); +} + +static int +smb_authsock_recv(ksocket_t so, void *buf, size_t len) +{ + int rc; + size_t iocnt = 0; + + rc = ksocket_recv(so, buf, len, MSG_WAITALL, &iocnt, CRED()); + if (rc == 0) { + if (iocnt == 0) { + DTRACE_PROBE1(discon, struct sonode *, so); + rc = ENOTCONN; + } else if (iocnt != len) { + /* Should not happen with MSG_WAITALL */ + DTRACE_PROBE1(short, size_t, iocnt); + rc = EIO; + } + } + if (rc != 0) { + DTRACE_PROBE1(error, int, rc); + } + + return (rc); +} + +void +smb_authsock_close(smb_user_t *user) +{ + + ASSERT(MUTEX_HELD(&user->u_mutex)); + if (user->u_authsock == NULL) + return; + (void) ksocket_close(user->u_authsock, CRED()); + user->u_authsock = NULL; + smb_threshold_exit(&user->u_server->sv_ssetup_ct); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_cred.c b/usr/src/uts/common/fs/smbsrv/smb_cred.c new file mode 100644 index 0000000000..c6956518fc --- /dev/null +++ b/usr/src/uts/common/fs/smbsrv/smb_cred.c @@ -0,0 +1,157 @@ +/* + * 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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + */ + +/* + * Authentication helpers for building credentials + */ + +#include <sys/types.h> +#include <sys/sid.h> +#include <sys/priv_names.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <netinet/in.h> +#include <smbsrv/smb_idmap.h> +#include <smbsrv/smb_kproto.h> +#include <smbsrv/smb_token.h> + +static void smb_cred_set_sid(smb_id_t *id, ksid_t *ksid); +static ksidlist_t *smb_cred_set_sidlist(smb_ids_t *token_grps); + +/* + * Allocate a Solaris cred and initialize it based on the access token. + * + * If the user can be mapped to a non-ephemeral ID, the cred gid is set + * to the Solaris user's primary group. + * + * If the mapped UID is ephemeral, or the primary group could not be + * obtained, the cred gid is set to whatever Solaris group is mapped + * to the token's primary group. + */ +cred_t * +smb_cred_create(smb_token_t *token) +{ + ksid_t ksid; + ksidlist_t *ksidlist = NULL; + smb_posix_grps_t *posix_grps; + cred_t *cr; + gid_t gid; + + ASSERT(token); + ASSERT(token->tkn_posix_grps); + posix_grps = token->tkn_posix_grps; + + cr = crget(); + ASSERT(cr != NULL); + + if (!IDMAP_ID_IS_EPHEMERAL(token->tkn_user.i_id) && + (posix_grps->pg_ngrps != 0)) { + gid = posix_grps->pg_grps[0]; + } else { + gid = token->tkn_primary_grp.i_id; + } + + if (crsetugid(cr, token->tkn_user.i_id, gid) != 0) { + crfree(cr); + return (NULL); + } + + if (crsetgroups(cr, posix_grps->pg_ngrps, posix_grps->pg_grps) != 0) { + crfree(cr); + return (NULL); + } + + smb_cred_set_sid(&token->tkn_user, &ksid); + crsetsid(cr, &ksid, KSID_USER); + smb_cred_set_sid(&token->tkn_primary_grp, &ksid); + crsetsid(cr, &ksid, KSID_GROUP); + smb_cred_set_sid(&token->tkn_owner, &ksid); + crsetsid(cr, &ksid, KSID_OWNER); + ksidlist = smb_cred_set_sidlist(&token->tkn_win_grps); + crsetsidlist(cr, ksidlist); + + /* + * In the AD world, "take ownership privilege" is very much + * like having Unix "root" privileges. It's normally given + * to members of the "Administrators" group, which normally + * includes the the local Administrator (like root) and when + * joined to a domain, "Domain Admins". + */ + if (smb_token_query_privilege(token, SE_TAKE_OWNERSHIP_LUID)) { + (void) crsetpriv(cr, + PRIV_FILE_CHOWN, + PRIV_FILE_DAC_READ, + PRIV_FILE_DAC_SEARCH, + PRIV_FILE_DAC_WRITE, + PRIV_FILE_OWNER, + NULL); + } + + return (cr); +} + +/* + * Initialize the ksid based on the given smb_id_t. + */ +static void +smb_cred_set_sid(smb_id_t *id, ksid_t *ksid) +{ + char sidstr[SMB_SID_STRSZ]; + int rc; + + ASSERT(id); + ASSERT(id->i_sid); + + ksid->ks_id = id->i_id; + smb_sid_tostr(id->i_sid, sidstr); + rc = smb_sid_splitstr(sidstr, &ksid->ks_rid); + ASSERT(rc == 0); + + ksid->ks_attr = id->i_attrs; + ksid->ks_domain = ksid_lookupdomain(sidstr); +} + +/* + * Allocate and initialize the ksidlist based on the access token group list. + */ +static ksidlist_t * +smb_cred_set_sidlist(smb_ids_t *token_grps) +{ + int i; + ksidlist_t *lp; + + lp = kmem_zalloc(KSIDLIST_MEM(token_grps->i_cnt), KM_SLEEP); + lp->ksl_ref = 1; + lp->ksl_nsid = token_grps->i_cnt; + lp->ksl_neid = 0; + + for (i = 0; i < lp->ksl_nsid; i++) { + smb_cred_set_sid(&token_grps->i_ids[i], &lp->ksl_sids[i]); + if (lp->ksl_sids[i].ks_id > IDMAP_WK__MAX_GID) + lp->ksl_neid++; + } + + return (lp); +} diff --git a/usr/src/uts/common/fs/smbsrv/smb_init.c b/usr/src/uts/common/fs/smbsrv/smb_init.c index 90619f8b4d..b2028603dc 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_init.c +++ b/usr/src/uts/common/fs/smbsrv/smb_init.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #include <sys/types.h> @@ -81,7 +81,7 @@ uint_t smb_audit_flags = * Maximum number of simultaneous authentication, share mapping, pipe open * requests to be processed. */ -int smb_ssetup_threshold = 256; +int smb_ssetup_threshold = SMB_AUTHSVC_MAXTHREAD; int smb_tcon_threshold = 1024; int smb_opipe_threshold = 1024; diff --git a/usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c b/usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c index a9b787f461..cf59d6eea2 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c +++ b/usr/src/uts/common/fs/smbsrv/smb_mbuf_marshaling.c @@ -21,6 +21,8 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ /* @@ -157,8 +159,10 @@ smb_mbc_vdecodef(mbuf_chain_t *mbc, char *fmt, va_list ap) uint32_t lval; int unicode = 0; int repc; + boolean_t repc_specified; while ((c = *fmt++) != 0) { + repc_specified = B_FALSE; repc = 1; if ('0' <= c && c <= '9') { @@ -167,9 +171,11 @@ smb_mbc_vdecodef(mbuf_chain_t *mbc, char *fmt, va_list ap) repc = repc * 10 + c - '0'; c = *fmt++; } while ('0' <= c && c <= '9'); + repc_specified = B_TRUE; } else if (c == '#') { repc = va_arg(ap, int); c = *fmt++; + repc_specified = B_TRUE; } switch (c) { @@ -296,7 +302,7 @@ smb_mbc_vdecodef(mbuf_chain_t *mbc, char *fmt, va_list ap) ascii_conversion: ASSERT(sr != NULL); cvalpp = va_arg(ap, uint8_t **); - if (repc <= 1) + if (!repc_specified) repc = 0; if (mbc_marshal_get_ascii_string(sr, mbc, cvalpp, repc) != 0) @@ -307,7 +313,7 @@ ascii_conversion: unicode_translation: ASSERT(sr != 0); cvalpp = va_arg(ap, uint8_t **); - if (repc <= 1) + if (!repc_specified) repc = 0; if (mbc->chain_offset & 1) mbc->chain_offset++; @@ -508,12 +514,14 @@ smb_mbc_vencodef(mbuf_chain_t *mbc, char *fmt, va_list ap) uint32_t lval; uint_t tag; int unicode = 0; - int repc = 1; + int repc; + boolean_t repc_specified; uint16_t wval; uint8_t cval; uint8_t c; while ((c = *fmt++) != 0) { + repc_specified = B_FALSE; repc = 1; if ('0' <= c && c <= '9') { @@ -522,9 +530,12 @@ smb_mbc_vencodef(mbuf_chain_t *mbc, char *fmt, va_list ap) repc = repc * 10 + c - '0'; c = *fmt++; } while ('0' <= c && c <= '9'); + repc_specified = B_TRUE; } else if (c == '#') { repc = va_arg(ap, int); c = *fmt++; + repc_specified = B_TRUE; + } switch (c) { @@ -647,6 +658,8 @@ smb_mbc_vencodef(mbuf_chain_t *mbc, char *fmt, va_list ap) case 's': /* ASCII/multibyte string */ ascii_conversion: cvalp = va_arg(ap, uint8_t *); + if (!repc_specified) + repc = 0; if (mbc_marshal_put_ascii_string(mbc, (char *)cvalp, repc) != 0) return (DECODE_NO_MORE_DATA); @@ -696,6 +709,8 @@ unicode_translation: if (mbc->chain_offset & 1) mbc->chain_offset++; cvalp = va_arg(ap, uint8_t *); + if (!repc_specified) + repc = 0; if (mbc_marshal_put_unicode_string(mbc, (char *)cvalp, repc) != 0) return (DECODE_NO_MORE_DATA); @@ -1040,7 +1055,7 @@ mbc_marshal_put_ascii_string(mbuf_chain_t *mbc, char *mbs, int repc) length += sizeof (char); - if ((repc > 1) && (repc < length)) + if ((repc > 0) && (repc < length)) length = repc; if (mbc_marshal_make_room(mbc, length)) return (DECODE_NO_MORE_DATA); @@ -1077,7 +1092,7 @@ mbc_marshal_put_unicode_string(mbuf_chain_t *mbc, char *ascii, int repc) length += sizeof (smb_wchar_t); - if ((repc > 1) && (repc < length)) + if ((repc > 0) && (repc < length)) length = repc; if (mbc_marshal_make_room(mbc, length)) diff --git a/usr/src/uts/common/fs/smbsrv/smb_negotiate.c b/usr/src/uts/common/fs/smbsrv/smb_negotiate.c index d774c07b48..08597f79ce 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_negotiate.c +++ b/usr/src/uts/common/fs/smbsrv/smb_negotiate.c @@ -229,7 +229,31 @@ static uint32_t smb_nt_tcp_rcvbuf = 1048560; /* scale factor of 4 */ static int smb_xlate_dialect(const char *); -int smb_cap_passthru = 1; +/* + * "Capabilities" offered by SMB1 Negotiate Protocol. + * See smb.h for descriptions. + * + * CAP_RAW_MODE, CAP_MPX_MODE are obsolete. + * UNICODE support is required for long share names, + * long file names and streams. + * + * For testing, one can patch this, i.e. remove the high bit to + * temporarily disable extended security, etc. + */ +uint32_t smb1srv_capabilities = + CAP_UNICODE | + CAP_LARGE_FILES | + CAP_NT_SMBS | + CAP_RPC_REMOTE_APIS | + CAP_STATUS32 | + CAP_LEVEL_II_OPLOCKS | + CAP_LOCK_AND_READ | + CAP_NT_FIND | + CAP_DFS | + CAP_INFOLEVEL_PASSTHRU | + CAP_LARGE_READX | + CAP_LARGE_WRITEX | + CAP_EXTENDED_SECURITY; smb_sdrc_t smb_pre_negotiate(smb_request_t *sr) @@ -296,8 +320,8 @@ smb_com_negotiate(smb_request_t *sr) return (SDRC_ERROR); } - sr->session->secmode = NEGOTIATE_SECURITY_CHALLENGE_RESPONSE | - NEGOTIATE_SECURITY_USER_LEVEL; + sr->session->secmode = NEGOTIATE_ENCRYPT_PASSWORDS | + NEGOTIATE_USER_SECURITY; secmode = sr->session->secmode; sesskey = sr->session->sesskey; @@ -308,30 +332,7 @@ smb_com_negotiate(smb_request_t *sr) bcopy(&sr->session->challenge_key, negprot->ni_key, SMB_CHALLENGE_SZ); nbdomain = sr->sr_cfg->skc_nbdomain; - /* - * UNICODE support is required for long share names, - * long file names and streams. Note: CAP_RAW_MODE - * is not supported because it does nothing to help - * modern clients and causes nasty complications. - */ - negprot->ni_capabilities = CAP_LARGE_FILES - | CAP_UNICODE - | CAP_NT_SMBS - | CAP_STATUS32 - | CAP_NT_FIND - | CAP_LEVEL_II_OPLOCKS - | CAP_LOCK_AND_READ - | CAP_RPC_REMOTE_APIS - | CAP_LARGE_READX - | CAP_LARGE_WRITEX - | CAP_DFS; - - if (smb_cap_passthru) - negprot->ni_capabilities |= CAP_INFOLEVEL_PASSTHRU; - else - cmn_err(CE_NOTE, "smbsrv: cap passthru is %s", - (negprot->ni_capabilities & CAP_INFOLEVEL_PASSTHRU) ? - "enabled" : "disabled"); + negprot->ni_capabilities = smb1srv_capabilities; switch (negprot->ni_dialect) { case PC_NETWORK_PROGRAM_1_0: /* core */ @@ -404,14 +405,9 @@ smb_com_negotiate(smb_request_t *sr) sizeof (smb_nt_tcp_rcvbuf), CRED()); /* - * Turn off Extended Security Negotiation + * Allow SMB signatures if using encrypted passwords */ - sr->smb_flg2 &= ~SMB_FLAGS2_EXT_SEC; - - /* - * Allow SMB signatures if security challenge response enabled - */ - if ((secmode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) && + if ((secmode & NEGOTIATE_ENCRYPT_PASSWORDS) && sr->sr_cfg->skc_signing_enable) { secmode |= NEGOTIATE_SECURITY_SIGNATURES_ENABLED; if (sr->sr_cfg->skc_signing_required) @@ -422,6 +418,19 @@ smb_com_negotiate(smb_request_t *sr) } /* + * Does the client want Extended Security? + * (and if we have it enabled) + * If so, handle as if a different dialect. + */ + if ((sr->smb_flg2 & SMB_FLAGS2_EXT_SEC) != 0 && + (negprot->ni_capabilities & CAP_EXTENDED_SECURITY) != 0) + goto NT_LM_0_12_ext_sec; + + /* Else deny knowledge of extended security. */ + sr->smb_flg2 &= ~SMB_FLAGS2_EXT_SEC; + negprot->ni_capabilities &= ~CAP_EXTENDED_SECURITY; + + /* * nbdomain is not expected to be aligned. * Use temporary buffer to avoid alignment padding */ @@ -457,6 +466,33 @@ smb_com_negotiate(smb_request_t *sr) smb_msgbuf_term(&mb); break; +NT_LM_0_12_ext_sec: + /* + * This is the "Extended Security" variant of + * dialect NT_LM_0_12. + */ + rc = smbsr_encode_result(sr, 17, VAR_BCC, + "bwbwwllllTwbw#c#c", + 17, /* wct */ + negprot->ni_index, /* dialect index */ + secmode, /* security mode */ + negprot->ni_maxmpxcount, /* max MPX */ + 1, /* max VCs */ + (DWORD)smb_maxbufsize, /* max buffer size */ + 0xFFFF, /* max raw size */ + sesskey, /* session key */ + negprot->ni_capabilities, + &negprot->ni_servertime, /* system time */ + negprot->ni_tzcorrection, + 0, /* encryption key length (MBZ) */ + VAR_BCC, + UUID_LEN, + sr->sr_cfg->skc_machine_uuid, + sr->sr_cfg->skc_negtok_len, + sr->sr_cfg->skc_negtok); + break; + + default: rc = smbsr_encode_result(sr, 1, 0, "bww", 1, -1, 0); return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); diff --git a/usr/src/uts/common/fs/smbsrv/smb_server.c b/usr/src/uts/common/fs/smbsrv/smb_server.c index 94eeb396dd..e4a305c7fa 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_server.c +++ b/usr/src/uts/common/fs/smbsrv/smb_server.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ /* @@ -1889,6 +1889,7 @@ smb_server_fclose(smb_llist_t *ll, uint32_t uniqid) return (rc); } +/* See also: libsmb smb_kmod_setcfg */ static void smb_server_store_cfg(smb_server_t *sv, smb_ioc_cfg_t *ioc) { @@ -1909,12 +1910,22 @@ smb_server_store_cfg(smb_server_t *sv, smb_ioc_cfg_t *ioc) sv->sv_cfg.skc_oplock_enable = ioc->oplock_enable; sv->sv_cfg.skc_sync_enable = ioc->sync_enable; sv->sv_cfg.skc_secmode = ioc->secmode; + sv->sv_cfg.skc_netbios_enable = ioc->netbios_enable; sv->sv_cfg.skc_ipv6_enable = ioc->ipv6_enable; sv->sv_cfg.skc_print_enable = ioc->print_enable; sv->sv_cfg.skc_traverse_mounts = ioc->traverse_mounts; - sv->sv_cfg.skc_netbios_enable = ioc->netbios_enable; sv->sv_cfg.skc_execflags = ioc->exec_flags; + sv->sv_cfg.skc_negtok_len = ioc->negtok_len; sv->sv_cfg.skc_version = ioc->version; + (void) memcpy(sv->sv_cfg.skc_machine_uuid, ioc->machine_uuid, + sizeof (uuid_t)); + (void) memcpy(sv->sv_cfg.skc_negtok, ioc->negtok, + sizeof (sv->sv_cfg.skc_negtok)); + (void) memcpy(sv->sv_cfg.skc_native_os, ioc->native_os, + sizeof (sv->sv_cfg.skc_native_os)); + (void) memcpy(sv->sv_cfg.skc_native_lm, ioc->native_lm, + sizeof (sv->sv_cfg.skc_native_lm)); + (void) strlcpy(sv->sv_cfg.skc_nbdomain, ioc->nbdomain, sizeof (sv->sv_cfg.skc_nbdomain)); (void) strlcpy(sv->sv_cfg.skc_fqdn, ioc->fqdn, diff --git a/usr/src/uts/common/fs/smbsrv/smb_session.c b/usr/src/uts/common/fs/smbsrv/smb_session.c index d084a3f29b..fc130cb973 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_session.c +++ b/usr/src/uts/common/fs/smbsrv/smb_session.c @@ -47,7 +47,6 @@ static void smb_session_cancel(smb_session_t *); static int smb_session_message(smb_session_t *); static int smb_session_xprt_puthdr(smb_session_t *, smb_xprt_t *, uint8_t *, size_t); -static smb_user_t *smb_session_lookup_user(smb_session_t *, char *, char *); static smb_tree_t *smb_session_get_tree(smb_session_t *, smb_tree_t *); static void smb_session_logoff(smb_session_t *); static void smb_request_init_command_mbuf(smb_request_t *sr); @@ -706,11 +705,16 @@ smb_session_delete(smb_session_t *session) ASSERT(session->s_magic == SMB_SESSION_MAGIC); - session->s_magic = 0; - if (session->sign_fini != NULL) session->sign_fini(session); + if (session->signing.mackey != NULL) { + kmem_free(session->signing.mackey, + session->signing.mackey_len); + } + + session->s_magic = 0; + smb_rwx_destroy(&session->s_lock); smb_net_txl_destructor(&session->s_txlst); @@ -828,63 +832,18 @@ smb_session_worker(void *arg) } /* - * smb_session_lookup_user - */ -static smb_user_t * -smb_session_lookup_user(smb_session_t *session, char *domain, char *name) -{ - smb_user_t *user; - smb_llist_t *ulist; - - ulist = &session->s_user_list; - smb_llist_enter(ulist, RW_READER); - user = smb_llist_head(ulist); - while (user) { - ASSERT(user->u_magic == SMB_USER_MAGIC); - if (!smb_strcasecmp(user->u_name, name, 0) && - !smb_strcasecmp(user->u_domain, domain, 0)) { - if (smb_user_hold(user)) - break; - } - user = smb_llist_next(ulist, user); - } - smb_llist_exit(ulist); - - return (user); -} - -/* - * If a user attempts to log in subsequently from the specified session, - * duplicates the existing SMB user instance such that all SMB user - * instances that corresponds to the same user on the given session - * reference the same user's cred. - * - * Returns NULL if the given user hasn't yet logged in from this - * specified session. Otherwise, returns a user instance that corresponds - * to this subsequent login. + * Find a user on the specified session by SMB UID. */ smb_user_t * -smb_session_dup_user(smb_session_t *session, char *domain, char *account_name) +smb_session_lookup_uid(smb_session_t *session, uint16_t uid) { - smb_user_t *orig_user = NULL; - smb_user_t *user = NULL; - - orig_user = smb_session_lookup_user(session, domain, - account_name); - - if (orig_user) { - user = smb_user_dup(orig_user); - smb_user_release(orig_user); - } - - return (user); + return (smb_session_lookup_uid_st(session, uid, + SMB_USER_STATE_LOGGED_ON)); } -/* - * Find a user on the specified session by SMB UID. - */ smb_user_t * -smb_session_lookup_uid(smb_session_t *session, uint16_t uid) +smb_session_lookup_uid_st(smb_session_t *session, uint16_t uid, + smb_user_state_t st) { smb_user_t *user; smb_llist_t *user_list; @@ -899,19 +858,16 @@ smb_session_lookup_uid(smb_session_t *session, uint16_t uid) SMB_USER_VALID(user); ASSERT(user->u_session == session); - if (user->u_uid == uid) { - if (!smb_user_hold(user)) - break; - - smb_llist_exit(user_list); - return (user); + if (user->u_uid == uid && user->u_state == st) { + smb_user_hold_internal(user); + break; } user = smb_llist_next(user_list, user); } smb_llist_exit(user_list); - return (NULL); + return (user); } void @@ -1233,9 +1189,21 @@ smb_session_logoff(smb_session_t *session) SMB_USER_VALID(user); ASSERT(user->u_session == session); - if (smb_user_hold(user)) { + switch (user->u_state) { + case SMB_USER_STATE_LOGGING_ON: + case SMB_USER_STATE_LOGGED_ON: + smb_user_hold_internal(user); smb_user_logoff(user); smb_user_release(user); + break; + + case SMB_USER_STATE_LOGGED_OFF: + case SMB_USER_STATE_LOGGING_OFF: + break; + + default: + ASSERT(0); + break; } user = smb_llist_next(&session->s_user_list, user); diff --git a/usr/src/uts/common/fs/smbsrv/smb_session_setup_andx.c b/usr/src/uts/common/fs/smbsrv/smb_session_setup_andx.c index 7eabad7513..5f98a16293 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_session_setup_andx.c +++ b/usr/src/uts/common/fs/smbsrv/smb_session_setup_andx.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #include <sys/types.h> @@ -32,95 +32,54 @@ #include <smbsrv/smb_kproto.h> #include <smbsrv/smb_token.h> -static int smb_authenticate(smb_request_t *, smb_arg_sessionsetup_t *); -static int smb_authenticate_core(smb_request_t *, smb_arg_sessionsetup_t *); -static uint32_t smb_priv_xlate(smb_token_t *); -#ifdef _KERNEL -static void smb_cred_set_sid(smb_id_t *id, ksid_t *ksid); -static ksidlist_t *smb_cred_set_sidlist(smb_ids_t *token_grps); -#endif /* _KERNEL */ - -/* - * In NTLM 0.12, the padding between the Native OS and Native LM is a bit - * strange. On NT4.0, there is a 2 byte pad between the OS (Windows NT 1381) - * and LM (Windows NT 4.0). On Windows 2000, there is no padding between - * the OS (Windows 2000 2195) and LM (Windows 2000 5.0). - * If the padding is removed from the decode string the NT4.0 LM comes out - * as an empty string. So if the client's native OS is Win NT we consider - * the padding otherwise we don't. - * - * For Pre-NTLM 0.12, despite the CIFS/1.0 spec, the user and domain are - * not always present in the message. We try to get the account name and - * the primary domain but we don't care about the the native OS or native - * LM fields. - * - * If the Native LM cannot be determined, default to Windows NT. - */ smb_sdrc_t smb_pre_session_setup_andx(smb_request_t *sr) { smb_arg_sessionsetup_t *sinfo; char *native_os; char *native_lm; - uint32_t junk_sesskey; - uint16_t maxbufsize; - uint16_t vcnumber; int rc = 0; sinfo = smb_srm_zalloc(sr, sizeof (smb_arg_sessionsetup_t)); sr->sr_ssetup = sinfo; - if (sr->session->dialect >= NT_LM_0_12) { - rc = smbsr_decode_vwv(sr, "b.wwwwlww4.l", &sr->andx_com, - &sr->andx_off, &maxbufsize, - &sinfo->ssi_maxmpxcount, &vcnumber, - &junk_sesskey, &sinfo->ssi_cipwlen, - &sinfo->ssi_cspwlen, &sinfo->ssi_capabilities); - if (rc != 0) - goto pre_session_setup_andx_done; - - sinfo->ssi_cipwd = smb_srm_zalloc(sr, sinfo->ssi_cipwlen + 1); - sinfo->ssi_cspwd = smb_srm_zalloc(sr, sinfo->ssi_cspwlen + 1); - - rc = smbsr_decode_data(sr, "%#c#cuuu", - sr, - sinfo->ssi_cipwlen, sinfo->ssi_cipwd, - sinfo->ssi_cspwlen, sinfo->ssi_cspwd, - &sinfo->ssi_user, - &sinfo->ssi_domain, - &native_os); - if (rc != 0) - goto pre_session_setup_andx_done; - - sinfo->ssi_cipwd[sinfo->ssi_cipwlen] = 0; - sinfo->ssi_cspwd[sinfo->ssi_cspwlen] = 0; + /* + * Enforce the minimum word count seen in the old protocol, + * to make sure we have enough to decode the common stuff. + * Further wcnt checks below. + */ + if (sr->smb_wct < 10) { + rc = -1; + goto done; + } - sr->session->native_os = smbnative_os_value(native_os); + /* + * Parse common part of SMB session setup. + * skip: vcnumber(2), sesskey(4) + */ + rc = smbsr_decode_vwv(sr, "b.www6.", + &sr->andx_com, &sr->andx_off, + &sinfo->ssi_maxbufsize, &sinfo->ssi_maxmpxcount); + if (rc != 0) + goto done; - if (sr->session->native_os == NATIVE_OS_WINNT) - rc = smbsr_decode_data(sr, "%,u", sr, &native_lm); - else - rc = smbsr_decode_data(sr, "%u", sr, &native_lm); + if (sr->session->dialect < NT_LM_0_12) { - if (rc != 0 || native_lm == NULL) - native_lm = "NT LAN Manager 4.0"; + sinfo->ssi_type = SMB_SSNSETUP_PRE_NTLM012; + sinfo->ssi_capabilities = 0; - sr->session->native_lm = smbnative_lm_value(native_lm); - } else { - rc = smbsr_decode_vwv(sr, "b.wwwwlw4.", &sr->andx_com, - &sr->andx_off, &maxbufsize, - &sinfo->ssi_maxmpxcount, &vcnumber, - &junk_sesskey, &sinfo->ssi_cipwlen); + rc = smbsr_decode_vwv(sr, "w4.", + &sinfo->ssi_lmpwlen); if (rc != 0) - goto pre_session_setup_andx_done; + goto done; - sinfo->ssi_cipwd = smb_srm_zalloc(sr, sinfo->ssi_cipwlen + 1); - rc = smbsr_decode_data(sr, "%#c", sr, sinfo->ssi_cipwlen, - sinfo->ssi_cipwd); + sinfo->ssi_lmpwd = smb_srm_zalloc(sr, sinfo->ssi_lmpwlen + 1); + rc = smbsr_decode_data(sr, "%#c", sr, sinfo->ssi_lmpwlen, + sinfo->ssi_lmpwd); if (rc != 0) - goto pre_session_setup_andx_done; + goto done; - sinfo->ssi_cipwd[sinfo->ssi_cipwlen] = 0; + sinfo->ssi_lmpwd[sinfo->ssi_lmpwlen] = 0; if (smbsr_decode_data(sr, "%u", sr, &sinfo->ssi_user) != 0) sinfo->ssi_user = ""; @@ -128,15 +87,108 @@ smb_pre_session_setup_andx(smb_request_t *sr) if (smbsr_decode_data(sr, "%u", sr, &sinfo->ssi_domain) != 0) sinfo->ssi_domain = ""; - native_lm = "NT LAN Manager 4.0"; - sr->session->native_os = NATIVE_OS_WINNT; - sr->session->native_lm = smbnative_lm_value(native_lm); + goto part2; + } + + /* + * We have dialect >= NT_LM_0_12 + */ + if (sr->smb_wct == 13) { + /* Old style (non-extended) request. */ + sinfo->ssi_type = SMB_SSNSETUP_NTLM012_NOEXT; + + rc = smbsr_decode_vwv(sr, "ww4.l", + &sinfo->ssi_lmpwlen, + &sinfo->ssi_ntpwlen, + &sinfo->ssi_capabilities); + if (rc != 0) + goto done; + + /* paranoid: ignore cap. ext. sec. here */ + sinfo->ssi_capabilities &= ~CAP_EXTENDED_SECURITY; + + sinfo->ssi_lmpwd = smb_srm_zalloc(sr, sinfo->ssi_lmpwlen + 1); + sinfo->ssi_ntpwd = smb_srm_zalloc(sr, sinfo->ssi_ntpwlen + 1); + + rc = smbsr_decode_data(sr, "%#c#cuu", sr, + sinfo->ssi_lmpwlen, sinfo->ssi_lmpwd, + sinfo->ssi_ntpwlen, sinfo->ssi_ntpwd, + &sinfo->ssi_user, &sinfo->ssi_domain); + if (rc != 0) + goto done; + + sinfo->ssi_lmpwd[sinfo->ssi_lmpwlen] = 0; + sinfo->ssi_ntpwd[sinfo->ssi_ntpwlen] = 0; + + goto part2; + } + + if (sr->smb_wct == 12) { + /* New style (extended) request. */ + sinfo->ssi_type = SMB_SSNSETUP_NTLM012_EXTSEC; + + rc = smbsr_decode_vwv(sr, "w4.l", + &sinfo->ssi_iseclen, + &sinfo->ssi_capabilities); + if (rc != 0) + goto done; + + if ((sinfo->ssi_capabilities & CAP_EXTENDED_SECURITY) == 0) { + rc = -1; + goto done; + } + + sinfo->ssi_isecblob = smb_srm_zalloc(sr, sinfo->ssi_iseclen); + rc = smbsr_decode_data(sr, "%#c", sr, + sinfo->ssi_iseclen, sinfo->ssi_isecblob); + if (rc != 0) + goto done; + + goto part2; } - sr->session->vcnumber = vcnumber; - sr->session->smb_msg_size = maxbufsize; + /* Invalid message */ + rc = -1; + goto done; + +part2: + /* + * Get the "Native OS" and "Native LanMan" strings. + * These are not critical to protocol function, so + * if we can't parse them, just guess "NT". + * These strings are free'd with the sr. + * + * In NTLM 0.12, the padding between the Native OS and Native LM + * is a bit strange. On NT4.0, there is a 2 byte pad between the + * OS (Windows NT 1381) and LM (Windows NT 4.0). On Windows 2000, + * there is no padding between the OS (Windows 2000 2195) and LM + * (Windows 2000 5.0). If the padding is removed from the decode + * string the NT4.0 LM comes out as an empty string. So if the + * client's native OS is Win NT, assume extra padding. + */ + rc = smbsr_decode_data(sr, "%u", sr, &native_os); + if (rc != 0 || native_os == NULL) + sinfo->ssi_native_os = NATIVE_OS_WINNT; + else + sinfo->ssi_native_os = smbnative_os_value(native_os); + + if (sinfo->ssi_native_os == NATIVE_OS_WINNT) + rc = smbsr_decode_data(sr, "%,u", sr, &native_lm); + else + rc = smbsr_decode_data(sr, "%u", sr, &native_lm); + if (rc != 0 || native_lm == NULL) + sinfo->ssi_native_lm = NATIVE_LM_NT; + else + sinfo->ssi_native_lm = smbnative_lm_value(native_lm); + rc = 0; + +done: + if (rc != 0) { + cmn_err(CE_NOTE, + "SmbSessonSetupX: client %s invalid request", + sr->session->ip_addr_str); + } -pre_session_setup_andx_done: DTRACE_SMB_2(op__SessionSetupX__start, smb_request_t *, sr, smb_arg_sessionsetup_t, sinfo); return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); @@ -150,18 +202,14 @@ smb_post_session_setup_andx(smb_request_t *sr) DTRACE_SMB_2(op__SessionSetupX__done, smb_request_t *, sr, smb_arg_sessionsetup_t, sinfo); - if (sinfo->ssi_cipwd != NULL) - bzero(sinfo->ssi_cipwd, sinfo->ssi_cipwlen + 1); + if (sinfo->ssi_lmpwd != NULL) + bzero(sinfo->ssi_lmpwd, sinfo->ssi_lmpwlen); - if (sinfo->ssi_cspwd != NULL) - bzero(sinfo->ssi_cspwd, sinfo->ssi_cspwlen + 1); + if (sinfo->ssi_ntpwd != NULL) + bzero(sinfo->ssi_ntpwd, sinfo->ssi_ntpwlen); } /* - * If signing has not already been enabled on this session check to see if - * it should be enabled. The first authenticated logon provides the MAC - * key and sequence numbers for signing all subsequent sessions on the same - * connection. * * NT systems use different native OS and native LanMan values dependent on * whether they are acting as a client or a server. NT 4.0 server responds @@ -174,329 +222,117 @@ smb_sdrc_t smb_com_session_setup_andx(smb_request_t *sr) { smb_arg_sessionsetup_t *sinfo = sr->sr_ssetup; + uint32_t status; + uint16_t action; int rc; - if (smb_authenticate(sr, sinfo) != 0) - return (SDRC_ERROR); - - if (sr->session->native_lm == NATIVE_LM_WIN2000) - sinfo->ssi_capabilities |= CAP_LARGE_FILES | - CAP_LARGE_READX | CAP_LARGE_WRITEX; - - if (!smb_oplock_levelII) - sr->session->capabilities &= ~CAP_LEVEL_II_OPLOCKS; - - sr->session->capabilities = sinfo->ssi_capabilities; - - rc = smbsr_encode_result(sr, 3, VAR_BCC, "bb.www%uuu", - 3, - sr->andx_com, - -1, /* andx_off */ - sinfo->ssi_guest ? 1 : 0, - VAR_BCC, - sr, - smbnative_os_str(&sr->sr_cfg->skc_version), - smbnative_lm_str(&sr->sr_cfg->skc_version), - sr->sr_cfg->skc_nbdomain); - - return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); -} - -static int -smb_authenticate(smb_request_t *sr, smb_arg_sessionsetup_t *sinfo) -{ - int rc; - smb_server_t *sv = sr->sr_server; - - if (smb_threshold_enter(&sv->sv_ssetup_ct) != 0) { - smbsr_error(sr, RPC_NT_SERVER_TOO_BUSY, 0, 0); - return (-1); - } - - rc = smb_authenticate_core(sr, sinfo); - smb_threshold_exit(&sv->sv_ssetup_ct); - return (rc); -} - -/* - * Authenticate a user. If the user has already been authenticated on - * this session, we can simply dup the user and return. - * - * Otherwise, the user information is passed to smbd for authentication. - * If smbd can authenticate the user an access token is returned and we - * generate a cred and new user based on the token. - */ -static int -smb_authenticate_core(smb_request_t *sr, smb_arg_sessionsetup_t *sinfo) -{ - char *hostname = sr->sr_cfg->skc_hostname; - int security = sr->sr_cfg->skc_secmode; - smb_token_t *token = NULL; - smb_user_t *user = NULL; - smb_logon_t user_info; - boolean_t need_lookup = B_FALSE; - uint32_t privileges; - cred_t *cr; - char *buf = NULL; - char *p; - - bzero(&user_info, sizeof (smb_logon_t)); - user_info.lg_e_domain = sinfo->ssi_domain; - - if ((*sinfo->ssi_user == '\0') && - (sinfo->ssi_cspwlen == 0) && - (sinfo->ssi_cipwlen == 0 || - (sinfo->ssi_cipwlen == 1 && *sinfo->ssi_cipwd == '\0'))) { - user_info.lg_e_username = "anonymous"; - user_info.lg_flags |= SMB_ATF_ANON; - } else { - user_info.lg_e_username = sinfo->ssi_user; - } - /* - * Handle user@domain format. We need to retain the original - * data as this is important in some forms of authentication. + * Some stuff we do only in the first in a (possible) + * sequence of session setup requests. */ - if (*sinfo->ssi_domain == '\0') { - buf = smb_srm_strdup(sr, sinfo->ssi_user); - if ((p = strchr(buf, '@')) != NULL) { - *p = '\0'; - user_info.lg_e_username = buf; - user_info.lg_e_domain = p + 1; - } - } + if (sinfo->ssi_type != SMB_SSNSETUP_NTLM012_EXTSEC || + sr->smb_uid == 0 || sr->smb_uid == 0xFFFF) { - /* - * If no domain name has been provided in domain mode we cannot - * determine if this is a local user or a domain user without - * obtaining an access token. So we postpone the lookup until - * after authentication. - */ - if (security == SMB_SECMODE_WORKGRP) { - user = smb_session_dup_user(sr->session, hostname, - user_info.lg_e_username); - } else if (*user_info.lg_e_domain != '\0') { - user = smb_session_dup_user(sr->session, user_info.lg_e_domain, - user_info.lg_e_username); - } else { - need_lookup = B_TRUE; - } + /* This is a first (or only) call */ + sr->session->smb_msg_size = sinfo->ssi_maxbufsize; + sr->session->smb_max_mpx = sinfo->ssi_maxmpxcount; + sr->session->capabilities = sinfo->ssi_capabilities; - if (user != NULL) { - sinfo->ssi_guest = SMB_USER_IS_GUEST(user); - sr->user_cr = user->u_cred; - sr->smb_uid = user->u_uid; - sr->uid_user = user; - return (0); - } - - user_info.lg_level = NETR_NETWORK_LOGON; - user_info.lg_domain = sinfo->ssi_domain; - user_info.lg_username = sinfo->ssi_user; - user_info.lg_workstation = sr->session->workstation; - user_info.lg_clnt_ipaddr = sr->session->ipaddr; - user_info.lg_local_ipaddr = sr->session->local_ipaddr; - user_info.lg_local_port = sr->session->s_local_port; - user_info.lg_challenge_key.val = sr->session->challenge_key; - user_info.lg_challenge_key.len = sr->session->challenge_len; - user_info.lg_nt_password.val = sinfo->ssi_cspwd; - user_info.lg_nt_password.len = sinfo->ssi_cspwlen; - user_info.lg_lm_password.val = sinfo->ssi_cipwd; - user_info.lg_lm_password.len = sinfo->ssi_cipwlen; - user_info.lg_native_os = sr->session->native_os; - user_info.lg_native_lm = sr->session->native_lm; - - DTRACE_PROBE1(smb__sessionsetup__clntinfo, smb_logon_t *, &user_info); - - if ((token = smb_get_token(sr->session, &user_info)) == NULL) { - smbsr_error(sr, 0, ERRSRV, ERRbadpw); - return (-1); - } + if (!smb_oplock_levelII) + sr->session->capabilities &= ~CAP_LEVEL_II_OPLOCKS; - if (need_lookup) { - user = smb_session_dup_user(sr->session, - token->tkn_domain_name, token->tkn_account_name); - if (user != NULL) { - sinfo->ssi_guest = SMB_USER_IS_GUEST(user); - sr->user_cr = user->u_cred; - sr->smb_uid = user->u_uid; - sr->uid_user = user; - smb_token_free(token); - return (0); - } + sr->session->native_os = sinfo->ssi_native_os; + sr->session->native_lm = sinfo->ssi_native_lm; } - if ((cr = smb_cred_create(token)) == NULL) { - smb_token_free(token); - smbsr_error(sr, 0, ERRDOS, ERROR_INVALID_HANDLE); - return (-1); - } - - privileges = smb_priv_xlate(token); - - user = smb_user_login(sr->session, cr, - token->tkn_domain_name, token->tkn_account_name, - token->tkn_flags, privileges, token->tkn_audit_sid); - crfree(cr); - /* - * Save the session key, and (maybe) enable signing, - * but only for real logon (not ANON or GUEST). + * The "meat" of authentication happens here. */ - if ((token->tkn_flags & (SMB_ATF_GUEST | SMB_ATF_ANON)) == 0) - (void) smb_sign_begin(sr, token); - - smb_token_free(token); - - if (user == NULL) { - smbsr_error(sr, 0, ERRDOS, ERROR_INVALID_HANDLE); - return (-1); - } - - sinfo->ssi_guest = SMB_USER_IS_GUEST(user); - sr->user_cr = user->u_cred; - sr->smb_uid = user->u_uid; - sr->uid_user = user; - return (0); -} - -#ifdef _KERNEL -/* - * Allocate a Solaris cred and initialize it based on the access token. - * - * If the user can be mapped to a non-ephemeral ID, the cred gid is set - * to the Solaris user's primary group. - * - * If the mapped UID is ephemeral, or the primary group could not be - * obtained, the cred gid is set to whatever Solaris group is mapped - * to the token's primary group. - */ -cred_t * -smb_cred_create(smb_token_t *token) -{ - ksid_t ksid; - ksidlist_t *ksidlist = NULL; - smb_posix_grps_t *posix_grps; - cred_t *cr; - gid_t gid; - - ASSERT(token); - ASSERT(token->tkn_posix_grps); - posix_grps = token->tkn_posix_grps; - - cr = crget(); - ASSERT(cr != NULL); - - if (!IDMAP_ID_IS_EPHEMERAL(token->tkn_user.i_id) && - (posix_grps->pg_ngrps != 0)) { - gid = posix_grps->pg_grps[0]; - } else { - gid = token->tkn_primary_grp.i_id; - } - - if (crsetugid(cr, token->tkn_user.i_id, gid) != 0) { - crfree(cr); - return (NULL); - } + if (sinfo->ssi_type == SMB_SSNSETUP_NTLM012_EXTSEC) + status = smb_authenticate_ext(sr); + else + status = smb_authenticate_old(sr); - if (crsetgroups(cr, posix_grps->pg_ngrps, posix_grps->pg_grps) != 0) { - crfree(cr); - return (NULL); - } + switch (status) { - smb_cred_set_sid(&token->tkn_user, &ksid); - crsetsid(cr, &ksid, KSID_USER); - smb_cred_set_sid(&token->tkn_primary_grp, &ksid); - crsetsid(cr, &ksid, KSID_GROUP); - smb_cred_set_sid(&token->tkn_owner, &ksid); - crsetsid(cr, &ksid, KSID_OWNER); - ksidlist = smb_cred_set_sidlist(&token->tkn_win_grps); - crsetsidlist(cr, ksidlist); + case NT_STATUS_SUCCESS: + break; /* - * In the AD world, "take ownership privilege" is very much - * like having Unix "root" privileges. It's normally given - * to members of the "Administrators" group, which normally - * includes the the local Administrator (like root) and when - * joined to a domain, "Domain Admins". + * This is not really an error, but tells the client + * it should send another session setup request. */ - if (smb_token_query_privilege(token, SE_TAKE_OWNERSHIP_LUID)) { - (void) crsetpriv(cr, - PRIV_FILE_CHOWN, - PRIV_FILE_DAC_READ, - PRIV_FILE_DAC_SEARCH, - PRIV_FILE_DAC_WRITE, - PRIV_FILE_OWNER, - NULL); - } - - return (cr); -} + case NT_STATUS_MORE_PROCESSING_REQUIRED: + smbsr_error(sr, status, 0, 0); + break; -/* - * Initialize the ksid based on the given smb_id_t. - */ -static void -smb_cred_set_sid(smb_id_t *id, ksid_t *ksid) -{ - char sidstr[SMB_SID_STRSZ]; - int rc; + case NT_STATUS_ACCESS_DENIED: + smbsr_error(sr, status, ERRDOS, ERROR_ACCESS_DENIED); + return (SDRC_ERROR); - ASSERT(id); - ASSERT(id->i_sid); + case NT_STATUS_TOO_MANY_SESSIONS: + smbsr_error(sr, status, ERRSRV, ERRtoomanyuids); + return (SDRC_ERROR); - ksid->ks_id = id->i_id; - smb_sid_tostr(id->i_sid, sidstr); - rc = smb_sid_splitstr(sidstr, &ksid->ks_rid); - ASSERT(rc == 0); + case NT_STATUS_NO_LOGON_SERVERS: + smbsr_error(sr, status, ERRDOS, ERROR_NO_LOGON_SERVERS); + return (SDRC_ERROR); - ksid->ks_attr = id->i_attrs; - ksid->ks_domain = ksid_lookupdomain(sidstr); -} + case NT_STATUS_NETLOGON_NOT_STARTED: + smbsr_error(sr, status, ERRDOS, ERROR_NETLOGON_NOT_STARTED); + return (SDRC_ERROR); -/* - * Allocate and initialize the ksidlist based on the access token group list. - */ -static ksidlist_t * -smb_cred_set_sidlist(smb_ids_t *token_grps) -{ - int i; - ksidlist_t *lp; - - lp = kmem_zalloc(KSIDLIST_MEM(token_grps->i_cnt), KM_SLEEP); - lp->ksl_ref = 1; - lp->ksl_nsid = token_grps->i_cnt; - lp->ksl_neid = 0; - - for (i = 0; i < lp->ksl_nsid; i++) { - smb_cred_set_sid(&token_grps->i_ids[i], &lp->ksl_sids[i]); - if (lp->ksl_sids[i].ks_id > IDMAP_WK__MAX_GID) - lp->ksl_neid++; - } + case NT_STATUS_USER_SESSION_DELETED: + smbsr_error(sr, status, ERRSRV, ERRbaduid); + return (SDRC_ERROR); - return (lp); -} -#endif /* _KERNEL */ + case NT_STATUS_INSUFF_SERVER_RESOURCES: + smbsr_error(sr, status, ERRSRV, ERRnoresource); + return (SDRC_ERROR); -/* - * Convert access token privileges to local definitions. - */ -static uint32_t -smb_priv_xlate(smb_token_t *token) -{ - uint32_t privileges = 0; + case NT_STATUS_INTERNAL_ERROR: + default: + smbsr_error(sr, status, ERRSRV, ERRsrverror); + return (SDRC_ERROR); + } - if (smb_token_query_privilege(token, SE_BACKUP_LUID)) - privileges |= SMB_USER_PRIV_BACKUP; + action = SMB_USER_IS_GUEST(sr->uid_user) ? 1 : 0; - if (smb_token_query_privilege(token, SE_RESTORE_LUID)) - privileges |= SMB_USER_PRIV_RESTORE; + switch (sinfo->ssi_type) { - if (smb_token_query_privilege(token, SE_TAKE_OWNERSHIP_LUID)) - privileges |= SMB_USER_PRIV_TAKE_OWNERSHIP; + default: + case SMB_SSNSETUP_PRE_NTLM012: + case SMB_SSNSETUP_NTLM012_NOEXT: - if (smb_token_query_privilege(token, SE_SECURITY_LUID)) - privileges |= SMB_USER_PRIV_SECURITY; + rc = smbsr_encode_result(sr, 3, VAR_BCC, "bb.www%uuu", + 3, + sr->andx_com, + -1, /* andx_off */ + action, + VAR_BCC, + sr, + sr->sr_cfg->skc_native_os, + sr->sr_cfg->skc_native_lm, + sr->sr_cfg->skc_nbdomain); + break; + + case SMB_SSNSETUP_NTLM012_EXTSEC: + + rc = smbsr_encode_result(sr, 4, VAR_BCC, "bb.wwww%#cuuu", + 4, + sr->andx_com, + -1, /* andx_off */ + action, + sinfo->ssi_oseclen, + VAR_BCC, + sr, + sinfo->ssi_oseclen, + sinfo->ssi_osecblob, + sr->sr_cfg->skc_native_os, + sr->sr_cfg->skc_native_lm, + sr->sr_cfg->skc_nbdomain); + break; + } - return (privileges); + return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); } diff --git a/usr/src/uts/common/fs/smbsrv/smb_signing.c b/usr/src/uts/common/fs/smbsrv/smb_signing.c index c1cd826e32..c6f0212d31 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_signing.c +++ b/usr/src/uts/common/fs/smbsrv/smb_signing.c @@ -44,7 +44,6 @@ #include <sys/isa_defs.h> #include <sys/byteorder.h> -#define SSN_KEY_LEN 16 #define SMB_SIG_SIZE 8 #define SMB_SIG_OFFS 14 #define SMB_HDRLEN 32 @@ -55,40 +54,47 @@ #define htolel(x) BSWAP_32(x) #endif -int -smb_sign_calc(struct mbuf_chain *mbc, - struct smb_sign *sign, - uint32_t seqnum, - unsigned char *mac_sign); +static int +smb_sign_calc(smb_request_t *sr, struct mbuf_chain *mbc, + uint32_t seqnum, unsigned char *sig); #ifdef DEBUG -static void +uint32_t smb_sign_debug_search = 10; + +/* + * Debug code to search +/- for the correct sequence number. + * If found, correct sign->seqnum and return 0, else return -1 + */ +static int smb_sign_find_seqnum( - uint32_t seqnum, - struct smb_sign *sign, - struct mbuf_chain *command, + smb_request_t *sr, + struct mbuf_chain *mbc, unsigned char *mac_sig, - unsigned char *sr_sig, - boolean_t *found) + unsigned char *sr_sig) { -int start_seqnum; -int i; - - /* Debug code to hunt for the sequence number */ - *found = B_FALSE; - start_seqnum = seqnum - 10; - if (start_seqnum < 0) - start_seqnum = 0; - for (i = start_seqnum; i <= start_seqnum + 20; i++) { - (void) smb_sign_calc(command, sign, i, mac_sig); + struct smb_sign *sign = &sr->session->signing; + uint32_t i, t; + + for (i = 1; i < smb_sign_debug_search; i++) { + t = sr->sr_seqnum + i; + (void) smb_sign_calc(sr, mbc, t, mac_sig); + if (memcmp(mac_sig, sr_sig, SMB_SIG_SIZE) == 0) { + goto found; + } + t = sr->sr_seqnum - i; + (void) smb_sign_calc(sr, mbc, t, mac_sig); if (memcmp(mac_sig, sr_sig, SMB_SIG_SIZE) == 0) { - sign->seqnum = i; - *found = B_TRUE; - break; + goto found; } - cmn_err(CE_WARN, "smb_sign_find_seqnum: seqnum:%d mismatch", i); } - cmn_err(CE_WARN, "smb_sign_find_seqnum: found=%d", *found); + cmn_err(CE_WARN, "smb_sign_find_seqnum: failed after %d", i); + return (-1); + +found: + cmn_err(CE_WARN, "smb_sign_find_seqnum: found! %d <- %d", + sign->seqnum, t); + sign->seqnum = t; + return (0); } #endif @@ -100,9 +106,9 @@ smb_sign_fini(smb_session_t *s) { smb_sign_mech_t *mech; - if ((mech = s->signing.mech) != NULL) { + if ((mech = s->sign_mech) != NULL) { kmem_free(mech, sizeof (*mech)); - s->signing.mech = NULL; + s->sign_mech = NULL; } } @@ -123,6 +129,15 @@ smb_sign_begin(smb_request_t *sr, smb_token_t *token) int rc; /* + * We should normally have a session key here because + * our caller filters out Anonymous and Guest logons. + * However, buggy clients could get us here without a + * session key, in which case: just don't sign. + */ + if (token->tkn_ssnkey.val == NULL || token->tkn_ssnkey.len == 0) + return (0); + + /* * Session-level initialization (once per session) */ smb_rwx_rwenter(&session->s_lock, RW_WRITER); @@ -139,7 +154,7 @@ smb_sign_begin(smb_request_t *sr, smb_token_t *token) /* * Get the mech handle */ - if (sign->mech == NULL) { + if (session->sign_mech == NULL) { mech = kmem_zalloc(sizeof (*mech), KM_SLEEP); rc = smb_md5_getmech(mech); if (rc != 0) { @@ -147,7 +162,7 @@ smb_sign_begin(smb_request_t *sr, smb_token_t *token) smb_rwx_rwexit(&session->s_lock); return (rc); } - sign->mech = mech; + session->sign_mech = mech; session->sign_fini = smb_sign_fini; } @@ -155,17 +170,16 @@ smb_sign_begin(smb_request_t *sr, smb_token_t *token) * Compute and store the signing (MAC) key. * * With extended security, the MAC key is the same as the - * session key (and we'll have sinfo->ssi_cspwlen == 0). + * session key (and we'll have sinfo->ssi_ntpwlen == 0). * With non-extended security, it's the concatenation of * the session key and the "NT response" we received. - * (NB: no extended security yet) */ - sign->mackey_len = SSN_KEY_LEN + sinfo->ssi_cspwlen; + sign->mackey_len = token->tkn_ssnkey.len + sinfo->ssi_ntpwlen; sign->mackey = kmem_alloc(sign->mackey_len, KM_SLEEP); - bcopy(token->tkn_session_key, sign->mackey, SSN_KEY_LEN); - if (sinfo->ssi_cspwlen > 0) { - bcopy(sinfo->ssi_cspwd, sign->mackey + SSN_KEY_LEN, - sinfo->ssi_cspwlen); + bcopy(token->tkn_ssnkey.val, sign->mackey, token->tkn_ssnkey.len); + if (sinfo->ssi_ntpwlen > 0) { + bcopy(sinfo->ssi_ntpwd, sign->mackey + token->tkn_ssnkey.len, + sinfo->ssi_ntpwlen); } session->signing.seqnum = 0; @@ -207,12 +221,12 @@ smb_sign_begin(smb_request_t *sr, smb_token_t *token) * Return 0 if success * */ -int -smb_sign_calc(struct mbuf_chain *mbc, - struct smb_sign *sign, - uint32_t seqnum, - unsigned char *mac_sign) +static int +smb_sign_calc(smb_request_t *sr, struct mbuf_chain *mbc, + uint32_t seqnum, unsigned char *mac_sign) { + smb_session_t *s = sr->session; + struct smb_sign *sign = &s->signing; smb_sign_ctx_t ctx = 0; uchar_t digest[MD5_DIGEST_LENGTH]; uchar_t *hdrp; @@ -241,10 +255,10 @@ smb_sign_calc(struct mbuf_chain *mbc, } s; } smbhdr; - if (sign->mech == NULL || sign->mackey == NULL) + if (s->sign_mech == NULL || sign->mackey == NULL) return (-1); - if ((rc = smb_md5_init(&ctx, sign->mech)) != 0) + if ((rc = smb_md5_init(&ctx, s->sign_mech)) != 0) return (rc); /* Digest the MAC Key */ @@ -313,11 +327,8 @@ smb_sign_calc(struct mbuf_chain *mbc, int smb_sign_check_request(smb_request_t *sr) { - struct mbuf_chain command = sr->command; + struct mbuf_chain mbc = sr->command; unsigned char mac_sig[SMB_SIG_SIZE]; - struct smb_sign *sign = &sr->session->signing; - int rtn = 0; - boolean_t found = B_TRUE; /* * Don't check secondary transactions - we dont know the sequence @@ -329,32 +340,31 @@ smb_sign_check_request(smb_request_t *sr) return (0); /* Reset the offset to begining of header */ - command.chain_offset = sr->orig_request_hdr; + mbc.chain_offset = sr->orig_request_hdr; /* calculate mac signature */ - if (smb_sign_calc(&command, sign, sr->sr_seqnum, mac_sig) != 0) + if (smb_sign_calc(sr, &mbc, sr->sr_seqnum, mac_sig) != 0) return (-1); /* compare the signatures */ - if (memcmp(mac_sig, sr->smb_sig, SMB_SIG_SIZE) != 0) { - DTRACE_PROBE2(smb__signing__req, smb_request_t, sr, - smb_sign_t *, sr->smb_sig); - cmn_err(CE_NOTE, "smb_sign_check_request: bad signature"); - /* - * check nearby sequence numbers in debug mode - */ -#ifdef DEBUG - if (smb_sign_debug) - smb_sign_find_seqnum(sr->sr_seqnum, sign, - &command, mac_sig, sr->smb_sig, &found); - else -#endif - found = B_FALSE; + if (memcmp(mac_sig, sr->smb_sig, SMB_SIG_SIZE) == 0) { + /* They match! OK, we're done. */ + return (0); + } + + DTRACE_PROBE2(smb__signature__mismatch, smb_request_t, sr, + unsigned char *, mac_sig); + cmn_err(CE_NOTE, "smb_sign_check_request: bad signature"); - if (found == B_FALSE) - rtn = -1; + /* + * check nearby sequence numbers in debug mode + */ +#ifdef DEBUG + if (smb_sign_debug) { + return (smb_sign_find_seqnum(sr, &mbc, mac_sig, sr->smb_sig)); } - return (rtn); +#endif + return (-1); } /* @@ -368,17 +378,15 @@ smb_sign_check_request(smb_request_t *sr) int smb_sign_check_secondary(smb_request_t *sr, unsigned int reply_seqnum) { - struct mbuf_chain command = sr->command; + struct mbuf_chain mbc = sr->command; unsigned char mac_sig[SMB_SIG_SIZE]; - struct smb_sign *sign = &sr->session->signing; int rtn = 0; /* Reset the offset to begining of header */ - command.chain_offset = sr->orig_request_hdr; + mbc.chain_offset = sr->orig_request_hdr; /* calculate mac signature */ - if (smb_sign_calc(&command, sign, reply_seqnum - 1, - mac_sig) != 0) + if (smb_sign_calc(sr, &mbc, reply_seqnum - 1, mac_sig) != 0) return (-1); @@ -403,22 +411,21 @@ smb_sign_check_secondary(smb_request_t *sr, unsigned int reply_seqnum) void smb_sign_reply(smb_request_t *sr, struct mbuf_chain *reply) { - struct mbuf_chain resp; - struct smb_sign *sign = &sr->session->signing; - unsigned char signature[SMB_SIG_SIZE]; + struct mbuf_chain mbc; + unsigned char mac[SMB_SIG_SIZE]; if (reply) - resp = *reply; + mbc = *reply; else - resp = sr->reply; + mbc = sr->reply; /* Reset offset to start of reply */ - resp.chain_offset = 0; + mbc.chain_offset = 0; /* * Calculate MAC signature */ - if (smb_sign_calc(&resp, sign, sr->reply_seqnum, signature) != 0) { + if (smb_sign_calc(sr, &mbc, sr->reply_seqnum, mac) != 0) { cmn_err(CE_WARN, "smb_sign_reply: error in smb_sign_calc"); return; } @@ -426,6 +433,6 @@ smb_sign_reply(smb_request_t *sr, struct mbuf_chain *reply) /* * Put signature in the response */ - (void) smb_mbc_poke(&resp, SMB_SIG_OFFS, "#c", - SMB_SIG_SIZE, signature); + (void) smb_mbc_poke(&mbc, SMB_SIG_OFFS, "#c", + SMB_SIG_SIZE, mac); } diff --git a/usr/src/uts/common/fs/smbsrv/smb_user.c b/usr/src/uts/common/fs/smbsrv/smb_user.c index 017e3771ed..7f43d188c9 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_user.c +++ b/usr/src/uts/common/fs/smbsrv/smb_user.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ /* @@ -68,40 +68,59 @@ * User State Machine * ------------------ * - * +-----------------------------+ T0 - * | SMB_USER_STATE_LOGGED_IN |<----------- Creation/Allocation + * + * | T0: Creation/Allocation + * | (1st session setup) + * v + * +-----------------------------+ + * | SMB_USER_STATE_LOGGING_ON |<----------+ + * +-----------------------------+ addl. session setup + * | | (more proc. required) + * | T2 | ^ + * | | | T1: (cont.) + * | +------->-------? + * v | T3: (fail) + * +-----------------------------+ v + * | SMB_USER_STATE_LOGGED_ON | (logged off) * +-----------------------------+ * | - * | T1 + * | T4 * | * v * +-----------------------------+ * | SMB_USER_STATE_LOGGING_OFF | * +-----------------------------+ * | - * | T2 + * | T5 * | * v - * +-----------------------------+ T3 + * +-----------------------------+ T6 * | SMB_USER_STATE_LOGGED_OFF |----------> Deletion/Free * +-----------------------------+ * - * SMB_USER_STATE_LOGGED_IN + * SMB_USER_STATE_LOGGING_ON * * While in this state: - * - The user is queued in the list of users of his session. + * - The user is in the list of users for his session. + * - References will be given out ONLY for session setup. + * - This user can not access anything yet. + * + * SMB_USER_STATE_LOGGED_ON + * + * While in this state: + * - The user is in the list of users for his session. * - References will be given out if the user is looked up. * - The user can access files and pipes. * * SMB_USER_STATE_LOGGING_OFF * * While in this state: - * - The user is queued in the list of users of his session. + * - The user is in the list of users for his session. * - References will not be given out if the user is looked up. * - The trees the user connected are being disconnected. * - The resources associated with the user remain. * - * SMB_USER_STATE_LOGGING_OFF + * SMB_USER_STATE_LOGGED_OFF * * While in this state: * - The user is queued in the list of users of his session. @@ -111,15 +130,34 @@ * * Transition T0 * - * This transition occurs in smb_user_login(). A new user is created and - * added to the list of users of a session. + * First request in an SMB Session Setup sequence creates a + * new user object and adds it to the list of users for + * this session. User UID is assigned and returned. * * Transition T1 * - * This transition occurs in smb_user_logoff(). + * Subsequent SMB Session Setup requests (on the same UID + * assigned in T0) update the state of this user object, + * communicating with smbd for the crypto work. * * Transition T2 * + * If the SMB Session Setup sequence is successful, T2 + * makes the new user object available for requests. + * + * Transition T3 + * + * If an Session Setup request gets an error other than + * the expected "more processing required", then T3 + * leads to state "LOGGED_OFF" and then tear-down of the + * partially constructed user. + * + * Transition T4 + * + * Normal SMB User Logoff request, or session tear-down. + * + * Transition T5 + * * This transition occurs in smb_user_release(). The resources associated * with the user are deleted as well as the user. For the transition to * occur, the user must be in the SMB_USER_STATE_LOGGED_OFF state and the @@ -169,9 +207,7 @@ #define ADMINISTRATORS_SID "S-1-5-32-544" -static boolean_t smb_user_is_logged_in(smb_user_t *); static int smb_user_enum_private(smb_user_t *, smb_svcenum_t *); -static void smb_user_nonauth_logon(smb_user_t *); static void smb_user_auth_logoff(smb_user_t *); @@ -179,77 +215,84 @@ static void smb_user_auth_logoff(smb_user_t *); * Create a new user. */ smb_user_t * -smb_user_login( - smb_session_t *session, - cred_t *cr, - char *domain_name, - char *account_name, - uint32_t flags, - uint32_t privileges, - uint32_t audit_sid) +smb_user_new(smb_session_t *session) { smb_user_t *user; ASSERT(session); ASSERT(session->s_magic == SMB_SESSION_MAGIC); - ASSERT(cr); - ASSERT(account_name); - ASSERT(domain_name); user = kmem_cache_alloc(smb_cache_user, KM_SLEEP); bzero(user, sizeof (smb_user_t)); + user->u_refcnt = 1; user->u_session = session; user->u_server = session->s_server; user->u_logon_time = gethrestime_sec(); - user->u_flags = flags; - user->u_name_len = strlen(account_name) + 1; - user->u_domain_len = strlen(domain_name) + 1; - user->u_name = smb_mem_strdup(account_name); - user->u_domain = smb_mem_strdup(domain_name); - user->u_audit_sid = audit_sid; - if (!smb_idpool_alloc(&session->s_uid_pool, &user->u_uid)) { - mutex_init(&user->u_mutex, NULL, MUTEX_DEFAULT, NULL); - smb_user_setcred(user, cr, privileges); - user->u_state = SMB_USER_STATE_LOGGED_IN; - user->u_magic = SMB_USER_MAGIC; - smb_llist_enter(&session->s_user_list, RW_WRITER); - smb_llist_insert_tail(&session->s_user_list, user); - smb_llist_exit(&session->s_user_list); - smb_server_inc_users(session->s_server); - return (user); - } - smb_mem_free(user->u_name); - smb_mem_free(user->u_domain); + if (smb_idpool_alloc(&session->s_uid_pool, &user->u_uid)) + goto errout; + + mutex_init(&user->u_mutex, NULL, MUTEX_DEFAULT, NULL); + user->u_state = SMB_USER_STATE_LOGGING_ON; + user->u_magic = SMB_USER_MAGIC; + + smb_llist_enter(&session->s_user_list, RW_WRITER); + smb_llist_insert_tail(&session->s_user_list, user); + smb_llist_exit(&session->s_user_list); + smb_server_inc_users(session->s_server); + + return (user); + +errout: + if (user->u_uid != 0) + smb_idpool_free(&session->s_uid_pool, user->u_uid); kmem_cache_free(smb_cache_user, user); return (NULL); } /* - * Create a new user based on an existing user, used to support - * additional SessionSetupX requests for a user on a session. - * - * Assumes the caller has a reference on the original user from - * a user_lookup_by_x call. + * Fill in the details of a user, meaning a transition + * from state LOGGING_ON to state LOGGED_ON. */ -smb_user_t * -smb_user_dup( - smb_user_t *orig_user) +int +smb_user_logon( + smb_user_t *user, + cred_t *cr, + char *domain_name, + char *account_name, + uint32_t flags, + uint32_t privileges, + uint32_t audit_sid) { - smb_user_t *user; - ASSERT(orig_user->u_magic == SMB_USER_MAGIC); - ASSERT(orig_user->u_refcnt); + ASSERT(user->u_magic == SMB_USER_MAGIC); + ASSERT(cr); + ASSERT(account_name); + ASSERT(domain_name); - user = smb_user_login(orig_user->u_session, orig_user->u_cred, - orig_user->u_domain, orig_user->u_name, orig_user->u_flags, - orig_user->u_privileges, orig_user->u_audit_sid); + mutex_enter(&user->u_mutex); - if (user) - smb_user_nonauth_logon(orig_user); + if (user->u_state != SMB_USER_STATE_LOGGING_ON) { + mutex_exit(&user->u_mutex); + return (-1); + } - return (user); + smb_authsock_close(user); + + user->u_state = SMB_USER_STATE_LOGGED_ON; + user->u_flags = flags; + user->u_name_len = strlen(account_name) + 1; + user->u_domain_len = strlen(domain_name) + 1; + user->u_name = smb_mem_strdup(account_name); + user->u_domain = smb_mem_strdup(domain_name); + user->u_audit_sid = audit_sid; + + smb_user_setcred(user, cr, privileges); + + mutex_exit(&user->u_mutex); + + return (0); } /* @@ -267,7 +310,14 @@ smb_user_logoff( mutex_enter(&user->u_mutex); ASSERT(user->u_refcnt); switch (user->u_state) { - case SMB_USER_STATE_LOGGED_IN: { + case SMB_USER_STATE_LOGGING_ON: { + smb_authsock_close(user); + user->u_state = SMB_USER_STATE_LOGGED_OFF; + smb_server_dec_users(user->u_server); + break; + } + + case SMB_USER_STATE_LOGGED_ON: { /* * The user is moved into a state indicating that the log off * process has started. @@ -303,7 +353,7 @@ smb_user_hold(smb_user_t *user) mutex_enter(&user->u_mutex); - if (smb_user_is_logged_in(user)) { + if (user->u_state == SMB_USER_STATE_LOGGED_ON) { user->u_refcnt++; mutex_exit(&user->u_mutex); return (B_TRUE); @@ -348,7 +398,8 @@ smb_user_release( smb_session_post_user(user->u_session, user); break; - case SMB_USER_STATE_LOGGED_IN: + case SMB_USER_STATE_LOGGING_ON: + case SMB_USER_STATE_LOGGED_ON: case SMB_USER_STATE_LOGGING_OFF: break; @@ -466,30 +517,6 @@ smb_user_enum(smb_user_t *user, smb_svcenum_t *svcenum) /* *************************** Static Functions ***************************** */ /* - * Determine whether or not a user is logged in. - * Typically, a reference can only be taken on a logged-in user. - * - * This is a private function and must be called with the user - * mutex held. - */ -static boolean_t -smb_user_is_logged_in(smb_user_t *user) -{ - switch (user->u_state) { - case SMB_USER_STATE_LOGGED_IN: - return (B_TRUE); - - case SMB_USER_STATE_LOGGING_OFF: - case SMB_USER_STATE_LOGGED_OFF: - return (B_FALSE); - - default: - ASSERT(0); - return (B_FALSE); - } -} - -/* * Delete a user. The tree list should be empty. * * Remove the user from the session's user list before freeing resources @@ -504,6 +531,7 @@ smb_user_delete(void *arg) SMB_USER_VALID(user); ASSERT(user->u_refcnt == 0); ASSERT(user->u_state == SMB_USER_STATE_LOGGED_OFF); + ASSERT(user->u_authsock == NULL); session = user->u_session; smb_llist_enter(&session->s_user_list, RW_WRITER); @@ -679,15 +707,6 @@ smb_user_netinfo_fini(smb_netuserinfo_t *info) } static void -smb_user_nonauth_logon(smb_user_t *user) -{ - uint32_t audit_sid = user->u_audit_sid; - - (void) smb_kdoor_upcall(user->u_server, SMB_DR_USER_NONAUTH_LOGON, - &audit_sid, xdr_uint32_t, NULL, NULL); -} - -static void smb_user_auth_logoff(smb_user_t *user) { uint32_t audit_sid = user->u_audit_sid; @@ -695,27 +714,3 @@ smb_user_auth_logoff(smb_user_t *user) (void) smb_kdoor_upcall(user->u_server, SMB_DR_USER_AUTH_LOGOFF, &audit_sid, xdr_uint32_t, NULL, NULL); } - -smb_token_t * -smb_get_token(smb_session_t *session, smb_logon_t *user_info) -{ - smb_token_t *token; - int rc; - - token = kmem_zalloc(sizeof (smb_token_t), KM_SLEEP); - - rc = smb_kdoor_upcall(session->s_server, SMB_DR_USER_AUTH_LOGON, - user_info, smb_logon_xdr, token, smb_token_xdr); - - if (rc != 0) { - kmem_free(token, sizeof (smb_token_t)); - return (NULL); - } - - if (!smb_token_valid(token)) { - smb_token_free(token); - return (NULL); - } - - return (token); -} diff --git a/usr/src/uts/common/gssapi/Makefile b/usr/src/uts/common/gssapi/Makefile index 82ec96631d..60396302fd 100644 --- a/usr/src/uts/common/gssapi/Makefile +++ b/usr/src/uts/common/gssapi/Makefile @@ -24,6 +24,7 @@ # All rights reserved. # # Copyright 2012 Milan Jurik. All rights reserved. +# Copyright 2014 Nexenta Systems, Inc. All rights reserved. # # uts/common/gssd/Makefile # @@ -31,7 +32,7 @@ include ../../../Makefile.master -INSTALLED_HDRS= gssapi.h gssapi_ext.h +INSTALLED_HDRS= gssapi.h gssapi_ext.h gssapi_krb5.h PRIVATE_HDRS= gssd.x gssd_prot.h HDRS= $(INSTALLED_HDRS) $(PRIVATE_HDRS) @@ -47,10 +48,16 @@ CHECKHDRS= $(INSTALLED_HDRS:%.h=%.check) # cstyle so it is unchecked UNCHECKED_HDRS= gss_prot.h -# install rule +# install rules $(GSSDDIRS)/%: % $(INS.file) +$(GSSDDIRS)/%: mechs/krb5/include/% + $(INS.file) + +# This is 3rd party code, so just skip hdrchk. +gssapi_krb5.check: + .KEEP_STATE: .PARALLEL: $(CHECKHDRS) diff --git a/usr/src/uts/common/gssapi/gen_oids.c b/usr/src/uts/common/gssapi/gen_oids.c index 3e2c0e9552..8c3866d7d9 100644 --- a/usr/src/uts/common/gssapi/gen_oids.c +++ b/usr/src/uts/common/gssapi/gen_oids.c @@ -57,15 +57,24 @@ */ static const gss_OID_desc oids[] = { + /* GSS_C_NT_USER_NAME */ {10, "\052\206\110\206\367\022\001\002\001\001"}, + /* GSS_C_NT_MACHINE_UID_NAME */ {10, "\052\206\110\206\367\022\001\002\001\002"}, + /* GSS_C_NT_STRING_UID_NAME */ {10, "\052\206\110\206\367\022\001\002\001\003"}, + /* gss_nt_service_name */ {10, "\052\206\110\206\367\022\001\002\001\004"}, + + /* GSS_C_NT_HOSTBASED_SERVICE */ {6, "\053\006\001\005\006\002"}, + /* GSS_C_NT_ANONYMOUS */ {6, "\053\006\001\005\006\003"}, + /* GSS_C_NT_EXPORT_NAME */ {6, "\053\006\001\005\006\004"}, - {11, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x05"}, + /* GSS_C_INQ_SSPI_SESSION_KEY */ + {11, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x05"}, }; const gss_OID_desc * const gss_nt_user_name = oids+0; @@ -93,4 +102,4 @@ const gss_OID GSS_C_NT_STRING_UID_NAME = (gss_OID)oids+2; const gss_OID GSS_C_NT_HOSTBASED_SERVICE = (gss_OID)oids+4; const gss_OID GSS_C_NT_ANONYMOUS = (gss_OID)oids+5; const gss_OID GSS_C_NT_EXPORT_NAME = (gss_OID)oids+6; -const gss_OID GSS_C_INQ_SSPI_SESSION_KEY = (gss_OID)oids+7; +const gss_OID GSS_C_INQ_SSPI_SESSION_KEY = (gss_OID)oids+7; diff --git a/usr/src/uts/common/smbsrv/Makefile b/usr/src/uts/common/smbsrv/Makefile index 9bb092095b..aaaee2bd93 100644 --- a/usr/src/uts/common/smbsrv/Makefile +++ b/usr/src/uts/common/smbsrv/Makefile @@ -20,6 +20,7 @@ # # # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2015 Nexenta Systems, Inc. All rights reserved. # include ../../../Makefile.master @@ -52,6 +53,7 @@ HDRS= alloc.h \ smb_ktypes.h \ smb_privilege.h \ smb_share.h \ + smb_signing.h \ smb_token.h \ smb_vops.h \ smb_xdr.h \ diff --git a/usr/src/uts/common/smbsrv/ndl/netlogon.ndl b/usr/src/uts/common/smbsrv/ndl/netlogon.ndl index de6c19049b..26df6a902a 100644 --- a/usr/src/uts/common/smbsrv/ndl/netlogon.ndl +++ b/usr/src/uts/common/smbsrv/ndl/netlogon.ndl @@ -21,7 +21,8 @@ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. - * Copyright 2012 Nexenta Systems, Inc. All rights reserved. + * + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #ifndef _MLSVC_NETR_NDL_ @@ -70,6 +71,12 @@ #define NETR_OPNUM_LogonGetDomainInfo 0x1D #define NETR_OPNUM_ServerPasswordSet2 0x1E +/* + * This is not a real NETR OPNUM. It's used to unpack the + * struct krb5_validation_info found in the Kerberos PAC. + */ +#define NETR_OPNUM_decode_krb5_pac 1000 + struct netr_sid { BYTE Revision; @@ -349,6 +356,15 @@ struct netr_validation_info3 { struct netr_sid_and_attributes *ExtraSids; }; +/* NETR_OPNUM_decode_krb5_pac */ +struct krb5_validation_info { + struct netr_validation_info3 info3; + /* Kerberos PAC "resource group" stuff. */ + struct netr_sid *rg_dom_sid; + DWORD rg_rid_cnt; + SIZE_IS(rg_rid_cnt) + struct netr_group_membership *rg_rids; +}; union netr_validation_u { CASE(3) struct netr_validation_info3 *info3; @@ -431,6 +447,10 @@ union netr_interface { struct netr_PasswordSet PasswordSet; CASE(NETR_OPNUM_ServerPasswordSet2) struct netr_PasswordSet2 PasswordSet2; + + /* Special, for smb_decode_krb5_pac() */ + CASE(NETR_OPNUM_decode_krb5_pac) + struct krb5_validation_info krb5pac; }; typedef union netr_interface netr_interface_t; EXTERNTYPEINFO(netr_interface) diff --git a/usr/src/uts/common/smbsrv/netrauth.h b/usr/src/uts/common/smbsrv/netrauth.h index 7beb52ad51..bc11d81e8c 100644 --- a/usr/src/uts/common/smbsrv/netrauth.h +++ b/usr/src/uts/common/smbsrv/netrauth.h @@ -21,6 +21,8 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ #ifndef _SMBSRV_NETRAUTH_H @@ -34,6 +36,7 @@ #include <smbsrv/wintypes.h> #include <smbsrv/netbios.h> #include <smbsrv/smbinfo.h> +#include <netdb.h> #ifdef __cplusplus extern "C" { @@ -69,6 +72,32 @@ extern "C" { #define NETR_VALIDATION_LEVEL3 0x03 /* + * Most of these are from: "MSV1_0_LM20_LOGON structure" + * http://msdn.microsoft.com/en-us/library/windows/desktop/aa378762 + * and a few are from the ntddk (ntmsv1_0.h) found many places. + */ +#define MSV1_0_CLEARTEXT_PASSWORD_ALLOWED 0x00000002 +#define MSV1_0_UPDATE_LOGON_STATISTICS 0x00000004 +#define MSV1_0_RETURN_USER_PARAMETERS 0x00000008 +#define MSV1_0_DONT_TRY_GUEST_ACCOUNT 0x00000010 +#define MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT 0x00000020 +#define MSV1_0_RETURN_PASSWORD_EXPIRY 0x00000040 +/* + * MSV1_0_USE_CLIENT_CHALLENGE means the LM response field contains the + * "client challenge" in the first 8 bytes instead of the LM response. + */ +#define MSV1_0_USE_CLIENT_CHALLENGE 0x00000080 +#define MSV1_0_TRY_GUEST_ACCOUNT_ONLY 0x00000100 +#define MSV1_0_RETURN_PROFILE_PATH 0x00000200 +#define MSV1_0_TRY_SPECIFIED_DOMAIN_ONLY 0x00000400 +#define MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT 0x00000800 +#define MSV1_0_DISABLE_PERSONAL_FALLBACK 0x00001000 +#define MSV1_0_ALLOW_FORCE_GUEST 0x00002000 +#define MSV1_0_CLEARTEXT_PASSWORD_SUPPLIED 0x00004000 +#define MSV1_0_USE_DOMAIN_FOR_ROUTING_ONLY 0x00008000 +#define MSV1_0_SUBAUTHENTICATION_DLL_EX 0x00100000 + +/* * This is a duplicate of the netr_credential * from netlogon.ndl. */ @@ -92,8 +121,8 @@ typedef struct netr_session_key { typedef struct netr_info { DWORD flags; - char server[NETBIOS_NAME_SZ * 2]; - char hostname[NETBIOS_NAME_SZ * 2]; + char server[MAXHOSTNAMELEN]; /* Current DC, FQDN */ + char hostname[NETBIOS_NAME_SZ * 2]; /* local "flat" name */ netr_cred_t client_challenge; netr_cred_t server_challenge; netr_cred_t client_credential; diff --git a/usr/src/uts/common/smbsrv/smb.h b/usr/src/uts/common/smbsrv/smb.h index 100691582b..9c421bc454 100644 --- a/usr/src/uts/common/smbsrv/smb.h +++ b/usr/src/uts/common/smbsrv/smb.h @@ -20,6 +20,7 @@ */ /* + * Copyright 2012 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ @@ -777,20 +778,12 @@ typedef uint32_t smb_utime_t; * The following bits may be set in the SecurityMode field of the * SMB_COM_NEGOTIATE response. * - * Notes: - * NEGOTIATE_SECURITY_SHARE_LEVEL is a montana2 invention. - * - * The NTDDK definitions are: - * #define NEGOTIATE_USER_SECURITY 0x01 - * #define NEGOTIATE_ENCRYPT_PASSWORDS 0x02 - * #define NEGOTIATE_SECURITY_SIGNATURES_ENABLED 0x04 - * #define NEGOTIATE_SECURITY_SIGNATURES_REQUIRED 0x08 + * Note: Same as the NTDDK definitions. */ -#define NEGOTIATE_SECURITY_SHARE_LEVEL 0x00 -#define NEGOTIATE_SECURITY_USER_LEVEL 0x01 -#define NEGOTIATE_SECURITY_CHALLENGE_RESPONSE 0x02 -#define NEGOTIATE_SECURITY_SIGNATURES_ENABLED 0x04 -#define NEGOTIATE_SECURITY_SIGNATURES_REQUIRED 0x08 +#define NEGOTIATE_USER_SECURITY 0x01 +#define NEGOTIATE_ENCRYPT_PASSWORDS 0x02 +#define NEGOTIATE_SECURITY_SIGNATURES_ENABLED 0x04 +#define NEGOTIATE_SECURITY_SIGNATURES_REQUIRED 0x08 /* diff --git a/usr/src/uts/common/smbsrv/smb_door.h b/usr/src/uts/common/smbsrv/smb_door.h index 909f0778bf..9e284f5d9d 100644 --- a/usr/src/uts/common/smbsrv/smb_door.h +++ b/usr/src/uts/common/smbsrv/smb_door.h @@ -67,7 +67,8 @@ typedef enum smb_dopcode { SMB_DR_QUOTA_SET, SMB_DR_DFS_GET_REFERRALS, SMB_DR_SHR_HOSTACCESS, - SMB_DR_SHR_EXEC + SMB_DR_SHR_EXEC, + SMB_DR_NOTIFY_DC_CHANGED } smb_dopcode_t; struct smb_event; diff --git a/usr/src/uts/common/smbsrv/smb_ioctl.h b/usr/src/uts/common/smbsrv/smb_ioctl.h index e620a2b762..8ccecd7415 100644 --- a/usr/src/uts/common/smbsrv/smb_ioctl.h +++ b/usr/src/uts/common/smbsrv/smb_ioctl.h @@ -150,6 +150,7 @@ typedef struct smb_ioc_fileid { uint32_t uniqid; } smb_ioc_fileid_t; +/* See also: smb_kmod_cfg_t */ typedef struct smb_ioc_cfg { smb_ioc_header_t hdr; uint32_t maxworkers; @@ -161,12 +162,18 @@ typedef struct smb_ioc_cfg { int32_t oplock_enable; int32_t sync_enable; int32_t secmode; + int32_t netbios_enable; int32_t ipv6_enable; int32_t print_enable; int32_t traverse_mounts; - int32_t netbios_enable; uint32_t exec_flags; + uint32_t negtok_len; smb_version_t version; + /* SMB negotiate protocol response. */ + uuid_t machine_uuid; + uchar_t negtok[SMB_PI_MAX_NEGTOK]; + char native_os[SMB_PI_MAX_NATIVE_OS]; + char native_lm[SMB_PI_MAX_LANMAN]; char nbdomain[NETBIOS_NAME_SZ]; char fqdn[SMB_PI_MAX_DOMAIN]; char hostname[SMB_PI_MAX_HOST]; diff --git a/usr/src/uts/common/smbsrv/smb_kproto.h b/usr/src/uts/common/smbsrv/smb_kproto.h index f19dd9926a..9f8e445481 100644 --- a/usr/src/uts/common/smbsrv/smb_kproto.h +++ b/usr/src/uts/common/smbsrv/smb_kproto.h @@ -553,6 +553,13 @@ void smb_request_cancel(smb_request_t *); void smb_request_wait(smb_request_t *); /* + * authentication support (smb_authenticate.c) + */ +int smb_authenticate_ext(smb_request_t *); +int smb_authenticate_old(smb_request_t *); +void smb_authsock_close(smb_user_t *); + +/* * session functions (file smb_session.c) */ smb_session_t *smb_session_create(ksocket_t, uint16_t, smb_server_t *, int); @@ -566,6 +573,8 @@ void smb_session_config(smb_session_t *session); void smb_session_disconnect_from_share(smb_llist_t *, char *); smb_user_t *smb_session_dup_user(smb_session_t *, char *, char *); smb_user_t *smb_session_lookup_uid(smb_session_t *, uint16_t); +smb_user_t *smb_session_lookup_uid_st(smb_session_t *session, + uint16_t uid, smb_user_state_t st); void smb_session_post_user(smb_session_t *, smb_user_t *); void smb_session_post_tree(smb_session_t *, smb_tree_t *); smb_tree_t *smb_session_lookup_tree(smb_session_t *, uint16_t); @@ -653,7 +662,8 @@ void smb_odir_resume_at(smb_odir_t *, smb_odir_resume_t *); /* * SMB user functions (file smb_user.c) */ -smb_user_t *smb_user_login(smb_session_t *, cred_t *, +smb_user_t *smb_user_new(smb_session_t *); +int smb_user_logon(smb_user_t *, cred_t *, char *, char *, uint32_t, uint32_t, uint32_t); smb_user_t *smb_user_dup(smb_user_t *); void smb_user_logoff(smb_user_t *); diff --git a/usr/src/uts/common/smbsrv/smb_ktypes.h b/usr/src/uts/common/smbsrv/smb_ktypes.h index 79ee5e1ece..42428b2b13 100644 --- a/usr/src/uts/common/smbsrv/smb_ktypes.h +++ b/usr/src/uts/common/smbsrv/smb_ktypes.h @@ -300,6 +300,7 @@ typedef struct smb_idpool { kmutex_t id_mutex; uint8_t *id_pool; uint32_t id_size; + uint32_t id_maxsize; uint8_t id_bit; uint8_t id_bit_idx; uint32_t id_idx; @@ -700,15 +701,34 @@ typedef struct smb_arg_negotiate { timestruc_t ni_servertime; } smb_arg_negotiate_t; +typedef enum { + SMB_SSNSETUP_PRE_NTLM012 = 1, + SMB_SSNSETUP_NTLM012_NOEXT, + SMB_SSNSETUP_NTLM012_EXTSEC +} smb_ssnsetup_type_t; + typedef struct smb_arg_sessionsetup { + smb_ssnsetup_type_t ssi_type; char *ssi_user; char *ssi_domain; - uint16_t ssi_cipwlen; - uint8_t *ssi_cipwd; - uint16_t ssi_cspwlen; - uint8_t *ssi_cspwd; + /* LM password hash, f.k.a. case-insensitive p/w */ + uint16_t ssi_lmpwlen; + uint8_t *ssi_lmpwd; + /* NT password hash, f.k.a. case-sensitive p/w */ + uint16_t ssi_ntpwlen; + uint8_t *ssi_ntpwd; + /* Incoming security blob */ + uint16_t ssi_iseclen; + uint8_t *ssi_isecblob; + /* Incoming security blob */ + uint16_t ssi_oseclen; + uint8_t *ssi_osecblob; + /* parameters */ + uint16_t ssi_maxbufsize; uint16_t ssi_maxmpxcount; uint32_t ssi_capabilities; + int ssi_native_os; + int ssi_native_lm; boolean_t ssi_guest; } smb_arg_sessionsetup_t; @@ -765,7 +785,6 @@ struct smb_sign { uint32_t seqnum; uint_t mackey_len; uint8_t *mackey; - void *mech; /* mechanism info */ }; #define SMB_SIGNING_ENABLED 1 @@ -902,7 +921,6 @@ typedef struct smb_session { int32_t s_gmtoff; uint32_t keep_alive; uint64_t opentime; - uint16_t vcnumber; uint16_t s_local_port; smb_inaddr_t ipaddr; smb_inaddr_t local_ipaddr; @@ -911,7 +929,9 @@ typedef struct smb_session { int native_lm; uint32_t capabilities; - struct smb_sign signing; + + struct smb_sign signing; /* SMB1 */ + void *sign_mech; /* mechanism info */ void (*sign_fini)(struct smb_session *); ksocket_t sock; @@ -939,6 +959,7 @@ typedef struct smb_session { * in SMB_SESSION_SETUP_ANDX */ uint16_t smb_msg_size; + uint16_t smb_max_mpx; uchar_t *outpipe_data; int outpipe_datalen; int outpipe_cookie; @@ -967,7 +988,8 @@ typedef struct smb_session { typedef enum { - SMB_USER_STATE_LOGGED_IN = 0, + SMB_USER_STATE_LOGGING_ON = 0, + SMB_USER_STATE_LOGGED_ON, SMB_USER_STATE_LOGGING_OFF, SMB_USER_STATE_LOGGED_OFF, SMB_USER_STATE_SENTINEL @@ -981,6 +1003,7 @@ typedef struct smb_user { struct smb_server *u_server; smb_session_t *u_session; + ksocket_t u_authsock; uint16_t u_name_len; char *u_name; uint16_t u_domain_len; diff --git a/usr/src/uts/common/smbsrv/smb_privilege.h b/usr/src/uts/common/smbsrv/smb_privilege.h index 8a6504a6f1..0bfd6cbdbf 100644 --- a/usr/src/uts/common/smbsrv/smb_privilege.h +++ b/usr/src/uts/common/smbsrv/smb_privilege.h @@ -26,6 +26,8 @@ #ifndef _SMB_PRIVILEGE_H #define _SMB_PRIVILEGE_H +#include <smbsrv/wintypes.h> + #ifdef __cplusplus extern "C" { #endif diff --git a/usr/src/uts/common/smbsrv/smb_signing.h b/usr/src/uts/common/smbsrv/smb_signing.h index 1b1694a99d..780ceb5c1d 100644 --- a/usr/src/uts/common/smbsrv/smb_signing.h +++ b/usr/src/uts/common/smbsrv/smb_signing.h @@ -23,6 +23,10 @@ #include <security/pkcs11.h> #endif +#ifdef __cplusplus +extern "C" { +#endif + #define MD5_DIGEST_LENGTH 16 /* MD5 digest length in bytes */ #ifdef _KERNEL @@ -47,4 +51,8 @@ int smb_md5_init(smb_sign_ctx_t *, smb_sign_mech_t *); int smb_md5_update(smb_sign_ctx_t, void *, size_t); int smb_md5_final(smb_sign_ctx_t, uint8_t *); +#ifdef __cplusplus +} +#endif + #endif /* _SMB_SIGNING_H_ */ diff --git a/usr/src/uts/common/smbsrv/smb_token.h b/usr/src/uts/common/smbsrv/smb_token.h index 99541ad670..dd20d90c9e 100644 --- a/usr/src/uts/common/smbsrv/smb_token.h +++ b/usr/src/uts/common/smbsrv/smb_token.h @@ -19,33 +19,35 @@ * CDDL HEADER END */ /* + * Copyright 2012 Nexenta Systems, Inc. All rights reserved. * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ #ifndef _SMB_TOKEN_H #define _SMB_TOKEN_H -#include <smbsrv/netrauth.h> +#include <smbsrv/smb_inet.h> #include <smbsrv/smb_privilege.h> #include <smbsrv/smb_sid.h> +/* + * Don't want <smbsrv/netrauth.h> in here, but + * uts/common/fs/smbsrv/smb_authenticate.c + * wants this. Todo: cleanup + */ +#define NETR_NETWORK_LOGON 0x02 + #ifdef __cplusplus extern "C" { #endif /* - * User Session Key - * - * This is part of the MAC key which is required for signing SMB messages. + * 32-bit opaque buffer (non-null terminated strings) + * See also: smb_buf32_xdr() */ -typedef struct smb_session_key { - uint8_t data[16]; -} smb_session_key_t; - -/* 32-bit opaque buffer (non-null terminated strings) */ typedef struct smb_buf32 { uint32_t len; uint8_t *val; @@ -96,7 +98,7 @@ typedef struct smb_token { char *tkn_domain_name; uint32_t tkn_flags; uint32_t tkn_audit_sid; - smb_session_key_t *tkn_session_key; + smb_buf32_t tkn_ssnkey; smb_posix_grps_t *tkn_posix_grps; } smb_token_t; @@ -116,6 +118,7 @@ typedef struct smb_logon { smb_buf32_t lg_challenge_key; smb_buf32_t lg_nt_password; smb_buf32_t lg_lm_password; + uint32_t lg_ntlm_flags; int lg_native_os; int lg_native_lm; uint32_t lg_flags; @@ -125,8 +128,70 @@ typedef struct smb_logon { uint32_t lg_status; /* filled in user space */ } smb_logon_t; -int smb_logon_xdr(); -int smb_token_xdr(); +/* + * This is the name of the local (AF_UNIX) socket + * where the SMB auth. service listens. + */ +#define SMB_AUTHSVC_SOCKNAME "/var/smb/lipc/smbauth" + +/* + * Maximum number of authentcation conversations at one time. + * Note this is _NOT_ the max. number of logged on users, + * which can be much larger. + */ +#define SMB_AUTHSVC_MAXTHREAD 256 + +/* + * Messages to and from the local security authority + * Type codes: + */ +typedef enum smb_lsa_mtype { + /* reply types */ + LSA_MTYPE_OK = 0, + LSA_MTYPE_ERROR, + LSA_MTYPE_ES_DONE, /* ext. sec: authenticated */ + LSA_MTYPE_ES_CONT, /* more processing required */ + LSA_MTYPE_TOKEN, /* smb_token_t */ + + /* request types */ + LSA_MTYPE_OLDREQ, /* non-ext. sec. session setup */ + LSA_MTYPE_CLINFO, /* client info sent at start of ES */ + LSA_MTYPE_ESFIRST, /* spnego initial message */ + LSA_MTYPE_ESNEXT, /* spnego continuation */ + LSA_MTYPE_GETTOK /* after ES auth, get token */ +} smb_lsa_mtype_t; + +/* + * msg: header common to all message types + */ +typedef struct smb_lsa_msg_hdr { + uint32_t lmh_msgtype; /* smb_lsa_mtype_t */ + uint32_t lmh_msglen; /* size of what follows */ +} smb_lsa_msg_hdr_t; + +/* + * eresp: error response + * msgtype: LSA_MTYPE_ERESP + */ +typedef struct smb_lsa_eresp { + uint32_t ler_ntstatus; + uint16_t ler_errclass; + uint16_t ler_errcode; +} smb_lsa_eresp_t; + +/* + * Message for LSA_MTYPE_CLINFO + */ +typedef struct smb_lsa_clinfo { + smb_inaddr_t lci_clnt_ipaddr; + unsigned char lci_challenge_key[8]; + int lci_native_os; + int lci_native_lm; +} smb_lsa_clinfo_t; + +struct XDR; +int smb_logon_xdr(struct XDR *, smb_logon_t *); +int smb_token_xdr(struct XDR *, smb_token_t *); #if defined(_KERNEL) || defined(_FAKE_KERNEL) void smb_token_free(smb_token_t *); diff --git a/usr/src/uts/common/smbsrv/smbinfo.h b/usr/src/uts/common/smbsrv/smbinfo.h index 8e7724bdaa..fdb7ae8d5e 100644 --- a/usr/src/uts/common/smbsrv/smbinfo.h +++ b/usr/src/uts/common/smbsrv/smbinfo.h @@ -27,6 +27,7 @@ #define _SMBSRV_SMBINFO_H #include <sys/types.h> +#include <sys/uuid.h> #include <smbsrv/netbios.h> #include <netinet/in.h> #include <smbsrv/smb_inet.h> @@ -93,6 +94,7 @@ extern "C" { #define SMB_PI_MAX_COMMENT 58 #define SMB_PI_MAX_NATIVE_OS 32 #define SMB_PI_MAX_LANMAN 32 +#define SMB_PI_MAX_NEGTOK 256 /* GUID and SMB negotiate token */ #define SMB_PI_KEEP_ALIVE_MIN (90 * 60) /* @@ -115,6 +117,7 @@ typedef struct smb_version { uint32_t sv_platform_id; } smb_version_t; +/* See also: smb_ioc_cfg_t */ typedef struct smb_kmod_cfg { uint32_t skc_maxworkers; uint32_t skc_maxconnections; @@ -130,7 +133,13 @@ typedef struct smb_kmod_cfg { int32_t skc_print_enable; int32_t skc_traverse_mounts; uint32_t skc_execflags; + uint32_t skc_negtok_len; smb_version_t skc_version; + /* SMB negotiate protocol response. */ + uuid_t skc_machine_uuid; + uchar_t skc_negtok[SMB_PI_MAX_NEGTOK]; + char skc_native_os[SMB_PI_MAX_NATIVE_OS]; + char skc_native_lm[SMB_PI_MAX_LANMAN]; char skc_nbdomain[NETBIOS_NAME_SZ]; char skc_fqdn[SMB_PI_MAX_DOMAIN]; char skc_hostname[SMB_PI_MAX_HOST]; |
