summaryrefslogtreecommitdiffstats
path: root/source3/printing/nt_printing.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/printing/nt_printing.c')
-rw-r--r--source3/printing/nt_printing.c2419
1 files changed, 2419 insertions, 0 deletions
diff --git a/source3/printing/nt_printing.c b/source3/printing/nt_printing.c
new file mode 100644
index 0000000..7907fb4
--- /dev/null
+++ b/source3/printing/nt_printing.c
@@ -0,0 +1,2419 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-2000,
+ * Copyright (C) Jean François Micouleau 1998-2000.
+ * Copyright (C) Gerald Carter 2002-2005.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "printing/nt_printing_tdb.h"
+#include "printing/queue_process.h"
+#include "../librpc/gen_ndr/ndr_spoolss.h"
+#include "rpc_server/spoolss/srv_spoolss_util.h"
+#include "nt_printing.h"
+#include "secrets.h"
+#include "../librpc/gen_ndr/netlogon.h"
+#include "../libcli/security/security.h"
+#include "passdb/machine_sid.h"
+#include "smbd/smbd.h"
+#include "auth.h"
+#include "messages.h"
+#include "rpc_server/spoolss/srv_spoolss_nt.h"
+#include "rpc_client/cli_winreg_spoolss.h"
+#include "lib/util/string_wrappers.h"
+#include "lib/global_contexts.h"
+
+/* Map generic permissions to printer object specific permissions */
+
+const struct generic_mapping printer_generic_mapping = {
+ PRINTER_READ,
+ PRINTER_WRITE,
+ PRINTER_EXECUTE,
+ PRINTER_ALL_ACCESS
+};
+
+/* Map generic permissions to print server object specific permissions */
+
+const struct generic_mapping printserver_generic_mapping = {
+ SERVER_READ,
+ SERVER_WRITE,
+ SERVER_EXECUTE,
+ SERVER_ALL_ACCESS
+};
+
+/* Map generic permissions to job object specific permissions */
+
+const struct generic_mapping job_generic_mapping = {
+ JOB_READ,
+ JOB_WRITE,
+ JOB_EXECUTE,
+ JOB_ALL_ACCESS
+};
+
+static bool print_driver_directories_init(void)
+{
+ int service;
+ size_t i;
+ char *driver_path;
+ bool ok;
+ TALLOC_CTX *mem_ctx = talloc_stackframe();
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+
+ const char *dir_list[] = {
+ "W32X86/PCC",
+ "x64/PCC",
+ "ARM64",
+ "color"
+ };
+
+ service = lp_servicenumber("print$");
+ if (service < 0) {
+ /* We don't have a print$ share */
+ DEBUG(5, ("No print$ share has been configured.\n"));
+ talloc_free(mem_ctx);
+ return true;
+ }
+
+ driver_path = lp_path(mem_ctx, lp_sub, service);
+ if (driver_path == NULL) {
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ ok = directory_create_or_exist(driver_path, 0755);
+ if (!ok) {
+ DEBUG(1, ("Failed to create printer driver directory %s\n",
+ driver_path));
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ for (i = 0; archi_table[i].long_archi != NULL; i++) {
+ const char *arch_path;
+
+ arch_path = talloc_asprintf(mem_ctx,
+ "%s/%s",
+ driver_path,
+ archi_table[i].short_archi);
+ if (arch_path == NULL) {
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ ok = directory_create_or_exist(arch_path, 0755);
+ if (!ok) {
+ DEBUG(1, ("Failed to create printer driver "
+ "architecture directory %s\n",
+ arch_path));
+ talloc_free(mem_ctx);
+ return false;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(dir_list); i++) {
+ const char *path;
+
+ path = talloc_asprintf(mem_ctx,
+ "%s/%s",
+ driver_path,
+ dir_list[i]);
+ if (path == NULL) {
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ ok = directory_create_or_exist(path, 0755);
+ if (!ok) {
+ DEBUG(1, ("Failed to create printer driver "
+ "architecture directory %s\n",
+ path));
+ talloc_free(mem_ctx);
+ return false;
+ }
+ }
+
+ driver_path = state_path(talloc_tos(), "DriverStore");
+ if (driver_path == NULL) {
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ ok = directory_create_or_exist(driver_path, 0755);
+ if (!ok) {
+ DEBUG(1,("failed to create path %s\n", driver_path));
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ driver_path = state_path(talloc_tos(), "DriverStore/FileRepository");
+ if (driver_path == NULL) {
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ ok = directory_create_or_exist(driver_path, 0755);
+ if (!ok) {
+ DEBUG(1,("failed to create path %s\n", driver_path));
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ driver_path = state_path(talloc_tos(), "DriverStore/Temp");
+ if (driver_path == NULL) {
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ ok = directory_create_or_exist(driver_path, 0755);
+ if (!ok) {
+ DEBUG(1,("failed to create path %s\n", driver_path));
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ talloc_free(mem_ctx);
+ return true;
+}
+
+/****************************************************************************
+ Forward a MSG_PRINTER_DRVUPGRADE message from another smbd to the
+ background lpq updater.
+****************************************************************************/
+
+static void forward_drv_upgrade_printer_msg(struct messaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data)
+{
+ send_to_bgqd(msg, msg_type, data->data, data->length);
+}
+
+/****************************************************************************
+ Open the NT printing tdbs. Done once before fork().
+****************************************************************************/
+
+bool nt_printing_init(struct messaging_context *msg_ctx)
+{
+ WERROR win_rc;
+
+ if (!print_driver_directories_init()) {
+ return false;
+ }
+
+ if (!nt_printing_tdb_upgrade()) {
+ return false;
+ }
+
+ /*
+ * register callback to handle updating printers as new
+ * drivers are installed. Forwards to background lpq updater.
+ */
+ messaging_register(msg_ctx, NULL, MSG_PRINTER_DRVUPGRADE,
+ forward_drv_upgrade_printer_msg);
+
+ if ( lp_security() == SEC_ADS ) {
+ win_rc = check_published_printers(msg_ctx);
+ if (!W_ERROR_IS_OK(win_rc))
+ DEBUG(0, ("nt_printing_init: error checking published printers: %s\n", win_errstr(win_rc)));
+ }
+
+ return true;
+}
+
+/*******************************************************************
+ Function to allow filename parsing "the old way".
+********************************************************************/
+
+static NTSTATUS driver_unix_convert(connection_struct *conn,
+ const char *old_name,
+ struct files_struct **pdirfsp,
+ struct smb_filename **psmb_fname)
+{
+ NTSTATUS status;
+ TALLOC_CTX *ctx = talloc_tos();
+ char *name = talloc_strdup(ctx, old_name);
+
+ if (!name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ unix_format(name);
+ name = unix_clean_name(ctx, name);
+ if (!name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ trim_string(name,"/","/");
+
+ status = filename_convert_dirfsp(ctx,
+ conn,
+ name,
+ 0, /* ucf_flags */
+ 0, /* twrp */
+ pdirfsp,
+ psmb_fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Function to do the mapping between the long architecture name and
+ the short one.
+****************************************************************************/
+
+const char *get_short_archi(const char *long_archi)
+{
+ int i=-1;
+
+ DEBUG(107,("Getting architecture dependent directory\n"));
+ do {
+ i++;
+ } while ( (archi_table[i].long_archi!=NULL ) &&
+ strcasecmp_m(long_archi, archi_table[i].long_archi) );
+
+ if (archi_table[i].long_archi==NULL) {
+ DEBUGADD(10,("Unknown architecture [%s] !\n", long_archi));
+ return NULL;
+ }
+
+ /* this might be client code - but shouldn't this be an fstrcpy etc? */
+
+ DEBUGADD(108,("index: [%d]\n", i));
+ DEBUGADD(108,("long architecture: [%s]\n", archi_table[i].long_archi));
+ DEBUGADD(108,("short architecture: [%s]\n", archi_table[i].short_archi));
+
+ return archi_table[i].short_archi;
+}
+
+/****************************************************************************
+ Read data from fsp on the vfs.
+****************************************************************************/
+
+static ssize_t printing_pread_data(files_struct *fsp,
+ char *buf,
+ off_t *poff,
+ size_t byte_count)
+{
+ size_t total=0;
+ off_t in_pos = *poff;
+
+ /* Don't allow integer wrap on read. */
+ if (in_pos + byte_count < in_pos) {
+ return -1;
+ }
+
+ while (total < byte_count) {
+ ssize_t ret = read_file(fsp,
+ buf + total,
+ in_pos,
+ byte_count - total);
+
+ if (ret == 0) {
+ *poff = in_pos;
+ return total;
+ }
+ if (ret == -1) {
+ if (errno == EINTR) {
+ continue;
+ } else {
+ return -1;
+ }
+ }
+ in_pos += ret;
+ total += ret;
+ }
+ *poff = in_pos;
+ return (ssize_t)total;
+}
+
+/****************************************************************************
+ Detect the major and minor version of a PE file.
+ Returns:
+
+ 1 if file is a PE file and we got version numbers,
+ 0 if this file is a PE file and we couldn't get the version numbers,
+ -1 on error.
+
+ NB. buf is passed into and freed inside this function. This is a
+ bad API design, but fixing this is a task for another day.
+****************************************************************************/
+
+static int handle_pe_file(files_struct *fsp,
+ off_t in_pos,
+ char *fname,
+ char *buf,
+ uint32_t *major,
+ uint32_t *minor)
+{
+ unsigned int i;
+ unsigned int num_sections;
+ unsigned int section_table_bytes;
+ ssize_t byte_count;
+ off_t rel_pos;
+ int ret = -1;
+
+ /* Just skip over optional header to get to section table */
+ rel_pos = SVAL(buf,PE_HEADER_OPTIONAL_HEADER_SIZE)-
+ (NE_HEADER_SIZE-PE_HEADER_SIZE);
+
+ if (in_pos + rel_pos < in_pos) {
+ /* Integer wrap. */
+ goto out;
+ }
+ in_pos = rel_pos + in_pos;
+
+ /* get the section table */
+ num_sections = SVAL(buf,PE_HEADER_NUMBER_OF_SECTIONS);
+
+ if (num_sections >= (UINT_MAX / PE_HEADER_SECT_HEADER_SIZE)) {
+ /* Integer wrap. */
+ goto out;
+ }
+
+ section_table_bytes = num_sections * PE_HEADER_SECT_HEADER_SIZE;
+ if (section_table_bytes == 0) {
+ goto out;
+ }
+
+ SAFE_FREE(buf);
+ buf = (char *)SMB_MALLOC(section_table_bytes);
+ if (buf == NULL) {
+ DBG_ERR("PE file [%s] section table malloc "
+ "failed bytes = %d\n",
+ fname,
+ section_table_bytes);
+ goto out;
+ }
+
+ byte_count = printing_pread_data(fsp, buf, &in_pos, section_table_bytes);
+ if (byte_count < section_table_bytes) {
+ DBG_NOTICE("PE file [%s] Section header too short, "
+ "bytes read = %lu\n",
+ fname,
+ (unsigned long)byte_count);
+ goto out;
+ }
+
+ /*
+ * Iterate the section table looking for
+ * the resource section ".rsrc"
+ */
+ for (i = 0; i < num_sections; i++) {
+ int sec_offset = i * PE_HEADER_SECT_HEADER_SIZE;
+
+ if (strcmp(".rsrc",
+ &buf[sec_offset+ PE_HEADER_SECT_NAME_OFFSET]) == 0) {
+ unsigned int section_pos = IVAL(buf,
+ sec_offset+
+ PE_HEADER_SECT_PTR_DATA_OFFSET);
+ unsigned int section_bytes = IVAL(buf,
+ sec_offset+
+ PE_HEADER_SECT_SIZE_DATA_OFFSET);
+
+ if (section_bytes == 0) {
+ goto out;
+ }
+
+ SAFE_FREE(buf);
+ buf=(char *)SMB_MALLOC(section_bytes);
+ if (buf == NULL) {
+ DBG_ERR("PE file [%s] version malloc "
+ "failed bytes = %d\n",
+ fname,
+ section_bytes);
+ goto out;
+ }
+
+ /*
+ * Read from the start of the .rsrc
+ * section info
+ */
+ in_pos = section_pos;
+
+ byte_count = printing_pread_data(fsp,
+ buf,
+ &in_pos,
+ section_bytes);
+ if (byte_count < section_bytes) {
+ DBG_NOTICE("PE file "
+ "[%s] .rsrc section too short, "
+ "bytes read = %lu\n",
+ fname,
+ (unsigned long)byte_count);
+ goto out;
+ }
+
+ if (section_bytes < VS_VERSION_INFO_UNICODE_SIZE) {
+ goto out;
+ }
+
+ for (i=0;
+ i< section_bytes - VS_VERSION_INFO_UNICODE_SIZE;
+ i++) {
+ /*
+ * Scan for 1st 3 unicoded bytes
+ * followed by word aligned magic
+ * value.
+ */
+ int mpos;
+ bool magic_match = false;
+
+ if (buf[i] == 'V' &&
+ buf[i+1] == '\0' &&
+ buf[i+2] == 'S') {
+ magic_match = true;
+ }
+
+ if (magic_match == false) {
+ continue;
+ }
+
+ /* Align to next long address */
+ mpos = (i + sizeof(VS_SIGNATURE)*2 +
+ 3) & 0xfffffffc;
+
+ if (IVAL(buf,mpos) == VS_MAGIC_VALUE) {
+ *major = IVAL(buf,
+ mpos+ VS_MAJOR_OFFSET);
+ *minor = IVAL(buf,
+ mpos+ VS_MINOR_OFFSET);
+
+ DBG_INFO("PE file [%s] Version = "
+ "%08x:%08x (%d.%d.%d.%d)\n",
+ fname,
+ *major,
+ *minor,
+ (*major>>16)&0xffff,
+ *major&0xffff,
+ (*minor>>16)&0xffff,
+ *minor&0xffff);
+ ret = 1;
+ goto out;
+ }
+ }
+ }
+ }
+
+ /* Version info not found, fall back to origin date/time */
+ DBG_DEBUG("PE file [%s] has no version info\n", fname);
+ ret = 0;
+
+ out:
+
+ SAFE_FREE(buf);
+ return ret;
+}
+
+/****************************************************************************
+ Detect the major and minor version of an NE file.
+ Returns:
+
+ 1 if file is an NE file and we got version numbers,
+ 0 if this file is an NE file and we couldn't get the version numbers,
+ -1 on error.
+
+ NB. buf is passed into and freed inside this function. This is a
+ bad API design, but fixing this is a task for another day.
+****************************************************************************/
+
+static int handle_ne_file(files_struct *fsp,
+ off_t in_pos,
+ char *fname,
+ char *buf,
+ uint32_t *major,
+ uint32_t *minor)
+{
+ unsigned int i;
+ ssize_t byte_count;
+ int ret = -1;
+
+ if (CVAL(buf,NE_HEADER_TARGET_OS_OFFSET) != NE_HEADER_TARGOS_WIN ) {
+ DBG_NOTICE("NE file [%s] wrong target OS = 0x%x\n",
+ fname,
+ CVAL(buf,NE_HEADER_TARGET_OS_OFFSET));
+ /*
+ * At this point, we assume the file is in error.
+ * It still could be something else besides a NE file,
+ * but it unlikely at this point.
+ */
+ goto out;
+ }
+
+ /* Allocate a bit more space to speed up things */
+ SAFE_FREE(buf);
+ buf=(char *)SMB_MALLOC(VS_NE_BUF_SIZE);
+ if (buf == NULL) {
+ DBG_ERR("NE file [%s] malloc failed bytes = %d\n",
+ fname,
+ PE_HEADER_SIZE);
+ goto out;
+ }
+
+ /*
+ * This is a HACK! I got tired of trying to sort through the
+ * messy 'NE' file format. If anyone wants to clean this up
+ * please have at it, but this works. 'NE' files will
+ * eventually fade away. JRR
+ */
+ byte_count = printing_pread_data(fsp, buf, &in_pos, VS_NE_BUF_SIZE);
+ while (byte_count > 0) {
+ /*
+ * Cover case that should not occur in a well
+ * formed 'NE' .dll file
+ */
+ if (byte_count-VS_VERSION_INFO_SIZE <= 0) {
+ break;
+ }
+
+ for(i=0; i<byte_count; i++) {
+ /*
+ * Fast skip past data that can't
+ * possibly match
+ */
+ if (buf[i] != 'V') {
+ byte_count = printing_pread_data(fsp,
+ buf,
+ &in_pos,
+ VS_NE_BUF_SIZE);
+ continue;
+ }
+
+ /*
+ * Potential match data crosses buf boundary,
+ * move it to beginning of buf, and fill the
+ * buf with as much as it will hold.
+ */
+ if (i>byte_count-VS_VERSION_INFO_SIZE) {
+ ssize_t amount_read;
+ ssize_t amount_unused = byte_count-i;
+
+ memmove(buf, &buf[i], amount_unused);
+ amount_read = printing_pread_data(fsp,
+ &buf[amount_unused],
+ &in_pos,
+ VS_NE_BUF_SIZE- amount_unused);
+ if (amount_read < 0) {
+ DBG_ERR("NE file [%s] Read "
+ "error, errno=%d\n",
+ fname,
+ errno);
+ goto out;
+ }
+
+ if (amount_read + amount_unused <
+ amount_read) {
+ /* Check for integer wrap. */
+ break;
+ }
+
+ byte_count = amount_read +
+ amount_unused;
+ if (byte_count < VS_VERSION_INFO_SIZE) {
+ break;
+ }
+
+ i = 0;
+ }
+
+ /*
+ * Check that the full signature string and
+ * the magic number that follows exist (not
+ * a perfect solution, but the chances that this
+ * occurs in code is, well, remote. Yes I know
+ * I'm comparing the 'V' twice, as it is
+ * simpler to read the code.
+ */
+ if (strcmp(&buf[i], VS_SIGNATURE) == 0) {
+ /*
+ * Compute skip alignment to next
+ * long address.
+ */
+ off_t cpos = in_pos;
+ int skip = -(cpos - (byte_count - i) +
+ sizeof(VS_SIGNATURE)) & 3;
+ if (IVAL(buf,
+ i+sizeof(VS_SIGNATURE)+skip)
+ != 0xfeef04bd) {
+ byte_count = printing_pread_data(fsp,
+ buf,
+ &in_pos,
+ VS_NE_BUF_SIZE);
+ continue;
+ }
+
+ *major = IVAL(buf,
+ i+sizeof(VS_SIGNATURE)+
+ skip+VS_MAJOR_OFFSET);
+ *minor = IVAL(buf,
+ i+sizeof(VS_SIGNATURE)+
+ skip+VS_MINOR_OFFSET);
+ DBG_INFO("NE file [%s] Version "
+ "= %08x:%08x (%d.%d.%d.%d)\n",
+ fname,
+ *major,
+ *minor,
+ (*major>>16)&0xffff,
+ *major&0xffff,
+ (*minor>>16)&0xffff,
+ *minor&0xffff);
+ ret = 1;
+ goto out;
+ }
+ }
+ }
+
+ /* Version info not found, fall back to origin date/time */
+ DBG_ERR("NE file [%s] Version info not found\n", fname);
+ ret = 0;
+
+ out:
+
+ SAFE_FREE(buf);
+ return ret;
+}
+
+/****************************************************************************
+ Version information in Microsoft files is held in a VS_VERSION_INFO structure.
+ There are two case to be covered here: PE (Portable Executable) and NE (New
+ Executable) files. Both files support the same INFO structure, but PE files
+ store the signature in unicode, and NE files store it as !unicode.
+ returns -1 on error, 1 on version info found, and 0 on no version info found.
+****************************************************************************/
+
+static int get_file_version(files_struct *fsp,
+ char *fname,
+ uint32_t *major,
+ uint32_t *minor)
+{
+ char *buf = NULL;
+ ssize_t byte_count;
+ off_t in_pos = fh_get_pos(fsp->fh);
+
+ buf=(char *)SMB_MALLOC(DOS_HEADER_SIZE);
+ if (buf == NULL) {
+ DBG_ERR("PE file [%s] DOS Header malloc failed bytes = %d\n",
+ fname,
+ DOS_HEADER_SIZE);
+ goto error_exit;
+ }
+
+ byte_count = printing_pread_data(fsp, buf, &in_pos, DOS_HEADER_SIZE);
+ if (byte_count < DOS_HEADER_SIZE) {
+ DBG_NOTICE("File [%s] DOS header too short, bytes read = %lu\n",
+ fname,
+ (unsigned long)byte_count);
+ goto no_version_info;
+ }
+
+ /* Is this really a DOS header? */
+ if (SVAL(buf,DOS_HEADER_MAGIC_OFFSET) != DOS_HEADER_MAGIC) {
+ DBG_INFO("File [%s] bad DOS magic = 0x%x\n",
+ fname,
+ SVAL(buf,DOS_HEADER_MAGIC_OFFSET));
+ goto no_version_info;
+ }
+
+ /*
+ * Skip OEM header (if any) and the
+ * DOS stub to start of Windows header.
+ */
+ in_pos = SVAL(buf,DOS_HEADER_LFANEW_OFFSET);
+
+ /* Note: DOS_HEADER_SIZE and NE_HEADER_SIZE are incidentally same */
+ byte_count = printing_pread_data(fsp, buf, &in_pos, NE_HEADER_SIZE);
+ if (byte_count < NE_HEADER_SIZE) {
+ DBG_NOTICE("File [%s] Windows header too short, "
+ "bytes read = %lu\n",
+ fname,
+ (unsigned long)byte_count);
+ /*
+ * Assume this isn't an error...
+ * the file just looks sort of like a PE/NE file
+ */
+ goto no_version_info;
+ }
+
+ /*
+ * The header may be a PE (Portable Executable)
+ * or an NE (New Executable).
+ */
+ if (IVAL(buf,PE_HEADER_SIGNATURE_OFFSET) == PE_HEADER_SIGNATURE) {
+ return handle_pe_file(fsp,
+ in_pos,
+ fname,
+ buf,
+ major,
+ minor);
+ } else if (SVAL(buf,NE_HEADER_SIGNATURE_OFFSET) ==
+ NE_HEADER_SIGNATURE) {
+ return handle_ne_file(fsp,
+ in_pos,
+ fname,
+ buf,
+ major,
+ minor);
+ } else {
+ /*
+ * Assume this isn't an error... the file just
+ * looks sort of like a PE/NE file.
+ */
+ DBG_NOTICE("File [%s] unknown file format, signature = 0x%x\n",
+ fname,
+ IVAL(buf,PE_HEADER_SIGNATURE_OFFSET));
+ /* Fallthrough into no_version_info: */
+ }
+
+ no_version_info:
+ SAFE_FREE(buf);
+ return 0;
+
+ error_exit:
+ SAFE_FREE(buf);
+ return -1;
+}
+
+/****************************************************************************
+Drivers for Microsoft systems contain multiple files. Often, multiple drivers
+share one or more files. During the MS installation process files are checked
+to insure that only a newer version of a shared file is installed over an
+older version. There are several possibilities for this comparison. If there
+is no previous version, the new one is newer (obviously). If either file is
+missing the version info structure, compare the creation date (on Unix use
+the modification date). Otherwise chose the numerically larger version number.
+****************************************************************************/
+
+static int file_version_is_newer(connection_struct *conn, fstring new_file, fstring old_file)
+{
+ bool use_version = true;
+
+ uint32_t new_major;
+ uint32_t new_minor;
+ time_t new_create_time;
+
+ uint32_t old_major;
+ uint32_t old_minor;
+ time_t old_create_time;
+
+ struct smb_filename *smb_fname = NULL;
+ files_struct *fsp = NULL;
+ struct files_struct *dirfsp = NULL;
+ SMB_STRUCT_STAT st;
+
+ NTSTATUS status;
+ int ret;
+
+ SET_STAT_INVALID(st);
+ new_create_time = (time_t)0;
+ old_create_time = (time_t)0;
+
+ /* Get file version info (if available) for previous file (if it exists) */
+ status = driver_unix_convert(conn, old_file, &dirfsp, &smb_fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto error_exit;
+ }
+
+ status = openat_pathref_fsp(conn->cwd_fsp, smb_fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = 1;
+ goto done;
+ }
+
+ status = SMB_VFS_CREATE_FILE(
+ conn, /* conn */
+ NULL, /* req */
+ dirfsp, /* dirfsp */
+ smb_fname, /* fname */
+ FILE_GENERIC_READ, /* access_mask */
+ FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
+ FILE_OPEN, /* create_disposition*/
+ 0, /* create_options */
+ FILE_ATTRIBUTE_NORMAL, /* file_attributes */
+ INTERNAL_OPEN_ONLY, /* oplock_request */
+ NULL, /* lease */
+ 0, /* allocation_size */
+ 0, /* private_flags */
+ NULL, /* sd */
+ NULL, /* ea_list */
+ &fsp, /* result */
+ NULL, /* pinfo */
+ NULL, NULL); /* create context */
+
+ if (!NT_STATUS_IS_OK(status)) {
+ /* Old file not found, so by definition new file is in fact newer */
+ DEBUG(10,("file_version_is_newer: Can't open old file [%s], "
+ "errno = %d\n", smb_fname_str_dbg(smb_fname),
+ errno));
+ ret = 1;
+ goto done;
+
+ }
+
+ ret = get_file_version(fsp, old_file, &old_major, &old_minor);
+ if (ret == -1) {
+ goto error_exit;
+ }
+
+ if (!ret) {
+ DEBUG(6,("file_version_is_newer: Version info not found [%s], "
+ "use mod time\n",
+ old_file));
+ use_version = false;
+ if (SMB_VFS_FSTAT(fsp, &st) == -1) {
+ goto error_exit;
+ }
+ old_create_time = convert_timespec_to_time_t(st.st_ex_mtime);
+ DEBUGADD(6,("file_version_is_newer: mod time = %ld sec\n",
+ (long)old_create_time));
+ }
+
+ close_file_free(NULL, &fsp, NORMAL_CLOSE);
+
+ /* Get file version info (if available) for new file */
+ status = driver_unix_convert(conn, new_file, &dirfsp, &smb_fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto error_exit;
+ }
+
+ status = openat_pathref_fsp(conn->cwd_fsp, smb_fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_NOTICE("Can't open new file [%s], errno = %d\n",
+ smb_fname_str_dbg(smb_fname), errno);
+ goto error_exit;
+ }
+
+ status = SMB_VFS_CREATE_FILE(
+ conn, /* conn */
+ NULL, /* req */
+ dirfsp, /* dirfsp */
+ smb_fname, /* fname */
+ FILE_GENERIC_READ, /* access_mask */
+ FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
+ FILE_OPEN, /* create_disposition*/
+ 0, /* create_options */
+ FILE_ATTRIBUTE_NORMAL, /* file_attributes */
+ INTERNAL_OPEN_ONLY, /* oplock_request */
+ NULL, /* lease */
+ 0, /* allocation_size */
+ 0, /* private_flags */
+ NULL, /* sd */
+ NULL, /* ea_list */
+ &fsp, /* result */
+ NULL, /* pinfo */
+ NULL, NULL); /* create context */
+
+ if (!NT_STATUS_IS_OK(status)) {
+ /* New file not found, this shouldn't occur if the caller did its job */
+ DEBUG(3,("file_version_is_newer: Can't open new file [%s], "
+ "errno = %d\n", smb_fname_str_dbg(smb_fname), errno));
+ goto error_exit;
+
+ }
+
+ ret = get_file_version(fsp, new_file, &new_major, &new_minor);
+ if (ret == -1) {
+ goto error_exit;
+ }
+
+ if (!ret) {
+ DEBUG(6,("file_version_is_newer: Version info not found [%s], "
+ "use mod time\n",
+ new_file));
+ use_version = false;
+ if (SMB_VFS_FSTAT(fsp, &st) == -1) {
+ goto error_exit;
+ }
+ new_create_time = convert_timespec_to_time_t(st.st_ex_mtime);
+ DEBUGADD(6,("file_version_is_newer: mod time = %ld sec\n",
+ (long)new_create_time));
+ }
+
+ close_file_free(NULL, &fsp, NORMAL_CLOSE);
+
+ if (use_version && (new_major != old_major || new_minor != old_minor)) {
+ /* Compare versions and choose the larger version number */
+ if (new_major > old_major ||
+ (new_major == old_major && new_minor > old_minor)) {
+
+ DEBUG(6,("file_version_is_newer: Replacing [%s] with [%s]\n", old_file, new_file));
+ ret = 1;
+ goto done;
+ }
+ else {
+ DEBUG(6,("file_version_is_newer: Leaving [%s] unchanged\n", old_file));
+ ret = 0;
+ goto done;
+ }
+
+ } else {
+ /* Compare modification time/dates and choose the newest time/date */
+ if (new_create_time > old_create_time) {
+ DEBUG(6,("file_version_is_newer: Replacing [%s] with [%s]\n", old_file, new_file));
+ ret = 1;
+ goto done;
+ }
+ else {
+ DEBUG(6,("file_version_is_newer: Leaving [%s] unchanged\n", old_file));
+ ret = 0;
+ goto done;
+ }
+ }
+
+ error_exit:
+ if(fsp)
+ close_file_free(NULL, &fsp, NORMAL_CLOSE);
+ ret = -1;
+ done:
+ TALLOC_FREE(smb_fname);
+ return ret;
+}
+
+/****************************************************************************
+Determine the correct cVersion associated with an architecture and driver
+****************************************************************************/
+static uint32_t get_correct_cversion(const struct auth_session_info *session_info,
+ const char *architecture,
+ const char *driverpath_in,
+ const char *driver_directory,
+ WERROR *perr)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ int cversion = -1;
+ NTSTATUS nt_status;
+ struct smb_filename *smb_fname = NULL;
+ files_struct *fsp = NULL;
+ struct files_struct *dirfsp = NULL;
+ struct conn_struct_tos *c = NULL;
+ connection_struct *conn = NULL;
+ char *printdollar = NULL;
+ char *printdollar_path = NULL;
+ char *working_dir = NULL;
+ int printdollar_snum;
+ uint32_t major, minor;
+ int ret;
+
+ *perr = WERR_INVALID_PARAMETER;
+
+ /* If architecture is Windows 95/98/ME, the version is always 0. */
+ if (strcmp(architecture, SPL_ARCH_WIN40) == 0) {
+ DEBUG(10,("get_correct_cversion: Driver is Win9x, cversion = 0\n"));
+ *perr = WERR_OK;
+ TALLOC_FREE(frame);
+ return 0;
+ }
+
+ /* If architecture is Windows x64, the version is always 3. */
+ if (strcmp(architecture, SPL_ARCH_X64) == 0 ||
+ strcmp(architecture, SPL_ARCH_ARM64) == 0) {
+ DBG_DEBUG("get_correct_cversion: this architecture must be, cversion = 3\n");
+ *perr = WERR_OK;
+ TALLOC_FREE(frame);
+ return 3;
+ }
+
+ printdollar_snum = find_service(frame, "print$", &printdollar);
+ if (!printdollar) {
+ *perr = WERR_NOT_ENOUGH_MEMORY;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ if (printdollar_snum == -1) {
+ *perr = WERR_BAD_NET_NAME;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ printdollar_path = lp_path(frame, lp_sub, printdollar_snum);
+ if (printdollar_path == NULL) {
+ *perr = WERR_NOT_ENOUGH_MEMORY;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ working_dir = talloc_asprintf(frame,
+ "%s/%s",
+ printdollar_path,
+ architecture);
+ /*
+ * If the driver has been uploaded into a temorpary driver
+ * directory, switch to the driver directory.
+ */
+ if (driver_directory != NULL) {
+ working_dir = talloc_asprintf(frame, "%s/%s/%s",
+ printdollar_path,
+ architecture,
+ driver_directory);
+ }
+
+ nt_status = create_conn_struct_tos_cwd(global_messaging_context(),
+ printdollar_snum,
+ working_dir,
+ session_info,
+ &c);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0,("get_correct_cversion: create_conn_struct "
+ "returned %s\n", nt_errstr(nt_status)));
+ *perr = ntstatus_to_werror(nt_status);
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ conn = c->conn;
+
+ nt_status = set_conn_force_user_group(conn, printdollar_snum);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("failed set force user / group\n"));
+ *perr = ntstatus_to_werror(nt_status);
+ goto error_free_conn;
+ }
+
+ if (!become_user_without_service_by_session(conn, session_info)) {
+ DEBUG(0, ("failed to become user\n"));
+ *perr = WERR_ACCESS_DENIED;
+ goto error_free_conn;
+ }
+
+ /*
+ * We switch to the directory where the driver files are located,
+ * so only work on the file names
+ */
+ nt_status = driver_unix_convert(conn,
+ driverpath_in,
+ &dirfsp,
+ &smb_fname);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ *perr = ntstatus_to_werror(nt_status);
+ goto error_exit;
+ }
+
+ nt_status = vfs_file_exist(conn, smb_fname);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(3,("get_correct_cversion: vfs_file_exist failed\n"));
+ *perr = WERR_FILE_NOT_FOUND;
+ goto error_exit;
+ }
+
+ nt_status = openat_pathref_fsp(conn->cwd_fsp, smb_fname);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DBG_NOTICE("Can't open file [%s]: %s\n",
+ smb_fname_str_dbg(smb_fname),
+ nt_errstr(nt_status));
+ *perr = WERR_ACCESS_DENIED;
+ goto error_exit;
+ }
+
+ nt_status = SMB_VFS_CREATE_FILE(
+ conn, /* conn */
+ NULL, /* req */
+ dirfsp, /* dirfsp */
+ smb_fname, /* fname */
+ FILE_GENERIC_READ, /* access_mask */
+ FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
+ FILE_OPEN, /* create_disposition*/
+ 0, /* create_options */
+ FILE_ATTRIBUTE_NORMAL, /* file_attributes */
+ INTERNAL_OPEN_ONLY, /* oplock_request */
+ NULL, /* lease */
+ 0, /* private_flags */
+ 0, /* allocation_size */
+ NULL, /* sd */
+ NULL, /* ea_list */
+ &fsp, /* result */
+ NULL, /* pinfo */
+ NULL, NULL); /* create context */
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(3,("get_correct_cversion: Can't open file [%s], errno = "
+ "%d\n", smb_fname_str_dbg(smb_fname), errno));
+ *perr = WERR_ACCESS_DENIED;
+ goto error_exit;
+ }
+
+ ret = get_file_version(fsp, smb_fname->base_name, &major, &minor);
+ if (ret == -1) {
+ *perr = WERR_INVALID_PARAMETER;
+ goto error_exit;
+ } else if (!ret) {
+ DEBUG(6,("get_correct_cversion: Version info not "
+ "found [%s]\n",
+ smb_fname_str_dbg(smb_fname)));
+ *perr = WERR_INVALID_PARAMETER;
+ goto error_exit;
+ }
+
+ /*
+ * This is a Microsoft'ism. See references in MSDN to VER_FILEVERSION
+ * for more details. Version in this case is not just the version of the
+ * file, but the version in the sense of kernel mode (2) vs. user mode
+ * (3) drivers. Other bits of the version fields are the version info.
+ * JRR 010716
+ */
+ cversion = major & 0x0000ffff;
+ switch (cversion) {
+ case 2: /* WinNT drivers */
+ case 3: /* Win2K drivers */
+ break;
+
+ default:
+ DEBUG(6,("get_correct_cversion: cversion "
+ "invalid [%s] cversion = %d\n",
+ smb_fname_str_dbg(smb_fname),
+ cversion));
+ goto error_exit;
+ }
+
+ DEBUG(10,("get_correct_cversion: Version info found [%s] major"
+ " = 0x%x minor = 0x%x\n",
+ smb_fname_str_dbg(smb_fname), major, minor));
+
+ DEBUG(10,("get_correct_cversion: Driver file [%s] cversion = %d\n",
+ smb_fname_str_dbg(smb_fname), cversion));
+ *perr = WERR_OK;
+
+ error_exit:
+ unbecome_user_without_service();
+ error_free_conn:
+ if (fsp != NULL) {
+ close_file_free(NULL, &fsp, NORMAL_CLOSE);
+ }
+ if (!W_ERROR_IS_OK(*perr)) {
+ cversion = -1;
+ }
+
+ TALLOC_FREE(frame);
+ return cversion;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+#define strip_driver_path(_mem_ctx, _element) do { \
+ if (_element && ((_p = strrchr((_element), '\\')) != NULL)) { \
+ (_element) = talloc_asprintf((_mem_ctx), "%s", _p+1); \
+ W_ERROR_HAVE_NO_MEMORY((_element)); \
+ } \
+} while (0);
+
+static WERROR clean_up_driver_struct_level(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ const char *architecture,
+ const char **driver_path,
+ const char **data_file,
+ const char **config_file,
+ const char **help_file,
+ struct spoolss_StringArray *dependent_files,
+ enum spoolss_DriverOSVersion *version,
+ uint32_t flags,
+ const char **driver_directory)
+{
+ const char *short_architecture;
+ int i;
+ WERROR err;
+ char *_p;
+
+ if (!*driver_path || !*data_file) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (!strequal(architecture, SPOOLSS_ARCHITECTURE_4_0) && !*config_file) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (flags & APD_COPY_FROM_DIRECTORY) {
+ char *path;
+ char *q;
+
+ /*
+ * driver_path is set to:
+ *
+ * \\PRINTSRV\print$\x64\{279245b0-a8bd-4431-bf6f-baee92ac15c0}\pscript5.dll
+ */
+ path = talloc_strdup(mem_ctx, *driver_path);
+ if (path == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* Remove pscript5.dll */
+ q = strrchr_m(path, '\\');
+ if (q == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+ *q = '\0';
+
+ /* Get \{279245b0-a8bd-4431-bf6f-baee92ac15c0} */
+ q = strrchr_m(path, '\\');
+ if (q == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Set driver_directory to:
+ *
+ * {279245b0-a8bd-4431-bf6f-baee92ac15c0}
+ *
+ * This is the directory where all the files have been uploaded
+ */
+ *driver_directory = q + 1;
+ }
+
+ /* clean up the driver name.
+ * we can get .\driver.dll
+ * or worse c:\windows\system\driver.dll !
+ */
+ /* using an intermediate string to not have overlapping memcpy()'s */
+
+ strip_driver_path(mem_ctx, *driver_path);
+ strip_driver_path(mem_ctx, *data_file);
+ if (*config_file) {
+ strip_driver_path(mem_ctx, *config_file);
+ }
+ if (help_file) {
+ strip_driver_path(mem_ctx, *help_file);
+ }
+
+ if (dependent_files && dependent_files->string) {
+ for (i=0; dependent_files->string[i]; i++) {
+ strip_driver_path(mem_ctx, dependent_files->string[i]);
+ }
+ }
+
+ short_architecture = get_short_archi(architecture);
+ if (!short_architecture) {
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+ }
+
+ /* jfm:7/16/2000 the client always sends the cversion=0.
+ * The server should check which version the driver is by reading
+ * the PE header of driver->driverpath.
+ *
+ * For Windows 95/98 the version is 0 (so the value sent is correct)
+ * For Windows NT (the architecture doesn't matter)
+ * NT 3.1: cversion=0
+ * NT 3.5/3.51: cversion=1
+ * NT 4: cversion=2
+ * NT2K: cversion=3
+ */
+
+ *version = get_correct_cversion(session_info,
+ short_architecture,
+ *driver_path,
+ *driver_directory,
+ &err);
+ if (*version == -1) {
+ return err;
+ }
+
+ return WERR_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+WERROR clean_up_driver_struct(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ const struct spoolss_AddDriverInfoCtr *r,
+ uint32_t flags,
+ const char **driver_directory)
+{
+ switch (r->level) {
+ case 3:
+ return clean_up_driver_struct_level(mem_ctx, session_info,
+ r->info.info3->architecture,
+ &r->info.info3->driver_path,
+ &r->info.info3->data_file,
+ &r->info.info3->config_file,
+ &r->info.info3->help_file,
+ r->info.info3->dependent_files,
+ &r->info.info3->version,
+ flags,
+ driver_directory);
+ case 6:
+ return clean_up_driver_struct_level(mem_ctx, session_info,
+ r->info.info6->architecture,
+ &r->info.info6->driver_path,
+ &r->info.info6->data_file,
+ &r->info.info6->config_file,
+ &r->info.info6->help_file,
+ r->info.info6->dependent_files,
+ &r->info.info6->version,
+ flags,
+ driver_directory);
+ case 8:
+ return clean_up_driver_struct_level(mem_ctx, session_info,
+ r->info.info8->architecture,
+ &r->info.info8->driver_path,
+ &r->info.info8->data_file,
+ &r->info.info8->config_file,
+ &r->info.info8->help_file,
+ r->info.info8->dependent_files,
+ &r->info.info8->version,
+ flags,
+ driver_directory);
+ default:
+ return WERR_NOT_SUPPORTED;
+ }
+}
+
+/****************************************************************************
+ This function sucks and should be replaced. JRA.
+****************************************************************************/
+
+static void convert_level_6_to_level3(struct spoolss_AddDriverInfo3 *dst,
+ const struct spoolss_AddDriverInfo6 *src)
+{
+ dst->version = src->version;
+
+ dst->driver_name = src->driver_name;
+ dst->architecture = src->architecture;
+ dst->driver_path = src->driver_path;
+ dst->data_file = src->data_file;
+ dst->config_file = src->config_file;
+ dst->help_file = src->help_file;
+ dst->monitor_name = src->monitor_name;
+ dst->default_datatype = src->default_datatype;
+ dst->_ndr_size_dependent_files = src->_ndr_size_dependent_files;
+ dst->dependent_files = src->dependent_files;
+}
+
+static void convert_level_8_to_level3(struct spoolss_AddDriverInfo3 *dst,
+ const struct spoolss_AddDriverInfo8 *src)
+{
+ dst->version = src->version;
+
+ dst->driver_name = src->driver_name;
+ dst->architecture = src->architecture;
+ dst->driver_path = src->driver_path;
+ dst->data_file = src->data_file;
+ dst->config_file = src->config_file;
+ dst->help_file = src->help_file;
+ dst->monitor_name = src->monitor_name;
+ dst->default_datatype = src->default_datatype;
+ dst->_ndr_size_dependent_files = src->_ndr_size_dependent_files;
+ dst->dependent_files = src->dependent_files;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static WERROR move_driver_file_to_download_area(TALLOC_CTX *mem_ctx,
+ connection_struct *conn,
+ const char *driver_file,
+ const char *short_architecture,
+ uint32_t driver_version,
+ uint32_t version,
+ const char *driver_directory)
+{
+ struct smb_filename *smb_fname_old = NULL;
+ struct smb_filename *smb_fname_new = NULL;
+ char *old_name = NULL;
+ char *new_name = NULL;
+ NTSTATUS status;
+ WERROR ret;
+
+ if (driver_directory != NULL) {
+ old_name = talloc_asprintf(mem_ctx,
+ "%s/%s/%s",
+ short_architecture,
+ driver_directory,
+ driver_file);
+ } else {
+ old_name = talloc_asprintf(mem_ctx,
+ "%s/%s",
+ short_architecture,
+ driver_file);
+ }
+ if (old_name == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ new_name = talloc_asprintf(mem_ctx, "%s/%d/%s",
+ short_architecture, driver_version, driver_file);
+ if (new_name == NULL) {
+ TALLOC_FREE(old_name);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ if (version != -1 && (version = file_version_is_newer(conn, old_name, new_name)) > 0) {
+ struct files_struct *dirfsp = NULL;
+
+ status = driver_unix_convert(conn,
+ old_name,
+ &dirfsp,
+ &smb_fname_old);
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = WERR_NOT_ENOUGH_MEMORY;
+ goto out;
+ }
+
+ /* Setup a synthetic smb_filename struct */
+ smb_fname_new = talloc_zero(mem_ctx, struct smb_filename);
+ if (!smb_fname_new) {
+ ret = WERR_NOT_ENOUGH_MEMORY;
+ goto out;
+ }
+
+ smb_fname_new->base_name = new_name;
+
+ DEBUG(10,("move_driver_file_to_download_area: copying '%s' to "
+ "'%s'\n", smb_fname_old->base_name,
+ smb_fname_new->base_name));
+
+ status = copy_file(mem_ctx, conn, smb_fname_old, smb_fname_new,
+ FILE_OVERWRITE_IF);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("move_driver_file_to_download_area: Unable "
+ "to rename [%s] to [%s]: %s\n",
+ smb_fname_old->base_name, new_name,
+ nt_errstr(status)));
+ ret = WERR_APP_INIT_FAILURE;
+ goto out;
+ }
+ }
+
+ ret = WERR_OK;
+ out:
+ TALLOC_FREE(smb_fname_old);
+ TALLOC_FREE(smb_fname_new);
+ return ret;
+}
+
+WERROR move_driver_to_download_area(const struct auth_session_info *session_info,
+ const struct spoolss_AddDriverInfoCtr *r,
+ const char *driver_directory)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ struct spoolss_AddDriverInfo3 *driver;
+ struct spoolss_AddDriverInfo3 converted_driver;
+ const char *short_architecture;
+ struct files_struct *dirfsp = NULL;
+ struct smb_filename *smb_dname = NULL;
+ char *new_dir = NULL;
+ struct conn_struct_tos *c = NULL;
+ connection_struct *conn = NULL;
+ NTSTATUS nt_status;
+ int i;
+ int ver = 0;
+ char *printdollar = NULL;
+ int printdollar_snum;
+ WERROR err = WERR_OK;
+
+ switch (r->level) {
+ case 3:
+ driver = r->info.info3;
+ break;
+ case 6:
+ convert_level_6_to_level3(&converted_driver, r->info.info6);
+ driver = &converted_driver;
+ break;
+ case 8:
+ convert_level_8_to_level3(&converted_driver, r->info.info8);
+ driver = &converted_driver;
+ break;
+ default:
+ DEBUG(0,("move_driver_to_download_area: Unknown info level (%u)\n", (unsigned int)r->level));
+ TALLOC_FREE(frame);
+ return WERR_INVALID_LEVEL;
+ }
+
+ short_architecture = get_short_archi(driver->architecture);
+ if (!short_architecture) {
+ TALLOC_FREE(frame);
+ return WERR_UNKNOWN_PRINTER_DRIVER;
+ }
+
+ printdollar_snum = find_service(frame, "print$", &printdollar);
+ if (!printdollar) {
+ TALLOC_FREE(frame);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ if (printdollar_snum == -1) {
+ TALLOC_FREE(frame);
+ return WERR_BAD_NET_NAME;
+ }
+
+ nt_status = create_conn_struct_tos_cwd(global_messaging_context(),
+ printdollar_snum,
+ lp_path(frame, lp_sub, printdollar_snum),
+ session_info,
+ &c);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0,("move_driver_to_download_area: create_conn_struct "
+ "returned %s\n", nt_errstr(nt_status)));
+ err = ntstatus_to_werror(nt_status);
+ TALLOC_FREE(frame);
+ return err;
+ }
+ conn = c->conn;
+
+ nt_status = set_conn_force_user_group(conn, printdollar_snum);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("failed set force user / group\n"));
+ err = ntstatus_to_werror(nt_status);
+ goto err_free_conn;
+ }
+
+ if (!become_user_without_service_by_session(conn, session_info)) {
+ DEBUG(0, ("failed to become user\n"));
+ err = WERR_ACCESS_DENIED;
+ goto err_free_conn;
+ }
+
+ new_dir = talloc_asprintf(frame,
+ "%s/%d",
+ short_architecture,
+ driver->version);
+ if (!new_dir) {
+ err = WERR_NOT_ENOUGH_MEMORY;
+ goto err_exit;
+ }
+ nt_status = driver_unix_convert(conn, new_dir, &dirfsp, &smb_dname);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ err = WERR_NOT_ENOUGH_MEMORY;
+ goto err_exit;
+ }
+
+ DEBUG(5,("Creating first directory: %s\n", smb_dname->base_name));
+
+ nt_status = create_directory(conn, NULL, dirfsp, smb_dname);
+ if (!NT_STATUS_IS_OK(nt_status)
+ && !NT_STATUS_EQUAL(nt_status, NT_STATUS_OBJECT_NAME_COLLISION)) {
+ DEBUG(0, ("failed to create driver destination directory: %s\n",
+ nt_errstr(nt_status)));
+ err = ntstatus_to_werror(nt_status);
+ goto err_exit;
+ }
+
+ /* For each driver file, archi\filexxx.yyy, if there is a duplicate file
+ * listed for this driver which has already been moved, skip it (note:
+ * drivers may list the same file name several times. Then check if the
+ * file already exists in archi\version\, if so, check that the version
+ * info (or time stamps if version info is unavailable) is newer (or the
+ * date is later). If it is, move it to archi\version\filexxx.yyy.
+ * Otherwise, delete the file.
+ *
+ * If a file is not moved to archi\version\ because of an error, all the
+ * rest of the 'unmoved' driver files are removed from archi\. If one or
+ * more of the driver's files was already moved to archi\version\, it
+ * potentially leaves the driver in a partially updated state. Version
+ * trauma will most likely occur if an client attempts to use any printer
+ * bound to the driver. Perhaps a rewrite to make sure the moves can be
+ * done is appropriate... later JRR
+ */
+
+ DEBUG(5,("Moving files now !\n"));
+
+ if (driver->driver_path && strlen(driver->driver_path)) {
+
+ err = move_driver_file_to_download_area(frame,
+ conn,
+ driver->driver_path,
+ short_architecture,
+ driver->version,
+ ver,
+ driver_directory);
+ if (!W_ERROR_IS_OK(err)) {
+ goto err_exit;
+ }
+ }
+
+ if (driver->data_file && strlen(driver->data_file)) {
+ if (!strequal(driver->data_file, driver->driver_path)) {
+
+ err = move_driver_file_to_download_area(frame,
+ conn,
+ driver->data_file,
+ short_architecture,
+ driver->version,
+ ver,
+ driver_directory);
+ if (!W_ERROR_IS_OK(err)) {
+ goto err_exit;
+ }
+ }
+ }
+
+ if (driver->config_file && strlen(driver->config_file)) {
+ if (!strequal(driver->config_file, driver->driver_path) &&
+ !strequal(driver->config_file, driver->data_file)) {
+
+ err = move_driver_file_to_download_area(frame,
+ conn,
+ driver->config_file,
+ short_architecture,
+ driver->version,
+ ver,
+ driver_directory);
+ if (!W_ERROR_IS_OK(err)) {
+ goto err_exit;
+ }
+ }
+ }
+
+ if (driver->help_file && strlen(driver->help_file)) {
+ if (!strequal(driver->help_file, driver->driver_path) &&
+ !strequal(driver->help_file, driver->data_file) &&
+ !strequal(driver->help_file, driver->config_file)) {
+
+ err = move_driver_file_to_download_area(frame,
+ conn,
+ driver->help_file,
+ short_architecture,
+ driver->version,
+ ver,
+ driver_directory);
+ if (!W_ERROR_IS_OK(err)) {
+ goto err_exit;
+ }
+ }
+ }
+
+ if (driver->dependent_files && driver->dependent_files->string) {
+ for (i=0; driver->dependent_files->string[i]; i++) {
+ if (!strequal(driver->dependent_files->string[i], driver->driver_path) &&
+ !strequal(driver->dependent_files->string[i], driver->data_file) &&
+ !strequal(driver->dependent_files->string[i], driver->config_file) &&
+ !strequal(driver->dependent_files->string[i], driver->help_file)) {
+ int j;
+ for (j=0; j < i; j++) {
+ if (strequal(driver->dependent_files->string[i], driver->dependent_files->string[j])) {
+ goto NextDriver;
+ }
+ }
+
+ err = move_driver_file_to_download_area(frame,
+ conn,
+ driver->dependent_files->string[i],
+ short_architecture,
+ driver->version,
+ ver,
+ driver_directory);
+ if (!W_ERROR_IS_OK(err)) {
+ goto err_exit;
+ }
+ }
+ NextDriver: ;
+ }
+ }
+
+ err = WERR_OK;
+ err_exit:
+ unbecome_user_without_service();
+ err_free_conn:
+ TALLOC_FREE(frame);
+ return err;
+}
+
+/****************************************************************************
+ Determine whether or not a particular driver is currently assigned
+ to a printer
+****************************************************************************/
+
+bool printer_driver_in_use(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ const struct spoolss_DriverInfo8 *r)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ int snum;
+ int n_services = lp_numservices();
+ bool in_use = false;
+ struct spoolss_PrinterInfo2 *pinfo2 = NULL;
+ WERROR result;
+
+ if (!r) {
+ return false;
+ }
+
+ DEBUG(10,("printer_driver_in_use: Beginning search through ntprinters.tdb...\n"));
+
+ /* loop through the printers.tdb and check for the drivername */
+
+ for (snum=0; snum<n_services && !in_use; snum++) {
+ if (!lp_snum_ok(snum) || !lp_printable(snum)) {
+ continue;
+ }
+
+ result = winreg_get_printer(mem_ctx, b,
+ lp_servicename(talloc_tos(), lp_sub, snum),
+ &pinfo2);
+ if (!W_ERROR_IS_OK(result)) {
+ continue; /* skip */
+ }
+
+ if (strequal(r->driver_name, pinfo2->drivername)) {
+ in_use = true;
+ }
+
+ TALLOC_FREE(pinfo2);
+ }
+
+ DEBUG(10,("printer_driver_in_use: Completed search through ntprinters.tdb...\n"));
+
+ if ( in_use ) {
+ struct spoolss_DriverInfo8 *driver = NULL;
+ WERROR werr;
+
+ DEBUG(5,("printer_driver_in_use: driver \"%s\" is currently in use\n", r->driver_name));
+
+ /* we can still remove the driver if there is one of
+ "Windows NT x86" version 2 or 3 left */
+
+ if (strequal(SPOOLSS_ARCHITECTURE_NT_X86, r->architecture)) {
+ if (r->version == 2) {
+ werr = winreg_get_driver(mem_ctx, b,
+ r->architecture,
+ r->driver_name,
+ 3, &driver);
+ } else if (r->version == 3) {
+ werr = winreg_get_driver(mem_ctx, b,
+ r->architecture,
+ r->driver_name,
+ 2, &driver);
+ } else {
+ DBG_ERR("Unknown driver version (%d)\n",
+ r->version);
+ werr = WERR_UNKNOWN_PRINTER_DRIVER;
+ }
+ } else if (strequal(SPOOLSS_ARCHITECTURE_x64, r->architecture)) {
+ werr = winreg_get_driver(mem_ctx, b,
+ SPOOLSS_ARCHITECTURE_NT_X86,
+ r->driver_name,
+ DRIVER_ANY_VERSION,
+ &driver);
+ } else {
+ DBG_ERR("Unknown driver architecture: %s\n",
+ r->architecture);
+ werr = WERR_UNKNOWN_PRINTER_DRIVER;
+ }
+
+ /* now check the error code */
+
+ if ( W_ERROR_IS_OK(werr) ) {
+ /* it's ok to remove the driver, we have other architectures left */
+ in_use = false;
+ talloc_free(driver);
+ }
+ }
+
+ /* report that the driver is not in use by default */
+
+ return in_use;
+}
+
+
+/**********************************************************************
+ Check to see if a ogiven file is in use by *info
+ *********************************************************************/
+
+static bool drv_file_in_use(const char *file, const struct spoolss_DriverInfo8 *info)
+{
+ int i = 0;
+
+ if ( !info )
+ return False;
+
+ /* mz: skip files that are in the list but already deleted */
+ if (!file || !file[0]) {
+ return false;
+ }
+
+ if (strequal(file, info->driver_path))
+ return True;
+
+ if (strequal(file, info->data_file))
+ return True;
+
+ if (strequal(file, info->config_file))
+ return True;
+
+ if (strequal(file, info->help_file))
+ return True;
+
+ /* see of there are any dependent files to examine */
+
+ if (!info->dependent_files)
+ return False;
+
+ while (info->dependent_files[i] && *info->dependent_files[i]) {
+ if (strequal(file, info->dependent_files[i]))
+ return True;
+ i++;
+ }
+
+ return False;
+
+}
+
+/**********************************************************************
+ Utility function to remove the dependent file pointed to by the
+ input parameter from the list
+ *********************************************************************/
+
+static void trim_dependent_file(TALLOC_CTX *mem_ctx, const char **files, int idx)
+{
+
+ /* bump everything down a slot */
+
+ while (files && files[idx+1]) {
+ files[idx] = talloc_strdup(mem_ctx, files[idx+1]);
+ idx++;
+ }
+
+ files[idx] = NULL;
+
+ return;
+}
+
+/**********************************************************************
+ Check if any of the files used by src are also used by drv
+ *********************************************************************/
+
+static bool trim_overlap_drv_files(TALLOC_CTX *mem_ctx,
+ struct spoolss_DriverInfo8 *src,
+ const struct spoolss_DriverInfo8 *drv)
+{
+ bool in_use = False;
+ int i = 0;
+
+ if ( !src || !drv )
+ return False;
+
+ /* check each file. Remove it from the src structure if it overlaps */
+
+ if (drv_file_in_use(src->driver_path, drv)) {
+ in_use = True;
+ DEBUG(10,("Removing driverfile [%s] from list\n", src->driver_path));
+ src->driver_path = talloc_strdup(mem_ctx, "");
+ if (!src->driver_path) { return false; }
+ }
+
+ if (drv_file_in_use(src->data_file, drv)) {
+ in_use = True;
+ DEBUG(10,("Removing datafile [%s] from list\n", src->data_file));
+ src->data_file = talloc_strdup(mem_ctx, "");
+ if (!src->data_file) { return false; }
+ }
+
+ if (drv_file_in_use(src->config_file, drv)) {
+ in_use = True;
+ DEBUG(10,("Removing configfile [%s] from list\n", src->config_file));
+ src->config_file = talloc_strdup(mem_ctx, "");
+ if (!src->config_file) { return false; }
+ }
+
+ if (drv_file_in_use(src->help_file, drv)) {
+ in_use = True;
+ DEBUG(10,("Removing helpfile [%s] from list\n", src->help_file));
+ src->help_file = talloc_strdup(mem_ctx, "");
+ if (!src->help_file) { return false; }
+ }
+
+ /* are there any dependentfiles to examine? */
+
+ if (!src->dependent_files)
+ return in_use;
+
+ while (src->dependent_files[i] && *src->dependent_files[i]) {
+ if (drv_file_in_use(src->dependent_files[i], drv)) {
+ in_use = True;
+ DEBUG(10,("Removing [%s] from dependent file list\n", src->dependent_files[i]));
+ trim_dependent_file(mem_ctx, src->dependent_files, i);
+ } else
+ i++;
+ }
+
+ return in_use;
+}
+
+/****************************************************************************
+ Determine whether or not a particular driver files are currently being
+ used by any other driver.
+
+ Return value is True if any files were in use by other drivers
+ and False otherwise.
+
+ Upon return, *info has been modified to only contain the driver files
+ which are not in use
+
+ Fix from mz:
+
+ This needs to check all drivers to ensure that all files in use
+ have been removed from *info, not just the ones in the first
+ match.
+****************************************************************************/
+
+bool printer_driver_files_in_use(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ struct spoolss_DriverInfo8 *info)
+{
+ uint32_t version;
+ struct spoolss_DriverInfo8 *driver;
+ bool in_use = false;
+ uint32_t i, num_drivers;
+ const char **drivers;
+ WERROR result;
+
+ if ( !info )
+ return False;
+
+ version = info->version;
+
+ /* loop over all driver versions */
+
+ DEBUG(5,("printer_driver_files_in_use: Beginning search of drivers...\n"));
+
+ /* get the list of drivers */
+
+ result = winreg_get_driver_list(mem_ctx, b,
+ info->architecture, version,
+ &num_drivers, &drivers);
+ if (!W_ERROR_IS_OK(result)) {
+ return true;
+ }
+
+ DEBUGADD(4, ("we have:[%d] drivers in environment [%s] and version [%d]\n",
+ num_drivers, info->architecture, version));
+
+ /* check each driver for overlap in files */
+
+ for (i = 0; i < num_drivers; i++) {
+ DEBUGADD(5,("\tdriver: [%s]\n", drivers[i]));
+
+ driver = NULL;
+
+ result = winreg_get_driver(mem_ctx, b,
+ info->architecture, drivers[i],
+ version, &driver);
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_free(drivers);
+ return True;
+ }
+
+ /* check if d2 uses any files from d1 */
+ /* only if this is a different driver than the one being deleted */
+
+ if (!strequal(info->driver_name, driver->driver_name)) {
+ if (trim_overlap_drv_files(mem_ctx, info, driver)) {
+ /* mz: Do not instantly return -
+ * we need to ensure this file isn't
+ * also in use by other drivers. */
+ in_use = true;
+ }
+ }
+
+ talloc_free(driver);
+ }
+
+ talloc_free(drivers);
+
+ DEBUG(5,("printer_driver_files_in_use: Completed search of drivers...\n"));
+
+ return in_use;
+}
+
+static NTSTATUS driver_unlink_internals(connection_struct *conn,
+ const char *short_arch,
+ int vers,
+ const char *fname)
+{
+ TALLOC_CTX *tmp_ctx = talloc_new(conn);
+ struct smb_filename *smb_fname = NULL;
+ char *print_dlr_path;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ print_dlr_path = talloc_asprintf(tmp_ctx, "%s/%d/%s",
+ short_arch, vers, fname);
+ if (print_dlr_path == NULL) {
+ goto err_out;
+ }
+
+ status = synthetic_pathref(tmp_ctx,
+ conn->cwd_fsp,
+ print_dlr_path,
+ NULL,
+ NULL,
+ 0,
+ 0,
+ &smb_fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto err_out;
+ }
+
+ status = unlink_internals(conn, NULL, 0, NULL, smb_fname);
+err_out:
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+/****************************************************************************
+ Actually delete the driver files. Make sure that
+ printer_driver_files_in_use() return False before calling
+ this.
+****************************************************************************/
+
+bool delete_driver_files(const struct auth_session_info *session_info,
+ const struct spoolss_DriverInfo8 *r)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ const char *short_arch;
+ struct conn_struct_tos *c = NULL;
+ connection_struct *conn = NULL;
+ NTSTATUS nt_status;
+ char *printdollar = NULL;
+ int printdollar_snum;
+ bool ret = false;
+
+ if (!r) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ DEBUG(6,("delete_driver_files: deleting driver [%s] - version [%d]\n",
+ r->driver_name, r->version));
+
+ printdollar_snum = find_service(frame, "print$", &printdollar);
+ if (!printdollar) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+ if (printdollar_snum == -1) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ nt_status = create_conn_struct_tos_cwd(global_messaging_context(),
+ printdollar_snum,
+ lp_path(frame, lp_sub, printdollar_snum),
+ session_info,
+ &c);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0,("delete_driver_files: create_conn_struct "
+ "returned %s\n", nt_errstr(nt_status)));
+ TALLOC_FREE(frame);
+ return false;
+ }
+ conn = c->conn;
+
+ nt_status = set_conn_force_user_group(conn, printdollar_snum);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("failed set force user / group\n"));
+ ret = false;
+ goto err_free_conn;
+ }
+
+ if (!become_user_without_service_by_session(conn, session_info)) {
+ DEBUG(0, ("failed to become user\n"));
+ ret = false;
+ goto err_free_conn;
+ }
+
+ if ( !CAN_WRITE(conn) ) {
+ DEBUG(3,("delete_driver_files: Cannot delete print driver when [print$] is read-only\n"));
+ ret = false;
+ goto err_out;
+ }
+
+ short_arch = get_short_archi(r->architecture);
+ if (short_arch == NULL) {
+ DEBUG(0, ("bad architecture %s\n", r->architecture));
+ ret = false;
+ goto err_out;
+ }
+
+ /* now delete the files */
+
+ if (r->driver_path && r->driver_path[0]) {
+ DEBUG(10,("deleting driverfile [%s]\n", r->driver_path));
+ driver_unlink_internals(conn, short_arch, r->version, r->driver_path);
+ }
+
+ if (r->config_file && r->config_file[0]) {
+ DEBUG(10,("deleting configfile [%s]\n", r->config_file));
+ driver_unlink_internals(conn, short_arch, r->version, r->config_file);
+ }
+
+ if (r->data_file && r->data_file[0]) {
+ DEBUG(10,("deleting datafile [%s]\n", r->data_file));
+ driver_unlink_internals(conn, short_arch, r->version, r->data_file);
+ }
+
+ if (r->help_file && r->help_file[0]) {
+ DEBUG(10,("deleting helpfile [%s]\n", r->help_file));
+ driver_unlink_internals(conn, short_arch, r->version, r->help_file);
+ }
+
+ if (r->dependent_files) {
+ int i = 0;
+ while (r->dependent_files[i] && r->dependent_files[i][0]) {
+ DEBUG(10,("deleting dependent file [%s]\n", r->dependent_files[i]));
+ driver_unlink_internals(conn, short_arch, r->version, r->dependent_files[i]);
+ i++;
+ }
+ }
+
+ ret = true;
+ err_out:
+ unbecome_user_without_service();
+ err_free_conn:
+ TALLOC_FREE(frame);
+ return ret;
+}
+
+/* error code:
+ 0: everything OK
+ 1: level not implemented
+ 2: file doesn't exist
+ 3: can't allocate memory
+ 4: can't free memory
+ 5: non existent struct
+*/
+
+/*
+ A printer and a printer driver are 2 different things.
+ NT manages them separately, Samba does the same.
+ Why ? Simply because it's easier and it makes sense !
+
+ Now explanation: You have 3 printers behind your samba server,
+ 2 of them are the same make and model (laser A and B). But laser B
+ has an 3000 sheet feeder and laser A doesn't such an option.
+ Your third printer is an old dot-matrix model for the accounting :-).
+
+ If the /usr/local/samba/lib directory (default dir), you will have
+ 5 files to describe all of this.
+
+ 3 files for the printers (1 by printer):
+ NTprinter_laser A
+ NTprinter_laser B
+ NTprinter_accounting
+ 2 files for the drivers (1 for the laser and 1 for the dot matrix)
+ NTdriver_printer model X
+ NTdriver_printer model Y
+
+jfm: I should use this comment for the text file to explain
+ same thing for the forms BTW.
+ Je devrais mettre mes commentaires en francais, ca serait mieux :-)
+
+*/
+
+/* Convert generic access rights to printer object specific access rights.
+ It turns out that NT4 security descriptors use generic access rights and
+ NT5 the object specific ones. */
+
+void map_printer_permissions(struct security_descriptor *sd)
+{
+ uint32_t i;
+
+ for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
+ se_map_generic(&sd->dacl->aces[i].access_mask,
+ &printer_generic_mapping);
+ }
+}
+
+void map_job_permissions(struct security_descriptor *sd)
+{
+ uint32_t i;
+
+ for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
+ se_map_generic(&sd->dacl->aces[i].access_mask,
+ &job_generic_mapping);
+ }
+}
+
+
+/****************************************************************************
+ Check a user has permissions to perform the given operation. We use the
+ permission constants defined in include/rpc_spoolss.h to check the various
+ actions we perform when checking printer access.
+
+ PRINTER_ACCESS_ADMINISTER:
+ print_queue_pause, print_queue_resume, update_printer_sec,
+ update_printer, spoolss_addprinterex_level_2,
+ _spoolss_setprinterdata
+
+ PRINTER_ACCESS_USE:
+ print_job_start
+
+ JOB_ACCESS_ADMINISTER:
+ print_job_delete, print_job_pause, print_job_resume,
+ print_queue_purge
+
+ Try access control in the following order (for performance reasons):
+ 1) root and SE_PRINT_OPERATOR can do anything (easy check)
+ 2) check security descriptor (bit comparisons in memory)
+ 3) "printer admins" (may result in numerous calls to winbind)
+
+ ****************************************************************************/
+WERROR print_access_check(const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx, int snum,
+ int access_type)
+{
+ struct spoolss_security_descriptor *secdesc = NULL;
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ uint32_t access_granted;
+ size_t sd_size;
+ NTSTATUS status;
+ WERROR result;
+ const char *pname;
+ TALLOC_CTX *mem_ctx = NULL;
+
+ /* If user is NULL then use the current_user structure */
+
+ /* Always allow root or SE_PRINT_OPERATROR to do anything */
+
+ if ((session_info->unix_token->uid == sec_initial_uid())
+ || security_token_has_privilege(session_info->security_token,
+ SEC_PRIV_PRINT_OPERATOR)) {
+ return WERR_OK;
+ }
+
+ /* Get printer name */
+
+ pname = lp_printername(talloc_tos(), lp_sub, snum);
+
+ if (!pname || !*pname) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ /* Get printer security descriptor */
+
+ if(!(mem_ctx = talloc_init("print_access_check"))) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ result = winreg_get_printer_secdesc_internal(mem_ctx,
+ get_session_info_system(),
+ msg_ctx,
+ pname,
+ &secdesc);
+ if (!W_ERROR_IS_OK(result)) {
+ talloc_destroy(mem_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ if (access_type == JOB_ACCESS_ADMINISTER) {
+ struct spoolss_security_descriptor *parent_secdesc = secdesc;
+
+ /* Create a child security descriptor to check permissions
+ against. This is because print jobs are child objects
+ objects of a printer. */
+ status = se_create_child_secdesc(mem_ctx,
+ &secdesc,
+ &sd_size,
+ parent_secdesc,
+ parent_secdesc->owner_sid,
+ parent_secdesc->group_sid,
+ false);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_destroy(mem_ctx);
+ return ntstatus_to_werror(status);
+ }
+
+ map_job_permissions(secdesc);
+ } else {
+ map_printer_permissions(secdesc);
+ }
+
+ /* Check access */
+ status = se_access_check(secdesc, session_info->security_token, access_type,
+ &access_granted);
+
+ DEBUG(4, ("access check was %s\n", NT_STATUS_IS_OK(status) ? "SUCCESS" : "FAILURE"));
+
+ talloc_destroy(mem_ctx);
+
+ return ntstatus_to_werror(status);
+}
+
+/****************************************************************************
+ Check the time parameters allow a print operation.
+*****************************************************************************/
+
+bool print_time_access_check(const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *servicename)
+{
+ struct spoolss_PrinterInfo2 *pinfo2 = NULL;
+ WERROR result;
+ bool ok = False;
+ time_t now = time(NULL);
+ struct tm *t;
+ uint32_t mins;
+
+ result = winreg_get_printer_internal(NULL, session_info, msg_ctx,
+ servicename, &pinfo2);
+ if (!W_ERROR_IS_OK(result)) {
+ return False;
+ }
+
+ if (pinfo2->starttime == 0 && pinfo2->untiltime == 0) {
+ ok = True;
+ }
+
+ t = gmtime(&now);
+ mins = (uint32_t)t->tm_hour*60 + (uint32_t)t->tm_min;
+
+ if (mins >= pinfo2->starttime && mins <= pinfo2->untiltime) {
+ ok = True;
+ }
+
+ TALLOC_FREE(pinfo2);
+
+ if (!ok) {
+ errno = EACCES;
+ }
+
+ return ok;
+}
+
+void nt_printer_remove(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *printer)
+{
+ WERROR result;
+
+ result = winreg_delete_printer_key_internal(mem_ctx, session_info, msg_ctx,
+ printer, "");
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("nt_printer_remove: failed to remove printer %s: "
+ "%s\n", printer, win_errstr(result)));
+ }
+}
+
+void nt_printer_add(TALLOC_CTX *mem_ctx,
+ const struct auth_session_info *session_info,
+ struct messaging_context *msg_ctx,
+ const char *printer)
+{
+ WERROR result;
+
+ result = winreg_create_printer_internal(mem_ctx, session_info, msg_ctx,
+ printer);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(0, ("nt_printer_add: failed to add printer %s: %s\n",
+ printer, win_errstr(result)));
+ }
+}