diff options
author | Gordon Ross <gwr@nexenta.com> | 2013-06-21 15:59:58 -0400 |
---|---|---|
committer | Gordon Ross <gwr@nexenta.com> | 2015-10-26 10:16:22 -0400 |
commit | 12b65585e720714b31036daaa2b30eb76014048e (patch) | |
tree | c413afd3fb76e04e53ec04ce601d8c3ff35c65ab /usr/src | |
parent | 056d3a7d553516b590a0543f4df3152a3144b42b (diff) | |
download | illumos-joyent-12b65585e720714b31036daaa2b30eb76014048e.tar.gz |
1122 smbsrv should use SPNEGO (inbound authentication)
Portions contributed by: Matt Barden <Matt.Barden@nexenta.com>
Portions contributed by: Kevin Crowe <kevin.crowe@nexenta.com>
Portions contributed by: Alek Pinchuk <alek@nexenta.com>
Reviewed by: Bayard Bell <bayard.bell@nexenta.com>
Reviewed by: Dan Fields <dan.fields@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
Approved by: Robert Mustacchi <rm@joyent.com>
Diffstat (limited to 'usr/src')
77 files changed, 4999 insertions, 1757 deletions
diff --git a/usr/src/cmd/idmap/idmapd/idmap_config.c b/usr/src/cmd/idmap/idmapd/idmap_config.c index b8c2867489..a95441d4b9 100644 --- a/usr/src/cmd/idmap/idmapd/idmap_config.c +++ b/usr/src/cmd/idmap/idmapd/idmap_config.c @@ -89,46 +89,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); + + return (0); +} + +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); -#if UUID_LEN != 16 -#error UUID size is not 16! -#endif + len = asprintf(machine_sid, "S-1-5-21-%u-%u-%u", + uv.v[3], uv.v[0], uv.v[1]); - 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; + if (len == -1 || *machine_sid == NULL) { + idmapdlog(LOG_ERR, "Out of memory"); + return (-1); } return (0); @@ -1537,12 +1543,29 @@ 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, "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); diff --git a/usr/src/cmd/idmap/idmapd/idmap_config.h b/usr/src/cmd/idmap/idmapd/idmap_config.h index 31ae7b6ad1..f2ed7caa7c 100644 --- a/usr/src/cmd/idmap/idmapd/idmap_config.h +++ b/usr/src/cmd/idmap/idmapd/idmap_config.h @@ -77,6 +77,7 @@ typedef struct idmap_pg_config { uint64_t list_size_limit; uint64_t id_cache_timeout; uint64_t name_cache_timeout; + char *machine_uuid; /* machine uuid */ char *machine_sid; /* machine sid */ char *default_domain; /* default domain name */ char *domain_name; /* AD domain name */ 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/fksmbd/Makefile b/usr/src/cmd/smbsrv/fksmbd/Makefile index 615818e44a..a245c2422e 100644 --- a/usr/src/cmd/smbsrv/fksmbd/Makefile +++ b/usr/src/cmd/smbsrv/fksmbd/Makefile @@ -26,11 +26,14 @@ PROG= fksmbd OBJS_SMBD= \ + smbd_authsvc.o \ smbd_doorsvc.o \ smbd_join.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 +62,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 +80,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/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/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/smbd/Makefile b/usr/src/cmd/smbsrv/smbd/Makefile index 8fd9ccb74a..134a317fd8 100644 --- a/usr/src/cmd/smbsrv/smbd/Makefile +++ b/usr/src/cmd/smbsrv/smbd/Makefile @@ -26,11 +26,14 @@ PROG= smbd OBJS= \ + smbd_authsvc.o \ smbd_doorsvc.o \ smbd_join.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 +57,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 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..f3c1351851 100644 --- a/usr/src/cmd/smbsrv/smbd/smbd.h +++ b/usr/src/cmd/smbsrv/smbd/smbd.h @@ -56,6 +56,7 @@ uint32_t smbd_join(smb_joininfo_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..b8705b1d91 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> @@ -572,27 +573,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 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/lib/Makefile b/usr/src/lib/Makefile index 704d7ba772..544a04dd33 100644 --- a/usr/src/lib/Makefile +++ b/usr/src/lib/Makefile @@ -19,11 +19,11 @@ # 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 (c) 2012, Joyent, Inc. All rights reserved. # Copyright (c) 2013 Gary Mills +# Copyright 2013 Nexenta Systems, Inc. All rights reserved. # Copyright 2014 Garrett D'Amore <garrett@damore.org> # Copyright (c) 2015 Gary Mills @@ -660,7 +660,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 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/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/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/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/common/libmlsvc.h b/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h index 881535d2f3..685a760e16 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h +++ b/usr/src/lib/smbsrv/libmlsvc/common/libmlsvc.h @@ -70,6 +70,10 @@ extern void mlsvc_fini(void); extern DWORD mlsvc_netlogon(char *, char *); extern DWORD mlsvc_join(smb_domainex_t *, char *, char *); +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 *); + /* * The maximum number of domains (NT limit). diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mapfile-vers b/usr/src/lib/smbsrv/libmlsvc/common/mapfile-vers index 29af98f2ac..eece54e174 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,7 @@ SYMBOL_VERSION SUNWprivate { mlsvc_netlogon; smb_autohome_add; smb_autohome_remove; + smb_decode_krb5_pac; smb_locate_dc; smb_logon; smb_logon_abort; @@ -76,6 +78,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/netr_logon.c b/usr/src/lib/smbsrv/libmlsvc/common/netr_logon.c index 566837fe2d..a3fbb4d96f 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 @@ -254,13 +333,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 +683,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/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/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..82bbe995c3 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 2014 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); @@ -303,7 +305,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 +366,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 +490,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 +501,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 +517,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 +540,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 diff --git a/usr/src/lib/smbsrv/libsmb/common/mapfile-vers b/usr/src/lib/smbsrv/libsmb/common/mapfile-vers index 3570d05ba4..c4c3814864 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; @@ -265,11 +265,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; 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..1110fd8792 100644 --- a/usr/src/lib/smbsrv/libsmb/common/smb_cfg.c +++ b/usr/src/lib/smbsrv/libsmb/common/smb_cfg.c @@ -58,6 +58,7 @@ 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_PG_NAME "config" @@ -121,14 +122,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 +135,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 */ }; @@ -800,7 +800,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 +810,32 @@ 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); +} + +/* * 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_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/common/smbns_krb.h b/usr/src/lib/smbsrv/libsmbns/common/smbns_krb.h index 279634afc2..d60bca8785 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, diff --git a/usr/src/lib/smbsrv/libsmbns/common/smbns_ksetpwd.c b/usr/src/lib/smbsrv/libsmbns/common/smbns_ksetpwd.c index 946ca6461a..7a6a3491a1 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, @@ -529,12 +538,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/tools/quick/make-smbsrv b/usr/src/tools/quick/make-smbsrv index 3eca17ff83..2dc2651ce6 100755 --- a/usr/src/tools/quick/make-smbsrv +++ b/usr/src/tools/quick/make-smbsrv @@ -88,10 +88,10 @@ 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 @@ -102,6 +102,8 @@ for lib in \ libcryptoutil \ libdevid \ libfakekernel \ + libgss \ + libkrb5 \ libidmap \ libpam \ libsec \ @@ -140,6 +142,8 @@ do_lib1() { for lib in \ libavl \ + libgss \ + libkrb5 \ libcmdutils \ libsqlite \ libuutil @@ -241,8 +245,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 diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files index cd9da11ac8..c90a5c1773 100644 --- a/usr/src/uts/common/Makefile.files +++ b/usr/src/uts/common/Makefile.files @@ -1182,10 +1182,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..54ea798a0d 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 @@ -69,6 +71,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. */ 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_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_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..5b7a4eba37 100644 --- a/usr/src/uts/common/smbsrv/smb_token.h +++ b/usr/src/uts/common/smbsrv/smb_token.h @@ -19,10 +19,11 @@ * 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 @@ -37,15 +38,9 @@ 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 +91,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 +111,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 +121,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]; |