diff options
author | Gordon Ross <gwr@nexenta.com> | 2013-09-08 19:24:51 -0400 |
---|---|---|
committer | Gordon Ross <gwr@nexenta.com> | 2015-06-13 13:27:15 -0400 |
commit | 68b2bbf26c7040fea4281dcb58b81e7627e46f34 (patch) | |
tree | 7f3a857b4f2d553f8ea52bebb9ae8b7cada1fb37 | |
parent | 5a48565528ab0659af6d43ebe1659bfff8074e8f (diff) | |
download | illumos-joyent-68b2bbf26c7040fea4281dcb58b81e7627e46f34.tar.gz |
5995 RPC over SMB named pipes should use AF_UNIX sockets
Reviewed by: Dan McDonald <danmcd@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Thomas Keiser <thomas.keiser@nexenta.com>
Reviewed by: Igor Kozhukhov <ikozhukhov@gmail.com>
Approved by: Robert Mustacchi <rm@joyent.com>
35 files changed, 1205 insertions, 1913 deletions
diff --git a/usr/src/cmd/smbsrv/fksmbd/Makefile b/usr/src/cmd/smbsrv/fksmbd/Makefile index cb72f8239e..615818e44a 100644 --- a/usr/src/cmd/smbsrv/fksmbd/Makefile +++ b/usr/src/cmd/smbsrv/fksmbd/Makefile @@ -31,7 +31,7 @@ OBJS_SMBD= \ smbd_logon.o \ smbd_main.o \ smbd_nicmon.o \ - smbd_opipe_doorsvc.o \ + smbd_pipesvc.o \ smbd_share_doorsvc.o \ smbd_spool.o \ smbd_vss.o \ @@ -41,7 +41,6 @@ OBJS_LOCAL = \ fksmbd_door.o \ fksmbd_kmod.o \ fksmbd_ksock.o \ - fksmbd_opipe.o \ fksmbd_log.o \ fksmbd_shr.o diff --git a/usr/src/cmd/smbsrv/fksmbd/Watch-pipesvc.d b/usr/src/cmd/smbsrv/fksmbd/Watch-pipesvc.d new file mode 100644 index 0000000000..f11156001d --- /dev/null +++ b/usr/src/cmd/smbsrv/fksmbd/Watch-pipesvc.d @@ -0,0 +1,62 @@ +#!/usr/sbin/dtrace -s +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + */ + +/* + * User-level dtrace for smbd + * Usage: dtrace -s ThisScript.d -p PID + */ + +#pragma D option flowindent + +pid$target:fksmbd:pipesvc_worker:entry +{ + self->trace++; +} +pid$target:fksmbd:pipesvc_worker:return +{ + self->trace--; +} + +pid$target:fksmbd::entry, +pid$target:libfksmbsrv.so.1::entry, +pid$target:libmlsvc.so.1::entry, +pid$target:libmlrpc.so.1::entry, +pid$target:libsmbns.so.1::entry, +pid$target:libsmb.so.1::entry +/self->trace/ +{ + printf("\t0x%x", arg0); + printf("\t0x%x", arg1); + printf("\t0x%x", arg2); + printf("\t0x%x", arg3); +} + +pid$target:fksmbd::return, +pid$target:libfksmbsrv.so.1::return, +pid$target:libmlsvc.so.1::return, +pid$target:libmlrpc.so.1::return, +pid$target:libsmbns.so.1::return, +pid$target:libsmb.so.1::return +/self->trace/ +{ + printf("\t0x%x", arg1); +} + +pid$target:libmlrpc.so.1:ndo_trace:entry +/self->trace/ +{ + printf("ndo_trace: %s", copyinstr(arg0)); +} diff --git a/usr/src/cmd/smbsrv/fksmbd/fksmbd_kmod.c b/usr/src/cmd/smbsrv/fksmbd/fksmbd_kmod.c index 067639453b..65fc7cecbb 100644 --- a/usr/src/cmd/smbsrv/fksmbd/fksmbd_kmod.c +++ b/usr/src/cmd/smbsrv/fksmbd/fksmbd_kmod.c @@ -134,7 +134,7 @@ smb_kmod_start(int opipe, int lmshr, int udoor) /* These are the "door" dispatch callbacks */ ioc.lmshr_func = NULL; /* not used */ - ioc.opipe_func = (void *)fksmbd_opipe_dispatch; + ioc.opipe_func = NULL; /* not used */ ioc.udoor_func = (void *)fksmbd_door_dispatch; rc = smb_kmod_ioctl(SMB_IOC_START, &ioc.hdr, sizeof (ioc)); diff --git a/usr/src/cmd/smbsrv/fksmbd/fksmbd_opipe.c b/usr/src/cmd/smbsrv/fksmbd/fksmbd_opipe.c deleted file mode 100644 index 7fd8df64d3..0000000000 --- a/usr/src/cmd/smbsrv/fksmbd/fksmbd_opipe.c +++ /dev/null @@ -1,156 +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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - */ - -#include <sys/list.h> -#include <assert.h> -#include <alloca.h> -#include <door.h> -#include <errno.h> -#include <syslog.h> -#include <unistd.h> -#include <stdio.h> -#include <synch.h> -#include <string.h> -#include <stdlib.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <pthread.h> -#include <strings.h> -#include <umem.h> - -#include <smbsrv/smb_door.h> -#include <smbsrv/smb_xdr.h> -#include <smbsrv/smb_token.h> -#include <smbsrv/libmlsvc.h> -#include <smbsrv/libsmbns.h> -#include "smbd.h" - -static int smbd_opipe_exec(uint32_t fid); - - -/* - * Process smbd opipe requests. - * - * This is a special version of smb_opipe_dispatch() - * for the "fake" smbsrv (running in user space). - * This is called via function pointer from - * smbsrv: smb_opipe_door_call() - * - * Very similar to smbd_opipe_dispatch() - */ -int -fksmbd_opipe_dispatch(door_arg_t *da) -{ - uint8_t *buf = (uint8_t *)da->data_ptr; - smb_doorhdr_t hdr; - size_t hdr_size; - uint8_t *data; - uint32_t datalen; - - if (!smbd_online()) - return (-1); - - bzero(&hdr, sizeof (smb_doorhdr_t)); - hdr_size = xdr_sizeof(smb_doorhdr_xdr, &hdr); - - if (da->data_ptr == NULL || da->data_size < hdr_size) - return (-1); - - if (smb_doorhdr_decode(&hdr, buf, hdr_size) == -1) - return (-1); - - if ((hdr.dh_magic != SMB_OPIPE_HDR_MAGIC) || (hdr.dh_fid == 0)) - return (-1); - - if (hdr.dh_datalen > SMB_OPIPE_DOOR_BUFSIZE) - hdr.dh_datalen = SMB_OPIPE_DOOR_BUFSIZE; - - data = buf + hdr_size; - datalen = hdr.dh_datalen; - - switch (hdr.dh_op) { - case SMB_OPIPE_OPEN: - hdr.dh_door_rc = ndr_pipe_open(hdr.dh_fid, data, datalen); - hdr.dh_datalen = 0; - hdr.dh_resid = 0; - datalen = hdr_size; - break; - - case SMB_OPIPE_CLOSE: - hdr.dh_door_rc = ndr_pipe_close(hdr.dh_fid); - hdr.dh_datalen = 0; - hdr.dh_resid = 0; - datalen = hdr_size; - break; - - case SMB_OPIPE_READ: - data = (uint8_t *)buf + hdr_size; - datalen = hdr.dh_datalen; - hdr.dh_door_rc = ndr_pipe_read(hdr.dh_fid, data, &datalen, - &hdr.dh_resid); - hdr.dh_datalen = datalen; - datalen += hdr_size; - break; - - case SMB_OPIPE_WRITE: - hdr.dh_door_rc = ndr_pipe_write(hdr.dh_fid, data, datalen); - hdr.dh_datalen = 0; - hdr.dh_resid = 0; - datalen = hdr_size; - break; - - case SMB_OPIPE_EXEC: - hdr.dh_door_rc = smbd_opipe_exec(hdr.dh_fid); - hdr.dh_datalen = 0; - hdr.dh_resid = 0; - datalen = hdr_size; - break; - - default: - return (-1); - } - - (void) smb_doorhdr_encode(&hdr, (uint8_t *)buf, hdr_size); - return (0); -} - -/* - * Normal (from a real kernel) up calls get a thread here. - * In the "fake" kernel (all user space) we don't need that. - * NB: arg will be freed by ndr_pipe_transact() - */ -static int -smbd_opipe_exec(uint32_t fid) -{ - uint32_t *arg; - - if ((arg = malloc(sizeof (uint32_t))) == NULL) - return (ENOMEM); - - *arg = fid; - - (void) ndr_pipe_transact(arg); - - return (0); -} diff --git a/usr/src/cmd/smbsrv/smbd/Makefile b/usr/src/cmd/smbsrv/smbd/Makefile index 2927c33ca1..8fd9ccb74a 100644 --- a/usr/src/cmd/smbsrv/smbd/Makefile +++ b/usr/src/cmd/smbsrv/smbd/Makefile @@ -31,7 +31,7 @@ OBJS= \ smbd_logon.o \ smbd_main.o \ smbd_nicmon.o \ - smbd_opipe_doorsvc.o \ + smbd_pipesvc.o \ smbd_share_doorsvc.o \ smbd_spool.o \ smbd_syslog.o \ diff --git a/usr/src/cmd/smbsrv/smbd/smbd.h b/usr/src/cmd/smbsrv/smbd/smbd.h index 750a662e9e..3ec5877fac 100644 --- a/usr/src/cmd/smbsrv/smbd/smbd.h +++ b/usr/src/cmd/smbsrv/smbd/smbd.h @@ -40,8 +40,8 @@ extern "C" { #include <smbsrv/libmlsvc.h> void smbd_report(const char *fmt, ...); -int smbd_opipe_start(void); -void smbd_opipe_stop(void); +int smbd_pipesvc_start(void); +void smbd_pipesvc_stop(void); int smbd_share_start(void); void smbd_share_stop(void); int smbd_nicmon_start(const char *); @@ -143,7 +143,6 @@ void *smbd_door_dispatch_op(void *); /* For fksmbd */ void fksmbd_init(void); int fksmbd_door_dispatch(smb_doorarg_t *); -int fksmbd_opipe_dispatch(door_arg_t *); #ifdef __cplusplus } diff --git a/usr/src/cmd/smbsrv/smbd/smbd_main.c b/usr/src/cmd/smbsrv/smbd/smbd_main.c index a0a8793aba..59b11eb702 100644 --- a/usr/src/cmd/smbsrv/smbd/smbd_main.c +++ b/usr/src/cmd/smbsrv/smbd/smbd_main.c @@ -431,7 +431,8 @@ smbd_service_init(void) { SMB_CVOL, 0755 }, { SMB_SYSROOT, 0755 }, { SMB_SYSTEM32, 0755 }, - { SMB_VSS, 0755 } + { SMB_VSS, 0755 }, + { SMB_PIPE_DIR, 0755 }, }; int rc, i; @@ -497,14 +498,13 @@ smbd_service_init(void) smbd_report("DC monitor initialization failed %s", strerror(errno)); - if (mlsvc_init() != 0) { - smbd_report("msrpc initialization failed"); + if (smbd_pipesvc_start() != 0) { + smbd_report("pipesvc initialization failed"); return (-1); } smbd.s_door_srv = smbd_door_start(); - smbd.s_door_opipe = smbd_opipe_start(); - if (smbd.s_door_srv < 0 || smbd.s_door_opipe < 0) { + if (smbd.s_door_srv < 0) { smbd_report("door initialization failed %s", strerror(errno)); return (-1); } @@ -553,7 +553,7 @@ smbd_service_fini(void) smb_kmod_stop(); smb_logon_abort(); smb_lgrp_stop(); - smbd_opipe_stop(); + smbd_pipesvc_stop(); smbd_door_stop(); smbd_spool_stop(); smbd_kernel_unbind(); diff --git a/usr/src/cmd/smbsrv/smbd/smbd_opipe_doorsvc.c b/usr/src/cmd/smbsrv/smbd/smbd_opipe_doorsvc.c deleted file mode 100644 index c092e1b395..0000000000 --- a/usr/src/cmd/smbsrv/smbd/smbd_opipe_doorsvc.c +++ /dev/null @@ -1,210 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ -/* - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#include <stdio.h> -#include <strings.h> -#include <stdlib.h> -#include <unistd.h> -#include <fcntl.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <door.h> -#include <errno.h> -#include <pthread.h> - -#include <smbsrv/libsmb.h> -#include <smbsrv/libmlrpc.h> -#include "smbd.h" - -static int smbd_opipe_fd = -1; -static int smbd_opipe_cookie = 0x50495045; /* PIPE */ -static pthread_mutex_t smbd_opipe_mutex = PTHREAD_MUTEX_INITIALIZER; -static smbd_door_t smbd_opipe_sdh; - -static void smbd_opipe_dispatch(void *, char *, size_t, door_desc_t *, uint_t); -static int smbd_opipe_exec_async(uint32_t); - -/* - * Create the smbd opipe door service. - * Returns the door descriptor on success. Otherwise returns -1. - */ -int -smbd_opipe_start(void) -{ - (void) pthread_mutex_lock(&smbd_opipe_mutex); - - if (smbd_opipe_fd != -1) { - (void) pthread_mutex_unlock(&smbd_opipe_mutex); - errno = EEXIST; - return (-1); - } - - smbd_door_init(&smbd_opipe_sdh, "opipe"); - - errno = 0; - if ((smbd_opipe_fd = door_create(smbd_opipe_dispatch, - &smbd_opipe_cookie, (DOOR_UNREF | DOOR_REFUSE_DESC))) < 0) { - smbd_opipe_fd = -1; - } - - (void) pthread_mutex_unlock(&smbd_opipe_mutex); - return (smbd_opipe_fd); -} - -/* - * Stop the smbd opipe door service. - */ -void -smbd_opipe_stop(void) -{ - (void) pthread_mutex_lock(&smbd_opipe_mutex); - - smbd_door_fini(&smbd_opipe_sdh); - - if (smbd_opipe_fd != -1) { - (void) door_revoke(smbd_opipe_fd); - smbd_opipe_fd = -1; - } - - (void) pthread_mutex_unlock(&smbd_opipe_mutex); -} - -/* - * Process smbd opipe requests. - */ -/*ARGSUSED*/ -static void -smbd_opipe_dispatch(void *cookie, char *argp, size_t arg_size, - door_desc_t *dd, uint_t n_desc) -{ - char buf[SMB_OPIPE_DOOR_BUFSIZE]; - smb_doorhdr_t hdr; - size_t hdr_size; - uint8_t *data; - uint32_t datalen; - - smbd_door_enter(&smbd_opipe_sdh); - - if (!smbd_online()) - smbd_door_return(&smbd_opipe_sdh, NULL, 0, NULL, 0); - - bzero(&hdr, sizeof (smb_doorhdr_t)); - hdr_size = xdr_sizeof(smb_doorhdr_xdr, &hdr); - - if ((cookie != &smbd_opipe_cookie) || (argp == NULL) || - (arg_size < hdr_size)) { - smbd_door_return(&smbd_opipe_sdh, NULL, 0, NULL, 0); - } - - if (smb_doorhdr_decode(&hdr, (uint8_t *)argp, hdr_size) == -1) - smbd_door_return(&smbd_opipe_sdh, NULL, 0, NULL, 0); - - if ((hdr.dh_magic != SMB_OPIPE_HDR_MAGIC) || (hdr.dh_fid == 0)) - smbd_door_return(&smbd_opipe_sdh, NULL, 0, NULL, 0); - - if (hdr.dh_datalen > SMB_OPIPE_DOOR_BUFSIZE) - hdr.dh_datalen = SMB_OPIPE_DOOR_BUFSIZE; - - data = (uint8_t *)argp + hdr_size; - datalen = hdr.dh_datalen; - - switch (hdr.dh_op) { - case SMB_OPIPE_OPEN: - hdr.dh_door_rc = ndr_pipe_open(hdr.dh_fid, data, datalen); - - hdr.dh_datalen = 0; - hdr.dh_resid = 0; - datalen = hdr_size; - break; - - case SMB_OPIPE_CLOSE: - hdr.dh_door_rc = ndr_pipe_close(hdr.dh_fid); - - hdr.dh_datalen = 0; - hdr.dh_resid = 0; - datalen = hdr_size; - break; - - case SMB_OPIPE_READ: - data = (uint8_t *)buf + hdr_size; - datalen = hdr.dh_datalen; - - hdr.dh_door_rc = ndr_pipe_read(hdr.dh_fid, data, &datalen, - &hdr.dh_resid); - - hdr.dh_datalen = datalen; - datalen += hdr_size; - break; - - case SMB_OPIPE_WRITE: - hdr.dh_door_rc = ndr_pipe_write(hdr.dh_fid, data, datalen); - - hdr.dh_datalen = 0; - hdr.dh_resid = 0; - datalen = hdr_size; - break; - - case SMB_OPIPE_EXEC: - hdr.dh_door_rc = smbd_opipe_exec_async(hdr.dh_fid); - - hdr.dh_datalen = 0; - hdr.dh_resid = 0; - datalen = hdr_size; - break; - - default: - smbd_door_return(&smbd_opipe_sdh, NULL, 0, NULL, 0); - break; - } - - (void) smb_doorhdr_encode(&hdr, (uint8_t *)buf, hdr_size); - smbd_door_return(&smbd_opipe_sdh, buf, datalen, NULL, 0); -} - -/* - * On success, arg will be freed by the thread. - */ -static int -smbd_opipe_exec_async(uint32_t fid) -{ - pthread_attr_t attr; - pthread_t tid; - uint32_t *arg; - int rc; - - if ((arg = malloc(sizeof (uint32_t))) == NULL) - return (ENOMEM); - - *arg = fid; - - (void) pthread_attr_init(&attr); - (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - rc = pthread_create(&tid, &attr, ndr_pipe_transact, arg); - (void) pthread_attr_destroy(&attr); - - if (rc != 0) - free(arg); - return (rc); -} diff --git a/usr/src/cmd/smbsrv/smbd/smbd_pipesvc.c b/usr/src/cmd/smbsrv/smbd/smbd_pipesvc.c new file mode 100644 index 0000000000..c6e6682b8e --- /dev/null +++ b/usr/src/cmd/smbsrv/smbd/smbd_pipesvc.c @@ -0,0 +1,394 @@ +/* + * 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. + */ + +/* + * This is the named pipe service for smbd. + */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <stdio.h> +#include <strings.h> +#include <stdlib.h> +#include <synch.h> +#include <unistd.h> +#include <fcntl.h> +#include <door.h> +#include <errno.h> +#include <pthread.h> +#include <signal.h> + +#include <smbsrv/libsmb.h> +#include <smbsrv/libmlsvc.h> +#include <smbsrv/smb_xdr.h> +#include "smbd.h" + +struct pipe_listener { + const char *name; + int max_allowed; + int max_seen; + int current; + pthread_t tid; +}; + +static void *pipesvc_listener(void *); +static void *pipesvc_worker(void *); +static int pipe_send(ndr_pipe_t *, void *, size_t); +static int pipe_recv(ndr_pipe_t *, void *, size_t); + +mutex_t pipesvc_mutex = DEFAULTMUTEX; +int pipesvc_workers_max = 500; +int pipesvc_workers_cur = 0; + +uint16_t pipe_max_msgsize = SMB_PIPE_MAX_MSGSIZE; + +/* + * Allow more opens on SRVSVC because that's used by many clients + * to get the share list, etc. + */ +#define SRVSVC_MAX_OPENS 200 +#define DEF_MAX_OPENS 50 + +#define NLISTENERS 11 +static struct pipe_listener +pipe_listeners[NLISTENERS] = { + { "eventlog", DEF_MAX_OPENS, 0, 0 }, + { "lsarpc", DEF_MAX_OPENS, 0, 0 }, + { "lsass", DEF_MAX_OPENS, 0, 0 }, + { "netdfs", DEF_MAX_OPENS, 0, 0 }, + { "netlogon", DEF_MAX_OPENS, 0, 0 }, + { "samr", DEF_MAX_OPENS, 0, 0 }, + { "spoolss", DEF_MAX_OPENS, 0, 0 }, + { "srvsvc", SRVSVC_MAX_OPENS, 0, 0 }, + { "svcctl", DEF_MAX_OPENS, 0, 0 }, + { "winreg", DEF_MAX_OPENS, 0, 0 }, + { "wkssvc", DEF_MAX_OPENS, 0, 0 }, +}; + +static ndr_pipe_t * +np_new(struct pipe_listener *pl, int fid) +{ + ndr_pipe_t *np; + size_t len; + + /* + * Allocating ndr_pipe_t + smb_netuserinfo_t as one. + * We could just make that part of ndr_pipe_t, but + * that struct is opaque to libmlrpc. + */ + len = sizeof (*np) + sizeof (smb_netuserinfo_t); + np = malloc(len); + if (np == NULL) + return (NULL); + + bzero(np, len); + np->np_listener = pl; + np->np_endpoint = pl->name; + np->np_user = (void*)(np + 1); + np->np_send = pipe_send; + np->np_recv = pipe_recv; + np->np_fid = fid; + np->np_max_xmit_frag = pipe_max_msgsize; + np->np_max_recv_frag = pipe_max_msgsize; + + return (np); +} + +static void +np_free(ndr_pipe_t *np) +{ + (void) close(np->np_fid); + free(np); +} + +/* + * Create the smbd opipe door service. + * Returns the door descriptor on success. Otherwise returns -1. + */ +int +smbd_pipesvc_start(void) +{ + pthread_t tid; + pthread_attr_t tattr; + struct pipe_listener *pl; + int i, rc; + + if (mlsvc_init() != 0) { + smbd_report("msrpc initialization failed"); + return (-1); + } + + (void) pthread_attr_init(&tattr); + (void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); + + for (i = 0; i < NLISTENERS; i++) { + pl = &pipe_listeners[i]; + pl->max_seen = 0; + + if (strcasecmp(pl->name, "spoolss") == 0 && + smb_config_getbool(SMB_CI_PRINT_ENABLE) == B_FALSE) + continue; + + rc = pthread_create(&tid, &tattr, pipesvc_listener, pl); + if (rc != 0) + break; + pipe_listeners[i].tid = tid; + } + + if (rc != 0) { + smbd_report("pipesvc pthread_create, %d", rc); + } + + (void) pthread_attr_destroy(&tattr); + + return (rc); +} + +void +smbd_pipesvc_stop(void) +{ + int i; + + (void) mutex_lock(&pipesvc_mutex); + for (i = 0; i < NLISTENERS; i++) { + if (pipe_listeners[i].tid == 0) + continue; + (void) pthread_kill(pipe_listeners[i].tid, SIGTERM); + pipe_listeners[i].tid = 0; + } + (void) mutex_unlock(&pipesvc_mutex); +} + +static void * +pipesvc_listener(void *varg) +{ + struct sockaddr_un sa; + int err, listen_fd, newfd, snlen; + struct pipe_listener *pl = varg; + ndr_pipe_t *np; + pthread_t tid; + int rc; + + listen_fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (listen_fd < 0) { + smbd_report("pipesvc_listener, so_create: %d", errno); + return (NULL); + } + + bzero(&sa, sizeof (sa)); + sa.sun_family = AF_UNIX; + (void) snprintf(sa.sun_path, sizeof (sa.sun_path), + "%s/%s", SMB_PIPE_DIR, pl->name); + + /* Bind it to a listening name. */ + (void) unlink(sa.sun_path); + if (bind(listen_fd, (struct sockaddr *)&sa, sizeof (sa)) < 0) { + smbd_report("pipesvc_listener, so_bind: %d", errno); + (void) close(listen_fd); + return (NULL); + } + + if (listen(listen_fd, SOMAXCONN) < 0) { + smbd_report("pipesvc_listener, listen: %d", errno); + (void) close(listen_fd); + return (NULL); + } + + for (;;) { + + snlen = sizeof (sa); + newfd = accept(listen_fd, (struct sockaddr *)&sa, &snlen); + if (newfd < 0) { + err = errno; + switch (err) { + case ECONNABORTED: + continue; + case EINTR: + /* normal termination */ + goto out; + default: + smbd_report("pipesvc_listener, " + "accept failed: %d", errno); + } + smbd_report("pipesvc_listener, accept: %d", err); + break; + } + + np = np_new(pl, newfd); + if (np == NULL) { + smbd_report("pipesvc_listener, alloc1 failed"); + (void) close(newfd); + continue; + } + + rc = pthread_create(&tid, NULL, pipesvc_worker, np); + if (rc != 0) { + smbd_report("pipesvc_listener, pthread_create: %d", + errno); + np_free(np); + continue; + } + (void) pthread_detach(tid); + + /* Note: np_free in pipesvc_worker */ + np = NULL; + } + +out: + (void) close(listen_fd); + pl->tid = 0; + return (NULL); +} + +static void * +pipesvc_worker(void *varg) +{ + XDR xdrs; + smb_pipehdr_t phdr; + ndr_pipe_t *np = varg; + struct pipe_listener *pl = np->np_listener; + void *buf = NULL; + uint32_t status; + ssize_t rc; + + (void) mutex_lock(&pipesvc_mutex); + if (pipesvc_workers_cur >= pipesvc_workers_max || + pl->current >= pl->max_allowed) { + (void) mutex_unlock(&pipesvc_mutex); + status = NT_STATUS_PIPE_NOT_AVAILABLE; + (void) send(np->np_fid, &status, sizeof (status), 0); + goto out_free_np; + } + pipesvc_workers_cur++; + pl->current++; + if (pl->max_seen < pl->current) + pl->max_seen = pl->current; + (void) mutex_unlock(&pipesvc_mutex); + + /* + * The smbsrv kmod sends us one initial message containing an + * XDR encoded smb_netuserinfo_t that we read and decode here, + * all unbeknownst to libmlrpc. + * + * Might be nice to enhance getpeerucred() so it can give us + * all the info smb_netuserinfo_t carries, and then use that, + * which would allow using a more generic RPC service. + */ + rc = pipe_recv(np, &phdr, sizeof (phdr)); + if (rc != 0) { + smbd_report("pipesvc_worker, recv1: %d", rc); + goto out_decr; + } + if (phdr.ph_magic != SMB_PIPE_HDR_MAGIC || + phdr.ph_uilen > 8192) { + smbd_report("pipesvc_worker, bad hdr"); + goto out_decr; + } + buf = malloc(phdr.ph_uilen); + if (buf == NULL) { + smbd_report("pipesvc_worker, alloc1 failed"); + goto out_decr; + } + rc = pipe_recv(np, buf, phdr.ph_uilen); + if (rc != 0) { + smbd_report("pipesvc_worker, recv2: %d", rc); + goto out_decr; + } + + xdrmem_create(&xdrs, buf, phdr.ph_uilen, XDR_DECODE); + if (!smb_netuserinfo_xdr(&xdrs, np->np_user)) { + smbd_report("pipesvc_worker, bad uinfo"); + goto out_free_buf; + } + + /* + * Later, could disallow opens of some pipes by + * anonymous users, etc. For now, reply "OK". + */ + status = 0; + rc = pipe_send(np, &status, sizeof (status)); + if (rc != 0) { + smbd_report("pipesvc_worker, send1: %d", rc); + goto out_free_buf; + } + + /* + * Run the RPC service loop worker, which + * returns when it sees the pipe close. + */ + ndr_pipe_worker(np); + + xdrs.x_op = XDR_FREE; + (void) smb_netuserinfo_xdr(&xdrs, np->np_user); + +out_free_buf: + free(buf); + xdr_destroy(&xdrs); + +out_decr: + (void) mutex_lock(&pipesvc_mutex); + pipesvc_workers_cur--; + pl->current--; + (void) mutex_unlock(&pipesvc_mutex); + +out_free_np: + /* Cleanup what came in by varg. */ + (void) shutdown(np->np_fid, SHUT_RDWR); + np_free(np); + return (NULL); +} + +/* + * These are the transport get/put callback functions provided + * via the ndr_pipe_t object to the libmlrpc`ndr_pipe_worker. + * These are called only with known PDU sizes and should + * loop as needed to transfer the entire message. + */ +static int +pipe_recv(ndr_pipe_t *np, void *buf, size_t len) +{ + int x; + + while (len > 0) { + x = recv(np->np_fid, buf, len, 0); + if (x < 0) + return (errno); + if (x == 0) + return (EIO); + buf = (char *)buf + x; + len -= x; + } + + return (0); +} + +static int +pipe_send(ndr_pipe_t *np, void *buf, size_t len) +{ + int x; + + while (len > 0) { + x = send(np->np_fid, buf, len, 0); + if (x < 0) + return (errno); + if (x == 0) + return (EIO); + buf = (char *)buf + x; + len -= x; + } + + return (0); +} diff --git a/usr/src/lib/libfakekernel/common/ksocket.c b/usr/src/lib/libfakekernel/common/ksocket.c index 53bcf87576..5ff3538926 100644 --- a/usr/src/lib/libfakekernel/common/ksocket.c +++ b/usr/src/lib/libfakekernel/common/ksocket.c @@ -21,7 +21,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. */ #include <sys/types.h> @@ -504,6 +504,27 @@ ksocket_setsockopt(ksocket_t ks, int level, int optname, const void *optval, return (0); } +int +ksocket_ioctl(ksocket_t ks, int cmd, intptr_t arg, int *rvp, struct cred *cr) +{ + int rval; + + /* All Solaris components should pass a cred for this operation. */ + ASSERT(cr != NULL); + + if (!KSOCKET_VALID(ks)) + return (ENOTSOCK); + + rval = ioctl(KSTOSO(ks), cmd, arg); + if (rvp != NULL) + *rvp = rval; + + if (rval != 0) + rval = errno; + + return (rval); +} + void ksocket_hold(ksocket_t ks) { diff --git a/usr/src/lib/libfakekernel/common/mapfile-vers b/usr/src/lib/libfakekernel/common/mapfile-vers index ea8b9b8ca0..ffbff01f7f 100644 --- a/usr/src/lib/libfakekernel/common/mapfile-vers +++ b/usr/src/lib/libfakekernel/common/mapfile-vers @@ -10,7 +10,7 @@ # # -# Copyright 2013 Nexenta Systems, Inc. All rights reserved. +# Copyright 2014 Nexenta Systems, Inc. All rights reserved. # # @@ -105,6 +105,7 @@ SYMBOL_VERSION SUNWprivate_1.1 { ksocket_getpeername; ksocket_getsockname; ksocket_hold; + ksocket_ioctl; ksocket_listen; ksocket_recv; ksocket_recvfrom; diff --git a/usr/src/lib/smbsrv/libfksmbsrv/Makefile.com b/usr/src/lib/smbsrv/libfksmbsrv/Makefile.com index 92afcba6ae..d40fa8d629 100644 --- a/usr/src/lib/smbsrv/libfksmbsrv/Makefile.com +++ b/usr/src/lib/smbsrv/libfksmbsrv/Makefile.com @@ -34,7 +34,6 @@ OBJS_LOCAL = \ fksmb_idmap.o \ fksmb_init.o \ fksmb_kdoor.o \ - fksmb_opipe_door.o \ fksmb_sign_pkcs.o \ fake_lookup.o \ fake_nblk.o \ diff --git a/usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_opipe_door.c b/usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_opipe_door.c deleted file mode 100644 index a61ea7194c..0000000000 --- a/usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_opipe_door.c +++ /dev/null @@ -1,131 +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 2011 Nexenta Systems, Inc. All rights reserved. - * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - */ - -/* - * This module provides the interface to NDR RPC. - */ - -#include <sys/stat.h> -#include <sys/door.h> -#include <sys/door_data.h> -#include <sys/uio.h> -#include <sys/ksynch.h> -#include <smbsrv/smb_kproto.h> -#include <smbsrv/smb_door.h> - -/* - * opipe door client (to user space door server). - */ -void -smb_opipe_door_init(smb_server_t *sv) -{ - sv->sv_opipe_door_id = -1; - mutex_init(&sv->sv_opipe_door_mutex, NULL, MUTEX_DEFAULT, NULL); - cv_init(&sv->sv_opipe_door_cv, NULL, CV_DEFAULT, NULL); -} - -void -smb_opipe_door_fini(smb_server_t *sv) -{ - smb_opipe_door_close(sv); - cv_destroy(&sv->sv_opipe_door_cv); - mutex_destroy(&sv->sv_opipe_door_mutex); -} - -void -fksmb_opipe_door_open(smb_server_t *sv, void *varg) -{ - /* varg is the "door" dispatch function. */ - sv->sv_opipe_door_hd = varg; -} - -/* - * Close the (user space) door. - */ -void -smb_opipe_door_close(smb_server_t *sv) -{ - sv->sv_opipe_door_hd = NULL; - sv->sv_opipe_door_id = -1; -} - - -/* - * opipe door call interface. - * Door serialization and call reference accounting is handled here. - */ -int -smb_opipe_door_call(smb_opipe_t *opipe) -{ - smb_server_t *sv = opipe->p_server; - fksmb_opipe_disp_func_t *func; - door_arg_t da; - smb_doorhdr_t hdr; - int rc; - - if (sv == NULL) - return (EFAULT); - if (smb_server_is_stopping(sv)) - return (-1); - - func = (fksmb_opipe_disp_func_t *)(sv->sv_opipe_door_hd); - if (func == NULL) - return (EFAULT); - - da.data_ptr = (char *)opipe->p_doorbuf; - da.data_size = SMB_OPIPE_DOOR_BUFSIZE; - da.desc_ptr = NULL; - da.desc_num = 0; - da.rbuf = (char *)opipe->p_doorbuf; - da.rsize = SMB_OPIPE_DOOR_BUFSIZE; - - - /* - * Do the "upcall" to smbd-d. In-kernel, this is: - * door_ki_upcall_limited(...) - */ - rc = (*func)(&da); - if (rc != 0) - return (rc); - - /* Check for door_return(NULL, 0, NULL, 0) */ - if (rc != 0 || da.data_size == 0 || da.rsize == 0) - return (-1); - - if (smb_doorhdr_decode(&hdr, (uint8_t *)da.data_ptr, da.rsize) == -1) - return (-1); - - if ((hdr.dh_magic != SMB_OPIPE_HDR_MAGIC) || - (hdr.dh_fid != opipe->p_hdr.dh_fid) || - (hdr.dh_op != opipe->p_hdr.dh_op) || - (hdr.dh_door_rc != 0) || - (hdr.dh_datalen > SMB_OPIPE_DOOR_BUFSIZE)) { - return (-1); - } - - opipe->p_hdr.dh_datalen = hdr.dh_datalen; - opipe->p_hdr.dh_resid = hdr.dh_resid; - return (0); -} diff --git a/usr/src/lib/smbsrv/libmlrpc/common/libmlrpc.h b/usr/src/lib/smbsrv/libmlrpc/common/libmlrpc.h index ba89e34739..5b55ce4c54 100644 --- a/usr/src/lib/smbsrv/libmlrpc/common/libmlrpc.h +++ b/usr/src/lib/smbsrv/libmlrpc/common/libmlrpc.h @@ -19,8 +19,8 @@ * CDDL HEADER END */ /* - * Copyright 2011 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. */ #ifndef _LIBMLRPC_H @@ -247,28 +247,18 @@ typedef struct ndr_binding { #define NDR_N_BINDING_POOL 2 typedef struct ndr_pipe { + void *np_listener; + const char *np_endpoint; + smb_netuserinfo_t *np_user; + int (*np_send)(struct ndr_pipe *, void *, size_t); + int (*np_recv)(struct ndr_pipe *, void *, size_t); int np_fid; - uint32_t np_txid; - smb_netuserinfo_t np_user; - char *np_buf; - struct uio np_uio; - iovec_t np_iov; - ndr_fraglist_t np_frags; - int np_refcnt; uint16_t np_max_xmit_frag; uint16_t np_max_recv_frag; ndr_binding_t *np_binding; ndr_binding_t np_binding_pool[NDR_N_BINDING_POOL]; } ndr_pipe_t; -typedef struct ndr_pipe_info { - uint32_t npi_fid; - uint32_t npi_permissions; - uint32_t npi_num_locks; - char npi_pathname[MAXPATHLEN]; - char npi_username[MAXNAMELEN]; -} ndr_pipe_info_t; - /* * Number of bytes required to align SIZE on the next dword/4-byte * boundary. @@ -429,7 +419,6 @@ int ndr_heap_avail(ndr_heap_t *); #define NDR_SIDDUP(XA, S) ndr_heap_siddup((XA)->heap, (S)) typedef struct ndr_xa { - int fid; unsigned short ptype; /* high bits special */ unsigned short opnum; ndr_stream_t recv_nds; @@ -476,7 +465,7 @@ typedef struct ndr_client { typedef struct ndr_handle { ndr_hdid_t nh_id; struct ndr_handle *nh_next; - int nh_fid; + ndr_pipe_t *nh_pipe; const ndr_service_t *nh_svc; ndr_client_t *nh_clnt; void *nh_data; @@ -495,7 +484,6 @@ typedef struct ndr_buf { /* ndr_ops.c */ int nds_initialize(ndr_stream_t *, unsigned, int, ndr_heap_t *); -void nds_finalize(ndr_stream_t *, ndr_fraglist_t *); void nds_destruct(ndr_stream_t *); void nds_show_state(ndr_stream_t *); @@ -522,11 +510,7 @@ unsigned ndr_bind_ack_hdr_size(ndr_xa_t *); unsigned ndr_alter_context_rsp_hdr_size(void); /* ndr_server.c */ -int ndr_pipe_open(int, uint8_t *, uint32_t); -int ndr_pipe_close(int); -int ndr_pipe_read(int, uint8_t *, uint32_t *, uint32_t *); -int ndr_pipe_write(int, uint8_t *, uint32_t); -void *ndr_pipe_transact(void *); +void ndr_pipe_worker(ndr_pipe_t *); int ndr_generic_call_stub(ndr_xa_t *); @@ -550,7 +534,7 @@ void ndr_uuid_unparse(ndr_uuid_t *, char *); ndr_hdid_t *ndr_hdalloc(const ndr_xa_t *, const void *); void ndr_hdfree(const ndr_xa_t *, const ndr_hdid_t *); ndr_handle_t *ndr_hdlookup(const ndr_xa_t *, const ndr_hdid_t *); -void ndr_hdclose(int fid); +void ndr_hdclose(ndr_pipe_t *); ssize_t ndr_uiomove(caddr_t, size_t, enum uio_rw, struct uio *); diff --git a/usr/src/lib/smbsrv/libmlrpc/common/mapfile-vers b/usr/src/lib/smbsrv/libmlrpc/common/mapfile-vers index c6a32420e0..5822d32711 100644 --- a/usr/src/lib/smbsrv/libmlrpc/common/mapfile-vers +++ b/usr/src/lib/smbsrv/libmlrpc/common/mapfile-vers @@ -20,6 +20,7 @@ # # # Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2013 Nexenta Systems, Inc. All rights reserved. # # @@ -68,11 +69,7 @@ SYMBOL_VERSION SUNWprivate { ndr_mbtowc; ndr_native_os; ndr_params; - ndr_pipe_open; - ndr_pipe_close; - ndr_pipe_read; - ndr_pipe_transact; - ndr_pipe_write; + ndr_pipe_worker; ndr_svc_binding_pool_init; ndr_svc_lookup_name; ndr_svc_register; diff --git a/usr/src/lib/smbsrv/libmlrpc/common/ndr_ops.c b/usr/src/lib/smbsrv/libmlrpc/common/ndr_ops.c index 0e8fdf575e..0cbcdc6e90 100644 --- a/usr/src/lib/smbsrv/libmlrpc/common/ndr_ops.c +++ b/usr/src/lib/smbsrv/libmlrpc/common/ndr_ops.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ /* @@ -160,44 +161,6 @@ nds_initialize(ndr_stream_t *nds, unsigned pdu_size_hint, return (0); } -void -nds_finalize(ndr_stream_t *nds, ndr_fraglist_t *frags) -{ - iovec_t *iov; - ndr_frag_t *frag; - uint32_t size = 0; - - bzero(frags, sizeof (ndr_fraglist_t)); - - for (frag = nds->frags.head; frag; frag = frag->next) - size += frag->len; - - if (size == 0 || size >= NDR_PDU_MAX_SIZE) - return; - - frags->iov = malloc(nds->frags.nfrag * sizeof (iovec_t)); - if (frags->iov == NULL) - return; - - frags->head = nds->frags.head; - frags->tail = nds->frags.tail; - frags->nfrag = nds->frags.nfrag; - bzero(&nds->frags, sizeof (ndr_fraglist_t)); - - frags->uio.uio_iov = frags->iov; - frags->uio.uio_iovcnt = frags->nfrag; - frags->uio.uio_offset = 0; - frags->uio.uio_segflg = UIO_USERSPACE; - frags->uio.uio_resid = size; - - iov = frags->uio.uio_iov; - for (frag = frags->head; frag; frag = frag->next) { - iov->iov_base = (caddr_t)frag->buf; - iov->iov_len = frag->len; - ++iov; - } -} - /* * nds_destruct * @@ -424,7 +387,6 @@ ndo_reset(ndr_stream_t *nds) static void ndo_destruct(ndr_stream_t *nds) { - ndr_frag_t *frag; ndo_printf(nds, 0, "destruct"); @@ -437,13 +399,6 @@ ndo_destruct(ndr_stream_t *nds) nds->pdu_base_offset = 0; } - while ((frag = nds->frags.head) != NULL) { - nds->frags.head = frag->next; - free(frag); - } - - bzero(&nds->frags, sizeof (ndr_fraglist_t)); - nds->outer_queue_head = 0; nds->outer_current = 0; nds->outer_queue_tailp = &nds->outer_queue_head; diff --git a/usr/src/lib/smbsrv/libmlrpc/common/ndr_server.c b/usr/src/lib/smbsrv/libmlrpc/common/ndr_server.c index bd51913552..198daa7d55 100644 --- a/usr/src/lib/smbsrv/libmlrpc/common/ndr_server.c +++ b/usr/src/lib/smbsrv/libmlrpc/common/ndr_server.c @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ /* @@ -27,478 +28,248 @@ */ #include <sys/byteorder.h> -#include <sys/errno.h> #include <sys/uio.h> -#include <thread.h> +#include <errno.h> #include <synch.h> #include <stdlib.h> #include <strings.h> #include <string.h> -#include <time.h> +#include <thread.h> #include <smbsrv/libsmb.h> #include <smbsrv/libmlrpc.h> #include <smbsrv/ntaccess.h> -/* - * Fragment size (5680: NT style). - */ -#define NDR_FRAG_SZ 5680 - -#define NDR_GROW_SIZE (8 * 1024) -#define NDR_GROW_MASK (NDR_GROW_SIZE - 1) -#define NDR_ALIGN_BUF(S) (((S) + NDR_GROW_SIZE) & ~NDR_GROW_MASK) - -#define NDR_PIPE_BUFSZ (64 * 1024) -#define NDR_PIPE_BUFMAX (64 * 1024 * 1024) -#define NDR_PIPE_MAX 128 - -static ndr_pipe_t ndr_pipe_table[NDR_PIPE_MAX]; -static mutex_t ndr_pipe_lock; - -static int ndr_pipe_process(ndr_pipe_t *); -static ndr_pipe_t *ndr_pipe_lookup(int); -static void ndr_pipe_release(ndr_pipe_t *); -static ndr_pipe_t *ndr_pipe_allocate(int); -static int ndr_pipe_grow(ndr_pipe_t *, size_t); -static void ndr_pipe_deallocate(ndr_pipe_t *); -static void ndr_pipe_rewind(ndr_pipe_t *); -static void ndr_pipe_flush(ndr_pipe_t *); +#define NDR_PIPE_SEND(np, buf, len) \ + ((np)->np_send)((np), (buf), (len)) +#define NDR_PIPE_RECV(np, buf, len) \ + ((np)->np_recv)((np), (buf), (len)) static int ndr_svc_process(ndr_xa_t *); -static int ndr_svc_defrag(ndr_xa_t *); static int ndr_svc_bind(ndr_xa_t *); static int ndr_svc_request(ndr_xa_t *); static void ndr_reply_prepare_hdr(ndr_xa_t *); static int ndr_svc_alter_context(ndr_xa_t *); static void ndr_reply_fault(ndr_xa_t *, unsigned long); -static int ndr_build_reply(ndr_xa_t *); -static void ndr_build_frag(ndr_stream_t *, uint8_t *, uint32_t); + +static int ndr_recv_request(ndr_xa_t *mxa); +static int ndr_recv_frag(ndr_xa_t *mxa); +static int ndr_send_reply(ndr_xa_t *); + +static int ndr_pipe_process(ndr_pipe_t *, ndr_xa_t *); /* - * Allocate and associate a service context with a fid. + * External entry point called by smbd. */ -int -ndr_pipe_open(int fid, uint8_t *data, uint32_t datalen) +void +ndr_pipe_worker(ndr_pipe_t *np) { - ndr_pipe_t *np; - - (void) mutex_lock(&ndr_pipe_lock); - - if ((np = ndr_pipe_lookup(fid)) != NULL) { - ndr_pipe_release(np); - (void) mutex_unlock(&ndr_pipe_lock); - return (EEXIST); - } - - if ((np = ndr_pipe_allocate(fid)) == NULL) { - (void) mutex_unlock(&ndr_pipe_lock); - return (ENOMEM); - } - - if (smb_netuserinfo_decode(&np->np_user, data, datalen, NULL) == -1) { - ndr_pipe_release(np); - (void) mutex_unlock(&ndr_pipe_lock); - return (EINVAL); - } + ndr_xa_t *mxa; + int rc; ndr_svc_binding_pool_init(&np->np_binding, np->np_binding_pool, NDR_N_BINDING_POOL); - (void) mutex_unlock(&ndr_pipe_lock); - return (0); -} - -/* - * Release the context associated with a fid when an opipe is closed. - */ -int -ndr_pipe_close(int fid) -{ - ndr_pipe_t *np; + if ((mxa = malloc(sizeof (*mxa))) == NULL) + return; - (void) mutex_lock(&ndr_pipe_lock); + do { + bzero(mxa, sizeof (*mxa)); + rc = ndr_pipe_process(np, mxa); + } while (rc == 0); - if ((np = ndr_pipe_lookup(fid)) == NULL) { - (void) mutex_unlock(&ndr_pipe_lock); - return (ENOENT); - } + free(mxa); /* - * Release twice: once for the lookup above - * and again to close the fid. + * Ensure that there are no RPC service policy handles + * (associated with this fid) left around. */ - ndr_pipe_release(np); - ndr_pipe_release(np); - (void) mutex_unlock(&ndr_pipe_lock); - return (0); -} - -/* - * Write RPC request data to the input stream. Input data is buffered - * until the response is requested. - */ -int -ndr_pipe_write(int fid, uint8_t *buf, uint32_t len) -{ - ndr_pipe_t *np; - ssize_t nbytes; - int rc; - - if (len == 0) - return (0); - - (void) mutex_lock(&ndr_pipe_lock); - - if ((np = ndr_pipe_lookup(fid)) == NULL) { - (void) mutex_unlock(&ndr_pipe_lock); - return (ENOENT); - } - - if ((rc = ndr_pipe_grow(np, len)) != 0) { - (void) mutex_unlock(&ndr_pipe_lock); - return (rc); - } - - nbytes = ndr_uiomove((caddr_t)buf, len, UIO_READ, &np->np_uio); - - ndr_pipe_release(np); - (void) mutex_unlock(&ndr_pipe_lock); - return ((nbytes == len) ? 0 : EIO); -} - -/* - * Read RPC response data. - */ -int -ndr_pipe_read(int fid, uint8_t *buf, uint32_t *len, uint32_t *resid) -{ - ndr_pipe_t *np; - ssize_t nbytes = *len; - - if (nbytes == 0) { - *resid = 0; - return (0); - } - - (void) mutex_lock(&ndr_pipe_lock); - if ((np = ndr_pipe_lookup(fid)) == NULL) { - (void) mutex_unlock(&ndr_pipe_lock); - return (ENOENT); - } - (void) mutex_unlock(&ndr_pipe_lock); - - *len = ndr_uiomove((caddr_t)buf, nbytes, UIO_WRITE, &np->np_frags.uio); - *resid = np->np_frags.uio.uio_resid; - - if (*resid == 0) { - /* - * Nothing left, cleanup the output stream. - */ - ndr_pipe_flush(np); - } - - (void) mutex_lock(&ndr_pipe_lock); - ndr_pipe_release(np); - (void) mutex_unlock(&ndr_pipe_lock); - return (0); -} - -/* - * If the input stream contains an RPC request, process the RPC transaction, - * which will place the RPC response in the output (frags) stream. - * - * arg is freed here; it must have been allocated by malloc(). - */ -void * -ndr_pipe_transact(void *arg) -{ - uint32_t *tmp = (uint32_t *)arg; - uint32_t fid; - ndr_pipe_t *np; - - if (arg == NULL) - return (NULL); - - fid = *tmp; - - (void) mutex_lock(&ndr_pipe_lock); - if ((np = ndr_pipe_lookup(fid)) == NULL) { - (void) mutex_unlock(&ndr_pipe_lock); - (void) smb_kmod_event_notify(fid); - free(arg); - return (NULL); - } - (void) mutex_unlock(&ndr_pipe_lock); - - if (ndr_pipe_process(np) != 0) - ndr_pipe_flush(np); - - (void) mutex_lock(&ndr_pipe_lock); - ndr_pipe_release(np); - (void) mutex_unlock(&ndr_pipe_lock); - (void) smb_kmod_event_notify(fid); - free(arg); - return (NULL); + ndr_hdclose(np); } /* - * Process a server-side RPC request. + * Process one server-side RPC request. */ static int -ndr_pipe_process(ndr_pipe_t *np) +ndr_pipe_process(ndr_pipe_t *np, ndr_xa_t *mxa) { - ndr_xa_t *mxa; ndr_stream_t *recv_nds; ndr_stream_t *send_nds; - char *data; - int datalen; - int rc; + int rc = ENOMEM; - data = np->np_buf; - datalen = np->np_uio.uio_offset; - - if (datalen == 0) - return (0); - - if ((mxa = (ndr_xa_t *)malloc(sizeof (ndr_xa_t))) == NULL) - return (ENOMEM); - - bzero(mxa, sizeof (ndr_xa_t)); - mxa->fid = np->np_fid; mxa->pipe = np; mxa->binding_list = np->np_binding; - if ((mxa->heap = ndr_heap_create()) == NULL) { - free(mxa); - return (ENOMEM); - } + if ((mxa->heap = ndr_heap_create()) == NULL) + goto out1; recv_nds = &mxa->recv_nds; - rc = nds_initialize(recv_nds, datalen, NDR_MODE_CALL_RECV, mxa->heap); - if (rc != 0) { - ndr_heap_destroy(mxa->heap); - free(mxa); - return (ENOMEM); - } - - /* - * Copy the input data and reset the input stream. - */ - bcopy(data, recv_nds->pdu_base_addr, datalen); - ndr_pipe_rewind(np); + rc = nds_initialize(recv_nds, 0, NDR_MODE_CALL_RECV, mxa->heap); + if (rc != 0) + goto out2; send_nds = &mxa->send_nds; rc = nds_initialize(send_nds, 0, NDR_MODE_RETURN_SEND, mxa->heap); - if (rc != 0) { - nds_destruct(&mxa->recv_nds); - ndr_heap_destroy(mxa->heap); - free(mxa); - return (ENOMEM); - } + if (rc != 0) + goto out3; + + rc = ndr_recv_request(mxa); + if (rc != 0) + goto out4; (void) ndr_svc_process(mxa); + (void) ndr_send_reply(mxa); + rc = 0; - nds_finalize(send_nds, &np->np_frags); - nds_destruct(&mxa->recv_nds); +out4: nds_destruct(&mxa->send_nds); +out3: + nds_destruct(&mxa->recv_nds); +out2: ndr_heap_destroy(mxa->heap); - free(mxa); - return (0); +out1: + return (rc); } /* - * Must be called with ndr_pipe_lock held. + * Check whether or not the specified user has administrator privileges, + * i.e. is a member of Domain Admins or Administrators. + * Returns true if the user is an administrator, otherwise returns false. */ -static ndr_pipe_t * -ndr_pipe_lookup(int fid) +boolean_t +ndr_is_admin(ndr_xa_t *xa) { - ndr_pipe_t *np; - int i; - - for (i = 0; i < NDR_PIPE_MAX; ++i) { - np = &ndr_pipe_table[i]; - - if (np->np_fid == fid) { - if (np->np_refcnt == 0) - return (NULL); + smb_netuserinfo_t *ctx = xa->pipe->np_user; - np->np_refcnt++; - return (np); - } - } - - return (NULL); + return (ctx->ui_flags & SMB_ATF_ADMIN); } /* - * Must be called with ndr_pipe_lock held. + * Check whether or not the specified user has power-user privileges, + * i.e. is a member of Domain Admins, Administrators or Power Users. + * This is typically required for operations such as managing shares. + * Returns true if the user is a power user, otherwise returns false. */ -static void -ndr_pipe_release(ndr_pipe_t *np) +boolean_t +ndr_is_poweruser(ndr_xa_t *xa) { - np->np_refcnt--; - ndr_pipe_deallocate(np); + smb_netuserinfo_t *ctx = xa->pipe->np_user; + + return ((ctx->ui_flags & SMB_ATF_ADMIN) || + (ctx->ui_flags & SMB_ATF_POWERUSER)); } -/* - * Must be called with ndr_pipe_lock held. - */ -static ndr_pipe_t * -ndr_pipe_allocate(int fid) +int32_t +ndr_native_os(ndr_xa_t *xa) { - ndr_pipe_t *np = NULL; - int i; + smb_netuserinfo_t *ctx = xa->pipe->np_user; - for (i = 0; i < NDR_PIPE_MAX; ++i) { - np = &ndr_pipe_table[i]; - - if (np->np_fid == 0) { - bzero(np, sizeof (ndr_pipe_t)); - - if ((np->np_buf = malloc(NDR_PIPE_BUFSZ)) == NULL) - return (NULL); - - ndr_pipe_rewind(np); - np->np_fid = fid; - np->np_refcnt = 1; - return (np); - } - } - - return (NULL); + return (ctx->ui_native_os); } /* - * If the desired space exceeds the current pipe size, try to expand - * the pipe. Leave the current pipe intact if the realloc fails. - * - * Must be called with ndr_pipe_lock held. + * Receive an entire RPC request (all fragments) + * Returns zero or an NDR fault code. */ static int -ndr_pipe_grow(ndr_pipe_t *np, size_t desired) +ndr_recv_request(ndr_xa_t *mxa) { - char *newbuf; - size_t current; - size_t required; - - required = np->np_uio.uio_offset + desired; - current = np->np_uio.uio_offset + np->np_uio.uio_resid; - - if (required <= current) - return (0); - - if (required > NDR_PIPE_BUFMAX) { - smb_tracef("ndr_pipe_grow: required=%d, max=%d (ENOSPC)", - required, NDR_PIPE_BUFMAX); - return (ENOSPC); - } - - required = NDR_ALIGN_BUF(required); - if (required > NDR_PIPE_BUFMAX) - required = NDR_PIPE_BUFMAX; - - if ((newbuf = realloc(np->np_buf, required)) == NULL) { - smb_tracef("ndr_pipe_grow: realloc failed (ENOMEM)"); - return (ENOMEM); - } + ndr_common_header_t *hdr = &mxa->recv_hdr.common_hdr; + ndr_stream_t *nds = &mxa->recv_nds; + unsigned long saved_size; + int rc; - np->np_buf = newbuf; - np->np_iov.iov_base = np->np_buf + np->np_uio.uio_offset; - np->np_uio.uio_resid += desired; - np->np_iov.iov_len += desired; - return (0); -} + rc = ndr_recv_frag(mxa); + if (rc != 0) + return (rc); + if (!NDR_IS_FIRST_FRAG(hdr->pfc_flags)) + return (NDR_DRC_FAULT_DECODE_FAILED); -/* - * Must be called with ndr_pipe_lock held. - */ -static void -ndr_pipe_deallocate(ndr_pipe_t *np) -{ - if (np->np_refcnt == 0) { - /* - * Ensure that there are no RPC service policy handles - * (associated with this fid) left around. - */ - ndr_hdclose(np->np_fid); - - ndr_pipe_rewind(np); - ndr_pipe_flush(np); - free(np->np_buf); - free(np->np_user.ui_domain); - free(np->np_user.ui_account); - free(np->np_user.ui_workstation); - bzero(np, sizeof (ndr_pipe_t)); + while (!NDR_IS_LAST_FRAG(hdr->pfc_flags)) { + rc = ndr_recv_frag(mxa); + if (rc != 0) + return (rc); } -} - -/* - * Rewind the input data stream, ready for the next write. - */ -static void -ndr_pipe_rewind(ndr_pipe_t *np) -{ - np->np_uio.uio_iov = &np->np_iov; - np->np_uio.uio_iovcnt = 1; - np->np_uio.uio_offset = 0; - np->np_uio.uio_segflg = UIO_USERSPACE; - np->np_uio.uio_resid = NDR_PIPE_BUFSZ; - np->np_iov.iov_base = np->np_buf; - np->np_iov.iov_len = NDR_PIPE_BUFSZ; -} - -/* - * Flush the output data stream. - */ -static void -ndr_pipe_flush(ndr_pipe_t *np) -{ - ndr_frag_t *frag; + nds->pdu_scan_offset = 0; - while ((frag = np->np_frags.head) != NULL) { - np->np_frags.head = frag->next; - free(frag); - } + /* + * This whacks nds->pdu_size, so save/restore. + * It leaves scan_offset after the header. + */ + saved_size = nds->pdu_size; + rc = ndr_decode_pdu_hdr(mxa); + nds->pdu_size = saved_size; - free(np->np_frags.iov); - bzero(&np->np_frags, sizeof (ndr_fraglist_t)); + return (rc); } /* - * Check whether or not the specified user has administrator privileges, - * i.e. is a member of Domain Admins or Administrators. - * Returns true if the user is an administrator, otherwise returns false. + * Read one fragment, leaving the decoded frag header in + * recv_hdr.common_hdr, and the data in the recv_nds. + * + * Returns zero or an NDR fault code. + * + * If a first frag, the header is included in the data + * placed in recv_nds (because it's not fully decoded + * until later - we only decode the common part here). + * Additional frags are placed in the recv_nds without + * the header, so that after the first frag header, + * the remaining data will be contiguous. We do this + * by simply not advancing the offset in recv_nds after + * reading and decoding these additional fragments, so + * the payload of such frags will overwrite what was + * (temporarily) the frag header. */ -boolean_t -ndr_is_admin(ndr_xa_t *xa) +static int +ndr_recv_frag(ndr_xa_t *mxa) { - smb_netuserinfo_t *ctx = &xa->pipe->np_user; + ndr_common_header_t *hdr = &mxa->recv_hdr.common_hdr; + ndr_stream_t *nds = &mxa->recv_nds; + unsigned char *data; + unsigned long next_offset; + unsigned long pay_size; + int rc; - return (ctx->ui_flags & SMB_ATF_ADMIN); -} + /* Make room for the frag header. */ + next_offset = nds->pdu_scan_offset + NDR_RSP_HDR_SIZE; + if (!NDS_GROW_PDU(nds, next_offset, 0)) + return (NDR_DRC_FAULT_OUT_OF_MEMORY); -/* - * Check whether or not the specified user has power-user privileges, - * i.e. is a member of Domain Admins, Administrators or Power Users. - * This is typically required for operations such as managing shares. - * Returns true if the user is a power user, otherwise returns false. - */ -boolean_t -ndr_is_poweruser(ndr_xa_t *xa) -{ - smb_netuserinfo_t *ctx = &xa->pipe->np_user; + /* Read the frag header. */ + data = nds->pdu_base_addr + nds->pdu_scan_offset; + rc = NDR_PIPE_RECV(mxa->pipe, data, NDR_RSP_HDR_SIZE); + if (rc != 0) + return (NDR_DRC_FAULT_RPCHDR_RECEIVED_RUNT); - return ((ctx->ui_flags & SMB_ATF_ADMIN) || - (ctx->ui_flags & SMB_ATF_POWERUSER)); -} + /* + * Decode the frag header, get the length. + * NB: It uses nds->pdu_scan_offset + */ + ndr_decode_frag_hdr(nds, hdr); + ndr_show_hdr(hdr); + if (hdr->frag_length < NDR_RSP_HDR_SIZE || + hdr->frag_length > mxa->pipe->np_max_xmit_frag) + return (NDR_DRC_FAULT_DECODE_FAILED); + + if (nds->pdu_scan_offset == 0) { + /* First frag: header stays in the data. */ + nds->pdu_scan_offset = next_offset; + } /* else overwrite with the payload */ + + /* Make room for the payload. */ + pay_size = hdr->frag_length - NDR_RSP_HDR_SIZE; + next_offset = nds->pdu_scan_offset + pay_size; + if (!NDS_GROW_PDU(nds, next_offset, 0)) + return (NDR_DRC_FAULT_OUT_OF_MEMORY); -int32_t -ndr_native_os(ndr_xa_t *xa) -{ - smb_netuserinfo_t *ctx = &xa->pipe->np_user; + /* Read the payload. */ + data = nds->pdu_base_addr + nds->pdu_scan_offset; + rc = NDR_PIPE_RECV(mxa->pipe, data, pay_size); + if (rc != 0) + return (NDR_DRC_FAULT_RPCHDR_RECEIVED_RUNT); + nds->pdu_scan_offset = next_offset; - return (ctx->ui_native_os); + return (NDR_DRC_OK); } /* @@ -508,16 +279,8 @@ ndr_native_os(ndr_xa_t *xa) static int ndr_svc_process(ndr_xa_t *mxa) { - ndr_common_header_t *hdr = &mxa->recv_hdr.common_hdr; - ndr_stream_t *nds = &mxa->recv_nds; - unsigned long saved_offset; - unsigned long saved_size; int rc; - rc = ndr_decode_pdu_hdr(mxa); - if (!NDR_DRC_IS_OK(rc)) - return (-1); - (void) ndr_reply_prepare_hdr(mxa); switch (mxa->ptype) { @@ -526,35 +289,6 @@ ndr_svc_process(ndr_xa_t *mxa) break; case NDR_PTYPE_REQUEST: - if (!NDR_IS_FIRST_FRAG(hdr->pfc_flags)) { - ndr_show_hdr(hdr); - rc = NDR_DRC_FAULT_DECODE_FAILED; - goto ndr_svc_process_fault; - } - - if (!NDR_IS_LAST_FRAG(hdr->pfc_flags)) { - /* - * Multi-fragment request. Preserve the PDU scan - * offset and size during defrag so that we can - * continue as if we had received contiguous data. - */ - saved_offset = nds->pdu_scan_offset; - saved_size = nds->pdu_size; - - nds->pdu_scan_offset = hdr->frag_length; - nds->pdu_size = nds->pdu_max_size; - - rc = ndr_svc_defrag(mxa); - if (NDR_DRC_IS_FAULT(rc)) { - ndr_show_hdr(hdr); - nds_show_state(nds); - goto ndr_svc_process_fault; - } - - nds->pdu_scan_offset = saved_offset; - nds->pdu_size = saved_size; - } - rc = ndr_svc_request(mxa); break; @@ -567,61 +301,13 @@ ndr_svc_process(ndr_xa_t *mxa) break; } -ndr_svc_process_fault: if (NDR_DRC_IS_FAULT(rc)) ndr_reply_fault(mxa, rc); - (void) ndr_build_reply(mxa); return (rc); } /* - * Remove RPC fragment headers from the received data stream. - * The first fragment has already been accounted for before this call. - * - * NDR stream on entry: - * - * |<-- frag 2 -->|<-- frag 3 -->| ... |<- last frag ->| - * - * +-----+--------+-----+--------+-----+-----+---------+ - * | hdr | data | hdr | data | ... | hdr | data | - * +-----+--------+-----+--------+-----+-----+---------+ - * - * NDR stream on return: - * - * +----------------------------------+ - * | data | - * +----------------------------------+ - */ -static int -ndr_svc_defrag(ndr_xa_t *mxa) -{ - ndr_stream_t *nds = &mxa->recv_nds; - ndr_common_header_t frag_hdr; - int frag_size; - int last_frag; - - do { - ndr_decode_frag_hdr(nds, &frag_hdr); - ndr_show_hdr(&frag_hdr); - - if (NDR_IS_FIRST_FRAG(frag_hdr.pfc_flags)) - return (NDR_DRC_FAULT_DECODE_FAILED); - - last_frag = NDR_IS_LAST_FRAG(frag_hdr.pfc_flags); - frag_size = frag_hdr.frag_length; - - if (frag_size > (nds->pdu_size - nds->pdu_scan_offset)) - return (NDR_DRC_FAULT_DECODE_FAILED); - - ndr_remove_frag_hdr(nds); - nds->pdu_scan_offset += frag_size - NDR_RSP_HDR_SIZE; - } while (!last_frag); - - return (NDR_DRC_OK); -} - -/* * Multiple p_cont_elem[]s, multiple transfer_syntaxes[] and multiple * p_results[] not supported. */ @@ -919,25 +605,37 @@ ndr_reply_prepare_hdr(ndr_xa_t *mxa) switch (mxa->ptype) { case NDR_PTYPE_BIND: + /* + * Compute the maximum fragment sizes for xmit/recv + * and store in the pipe endpoint. Note "xmit" is + * client-to-server; "recv" is server-to-client. + */ + if (mxa->pipe->np_max_xmit_frag > + mxa->recv_hdr.bind_hdr.max_xmit_frag) + mxa->pipe->np_max_xmit_frag = + mxa->recv_hdr.bind_hdr.max_xmit_frag; + if (mxa->pipe->np_max_recv_frag > + mxa->recv_hdr.bind_hdr.max_recv_frag) + mxa->pipe->np_max_recv_frag = + mxa->recv_hdr.bind_hdr.max_recv_frag; + hdr->ptype = NDR_PTYPE_BIND_ACK; mxa->send_hdr.bind_ack_hdr.max_xmit_frag = - mxa->recv_hdr.bind_hdr.max_xmit_frag; + mxa->pipe->np_max_xmit_frag; mxa->send_hdr.bind_ack_hdr.max_recv_frag = - mxa->recv_hdr.bind_hdr.max_recv_frag; + mxa->pipe->np_max_recv_frag; + + /* + * We're supposed to assign a unique "assoc group" + * (identifies this connection for the client). + * Using the pipe address is adequate. + */ mxa->send_hdr.bind_ack_hdr.assoc_group_id = mxa->recv_hdr.bind_hdr.assoc_group_id; - if (mxa->send_hdr.bind_ack_hdr.assoc_group_id == 0) - mxa->send_hdr.bind_ack_hdr.assoc_group_id = time(0); + mxa->send_hdr.bind_ack_hdr.assoc_group_id = + (DWORD)(uintptr_t)mxa->pipe; - /* - * Save the maximum fragment sizes - * for use with subsequent requests. - */ - mxa->pipe->np_max_xmit_frag = - mxa->recv_hdr.bind_hdr.max_xmit_frag; - mxa->pipe->np_max_recv_frag = - mxa->recv_hdr.bind_hdr.max_recv_frag; break; case NDR_PTYPE_REQUEST: @@ -1031,7 +729,7 @@ ndr_reply_fault(ndr_xa_t *mxa, unsigned long drc) * non-standard. */ static int -ndr_build_reply(ndr_xa_t *mxa) +ndr_send_reply(ndr_xa_t *mxa) { ndr_common_header_t *hdr = &mxa->send_hdr.common_hdr; ndr_stream_t *nds = &mxa->send_nds; @@ -1041,7 +739,7 @@ ndr_build_reply(ndr_xa_t *mxa) unsigned long pdu_data_size; unsigned long frag_data_size; - frag_size = NDR_FRAG_SZ; + frag_size = mxa->pipe->np_max_recv_frag; pdu_size = nds->pdu_size; pdu_buf = nds->pdu_base_addr; @@ -1079,22 +777,14 @@ ndr_build_reply(ndr_xa_t *mxa) nds->pdu_scan_offset = 0; (void) ndr_encode_pdu_hdr(mxa); pdu_size = nds->pdu_size; - ndr_build_frag(nds, pdu_buf, pdu_size); + (void) NDR_PIPE_SEND(mxa->pipe, pdu_buf, pdu_size); return (0); } /* * Multiple fragment response. - */ - hdr->pfc_flags = NDR_PFC_FIRST_FRAG; - hdr->frag_length = frag_size; - mxa->send_hdr.response_hdr.alloc_hint = pdu_size - NDR_RSP_HDR_SIZE; - nds->pdu_scan_offset = 0; - (void) ndr_encode_pdu_hdr(mxa); - ndr_build_frag(nds, pdu_buf, frag_size); - - /* - * We need to update the 24-byte header in subsequent fragments. + * + * We need to update the RPC header for every fragment. * * pdu_data_size: total data remaining to be handled * frag_size: total fragment size including header @@ -1104,61 +794,45 @@ ndr_build_reply(ndr_xa_t *mxa) pdu_data_size = pdu_size - NDR_RSP_HDR_SIZE; frag_data_size = frag_size - NDR_RSP_HDR_SIZE; - while (pdu_data_size) { - mxa->send_hdr.response_hdr.alloc_hint -= frag_data_size; - pdu_data_size -= frag_data_size; - pdu_buf += frag_data_size; + /* + * Send the first frag. + */ + hdr->pfc_flags = NDR_PFC_FIRST_FRAG; + hdr->frag_length = frag_size; + mxa->send_hdr.response_hdr.alloc_hint = pdu_data_size; + nds->pdu_scan_offset = 0; + (void) ndr_encode_pdu_hdr(mxa); + (void) NDR_PIPE_SEND(mxa->pipe, pdu_buf, frag_size); + pdu_data_size -= frag_data_size; + pdu_buf += frag_data_size; - if (pdu_data_size <= frag_data_size) { - frag_data_size = pdu_data_size; - frag_size = frag_data_size + NDR_RSP_HDR_SIZE; - hdr->pfc_flags = NDR_PFC_LAST_FRAG; - } else { - hdr->pfc_flags = 0; - } + /* + * Send "middle" (full-sized) fragments... + */ + hdr->pfc_flags = 0; + while (pdu_data_size > frag_data_size) { hdr->frag_length = frag_size; + mxa->send_hdr.response_hdr.alloc_hint = pdu_data_size; nds->pdu_scan_offset = 0; (void) ndr_encode_pdu_hdr(mxa); bcopy(nds->pdu_base_addr, pdu_buf, NDR_RSP_HDR_SIZE); - - ndr_build_frag(nds, pdu_buf, frag_size); - - if (hdr->pfc_flags & NDR_PFC_LAST_FRAG) - break; + (void) NDR_PIPE_SEND(mxa->pipe, pdu_buf, frag_size); + pdu_data_size -= frag_data_size; + pdu_buf += frag_data_size; } - return (0); -} - -/* - * ndr_build_frag - * - * Build an RPC PDU fragment from the specified buffer. - * If malloc fails, the client will see a header/pdu inconsistency - * and report an error. - */ -static void -ndr_build_frag(ndr_stream_t *nds, uint8_t *buf, uint32_t len) -{ - ndr_frag_t *frag; - int size = sizeof (ndr_frag_t) + len; - - if ((frag = (ndr_frag_t *)malloc(size)) == NULL) - return; + /* + * Last frag (pdu_data_size <= frag_data_size) + */ + hdr->pfc_flags = NDR_PFC_LAST_FRAG; + frag_size = pdu_data_size + NDR_RSP_HDR_SIZE; + hdr->frag_length = frag_size; + mxa->send_hdr.response_hdr.alloc_hint = pdu_data_size; + nds->pdu_scan_offset = 0; + (void) ndr_encode_pdu_hdr(mxa); + bcopy(nds->pdu_base_addr, pdu_buf, NDR_RSP_HDR_SIZE); + (void) NDR_PIPE_SEND(mxa->pipe, pdu_buf, frag_size); - frag->next = NULL; - frag->buf = (uint8_t *)frag + sizeof (ndr_frag_t); - frag->len = len; - bcopy(buf, frag->buf, len); - - if (nds->frags.head == NULL) { - nds->frags.head = frag; - nds->frags.tail = frag; - nds->frags.nfrag = 1; - } else { - nds->frags.tail->next = frag; - nds->frags.tail = frag; - ++nds->frags.nfrag; - } + return (0); } diff --git a/usr/src/lib/smbsrv/libmlrpc/common/ndr_svc.c b/usr/src/lib/smbsrv/libmlrpc/common/ndr_svc.c index ce4af4c094..d5c5f95f01 100644 --- a/usr/src/lib/smbsrv/libmlrpc/common/ndr_svc.c +++ b/usr/src/lib/smbsrv/libmlrpc/common/ndr_svc.c @@ -21,6 +21,8 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ #include <uuid/uuid.h> @@ -211,7 +213,7 @@ ndr_hdalloc(const ndr_xa_t *xa, const void *data) ++id.data2; bcopy(&id, &hd->nh_id, sizeof (ndr_hdid_t)); - hd->nh_fid = xa->fid; + hd->nh_pipe = xa->pipe; hd->nh_svc = xa->binding->service; hd->nh_data = (void *)data; hd->nh_data_free = NULL; @@ -290,7 +292,7 @@ ndr_hdlookup(const ndr_xa_t *xa, const ndr_hdid_t *id) * Called when a pipe is closed to release any associated handles. */ void -ndr_hdclose(int fid) +ndr_hdclose(ndr_pipe_t *pipe) { ndr_handle_t *hd; ndr_handle_t **pphd; @@ -301,7 +303,7 @@ ndr_hdclose(int fid) while (*pphd) { hd = *pphd; - if (hd->nh_fid == fid) { + if (hd->nh_pipe == pipe) { *pphd = hd->nh_next; if (hd->nh_data_free) diff --git a/usr/src/lib/smbsrv/libmlsvc/common/lsar_svc.c b/usr/src/lib/smbsrv/libmlsvc/common/lsar_svc.c index e04ef02503..31e3175416 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/lsar_svc.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/lsar_svc.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ /* @@ -517,7 +518,7 @@ static int lsarpc_s_GetConnectedUser(void *arg, ndr_xa_t *mxa) { struct mslsa_GetConnectedUser *param = arg; - smb_netuserinfo_t *user = &mxa->pipe->np_user; + smb_netuserinfo_t *user = mxa->pipe->np_user; DWORD status = NT_STATUS_SUCCESS; smb_domainex_t di; int rc1; diff --git a/usr/src/lib/smbsrv/libmlsvc/common/spoolss_svc.c b/usr/src/lib/smbsrv/libmlsvc/common/spoolss_svc.c index 5c1bba93cf..ba1c9caece 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/spoolss_svc.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/spoolss_svc.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012 Nexenta Systems, Inc. All rights reserved. + * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ /* @@ -326,9 +326,9 @@ spoolss_s_StartDocPrinter(void *arg, ndr_xa_t *mxa) else (void) strlcpy(spfile->sd_printer_name, "printer", MAXPATHLEN); - spfile->sd_ipaddr = mxa->pipe->np_user.ui_ipaddr; + spfile->sd_ipaddr = mxa->pipe->np_user->ui_ipaddr; (void) strlcpy((char *)spfile->sd_username, - mxa->pipe->np_user.ui_account, MAXNAMELEN); + mxa->pipe->np_user->ui_account, MAXNAMELEN); (void) memcpy(&spfile->sd_handle, ¶m->handle, sizeof (ndr_hdid_t)); /* diff --git a/usr/src/lib/smbsrv/libmlsvc/common/srvsvc_svc.c b/usr/src/lib/smbsrv/libmlsvc/common/srvsvc_svc.c index 4a6dace4b4..89a395cca5 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/srvsvc_svc.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/srvsvc_svc.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ /* @@ -2677,7 +2678,7 @@ mlsvc_NetShareEnumCommon(ndr_xa_t *mxa, smb_svcenum_t *se, static boolean_t srvsvc_add_autohome(ndr_xa_t *mxa, smb_svcenum_t *se, void *infop) { - smb_netuserinfo_t *user = &mxa->pipe->np_user; + smb_netuserinfo_t *user = mxa->pipe->np_user; char *username; smb_share_t si; DWORD status; diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files index ca912c05dd..40caa53481 100644 --- a/usr/src/uts/common/Makefile.files +++ b/usr/src/uts/common/Makefile.files @@ -1221,7 +1221,6 @@ SMBSRV_OBJS += $(SMBSRV_SHARED_OBJS) \ smb_ofile.o \ smb_open_andx.o \ smb_opipe.o \ - smb_opipe_door.o \ smb_oplock.o \ smb_pathname.o \ smb_print.o \ diff --git a/usr/src/uts/common/fs/smbsrv/smb_common_open.c b/usr/src/uts/common/fs/smbsrv/smb_common_open.c index 6e217c51ed..91f9a51bab 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_common_open.c +++ b/usr/src/uts/common/fs/smbsrv/smb_common_open.c @@ -38,7 +38,7 @@ #include <smbsrv/smb_fsops.h> #include <smbsrv/smbinfo.h> -volatile uint32_t smb_fids = 0; +static volatile uint32_t smb_fids = 0; #define SMB_UNIQ_FID() atomic_inc_32_nv(&smb_fids) static uint32_t smb_open_subr(smb_request_t *); @@ -378,7 +378,9 @@ smb_open_subr(smb_request_t *sr) * No further processing for IPC, we need to either * raise an exception or return success here. */ - if ((status = smb_opipe_open(sr)) != NT_STATUS_SUCCESS) + uniq_fid = SMB_UNIQ_FID(); + status = smb_opipe_open(sr, uniq_fid); + if (status != NT_STATUS_SUCCESS) smbsr_error(sr, status, 0, 0); smb_threshold_exit(&sv->sv_opipe_ct); @@ -825,18 +827,24 @@ smb_open_subr(smb_request_t *sr) status = NT_STATUS_SUCCESS; - of = smb_ofile_open(sr, node, sr->smb_pid, op, SMB_FTYPE_DISK, uniq_fid, + of = smb_ofile_open(sr, node, op, SMB_FTYPE_DISK, uniq_fid, &err); if (of == NULL) { smbsr_error(sr, err.status, err.errcls, err.errcode); status = err.status; } - if (status == NT_STATUS_SUCCESS) { - if (!smb_tree_is_connected(sr->tid_tree)) { - smbsr_error(sr, 0, ERRSRV, ERRinvnid); - status = NT_STATUS_UNSUCCESSFUL; - } + /* + * We might have blocked in smb_ofile_open long enough so a + * tree disconnect might have happened. In that case, we've + * just added an ofile to a tree that's disconnecting, and + * need to undo that to avoid interfering with tear-down of + * the tree connection. + */ + if (status == NT_STATUS_SUCCESS && + !smb_tree_is_connected(sr->tid_tree)) { + smbsr_error(sr, 0, ERRSRV, ERRinvnid); + status = NT_STATUS_INVALID_PARAMETER; } /* diff --git a/usr/src/uts/common/fs/smbsrv/smb_common_transact.c b/usr/src/uts/common/fs/smbsrv/smb_common_transact.c index 47607b4bb9..6fe650735e 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_common_transact.c +++ b/usr/src/uts/common/fs/smbsrv/smb_common_transact.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 2014 Nexenta Systems, Inc. All rights reserved. */ #include <smbsrv/smb_kproto.h> @@ -1383,16 +1383,74 @@ is_supported_mailslot(const char *mailslot) } /* - * Currently, just return false if the pipe is \\PIPE\repl. - * Otherwise, return true. + * smb_trans_nmpipe + * + * This is used for RPC bind and request transactions. + * + * If the data available from the pipe is larger than the maximum + * data size requested by the client, return as much as requested. + * The residual data remains in the pipe until the client comes back + * with a read request or closes the pipe. + * + * When we read less than what's available, we MUST return the + * status NT_STATUS_BUFFER_OVERFLOW (or ERRDOS/ERROR_MORE_DATA) */ -static boolean_t -is_supported_pipe(const char *pname) +static smb_sdrc_t +smb_trans_nmpipe(smb_request_t *sr, smb_xa_t *xa) { - if (smb_strcasecmp(pname, PIPE_REPL, 0) == 0) - return (B_FALSE); + smb_vdb_t vdb; + struct mbuf *mb; + int rc; + + smbsr_lookup_file(sr); + if (sr->fid_ofile == NULL) { + smbsr_error(sr, NT_STATUS_INVALID_HANDLE, + ERRDOS, ERRbadfid); + return (SDRC_ERROR); + } + + rc = smb_mbc_decodef(&xa->req_data_mb, "#B", + xa->smb_tdscnt, &vdb); + if (rc != 0) { + /* Not enough data sent. */ + smbsr_error(sr, 0, ERRSRV, ERRerror); + return (SDRC_ERROR); + } + + rc = smb_opipe_write(sr, &vdb.vdb_uio); + if (rc != 0) { + smbsr_errno(sr, rc); + return (SDRC_ERROR); + } + + vdb.vdb_tag = 0; + vdb.vdb_uio.uio_iov = &vdb.vdb_iovec[0]; + vdb.vdb_uio.uio_iovcnt = MAX_IOVEC; + vdb.vdb_uio.uio_segflg = UIO_SYSSPACE; + vdb.vdb_uio.uio_extflg = UIO_COPY_DEFAULT; + vdb.vdb_uio.uio_loffset = (offset_t)0; + vdb.vdb_uio.uio_resid = xa->smb_mdrcnt; + mb = smb_mbuf_allocate(&vdb.vdb_uio); + + rc = smb_opipe_read(sr, &vdb.vdb_uio); + if (rc == E2BIG) { + /* + * Note: E2BIG is not a real error. It just + * tells us there's more data to be read. + */ + smbsr_status(sr, NT_STATUS_BUFFER_OVERFLOW, + ERRDOS, ERROR_MORE_DATA); + rc = 0; + } + if (rc != 0) { + smbsr_errno(sr, rc); + return (SDRC_ERROR); + } + + smb_mbuf_trim(mb, xa->smb_mdrcnt - vdb.vdb_uio.uio_resid); + MBC_ATTACH_MBUF(&xa->rep_data_mb, mb); - return (B_TRUE); + return (SDRC_SUCCESS); } static smb_sdrc_t @@ -1405,7 +1463,6 @@ smb_trans_dispatch(smb_request_t *sr, smb_xa_t *xa) uint16_t devstate; char *req_fmt; char *rep_fmt; - smb_vdb_t vdb; if (xa->smb_suwcnt > 0 && STYPE_ISIPC(sr->tid_tree->t_res_type)) { rc = smb_mbc_decodef(&xa->req_setup_mb, "ww", &opcode, @@ -1422,26 +1479,11 @@ smb_trans_dispatch(smb_request_t *sr, smb_xa_t *xa) break; case TRANS_TRANSACT_NMPIPE: - smbsr_lookup_file(sr); - if (sr->fid_ofile == NULL) { - smbsr_error(sr, NT_STATUS_INVALID_HANDLE, - ERRDOS, ERRbadfid); - return (SDRC_ERROR); - } - - rc = smb_mbc_decodef(&xa->req_data_mb, "#B", - xa->smb_tdscnt, &vdb); - if (rc != 0) - goto trans_err_not_supported; - - rc = smb_opipe_transact(sr, &vdb.vdb_uio); + rc = smb_trans_nmpipe(sr, xa); break; case TRANS_WAIT_NMPIPE: - if (!is_supported_pipe(xa->xa_pipe_name)) { - smbsr_error(sr, 0, ERRDOS, ERRbadfile); - return (SDRC_ERROR); - } + delay(SEC_TO_TICK(1)); rc = SDRC_SUCCESS; break; diff --git a/usr/src/uts/common/fs/smbsrv/smb_dispatch.c b/usr/src/uts/common/fs/smbsrv/smb_dispatch.c index e2488c244f..da874155ff 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_dispatch.c +++ b/usr/src/uts/common/fs/smbsrv/smb_dispatch.c @@ -970,6 +970,8 @@ static const struct { { EROFS, ERRHRD, ERRnowrite, NT_STATUS_ACCESS_DENIED }, { ESTALE, ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE }, { EBADF, ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE }, + { ENOTSOCK, ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE }, + { EPIPE, ERRDOS, ERROR_BROKEN_PIPE, NT_STATUS_PIPE_BROKEN }, { EEXIST, ERRDOS, ERRfilexists, NT_STATUS_OBJECT_NAME_COLLISION }, { ENXIO, ERRSRV, ERRinvdevice, NT_STATUS_BAD_DEVICE_TYPE }, { ESRCH, ERRDOS, ERROR_FILE_NOT_FOUND, diff --git a/usr/src/uts/common/fs/smbsrv/smb_ofile.c b/usr/src/uts/common/fs/smbsrv/smb_ofile.c index 41515ee392..a21461efe3 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_ofile.c +++ b/usr/src/uts/common/fs/smbsrv/smb_ofile.c @@ -177,7 +177,6 @@ smb_ofile_t * smb_ofile_open( smb_request_t *sr, smb_node_t *node, - uint16_t pid, struct open_param *op, uint16_t ftype, uint32_t uniqid, @@ -205,7 +204,7 @@ smb_ofile_open( of->f_refcnt = 1; of->f_fid = fid; of->f_uniqid = uniqid; - of->f_opened_by_pid = pid; + of->f_opened_by_pid = sr->smb_pid; of->f_granted_access = op->desired_access; of->f_share_access = op->share_access; of->f_create_options = op->create_options; @@ -230,7 +229,8 @@ smb_ofile_open( of->f_state = SMB_OFILE_STATE_OPEN; if (ftype == SMB_FTYPE_MESG_PIPE) { - of->f_pipe = smb_opipe_alloc(tree->t_server); + /* See smb_opipe_open. */ + of->f_pipe = op->pipe; smb_server_inc_pipes(of->f_server); } else { ASSERT(ftype == SMB_FTYPE_DISK); /* Regular file, not a pipe */ @@ -324,113 +324,104 @@ smb_ofile_close(smb_ofile_t *of, int32_t mtime_sec) mutex_enter(&of->f_mutex); ASSERT(of->f_refcnt); - switch (of->f_state) { - case SMB_OFILE_STATE_OPEN: { - - of->f_state = SMB_OFILE_STATE_CLOSING; + if (of->f_state != SMB_OFILE_STATE_OPEN) { mutex_exit(&of->f_mutex); + return; + } + of->f_state = SMB_OFILE_STATE_CLOSING; + mutex_exit(&of->f_mutex); - if (of->f_ftype == SMB_FTYPE_MESG_PIPE) { - smb_opipe_close(of); - smb_server_dec_pipes(of->f_server); - } else { - smb_attr_t *pa = &of->f_pending_attr; - - /* - * In here we make changes to of->f_pending_attr - * while not holding of->f_mutex. This is OK - * because we've changed f_state to CLOSING, - * so no more threads will take this path. - */ - if (mtime_sec != 0) { - pa->sa_vattr.va_mtime.tv_sec = mtime_sec; - pa->sa_mask |= SMB_AT_MTIME; - } + if (of->f_ftype == SMB_FTYPE_MESG_PIPE) { + smb_opipe_close(of); + smb_server_dec_pipes(of->f_server); + } else { + smb_attr_t *pa = &of->f_pending_attr; - /* - * If we have ever modified data via this handle - * (write or truncate) and if the mtime was not - * set via this handle, update the mtime again - * during the close. Windows expects this. - * [ MS-FSA 2.1.5.4 "Update Timestamps" ] - */ - if (of->f_written && - (pa->sa_mask & SMB_AT_MTIME) == 0) { - pa->sa_mask |= SMB_AT_MTIME; - gethrestime(&now); - pa->sa_vattr.va_mtime = now; - } + /* + * In here we make changes to of->f_pending_attr + * while not holding of->f_mutex. This is OK + * because we've changed f_state to CLOSING, + * so no more threads will take this path. + */ + if (mtime_sec != 0) { + pa->sa_vattr.va_mtime.tv_sec = mtime_sec; + pa->sa_mask |= SMB_AT_MTIME; + } - if (of->f_flags & SMB_OFLAGS_SET_DELETE_ON_CLOSE) { - if (smb_tree_has_feature(of->f_tree, - SMB_TREE_CATIA)) { - flags |= SMB_CATIA; - } - (void) smb_node_set_delete_on_close(of->f_node, - of->f_cr, flags); - } - smb_fsop_unshrlock(of->f_cr, of->f_node, of->f_uniqid); - smb_node_destroy_lock_by_ofile(of->f_node, of); + /* + * If we have ever modified data via this handle + * (write or truncate) and if the mtime was not + * set via this handle, update the mtime again + * during the close. Windows expects this. + * [ MS-FSA 2.1.5.4 "Update Timestamps" ] + */ + if (of->f_written && + (pa->sa_mask & SMB_AT_MTIME) == 0) { + pa->sa_mask |= SMB_AT_MTIME; + gethrestime(&now); + pa->sa_vattr.va_mtime = now; + } - if (smb_node_is_file(of->f_node)) { - (void) smb_fsop_close(of->f_node, of->f_mode, - of->f_cr); - smb_oplock_release(of->f_node, of); - } - if (smb_node_dec_open_ofiles(of->f_node) == 0) { - /* - * Last close. The f_pending_attr has - * only times (atime,ctime,mtime) so - * we can borrow it to commit the - * n_pending_dosattr from the node. - */ - pa->sa_dosattr = - of->f_node->n_pending_dosattr; - if (pa->sa_dosattr != 0) - pa->sa_mask |= SMB_AT_DOSATTR; - /* Let's leave this zero when not in use. */ - of->f_node->n_allocsz = 0; - } - if (pa->sa_mask != 0) { - /* - * Commit any pending attributes from - * the ofile we're closing. Note that - * we pass NULL as the ofile to setattr - * so it will write to the file system - * and not keep anything on the ofile. - * This clears n_pending_dosattr if - * there are no opens, otherwise the - * dosattr will be pending again. - */ - (void) smb_node_setattr(NULL, of->f_node, - of->f_cr, NULL, pa); + if (of->f_flags & SMB_OFLAGS_SET_DELETE_ON_CLOSE) { + if (smb_tree_has_feature(of->f_tree, + SMB_TREE_CATIA)) { + flags |= SMB_CATIA; } + (void) smb_node_set_delete_on_close(of->f_node, + of->f_cr, flags); + } + smb_fsop_unshrlock(of->f_cr, of->f_node, of->f_uniqid); + smb_node_destroy_lock_by_ofile(of->f_node, of); + if (smb_node_is_file(of->f_node)) { + (void) smb_fsop_close(of->f_node, of->f_mode, + of->f_cr); + smb_oplock_release(of->f_node, of); + } + if (smb_node_dec_open_ofiles(of->f_node) == 0) { /* - * Cancel any notify change requests that - * may be using this open instance. + * Last close. The f_pending_attr has + * only times (atime,ctime,mtime) so + * we can borrow it to commit the + * n_pending_dosattr from the node. */ - if (of->f_node->n_fcn.fcn_count) - smb_notify_file_closed(of); - - smb_server_dec_files(of->f_server); + pa->sa_dosattr = + of->f_node->n_pending_dosattr; + if (pa->sa_dosattr != 0) + pa->sa_mask |= SMB_AT_DOSATTR; + /* Let's leave this zero when not in use. */ + of->f_node->n_allocsz = 0; + } + if (pa->sa_mask != 0) { + /* + * Commit any pending attributes from + * the ofile we're closing. Note that + * we pass NULL as the ofile to setattr + * so it will write to the file system + * and not keep anything on the ofile. + * This clears n_pending_dosattr if + * there are no opens, otherwise the + * dosattr will be pending again. + */ + (void) smb_node_setattr(NULL, of->f_node, + of->f_cr, NULL, pa); } - atomic_dec_32(&of->f_tree->t_open_files); - mutex_enter(&of->f_mutex); - ASSERT(of->f_refcnt); - ASSERT(of->f_state == SMB_OFILE_STATE_CLOSING); - of->f_state = SMB_OFILE_STATE_CLOSED; - break; - } - case SMB_OFILE_STATE_CLOSED: - case SMB_OFILE_STATE_CLOSING: - break; + /* + * Cancel any notify change requests that + * may be using this open instance. + */ + if (of->f_node->n_fcn.fcn_count) + smb_notify_file_closed(of); - default: - ASSERT(0); - break; + smb_server_dec_files(of->f_server); } + atomic_dec_32(&of->f_tree->t_open_files); + + mutex_enter(&of->f_mutex); + ASSERT(of->f_refcnt); + ASSERT(of->f_state == SMB_OFILE_STATE_CLOSING); + of->f_state = SMB_OFILE_STATE_CLOSED; mutex_exit(&of->f_mutex); } @@ -541,14 +532,14 @@ smb_ofile_hold(smb_ofile_t *of) mutex_enter(&of->f_mutex); - if (smb_ofile_is_open_locked(of)) { - of->f_refcnt++; + if (of->f_state != SMB_OFILE_STATE_OPEN) { mutex_exit(&of->f_mutex); - return (B_TRUE); + return (B_FALSE); } + of->f_refcnt++; mutex_exit(&of->f_mutex); - return (B_FALSE); + return (B_TRUE); } /* diff --git a/usr/src/uts/common/fs/smbsrv/smb_opipe.c b/usr/src/uts/common/fs/smbsrv/smb_opipe.c index 116cdc6e3b..5eacc82a60 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_opipe.c +++ b/usr/src/uts/common/fs/smbsrv/smb_opipe.c @@ -19,8 +19,8 @@ * CDDL HEADER END */ /* - * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ /* @@ -28,32 +28,28 @@ */ #include <sys/stat.h> -#include <sys/door.h> -#include <sys/door_data.h> #include <sys/uio.h> #include <sys/ksynch.h> +#include <sys/stropts.h> +#include <sys/socket.h> +#include <sys/filio.h> #include <smbsrv/smb_kproto.h> #include <smbsrv/smb_xdr.h> -#define SMB_OPIPE_ISOPEN(OPIPE) \ - (((OPIPE)->p_hdr.dh_magic == SMB_OPIPE_HDR_MAGIC) && \ - ((OPIPE)->p_hdr.dh_fid)) - -extern volatile uint32_t smb_fids; -#define SMB_UNIQ_FID() atomic_inc_32_nv(&smb_fids) - -static int smb_opipe_do_open(smb_request_t *, smb_opipe_t *); -static char *smb_opipe_lookup(const char *); -static int smb_opipe_sethdr(smb_opipe_t *, uint32_t, uint32_t); -static int smb_opipe_exec(smb_opipe_t *); -static void smb_opipe_enter(smb_opipe_t *); -static void smb_opipe_exit(smb_opipe_t *); - - -smb_opipe_t * -smb_opipe_alloc(smb_server_t *sv) +/* + * Allocate a new opipe and return it, or NULL, in which case + * the caller will report "internal error". + */ +static smb_opipe_t * +smb_opipe_alloc(smb_request_t *sr) { + smb_server_t *sv = sr->sr_server; smb_opipe_t *opipe; + ksocket_t sock; + + if (ksocket_socket(&sock, AF_UNIX, SOCK_STREAM, 0, + KSOCKET_SLEEP, sr->user_cr) != 0) + return (NULL); opipe = kmem_cache_alloc(smb_cache_opipe, KM_SLEEP); @@ -62,14 +58,18 @@ smb_opipe_alloc(smb_server_t *sv) cv_init(&opipe->p_cv, NULL, CV_DEFAULT, NULL); opipe->p_magic = SMB_OPIPE_MAGIC; opipe->p_server = sv; - - smb_llist_enter(&sv->sv_opipe_list, RW_WRITER); - smb_llist_insert_tail(&sv->sv_opipe_list, opipe); - smb_llist_exit(&sv->sv_opipe_list); + opipe->p_refcnt = 1; + opipe->p_socket = sock; return (opipe); } +/* + * Destroy an opipe. This is normally called from smb_ofile_delete + * when the ofile has no more references and is about to be free'd. + * This is also called here in error handling code paths, before + * the opipe is installed under an ofile. + */ void smb_opipe_dealloc(smb_opipe_t *opipe) { @@ -79,12 +79,14 @@ smb_opipe_dealloc(smb_opipe_t *opipe) sv = opipe->p_server; SMB_SERVER_VALID(sv); - smb_llist_enter(&sv->sv_opipe_list, RW_WRITER); - smb_llist_remove(&sv->sv_opipe_list, opipe); - smb_llist_exit(&sv->sv_opipe_list); + /* + * This is called in the error path when opening, + * in which case we close the socket here. + */ + if (opipe->p_socket != NULL) + (void) ksocket_close(opipe->p_socket, zone_kcred()); opipe->p_magic = (uint32_t)~SMB_OPIPE_MAGIC; - smb_event_destroy(opipe->p_event); cv_destroy(&opipe->p_cv); mutex_destroy(&opipe->p_mutex); @@ -92,299 +94,198 @@ smb_opipe_dealloc(smb_opipe_t *opipe) } /* - * smb_opipe_open - * - * Open a well-known RPC named pipe. This routine should be called if - * a file open is requested on a share of type STYPE_IPC. - * If we recognize the pipe, we setup a new ofile. - * - * Returns 0 on success, Otherwise an NT status is returned to indicate - * an error. + * Helper for open: build pipe name and connect. */ -int -smb_opipe_open(smb_request_t *sr) +static int +smb_opipe_connect(smb_request_t *sr, smb_opipe_t *opipe) { + struct sockaddr_un saddr; smb_arg_open_t *op = &sr->sr_open; - smb_ofile_t *of; - smb_opipe_t *opipe; - smb_doorhdr_t hdr; - smb_error_t err; - char *pipe_name; + const char *name; + int rc; - if ((pipe_name = smb_opipe_lookup(op->fqi.fq_path.pn_path)) == NULL) - return (NT_STATUS_OBJECT_NAME_NOT_FOUND); + name = op->fqi.fq_path.pn_path; + name += strspn(name, "\\"); + if (smb_strcasecmp(name, "PIPE", 4) == 0) { + name += 4; + name += strspn(name, "\\"); + } + (void) strlcpy(opipe->p_name, name, SMB_OPIPE_MAXNAME); + (void) smb_strlwr(opipe->p_name); - /* - * If printing is disabled, pretend spoolss does not exist. - */ - if (sr->sr_server->sv_cfg.skc_print_enable == 0 && - strcmp(pipe_name, "SPOOLSS") == 0) - return (NT_STATUS_OBJECT_NAME_NOT_FOUND); + bzero(&saddr, sizeof (saddr)); + saddr.sun_family = AF_UNIX; + (void) snprintf(saddr.sun_path, sizeof (saddr.sun_path), + "%s/%s", SMB_PIPE_DIR, opipe->p_name); + rc = ksocket_connect(opipe->p_socket, (struct sockaddr *)&saddr, + sizeof (saddr), sr->user_cr); - op->create_options = 0; + return (rc); +} - of = smb_ofile_open(sr, NULL, sr->smb_pid, op, SMB_FTYPE_MESG_PIPE, - SMB_UNIQ_FID(), &err); +/* + * Helper for open: encode and send the user info. + * + * We send information about this client + user to the + * pipe service so it can use it for access checks. + * The service MAY deny the open based on this info, + * (i.e. anonymous session trying to open a pipe that + * requires authentication) in which case we will read + * an error status from the service and return that. + */ +static void +smb_opipe_send_userinfo(smb_request_t *sr, smb_opipe_t *opipe, + smb_error_t *errp) +{ + XDR xdrs; + smb_netuserinfo_t nui; + smb_pipehdr_t phdr; + char *buf; + uint32_t buflen; + uint32_t status; + size_t iocnt = 0; + int rc; - if (of == NULL) - return (err.status); + /* + * Any errors building the XDR message etc. + */ + errp->status = NT_STATUS_INTERNAL_ERROR; - if (!smb_tree_is_connected(sr->tid_tree)) { - smb_ofile_close(of, 0); - smb_ofile_release(of); - return (NT_STATUS_OBJECT_NAME_NOT_FOUND); - } + smb_user_netinfo_init(sr->uid_user, &nui); + phdr.ph_magic = SMB_PIPE_HDR_MAGIC; + phdr.ph_uilen = xdr_sizeof(smb_netuserinfo_xdr, &nui); - op->dsize = 0x01000; - op->dattr = FILE_ATTRIBUTE_NORMAL; - op->ftype = SMB_FTYPE_MESG_PIPE; - op->action_taken = SMB_OACT_LOCK | SMB_OACT_OPENED; /* 0x8001 */ - op->devstate = SMB_PIPE_READMODE_MESSAGE - | SMB_PIPE_TYPE_MESSAGE - | SMB_PIPE_UNLIMITED_INSTANCES; /* 0x05ff */ - op->fileid = of->f_fid; + buflen = sizeof (phdr) + phdr.ph_uilen; + buf = kmem_alloc(buflen, KM_SLEEP); - sr->smb_fid = of->f_fid; - sr->fid_ofile = of; + bcopy(&phdr, buf, sizeof (phdr)); + xdrmem_create(&xdrs, buf + sizeof (phdr), + buflen - (sizeof (phdr)), XDR_ENCODE); + if (!smb_netuserinfo_xdr(&xdrs, &nui)) + goto out; - opipe = of->f_pipe; - smb_opipe_enter(opipe); + /* + * If we fail sending the netuserinfo or recv'ing the + * status reponse, we have probably run into the limit + * on the number of open pipes. That's this status: + */ + errp->status = NT_STATUS_PIPE_NOT_AVAILABLE; + + rc = ksocket_send(opipe->p_socket, buf, buflen, 0, + &iocnt, sr->user_cr); + if (rc == 0 && iocnt != buflen) + rc = EIO; + if (rc != 0) + goto out; - opipe->p_server = of->f_server; - opipe->p_name = pipe_name; - opipe->p_doorbuf = kmem_zalloc(SMB_OPIPE_DOOR_BUFSIZE, KM_SLEEP); + rc = ksocket_recv(opipe->p_socket, &status, sizeof (status), 0, + &iocnt, sr->user_cr); + if (rc != 0 || iocnt != sizeof (status)) + goto out; /* - * p_data points to the offset within p_doorbuf at which - * data will be written or read. + * Return the status we read from the pipe service, + * normally NT_STATUS_SUCCESS, but could be something + * else like NT_STATUS_ACCESS_DENIED. */ - opipe->p_data = opipe->p_doorbuf + xdr_sizeof(smb_doorhdr_xdr, &hdr); - - if (smb_opipe_do_open(sr, opipe) != 0) { - /* - * On error, reset the header to clear the fid, - * which avoids confusion when smb_opipe_close() is - * called by smb_ofile_close(). - */ - bzero(&opipe->p_hdr, sizeof (smb_doorhdr_t)); - kmem_free(opipe->p_doorbuf, SMB_OPIPE_DOOR_BUFSIZE); - smb_opipe_exit(opipe); - smb_ofile_close(of, 0); - return (NT_STATUS_NO_MEMORY); - } - smb_opipe_exit(opipe); - return (NT_STATUS_SUCCESS); + errp->status = status; + +out: + xdr_destroy(&xdrs); + kmem_free(buf, buflen); + smb_user_netinfo_fini(&nui); } /* - * smb_opipe_lookup + * smb_opipe_open * - * Lookup a path to see if it's a well-known RPC named pipe that we support. - * The full pipe path will be in the form \\PIPE\\SERVICE. The first part - * can be assumed, so all we need here are the service names. + * Open an RPC named pipe. This routine should be called if + * a file open is requested on a share of type STYPE_IPC. + * If we recognize the pipe, we setup a new ofile. * - * Returns a pointer to the pipe name (without any leading \'s) on success. - * Otherwise returns a null pointer. + * Returns 0 on success, Otherwise an NT status code. */ -static char * -smb_opipe_lookup(const char *path) +int +smb_opipe_open(smb_request_t *sr, uint32_t uniqid) { - static char *named_pipes[] = { - "lsass", - "LSARPC", - "NETLOGON", - "SAMR", - "SPOOLSS", - "SRVSVC", - "SVCCTL", - "WINREG", - "WKSSVC", - "EVENTLOG", - "NETDFS" - }; - - const char *name; - int i; + smb_arg_open_t *op = &sr->sr_open; + smb_ofile_t *ofile; + smb_opipe_t *opipe; + smb_error_t err; - if (path == NULL) - return (NULL); + opipe = smb_opipe_alloc(sr); + if (opipe == NULL) + return (NT_STATUS_INTERNAL_ERROR); - name = path; - name += strspn(name, "\\"); - if (smb_strcasecmp(name, "PIPE", 4) == 0) { - path += 4; - name += strspn(name, "\\"); + if (smb_opipe_connect(sr, opipe) != 0) { + smb_opipe_dealloc(opipe); + return (NT_STATUS_OBJECT_NAME_NOT_FOUND); } - for (i = 0; i < sizeof (named_pipes) / sizeof (named_pipes[0]); ++i) { - if (smb_strcasecmp(name, named_pipes[i], 0) == 0) - return (named_pipes[i]); + smb_opipe_send_userinfo(sr, opipe, &err); + if (err.status != 0) { + smb_opipe_dealloc(opipe); + return (err.status); } - return (NULL); -} - -/* - * Initialize the opipe header and context, and make the door call. - */ -static int -smb_opipe_do_open(smb_request_t *sr, smb_opipe_t *opipe) -{ - smb_netuserinfo_t *userinfo = &opipe->p_user; - smb_user_t *user = sr->uid_user; - smb_server_t *sv = sr->sr_server; - uint8_t *buf = opipe->p_doorbuf; - uint32_t buflen = SMB_OPIPE_DOOR_BUFSIZE; - uint32_t len; - - if ((opipe->p_event = smb_event_create(sv, SMB_EVENT_TIMEOUT)) == NULL) - return (-1); - - smb_user_netinfo_init(user, userinfo); - len = xdr_sizeof(smb_netuserinfo_xdr, userinfo); - - bzero(&opipe->p_hdr, sizeof (smb_doorhdr_t)); - opipe->p_hdr.dh_magic = SMB_OPIPE_HDR_MAGIC; - opipe->p_hdr.dh_flags = SMB_DF_SYSSPACE; - opipe->p_hdr.dh_fid = smb_event_txid(opipe->p_event); + /* + * Note: If smb_ofile_open succeeds, the new ofile is + * in the FID lists can can be used by I/O requests. + */ + op->create_options = 0; + op->pipe = opipe; + ofile = smb_ofile_open(sr, NULL, op, + SMB_FTYPE_MESG_PIPE, uniqid, &err); + op->pipe = NULL; + if (ofile == NULL) { + smb_opipe_dealloc(opipe); + return (err.status); + } - if (smb_opipe_sethdr(opipe, SMB_OPIPE_OPEN, len) == -1) - return (-1); + /* An "up" pointer, for debug. */ + opipe->p_ofile = ofile; - len = xdr_sizeof(smb_doorhdr_xdr, &opipe->p_hdr); - buf += len; - buflen -= len; + op->dsize = 0x01000; + op->dattr = FILE_ATTRIBUTE_NORMAL; + op->ftype = SMB_FTYPE_MESG_PIPE; + op->action_taken = SMB_OACT_LOCK | SMB_OACT_OPENED; /* 0x8001 */ + op->devstate = SMB_PIPE_READMODE_MESSAGE + | SMB_PIPE_TYPE_MESSAGE + | SMB_PIPE_UNLIMITED_INSTANCES; /* 0x05ff */ + op->fileid = ofile->f_fid; - if (smb_netuserinfo_encode(userinfo, buf, buflen, NULL) == -1) - return (-1); + sr->smb_fid = ofile->f_fid; + sr->fid_ofile = ofile; - return (smb_opipe_door_call(opipe)); + return (NT_STATUS_SUCCESS); } /* * smb_opipe_close * - * Called whenever an IPC file/pipe is closed. + * Called by smb_ofile_close for pipes. + * + * Note: ksocket_close may block while waiting for + * any I/O threads with a hold to get out. */ void smb_opipe_close(smb_ofile_t *of) { smb_opipe_t *opipe; + ksocket_t sock; - ASSERT(of); + ASSERT(of->f_state == SMB_OFILE_STATE_CLOSING); ASSERT(of->f_ftype == SMB_FTYPE_MESG_PIPE); - opipe = of->f_pipe; SMB_OPIPE_VALID(opipe); - (void) smb_server_cancel_event(of->f_server, opipe->p_hdr.dh_fid); - smb_opipe_enter(opipe); - - if (SMB_OPIPE_ISOPEN(opipe)) { - (void) smb_opipe_sethdr(opipe, SMB_OPIPE_CLOSE, 0); - (void) smb_opipe_door_call(opipe); - bzero(&opipe->p_hdr, sizeof (smb_doorhdr_t)); - kmem_free(opipe->p_doorbuf, SMB_OPIPE_DOOR_BUFSIZE); - } - - smb_user_netinfo_fini(&opipe->p_user); - smb_opipe_exit(opipe); -} - -static int -smb_opipe_sethdr(smb_opipe_t *opipe, uint32_t cmd, uint32_t datalen) -{ - opipe->p_hdr.dh_op = cmd; - opipe->p_hdr.dh_txid = opipe->p_hdr.dh_fid; - opipe->p_hdr.dh_datalen = datalen; - opipe->p_hdr.dh_resid = 0; - opipe->p_hdr.dh_door_rc = EINVAL; - - return (smb_doorhdr_encode(&opipe->p_hdr, opipe->p_doorbuf, - SMB_OPIPE_DOOR_BUFSIZE)); -} - -/* - * smb_opipe_transact - * - * This is the entry point for RPC bind and request transactions. - * The fid is an arbitrary id used to associate RPC requests with a - * particular binding handle. - * - * If the data to be returned is larger than the client expects, we - * return as much as the client can handle and report a buffer overflow - * warning, which informs the client that we have more data to return. - * The residual data remains in the pipe until the client claims it or - * closes the pipe. - */ -smb_sdrc_t -smb_opipe_transact(smb_request_t *sr, struct uio *uio) -{ - smb_xa_t *xa; - smb_opipe_t *opipe; - struct mbuf *mhead; - int mdrcnt; - int nbytes; - int rc; - - if ((rc = smb_opipe_write(sr, uio)) != 0) { - if (rc == EBADF) - smbsr_error(sr, NT_STATUS_INVALID_HANDLE, - ERRDOS, ERROR_INVALID_HANDLE); - else - smbsr_error(sr, NT_STATUS_INTERNAL_ERROR, - ERRDOS, ERROR_INTERNAL_ERROR); - return (SDRC_ERROR); - } - - opipe = sr->fid_ofile->f_pipe; - - if ((rc = smb_opipe_exec(opipe)) != 0) { - smbsr_error(sr, NT_STATUS_INTERNAL_ERROR, - ERRDOS, ERROR_INTERNAL_ERROR); - return (SDRC_ERROR); - } - - xa = sr->r_xa; - mdrcnt = xa->smb_mdrcnt; - smb_opipe_enter(opipe); - - if (smb_opipe_sethdr(opipe, SMB_OPIPE_READ, mdrcnt) == -1) { - smb_opipe_exit(opipe); - smbsr_error(sr, NT_STATUS_INTERNAL_ERROR, - ERRDOS, ERROR_INTERNAL_ERROR); - return (SDRC_ERROR); - } - - rc = smb_opipe_door_call(opipe); - nbytes = opipe->p_hdr.dh_datalen; - - if (rc != 0) { - smb_opipe_exit(opipe); - smbsr_error(sr, NT_STATUS_INTERNAL_ERROR, - ERRDOS, ERROR_INTERNAL_ERROR); - return (SDRC_ERROR); - } - - if (nbytes) { - mhead = smb_mbuf_get(opipe->p_data, nbytes); - xa->rep_data_mb.max_bytes = nbytes; - MBC_ATTACH_MBUF(&xa->rep_data_mb, mhead); - } - - if (opipe->p_hdr.dh_resid) { - /* - * The pipe contains more data than mdrcnt, warn the - * client that there is more data in the pipe. - * Typically, the client will call SmbReadX, which - * will call smb_opipe_read, to get the data. - */ - smbsr_warn(sr, NT_STATUS_BUFFER_OVERFLOW, - ERRDOS, ERROR_MORE_DATA); - } + mutex_enter(&opipe->p_mutex); + sock = opipe->p_socket; + opipe->p_socket = NULL; + mutex_exit(&opipe->p_mutex); - smb_opipe_exit(opipe); - return (SDRC_SUCCESS); + (void) ksocket_shutdown(sock, SHUT_RDWR, of->f_cr); + (void) ksocket_close(sock, of->f_cr); } /* @@ -398,145 +299,111 @@ smb_opipe_transact(smb_request_t *sr, struct uio *uio) int smb_opipe_write(smb_request_t *sr, struct uio *uio) { + struct nmsghdr msghdr; + smb_ofile_t *ofile; smb_opipe_t *opipe; - uint32_t buflen; - uint32_t len; - int rc; - - ASSERT(sr->fid_ofile); - ASSERT(sr->fid_ofile->f_ftype == SMB_FTYPE_MESG_PIPE); + ksocket_t sock; + size_t sent = 0; + int rc = 0; - opipe = sr->fid_ofile->f_pipe; + ofile = sr->fid_ofile; + ASSERT(ofile->f_ftype == SMB_FTYPE_MESG_PIPE); + opipe = ofile->f_pipe; SMB_OPIPE_VALID(opipe); - smb_opipe_enter(opipe); - if (!SMB_OPIPE_ISOPEN(opipe)) { - smb_opipe_exit(opipe); + mutex_enter(&opipe->p_mutex); + sock = opipe->p_socket; + if (sock != NULL) + ksocket_hold(sock); + mutex_exit(&opipe->p_mutex); + if (sock == NULL) return (EBADF); - } - rc = smb_opipe_sethdr(opipe, SMB_OPIPE_WRITE, uio->uio_resid); - len = xdr_sizeof(smb_doorhdr_xdr, &opipe->p_hdr); - if (rc == -1 || len == 0) { - smb_opipe_exit(opipe); - return (ENOMEM); - } + bzero(&msghdr, sizeof (msghdr)); + msghdr.msg_iov = uio->uio_iov; + msghdr.msg_iovlen = uio->uio_iovcnt; - buflen = SMB_OPIPE_DOOR_BUFSIZE - len; - (void) uiomove((caddr_t)opipe->p_data, buflen, UIO_WRITE, uio); + /* + * This should block until we've sent it all, + * or given up due to errors (pipe closed). + */ + while (uio->uio_resid > 0) { + rc = ksocket_sendmsg(sock, &msghdr, 0, &sent, ofile->f_cr); + if (rc != 0) + break; + uio->uio_resid -= sent; + } - rc = smb_opipe_door_call(opipe); + ksocket_rele(sock); - smb_opipe_exit(opipe); - return ((rc == 0) ? 0 : EIO); + return (rc); } /* * smb_opipe_read * - * This interface may be called because smb_opipe_transact could not return - * all of the data in the original transaction or to form the second half - * of a transaction set up using smb_opipe_write. Either way, we just need - * to read data from the pipe and return it. - * - * The response data is encoded into raw_data as required by the smb_read - * functions. The uio_resid value indicates the number of bytes read. + * This interface may be called from smb_opipe_transact (write, read) + * or from smb_read / smb2_read to get the rest of an RPC response. + * The response data (and length) are returned via the uio. */ int smb_opipe_read(smb_request_t *sr, struct uio *uio) { + struct nmsghdr msghdr; + smb_ofile_t *ofile; smb_opipe_t *opipe; - struct mbuf *mhead; - uint32_t nbytes; + ksocket_t sock; + size_t recvcnt = 0; int rc; - ASSERT(sr->fid_ofile); - ASSERT(sr->fid_ofile->f_ftype == SMB_FTYPE_MESG_PIPE); - - opipe = sr->fid_ofile->f_pipe; + ofile = sr->fid_ofile; + ASSERT(ofile->f_ftype == SMB_FTYPE_MESG_PIPE); + opipe = ofile->f_pipe; SMB_OPIPE_VALID(opipe); - if ((rc = smb_opipe_exec(opipe)) != 0) - return (EIO); - - smb_opipe_enter(opipe); - - if (!SMB_OPIPE_ISOPEN(opipe)) { - smb_opipe_exit(opipe); + mutex_enter(&opipe->p_mutex); + sock = opipe->p_socket; + if (sock != NULL) + ksocket_hold(sock); + mutex_exit(&opipe->p_mutex); + if (sock == NULL) return (EBADF); - } - - if (smb_opipe_sethdr(opipe, SMB_OPIPE_READ, uio->uio_resid) == -1) { - smb_opipe_exit(opipe); - return (ENOMEM); - } - rc = smb_opipe_door_call(opipe); - nbytes = opipe->p_hdr.dh_datalen; + bzero(&msghdr, sizeof (msghdr)); + msghdr.msg_iov = uio->uio_iov; + msghdr.msg_iovlen = uio->uio_iovcnt; - if (rc != 0 || nbytes > uio->uio_resid) { - smb_opipe_exit(opipe); - return (EIO); - } - - if (nbytes) { - mhead = smb_mbuf_get(opipe->p_data, nbytes); - MBC_SETUP(&sr->raw_data, nbytes); - MBC_ATTACH_MBUF(&sr->raw_data, mhead); - uio->uio_resid -= nbytes; + /* + * This should block only if there's no data. + * A single call to recvmsg does just that. + * (Intentionaly no recv loop here.) + */ + rc = ksocket_recvmsg(sock, &msghdr, 0, + &recvcnt, ofile->f_cr); + if (rc != 0) + goto out; + + if (recvcnt == 0) { + /* Other side closed. */ + rc = EPIPE; + goto out; } + uio->uio_resid -= recvcnt; - smb_opipe_exit(opipe); - return (rc); -} - -static int -smb_opipe_exec(smb_opipe_t *opipe) -{ - uint32_t len; - int rc; - - smb_opipe_enter(opipe); - - rc = smb_opipe_sethdr(opipe, SMB_OPIPE_EXEC, 0); - len = xdr_sizeof(smb_doorhdr_xdr, &opipe->p_hdr); - if (rc == -1 || len == 0) { - smb_opipe_exit(opipe); - return (ENOMEM); + /* + * If we filled the user's buffer, + * find out if there's more data. + */ + if (uio->uio_resid == 0) { + int rc2, nread, trval; + rc2 = ksocket_ioctl(sock, FIONREAD, (intptr_t)&nread, + &trval, ofile->f_cr); + if (rc2 == 0 && nread != 0) + rc = E2BIG; /* more data */ } - if ((rc = smb_opipe_door_call(opipe)) == 0) - rc = smb_event_wait(opipe->p_event); +out: + ksocket_rele(sock); - smb_opipe_exit(opipe); return (rc); } - -/* - * Named pipe I/O is serialized per fid to ensure that each request - * has exclusive opipe access for the duration of the request. - */ -static void -smb_opipe_enter(smb_opipe_t *opipe) -{ - mutex_enter(&opipe->p_mutex); - - while (opipe->p_busy) - cv_wait(&opipe->p_cv, &opipe->p_mutex); - - opipe->p_busy = 1; - mutex_exit(&opipe->p_mutex); -} - -/* - * Exit busy state. If we have exec'd an RPC, we may have - * to wait for notification that processing has completed. - */ -static void -smb_opipe_exit(smb_opipe_t *opipe) -{ - mutex_enter(&opipe->p_mutex); - opipe->p_busy = 0; - cv_signal(&opipe->p_cv); - mutex_exit(&opipe->p_mutex); -} diff --git a/usr/src/uts/common/fs/smbsrv/smb_opipe_door.c b/usr/src/uts/common/fs/smbsrv/smb_opipe_door.c deleted file mode 100644 index 6dc1b8c007..0000000000 --- a/usr/src/uts/common/fs/smbsrv/smb_opipe_door.c +++ /dev/null @@ -1,188 +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 2011 Nexenta Systems, Inc. All rights reserved. - * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - */ - -/* - * This module provides the interface to the opipe door. - * (used by the NDR RPC services). - */ - -#include <sys/stat.h> -#include <sys/door.h> -#include <sys/door_data.h> -#include <sys/uio.h> -#include <sys/ksynch.h> -#include <smbsrv/smb_kproto.h> -#include <smbsrv/smb_xdr.h> - -#ifdef _FAKE_KERNEL -#error "See libfksmbsrv" -#endif /* _FAKE_KERNEL */ - -static int smb_opipe_door_upcall(smb_opipe_t *); - -/* - * opipe door client (to user space door server). - */ -void -smb_opipe_door_init(smb_server_t *sv) -{ - sv->sv_opipe_door_id = -1; - mutex_init(&sv->sv_opipe_door_mutex, NULL, MUTEX_DEFAULT, NULL); - cv_init(&sv->sv_opipe_door_cv, NULL, CV_DEFAULT, NULL); -} - -void -smb_opipe_door_fini(smb_server_t *sv) -{ - smb_opipe_door_close(sv); - cv_destroy(&sv->sv_opipe_door_cv); - mutex_destroy(&sv->sv_opipe_door_mutex); -} - -/* - * Open the (user space) door. If the door is already open, - * close it first because the door-id has probably changed. - */ -int -smb_opipe_door_open(smb_server_t *sv, int door_id) -{ - smb_opipe_door_close(sv); - - mutex_enter(&sv->sv_opipe_door_mutex); - sv->sv_opipe_door_ncall = 0; - - if (sv->sv_opipe_door_hd == NULL) { - sv->sv_opipe_door_id = door_id; - sv->sv_opipe_door_hd = door_ki_lookup(door_id); - } - - mutex_exit(&sv->sv_opipe_door_mutex); - return ((sv->sv_opipe_door_hd == NULL) ? -1 : 0); -} - -/* - * Close the (user space) door. - */ -void -smb_opipe_door_close(smb_server_t *sv) -{ - mutex_enter(&sv->sv_opipe_door_mutex); - - if (sv->sv_opipe_door_hd != NULL) { - while (sv->sv_opipe_door_ncall > 0) - cv_wait(&sv->sv_opipe_door_cv, - &sv->sv_opipe_door_mutex); - - door_ki_rele(sv->sv_opipe_door_hd); - sv->sv_opipe_door_hd = NULL; - } - - mutex_exit(&sv->sv_opipe_door_mutex); -} - -/* - * opipe door call interface. - * Door serialization and call reference accounting is handled here. - */ -int -smb_opipe_door_call(smb_opipe_t *opipe) -{ - int rc; - smb_server_t *sv = opipe->p_server; - - mutex_enter(&sv->sv_opipe_door_mutex); - - if (sv->sv_opipe_door_hd == NULL) { - mutex_exit(&sv->sv_opipe_door_mutex); - - if (smb_opipe_door_open(sv, sv->sv_opipe_door_id) != 0) - return (-1); - - mutex_enter(&sv->sv_opipe_door_mutex); - } - - sv->sv_opipe_door_ncall++; - mutex_exit(&sv->sv_opipe_door_mutex); - - rc = smb_opipe_door_upcall(opipe); - - mutex_enter(&sv->sv_opipe_door_mutex); - if ((--sv->sv_opipe_door_ncall) == 0) - cv_signal(&sv->sv_opipe_door_cv); - mutex_exit(&sv->sv_opipe_door_mutex); - return (rc); -} - -/* - * Door upcall wrapper - handles data marshalling. - * This function should only be called by smb_opipe_door_call. - */ -static int -smb_opipe_door_upcall(smb_opipe_t *opipe) -{ - smb_server_t *sv = opipe->p_server; - door_arg_t da; - smb_doorhdr_t hdr; - int i; - int rc; - - da.data_ptr = (char *)opipe->p_doorbuf; - da.data_size = SMB_OPIPE_DOOR_BUFSIZE; - da.desc_ptr = NULL; - da.desc_num = 0; - da.rbuf = (char *)opipe->p_doorbuf; - da.rsize = SMB_OPIPE_DOOR_BUFSIZE; - - for (i = 0; i < 3; ++i) { - if (smb_server_is_stopping(sv)) - return (-1); - - if ((rc = door_ki_upcall_limited(sv->sv_opipe_door_hd, - &da, NULL, SIZE_MAX, 0)) == 0) - break; - - if (rc != EAGAIN && rc != EINTR) - return (-1); - } - - /* Check for door_return(NULL, 0, NULL, 0) */ - if (rc != 0 || da.data_size == 0 || da.rsize == 0) - return (-1); - - if (smb_doorhdr_decode(&hdr, (uint8_t *)da.data_ptr, da.rsize) == -1) - return (-1); - - if ((hdr.dh_magic != SMB_OPIPE_HDR_MAGIC) || - (hdr.dh_fid != opipe->p_hdr.dh_fid) || - (hdr.dh_op != opipe->p_hdr.dh_op) || - (hdr.dh_door_rc != 0) || - (hdr.dh_datalen > SMB_OPIPE_DOOR_BUFSIZE)) { - return (-1); - } - - opipe->p_hdr.dh_datalen = hdr.dh_datalen; - opipe->p_hdr.dh_resid = hdr.dh_resid; - return (0); -} diff --git a/usr/src/uts/common/fs/smbsrv/smb_read.c b/usr/src/uts/common/fs/smbsrv/smb_read.c index c5db79c227..2ac137ca10 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_read.c +++ b/usr/src/uts/common/fs/smbsrv/smb_read.c @@ -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 <smbsrv/smb_kproto.h> @@ -420,7 +421,14 @@ smb_common_read(smb_request_t *sr, smb_rw_param_t *param) break; case STYPE_IPC: + sr->raw_data.max_bytes = vdb->vdb_uio.uio_resid; + top = smb_mbuf_allocate(&vdb->vdb_uio); + rc = smb_opipe_read(sr, &vdb->vdb_uio); + + sr->raw_data.max_bytes -= vdb->vdb_uio.uio_resid; + smb_mbuf_trim(top, sr->raw_data.max_bytes); + MBC_ATTACH_MBUF(&sr->raw_data, top); break; default: diff --git a/usr/src/uts/common/fs/smbsrv/smb_server.c b/usr/src/uts/common/fs/smbsrv/smb_server.c index 2ac120c137..94eeb396dd 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_server.c +++ b/usr/src/uts/common/fs/smbsrv/smb_server.c @@ -404,9 +404,6 @@ smb_server_create(void) cv_init(&sv->sv_cv, NULL, CV_DEFAULT, NULL); cv_init(&sv->sp_info.sp_cv, NULL, CV_DEFAULT, NULL); - smb_llist_constructor(&sv->sv_opipe_list, sizeof (smb_opipe_t), - offsetof(smb_opipe_t, p_lnd)); - smb_llist_constructor(&sv->sv_event_list, sizeof (smb_event_t), offsetof(smb_event_t, se_lnd)); @@ -426,7 +423,6 @@ smb_server_create(void) smb_kdoor_init(sv); smb_kshare_init(sv); - smb_opipe_door_init(sv); smb_server_kstat_init(sv); smb_threshold_init(&sv->sv_ssetup_ct, SMB_SSETUP_CMD, @@ -502,10 +498,8 @@ smb_server_delete(void) smb_server_listener_destroy(&sv->sv_tcp_daemon); rw_destroy(&sv->sv_cfg_lock); smb_server_kstat_fini(sv); - smb_opipe_door_fini(sv); smb_kshare_fini(sv); smb_kdoor_fini(sv); - smb_llist_destructor(&sv->sv_opipe_list); smb_llist_destructor(&sv->sv_event_list); kmem_free(sv->sv_disp_stats, @@ -619,14 +613,9 @@ smb_server_start(smb_ioc_start_t *ioc) cmn_err(CE_WARN, "Cannot open smbd door"); break; } - if (rc = smb_opipe_door_open(sv, ioc->opipe)) { - cmn_err(CE_WARN, "Cannot open opipe door"); - break; - } #else /* _KERNEL */ /* Fake kernel does not use the kshare_door */ fksmb_kdoor_open(sv, ioc->udoor_func); - fksmb_opipe_door_open(sv, ioc->opipe_func); #endif /* _KERNEL */ if (rc = smb_thread_start(&sv->si_thread_timers)) @@ -1413,7 +1402,6 @@ smb_server_shutdown(smb_server_t *sv) smb_threshold_wake_all(&sv->sv_tcon_ct); smb_threshold_wake_all(&sv->sv_opipe_ct); - smb_opipe_door_close(sv); smb_kdoor_close(sv); #ifdef _KERNEL smb_kshare_door_fini(sv->sv_lmshrd); diff --git a/usr/src/uts/common/smbsrv/ndr.h b/usr/src/uts/common/smbsrv/ndr.h index 3652c2e0b7..584c0798e4 100644 --- a/usr/src/uts/common/smbsrv/ndr.h +++ b/usr/src/uts/common/smbsrv/ndr.h @@ -234,20 +234,6 @@ typedef struct ndr_stream_ops { #define NDS_RESET(NDS) (*(NDS)->ndo->ndo_reset)(NDS) #define NDS_DESTRUCT(NDS) (*(NDS)->ndo->ndo_destruct)(NDS) -typedef struct ndr_frag { - struct ndr_frag *next; - uint8_t *buf; - uint32_t len; -} ndr_frag_t; - -typedef struct ndr_fraglist { - struct uio uio; - iovec_t *iov; - ndr_frag_t *head; - ndr_frag_t *tail; - uint32_t nfrag; -} ndr_fraglist_t; - typedef struct ndr_stream { unsigned long pdu_size; unsigned long pdu_max_size; @@ -255,7 +241,6 @@ typedef struct ndr_stream { unsigned long pdu_scan_offset; unsigned char *pdu_base_addr; - ndr_fraglist_t frags; ndr_stream_ops_t *ndo; unsigned char m_op; diff --git a/usr/src/uts/common/smbsrv/smb_kproto.h b/usr/src/uts/common/smbsrv/smb_kproto.h index 879b21cdba..122f841a5e 100644 --- a/usr/src/uts/common/smbsrv/smb_kproto.h +++ b/usr/src/uts/common/smbsrv/smb_kproto.h @@ -375,11 +375,9 @@ int smb_net_txr_send(ksocket_t, smb_txlst_t *, smb_txreq_t *); /* * SMB RPC interface */ -smb_opipe_t *smb_opipe_alloc(smb_server_t *); void smb_opipe_dealloc(smb_opipe_t *); -int smb_opipe_open(smb_request_t *); +int smb_opipe_open(smb_request_t *, uint32_t); void smb_opipe_close(smb_ofile_t *); -smb_sdrc_t smb_opipe_transact(smb_request_t *, struct uio *); int smb_opipe_read(smb_request_t *, struct uio *); int smb_opipe_write(smb_request_t *, struct uio *); @@ -598,7 +596,7 @@ void smb_request_free(smb_request_t *); smb_ofile_t *smb_ofile_lookup_by_fid(smb_request_t *, uint16_t); smb_ofile_t *smb_ofile_lookup_by_uniqid(smb_tree_t *, uint32_t); boolean_t smb_ofile_disallow_fclose(smb_ofile_t *); -smb_ofile_t *smb_ofile_open(smb_request_t *, smb_node_t *, uint16_t, +smb_ofile_t *smb_ofile_open(smb_request_t *, smb_node_t *, smb_arg_open_t *, uint16_t, uint32_t, smb_error_t *); void smb_ofile_close(smb_ofile_t *, int32_t); void smb_ofile_delete(void *); diff --git a/usr/src/uts/common/smbsrv/smb_ktypes.h b/usr/src/uts/common/smbsrv/smb_ktypes.h index 74e2b9f437..64f96db9bd 100644 --- a/usr/src/uts/common/smbsrv/smb_ktypes.h +++ b/usr/src/uts/common/smbsrv/smb_ktypes.h @@ -1132,6 +1132,7 @@ typedef struct smb_tree { #define SMB_OPIPE_MAGIC 0x50495045 /* 'PIPE' */ #define SMB_OPIPE_VALID(p) \ ASSERT(((p) != NULL) && (p)->p_magic == SMB_OPIPE_MAGIC) +#define SMB_OPIPE_MAXNAME 32 /* * Data structure for SMB_FTYPE_MESG_PIPE ofiles, which is used @@ -1139,17 +1140,14 @@ typedef struct smb_tree { */ typedef struct smb_opipe { uint32_t p_magic; - list_node_t p_lnd; kmutex_t p_mutex; kcondvar_t p_cv; + struct smb_ofile *p_ofile; struct smb_server *p_server; - struct smb_event *p_event; - char *p_name; - uint32_t p_busy; - smb_doorhdr_t p_hdr; - smb_netuserinfo_t p_user; - uint8_t *p_doorbuf; - uint8_t *p_data; + uint32_t p_refcnt; + ksocket_t p_socket; + /* This is the "flat" name, without path prefix */ + char p_name[SMB_OPIPE_MAXNAME]; } smb_opipe_t; /* @@ -1439,8 +1437,8 @@ typedef struct open_param { uint64_t fileid; uint32_t rootdirfid; smb_ofile_t *dir; - /* This is only set by NTTransactCreate */ - struct smb_sd *sd; + smb_opipe_t *pipe; /* for smb_opipe_open */ + struct smb_sd *sd; /* for NTTransactCreate */ uint8_t op_oplock_level; /* requested/granted level */ boolean_t op_oplock_levelII; /* TRUE if levelII supported */ } smb_arg_open_t; @@ -1642,6 +1640,14 @@ typedef struct smb_request { struct smb_ofile *fid_ofile; smb_user_t *uid_user; + cred_t *user_cr; + kthread_t *sr_worker; + hrtime_t sr_time_submitted; + hrtime_t sr_time_active; + hrtime_t sr_time_start; + int32_t sr_txb; + uint32_t sr_seqnum; + union { smb_arg_negotiate_t *negprot; smb_arg_sessionsetup_t *ssetup; @@ -1651,14 +1657,6 @@ typedef struct smb_request { smb_rw_param_t *rw; int32_t timestamp; } arg; - - cred_t *user_cr; - kthread_t *sr_worker; - hrtime_t sr_time_submitted; - hrtime_t sr_time_active; - hrtime_t sr_time_start; - int32_t sr_txb; - uint32_t sr_seqnum; } smb_request_t; #define sr_ssetup arg.ssetup @@ -1859,13 +1857,6 @@ typedef struct smb_server { kmutex_t sv_kdoor_mutex; kcondvar_t sv_kdoor_cv; - /* RPC pipes (client side) */ - struct __door_handle *sv_opipe_door_hd; - int sv_opipe_door_id; - uint64_t sv_opipe_door_ncall; - kmutex_t sv_opipe_door_mutex; - kcondvar_t sv_opipe_door_cv; - int32_t si_gmtoff; smb_thread_t si_thread_timers; diff --git a/usr/src/uts/common/smbsrv/smb_share.h b/usr/src/uts/common/smbsrv/smb_share.h index 2ca97856c0..319be08801 100644 --- a/usr/src/uts/common/smbsrv/smb_share.h +++ b/usr/src/uts/common/smbsrv/smb_share.h @@ -47,6 +47,9 @@ extern "C" { #define SMB_SYSTEM32 SMB_SYSROOT "/system32" #define SMB_VSS SMB_SYSTEM32 "/vss" +/* Exported named pipes are in... */ +#define SMB_PIPE_DIR "/var/smb/pipe" + /* * Share Properties: * diff --git a/usr/src/uts/common/smbsrv/smb_xdr.h b/usr/src/uts/common/smbsrv/smb_xdr.h index 8990884b19..e4df610969 100644 --- a/usr/src/uts/common/smbsrv/smb_xdr.h +++ b/usr/src/uts/common/smbsrv/smb_xdr.h @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ #ifndef _SMBSRV_SMB_XDR_H @@ -60,21 +60,27 @@ typedef struct smb_string { struct smb_buf32; -#define SMB_OPIPE_HDR_MAGIC 0x4F484452 /* OHDR */ -#define SMB_OPIPE_DOOR_BUFSIZE (30 * 1024) +/* + * Initial message on server named pipes. + * Followed by smb_netuserinfo + */ +typedef struct smb_pipehdr { + uint32_t ph_magic; + uint32_t ph_uilen; +} smb_pipehdr_t; + +#define SMB_PIPE_HDR_MAGIC 0x50495045 /* PIPE */ /* - * Door operations for opipes. + * Maximum message size for SMB named pipes. + * Should be less than PIPE_BUF (5120). + * Use the same value Windows does. + */ +#define SMB_PIPE_MAX_MSGSIZE 4280 + +/* + * Door up-call stuff shared with smbd */ -typedef enum { - SMB_OPIPE_NULL = 0, - SMB_OPIPE_LOOKUP, - SMB_OPIPE_OPEN, - SMB_OPIPE_CLOSE, - SMB_OPIPE_READ, - SMB_OPIPE_WRITE, - SMB_OPIPE_EXEC -} smb_opipe_op_t; #define SMB_DOOR_HDR_MAGIC 0x444F4F52 /* DOOR */ |