diff options
Diffstat (limited to 'lib/printer_driver/printer_driver.c')
-rw-r--r-- | lib/printer_driver/printer_driver.c | 1252 |
1 files changed, 1252 insertions, 0 deletions
diff --git a/lib/printer_driver/printer_driver.c b/lib/printer_driver/printer_driver.c new file mode 100644 index 0000000..35260c2 --- /dev/null +++ b/lib/printer_driver/printer_driver.c @@ -0,0 +1,1252 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Guenther Deschner 2016 + + 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 "librpc/gen_ndr/ndr_spoolss.h" +#include "rpc_client/init_spoolss.h" +#include "libgpo/gpo_ini.h" +#include "printer_driver.h" + +#define ADD_TO_ARRAY(mem_ctx, type, elem, array, num) \ +do { \ + *(array) = talloc_realloc(mem_ctx, (*(array)), type, (*(num))+1); \ + SMB_ASSERT((*(array)) != NULL); \ + (*(array))[*(num)] = (elem); \ + (*(num)) += 1; \ +} while (0) + + +/* GetPrinterDriverDirectory -> drivers and dependent files */ +#define PRINTER_INF_DIRID_66000 + +/* GetPrintProcessorDirectory -> print processors */ +#define PRINTER_INF_DIRID_66001 + +/* GetColorDirectory -> color profiles */ +#define PRINTER_INF_DIRID_66003 + +static const char *get_string_unquote(const char *s) +{ + bool ok; + size_t len; + + if (s == NULL) { + return NULL; + } + + len = strlen(s); + if (len < 2) { + return s; + } + + if (s[0] == '"' && s[len-1] == '"') { + ok = trim_string(discard_const(s), "\"", "\""); + if (!ok) { + return NULL; + } + } + + return s; +} + +/* + * '%STRING%' indicates STRING is localized in the [Strings] section + */ + +static const char *get_string_token(struct gp_inifile_context *ctx, + const char *s) +{ + NTSTATUS status; + bool ok; + char *key; + const char *s2; + + if (s != NULL && s[0] != '%' && s[strlen(s)-1] != '%') { + return s; + } + + ok = trim_string(discard_const(s), "%", "%"); + if (!ok) { + return NULL; + } + + key = talloc_asprintf(ctx, "Strings:%s", s); + if (key == NULL) { + return NULL; + } + + status = gp_inifile_getstring(ctx, key, &s2); + talloc_free(key); + if (!NT_STATUS_IS_OK(status)) { + /* what can you do... */ + return s; + } + + return s2; +} + +static NTSTATUS gp_inifile_getstring_ext(struct gp_inifile_context *ctx, + const char *key, + const char **ret) +{ + NTSTATUS status; + const char *s; + + status = gp_inifile_getstring(ctx, key, &s); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + s = get_string_unquote(s); + if (s == NULL) { + return NT_STATUS_INTERNAL_ERROR; + } + + if (s[0] == '%' && s[strlen(s)-1] == '%') { + s = get_string_token(ctx, s); + } + + s = get_string_unquote(s); + if (s == NULL) { + return NT_STATUS_INTERNAL_ERROR; + } + + *ret = s; + + return NT_STATUS_OK; +} + +static NTSTATUS find_manufacturer_name(struct gp_inifile_context *ctx, + TALLOC_CTX *mem_ctx, + const char *section_name, + const char **manufacturer_name) +{ + NTSTATUS status; + size_t num_keys = 0; + const char **keys = NULL; + const char **values = NULL; + const char *s; + char *p; + + status = gp_inifile_enum_section(ctx, section_name, &num_keys, &keys, &values); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (num_keys < 1) { + return NT_STATUS_INVALID_PARAMETER; + } + + s = talloc_strdup(mem_ctx, keys[0]); + if (s == NULL) { + return NT_STATUS_NO_MEMORY; + } + + p = strchr(s, ':'); + if (p == NULL) { + return NT_STATUS_NO_MEMORY; + } + *p = '\0'; + p++; + + s = get_string_unquote(p); + if (s == NULL) { + return NT_STATUS_INTERNAL_ERROR; + } + + s = get_string_token(ctx, s); + + s = get_string_unquote(s); + if (s == NULL) { + return NT_STATUS_INTERNAL_ERROR; + } + + if (s != NULL) { + *manufacturer_name = talloc_strdup(mem_ctx, s); + if (*manufacturer_name == NULL) { + return NT_STATUS_NO_MEMORY; + } + } + + talloc_free(keys); + talloc_free(values); + + return NT_STATUS_OK; +} + +static NTSTATUS find_manufacturer_url(struct gp_inifile_context *ctx, + TALLOC_CTX *mem_ctx, + const char *section_name, + const char *manufacturer_name, + const char **manufacturer_url) +{ + NTSTATUS status; + size_t num_keys = 0; + const char **keys = NULL; + const char **values = NULL; + const char *s; + char *p; + + status = gp_inifile_enum_section(ctx, section_name, &num_keys, &keys, &values); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (num_keys < 1) { + return NT_STATUS_INVALID_PARAMETER; + } + + p = strchr(keys[0], ':'); + if (p == NULL) { + return NT_STATUS_NO_MEMORY; + } + *p = '\0'; + p++; + + s = get_string_unquote(p); + if (s == NULL) { + return NT_STATUS_INTERNAL_ERROR; + } + + s = get_string_token(ctx, s); + + s = get_string_unquote(s); + if (s == NULL) { + return NT_STATUS_INTERNAL_ERROR; + } + + if (strequal(s, manufacturer_name)) { + s = get_string_unquote(values[0]); + if (s == NULL) { + return NT_STATUS_INTERNAL_ERROR; + } + } + + if (s != NULL) { + *manufacturer_url = talloc_strdup(mem_ctx, s); + if (*manufacturer_url == NULL) { + return NT_STATUS_NO_MEMORY; + } + } + + talloc_free(keys); + talloc_free(values); + + return NT_STATUS_OK; +} + +static NTSTATUS add_string_to_spoolss_array(TALLOC_CTX *mem_ctx, + const char *s, + struct spoolss_StringArray **r) +{ + size_t count = 2; + struct spoolss_StringArray *a = *r; + bool ok; + int i; + + if (a == NULL) { + a = talloc_zero(mem_ctx, struct spoolss_StringArray); + if (a == NULL) { + return NT_STATUS_NO_MEMORY; + } + } + + if (a->string == NULL) { + a->string = talloc_zero_array(a, const char *, count); + if (a->string == NULL) { + return NT_STATUS_NO_MEMORY; + } + } + + for (i = 0; a->string[i] != NULL; i++) { ;; } + count = i; + + ok = add_string_to_array(mem_ctx, s, &a->string, &count); + if (!ok) { + return NT_STATUS_NO_MEMORY; + } + + a->string = talloc_realloc(mem_ctx, a->string, const char *, count + 1); + if (a->string == NULL) { + return NT_STATUS_NO_MEMORY; + } + a->string[count] = NULL; + + *r = a; + + return NT_STATUS_OK; +} + +static NTSTATUS add_dependent_driver_file(TALLOC_CTX *mem_ctx, + const char *file, + struct spoolss_StringArray **r) +{ + char *p; + + if (file == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (file[0] == '@') { + file++; + } + + p = strchr(file, ','); + if (p != NULL) { + *p = '\0'; + } + + return add_string_to_spoolss_array(mem_ctx, file, r); +} + +/* + * https://msdn.microsoft.com/de-de/windows/hardware/drivers/install/inf-manufacturer-section + * + * [Manufacturer] + * "Kyocera"=Kyocera,NTx86.5.1,NTx86.6.0,NTamd64.5.1,NTamd64.6.0 + */ + +static NTSTATUS enum_devices_in_toc(struct gp_inifile_context *ctx, + TALLOC_CTX *mem_ctx, + size_t *pnum_devices, + const char ***pdevices, + const char ***pdevice_values) +{ + NTSTATUS status; + size_t i, num_manufacturers = 0; + const char **manufacturers = NULL; + const char **values = NULL; + char *p; + bool ok; + + status = gp_inifile_enum_section(ctx, "Manufacturer", &num_manufacturers, &manufacturers, &values); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + for (i = 0; i < num_manufacturers; i++) { + + const char *models_section_name; + const char *s; + char **decorations; + int j; + + DEBUG(11,("processing manufacturer: %s\n", manufacturers[i])); + + status = gp_inifile_getstring(ctx, manufacturers[i], &s); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + decorations = str_list_make_v3(mem_ctx, s, ","); + if (decorations == NULL) { + return NT_STATUS_NO_MEMORY; + } + + models_section_name = decorations[0]; + + for (j = 1; decorations[j] != NULL; j++) { + + /* + * https://msdn.microsoft.com/de-de/windows/hardware/drivers/install/inf-models-section + */ + + const char *decorated_models_section_name; + size_t d, num_devices = 0; + const char **devices = NULL; + const char **device_values = NULL; + size_t c = 0; + + decorated_models_section_name = talloc_asprintf(mem_ctx, "%s.%s", + models_section_name, + decorations[j]); + if (decorated_models_section_name == NULL) { + return NT_STATUS_NO_MEMORY; + } + + DEBUG(11,("processing decorated models_section_name: %s\n", + decorated_models_section_name)); + + status = gp_inifile_enum_section(ctx, decorated_models_section_name, + &num_devices, &devices, + &device_values); + for (d = 0; d < num_devices; d++) { + + DEBUG(11,("processing device: %s\n", + devices[d])); + + s = talloc_strdup(mem_ctx, devices[d]); + if (s == NULL) { + return NT_STATUS_NO_MEMORY; + } + + p = strchr(s, ':'); + if (p == NULL) { + return NT_STATUS_DRIVER_INTERNAL_ERROR; + } + + *p = '\0'; + p++; + + s = get_string_unquote(p); + + ok = add_string_to_array(mem_ctx, s, pdevices, pnum_devices); + if (!ok) { + return NT_STATUS_NO_MEMORY; + } + ok = add_string_to_array(mem_ctx, device_values[d], pdevice_values, &c); + if (!ok) { + return NT_STATUS_NO_MEMORY; + } + } + } + } + + return NT_STATUS_OK; +} + +static NTSTATUS find_device_in_toc(struct gp_inifile_context *ctx, + TALLOC_CTX *mem_ctx, + const char *device_description, + const char **value) +{ + NTSTATUS status; + size_t d, num_devices = 0; + const char **devices = NULL; + const char **device_values = NULL; + + if (device_description == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + status = enum_devices_in_toc(ctx, mem_ctx, + &num_devices, + &devices, + &device_values); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + for (d = 0; d < num_devices; d++) { + + if (strequal(device_description, devices[d])) { + + DEBUG(10,("found device_description: %s\n", + device_description)); + + *value = talloc_strdup(mem_ctx, device_values[d]); + if (*value == NULL) { + return NT_STATUS_NO_MEMORY; + } + DEBUGADD(10,("and returned: %s\n", *value)); + + return NT_STATUS_OK; + } + } + + return NT_STATUS_DRIVER_INTERNAL_ERROR; +} + +/* + * https://msdn.microsoft.com/de-de/windows/hardware/drivers/install/inf-copyfiles-directive + */ + +static NTSTATUS process_driver_section_copyfiles(struct gp_inifile_context *ctx, + TALLOC_CTX *mem_ctx, + const char *driver_section, + struct spoolss_AddDriverInfo8 *r) +{ + NTSTATUS status; + size_t i, num_keys = 0; + char *p, *key; + const char **keys = NULL; + const char **values = NULL; + char *str; + const char *s; + + key = talloc_asprintf(mem_ctx, "%s:%s", driver_section, "CopyFiles"); + if (key == NULL) { + return NT_STATUS_NO_MEMORY; + } + + DEBUG(10,("Checking for CopyFiles entry in %s\n", driver_section)); + + status = gp_inifile_getstring(ctx, key, &s); + if (!NT_STATUS_IS_OK(status)) { + return NT_STATUS_OK; + } + + DEBUG(10,("these are the files to copy: %s\n", s)); + + while (next_token_talloc(mem_ctx, &s, &str, ",")) { + + DEBUG(10,("trying section: %s\n", str)); + + if (str[0] == '@') { + DEBUG(10,("adding dependent driver file: %s\n", str)); + status = add_dependent_driver_file(mem_ctx, str, &r->dependent_files); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + continue; + } + + status = gp_inifile_enum_section(ctx, str, &num_keys, &keys, &values); + if (NT_STATUS_IS_OK(status)) { + for (i = 0; i < num_keys; i++) { + p = strchr(keys[i], ':'); + if (p == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + *p = '\0'; + p++; + + DEBUG(10,("adding dependent driver file: %s\n", p)); + + status = add_dependent_driver_file(mem_ctx, p, &r->dependent_files); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + TALLOC_FREE(keys); + TALLOC_FREE(values); + } + } + + return NT_STATUS_OK; +} + +#define process_driver_section_val(_ctx, _mem_ctx, _section, _r, _key, _element) \ +do { \ + NTSTATUS _status; \ + const char *__key, *_s; \ + __key = talloc_asprintf(_mem_ctx, "%s:%s", _section, _key); \ + NT_STATUS_HAVE_NO_MEMORY(__key); \ + _status = gp_inifile_getstring(_ctx, __key, &_s); \ + if (NT_STATUS_IS_OK(_status)) { \ + (_r)->_element = talloc_strdup(mem_ctx, _s); \ + NT_STATUS_HAVE_NO_MEMORY((_r)->_element); \ + } \ +} while(0); + +static NTSTATUS process_driver_section_colorprofiles(struct gp_inifile_context *ctx, + TALLOC_CTX *mem_ctx, + const char *section, + struct spoolss_AddDriverInfo8 *r) +{ + NTSTATUS status; + const char *key, *s; + + key = talloc_asprintf(mem_ctx, "%s:%s", section, "ColorProfiles"); + if (key == NULL) { + return NT_STATUS_NO_MEMORY; + } + + status = gp_inifile_getstring_ext(ctx, key, &s); + if (NT_STATUS_IS_OK(status)) { + + status = add_string_to_spoolss_array(mem_ctx, s, &r->color_profiles); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + return NT_STATUS_OK; +} + +static NTSTATUS process_driver_section_printprocessor(struct gp_inifile_context *ctx, + TALLOC_CTX *mem_ctx, + const char *section, + struct spoolss_AddDriverInfo8 *r) +{ + NTSTATUS status; + char *key, *p; + const char *s; + + key = talloc_asprintf(mem_ctx, "%s:%s", section, "PrintProcessor"); + if (key == NULL) { + return NT_STATUS_NO_MEMORY; + } + + status = gp_inifile_getstring_ext(ctx, key, &s); + if (NT_STATUS_IS_OK(status)) { + s = get_string_unquote(s); + + p = strchr(s, ','); + if (p == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + *p = '\0'; + r->print_processor = talloc_strdup(mem_ctx, s); + if (r->print_processor == NULL) { + return NT_STATUS_NO_MEMORY; + } + } + + return NT_STATUS_OK; +} + +static NTSTATUS process_driver_section_data_section(struct gp_inifile_context *ctx, + TALLOC_CTX *mem_ctx, + const char *section, + struct spoolss_AddDriverInfo8 *r) +{ + NTSTATUS status; + char *key; + const char *s; + + key = talloc_asprintf(mem_ctx, "%s:%s", section, "DataSection"); + if (key == NULL) { + return NT_STATUS_NO_MEMORY; + } + + status = gp_inifile_getstring(ctx, key, &s); + if (NT_STATUS_IS_OK(status)) { + process_driver_section_val(ctx, mem_ctx, s, r, + "DriverFile", driver_path); + process_driver_section_val(ctx, mem_ctx, s, r, + "HelpFile", help_file); + process_driver_section_val(ctx, mem_ctx, s, r, + "DataFile", data_file); + process_driver_section_val(ctx, mem_ctx, s, r, + "ConfigFile", config_file); + } + + return NT_STATUS_OK; +} + + +static NTSTATUS process_one_core_driver_section(struct gp_inifile_context *core_ctx, + TALLOC_CTX *mem_ctx, + const char *driver_section, + struct spoolss_AddDriverInfo8 *r) +{ + NTSTATUS status; + size_t i, num_keys = 0; + const char **keys = NULL; + const char **values = NULL; + + DEBUG(10,("CoreDriverSection is: %s\n", driver_section)); + + status = gp_inifile_enum_section(core_ctx, driver_section, &num_keys, &keys, &values); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + for (i = 0; i < num_keys; i++) { + + status = process_driver_section_copyfiles(core_ctx, mem_ctx, driver_section, r); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + process_driver_section_val(core_ctx, mem_ctx, driver_section, r, + "DriverFile", driver_path); + process_driver_section_val(core_ctx, mem_ctx, driver_section, r, + "HelpFile", help_file); + process_driver_section_val(core_ctx, mem_ctx, driver_section, r, + "ConfigFile", config_file); + + status = process_driver_section_colorprofiles(core_ctx, mem_ctx, driver_section, r); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + talloc_free(keys); + talloc_free(values); + + return NT_STATUS_OK; +} + +/* + * CoreDriverSections="{D20EA372-DD35-4950-9ED8-A6335AFE79F0},UNIDRV_BIDI.OEM,UNIDRV_BIDI_DATA","{D20EA372-DD35-4950-9ED8-A6335AFE79F2},PCLXL.OEM","{D20EA372-DD35-4950-9ED8-A6335AFE79F3},sRGBPROFILE.OEM" + */ +static NTSTATUS process_core_driver_sections(struct gp_inifile_context *core_ctx, + TALLOC_CTX *mem_ctx, + const char *value, + struct spoolss_AddDriverInfo8 *r) +{ + NTSTATUS status; + char *p; + char **list; + int i; + + list = str_list_make_v3(mem_ctx, value, ","); + if (list == NULL) { + return NT_STATUS_NO_MEMORY; + } + + for (i = 0; list[i] != NULL; i++) { + char **array; + int a; + + /* FIXME: do we have to validate the core driver guid ? */ + + p = strchr(list[i], ','); + if (p != NULL) { + *p = '\0'; + p++; + } + + DEBUG(10,("CoreDriverSections we have to process: %s\n", p)); + + array = str_list_make_v3(mem_ctx, p, ","); + if (array == NULL) { + return NT_STATUS_NO_MEMORY; + } + + for (a = 0; array[a] != NULL; a++) { + + if (core_ctx == NULL) { + DEBUG(0,("Need to process CoreDriverSections but " + "have no Core Driver Context!\n")); + return NT_STATUS_DRIVER_INTERNAL_ERROR; + } + + status = process_one_core_driver_section(core_ctx, mem_ctx, array[a], r); + if (!NT_STATUS_IS_OK(status)) { + continue; + } + } + } + + return NT_STATUS_OK; +} + +/* + * https://msdn.microsoft.com/de-de/windows/hardware/drivers/install/inf-ddinstall-section + */ +static NTSTATUS find_driver_files(struct gp_inifile_context *ctx, + struct gp_inifile_context *core_ctx, + TALLOC_CTX *mem_ctx, + const char *driver_name, + struct spoolss_AddDriverInfo8 *r) +{ + NTSTATUS status; + char *key; + const char *s; + const char *value; + char *install_section_name; + bool ok; + char *hw_id; + + status = find_device_in_toc(ctx, mem_ctx, driver_name, &value); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + r->driver_name = talloc_strdup(mem_ctx, driver_name); + if (r->driver_name == NULL) { + return NT_STATUS_NO_MEMORY; + } + + ok = next_token_talloc(mem_ctx, &value, &install_section_name, ","); + if (!ok) { + return NT_STATUS_INVALID_PARAMETER; + } + + DEBUG(10,("driver_name: %s, value: %s, install_section_name: %s\n", + driver_name, value, install_section_name)); + + /* Hardware Id is optional */ + ok = next_token_talloc(mem_ctx, &value, &hw_id, ","); + if (ok) { + r->hardware_id = hw_id; + } + + status = process_driver_section_copyfiles(ctx, mem_ctx, install_section_name, r); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + process_driver_section_val(ctx, mem_ctx, install_section_name, r, + "DriverFile", driver_path); + process_driver_section_val(ctx, mem_ctx, install_section_name, r, + "HelpFile", help_file); + process_driver_section_val(ctx, mem_ctx, install_section_name, r, + "DataFile", data_file); + process_driver_section_val(ctx, mem_ctx, install_section_name, r, + "ConfigFile", config_file); + + status = process_driver_section_printprocessor(ctx, mem_ctx, install_section_name, r); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = process_driver_section_data_section(ctx, mem_ctx, install_section_name, r); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + key = talloc_asprintf(mem_ctx, "%s:%s", install_section_name, "CoreDriverSections"); + if (key == NULL) { + return NT_STATUS_NO_MEMORY; + } + + status = gp_inifile_getstring(ctx, key, &s); + if (NT_STATUS_IS_OK(status)) { + + DEBUG(10,("found CoreDriverSections: %s\n", s)); + + status = process_core_driver_sections(core_ctx, mem_ctx, s, r); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + return NT_STATUS_OK; +} + +struct inf_context { + struct gp_inifile_context *ctx; + struct gp_inifile_context *core_ctx; +}; + +static NTSTATUS init_inf_context(TALLOC_CTX *mem_ctx, + const char *inf_filename, + const char *core_filename, + struct inf_context **_inf_ctx) +{ + NTSTATUS status; + struct gp_inifile_context *ctx; + struct gp_inifile_context *core_ctx = NULL; + struct inf_context *inf_ctx; + + inf_ctx = talloc_zero(mem_ctx, struct inf_context); + if (inf_ctx == NULL) { + return NT_STATUS_NO_MEMORY; + } + + status = gp_inifile_init_context_direct(mem_ctx, + inf_filename, + &ctx); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10,("init_inf_context: failed to load %s\n", inf_filename)); + return status; + } + + if (ctx->generated_filename != NULL) { + unlink(ctx->generated_filename); + } + + if (core_filename != NULL) { + status = gp_inifile_init_context_direct(mem_ctx, + core_filename, + &core_ctx); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10,("init_inf_context: failed to load %s\n", core_filename)); + return status; + } + + if (core_ctx->generated_filename != NULL) { + unlink(core_ctx->generated_filename); + } + } + + inf_ctx->ctx = ctx; + inf_ctx->core_ctx = core_ctx; + + *_inf_ctx = inf_ctx; + + return NT_STATUS_OK; +} + +static NTSTATUS process_driver_driverver(struct gp_inifile_context *ctx, + struct spoolss_AddDriverInfo8 *r) +{ + NTSTATUS status; + const char *s; + char *p; + bool ok; + const char *str; + + status = gp_inifile_getstring(ctx, "Version:DriverVer", &s); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + str = talloc_strdup(ctx, s); + if (str == NULL) { + return NT_STATUS_NO_MEMORY; + } + + p = strchr(str, ','); + if (p) { + *p = '\0'; + p++; + } + + ok = spoolss_timestr_to_NTTIME(str, &r->driver_date); + if (!ok) { + return NT_STATUS_INVALID_PARAMETER; + } + + ok = spoolss_driver_version_to_qword(p, &r->driver_version); + if (!ok) { + return NT_STATUS_INVALID_PARAMETER; + } + + return NT_STATUS_OK; +} + +/* + * Parse a SourceDisksNames section, + * https://msdn.microsoft.com/de-de/windows/hardware/drivers/install/inf-sourcedisksnames-section?f=255&MSPPError=-2147217396 + */ +static NTSTATUS process_source_disk_name(struct gp_inifile_context *ctx, + TALLOC_CTX *mem_ctx, + const char *short_environment, + const char **source_disk_name) +{ + NTSTATUS status; + bool ok; + const char *key; + size_t i, num_keys = 0; + const char **keys = NULL; + const char **values = NULL; + + key = talloc_asprintf(mem_ctx, "SourceDisksNames.%s", short_environment); + if (key == NULL) { + return NT_STATUS_NO_MEMORY; + } + + status = gp_inifile_enum_section(ctx, key, &num_keys, &keys, &values); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (keys == NULL && values == NULL) { + key = "SourceDisksNames"; + + status = gp_inifile_enum_section(ctx, key, &num_keys, &keys, &values); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + for (i = 0; i < num_keys; i++) { + + /* + * 1 = %Disk1%,,,"Amd64" + * diskid = disk-description[,[tag-or-cab-file],[unused],[path],[flags][,tag-file]] + */ + char *disk_description, *tag_or_cab_file, *unused, *path; + + ok = next_token_no_ltrim_talloc(mem_ctx, &values[i], &disk_description, ","); + if (!ok) { + continue; + } + + ok = next_token_no_ltrim_talloc(mem_ctx, &values[i], &tag_or_cab_file, ","); + if (!ok) { + continue; + } + + ok = next_token_no_ltrim_talloc(mem_ctx, &values[i], &unused, ","); + if (!ok) { + continue; + } + + ok = next_token_no_ltrim_talloc(mem_ctx, &values[i], &path, ","); + if (!ok) { + continue; + } + + *source_disk_name = path; + + return NT_STATUS_OK; + } + + return NT_STATUS_NOT_FOUND; +} + +static NTSTATUS setup_driver_by_name(TALLOC_CTX *mem_ctx, + struct inf_context *inf_ctx, + const char *filename, + const char *environment, + const char *driver_name, + struct spoolss_AddDriverInfo8 *r, + const char **source_disk_name) +{ + NTSTATUS status; + struct gp_inifile_context *ctx = inf_ctx->ctx; + struct gp_inifile_context *core_ctx = inf_ctx->core_ctx; + char *key; + bool ok; + const char *short_environment; + const char *s; + + short_environment = spoolss_get_short_filesys_environment(environment); + if (short_environment == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + status = find_driver_files(ctx, core_ctx, mem_ctx, driver_name, r); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = process_source_disk_name(ctx, mem_ctx, + short_environment, + source_disk_name); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + r->inf_path = talloc_strdup(mem_ctx, filename); + if (r->inf_path == NULL) { + return NT_STATUS_NO_MEMORY; + } + + r->architecture = talloc_strdup(mem_ctx, environment); + if (r->architecture == NULL) { + return NT_STATUS_NO_MEMORY; + } + + if (r->print_processor == NULL) { + r->print_processor = talloc_strdup(mem_ctx, "winprint"); + if (r->print_processor == NULL) { + return NT_STATUS_NO_MEMORY; + } + } + + status = gp_inifile_getstring_ext(ctx, "Version:Class", &s); + if (NT_STATUS_IS_OK(status)) { + if (strequal(s, "Printer")) { + r->printer_driver_attributes |= PRINTER_DRIVER_CLASS; + } + } + + status = gp_inifile_getstring(ctx, "Version:Signature", &s); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (!strequal(s, "\"$Windows NT$\"")) { + return NT_STATUS_INVALID_SIGNATURE; + } + + r->version = SPOOLSS_DRIVER_VERSION_200X; + status = gp_inifile_getstring(ctx, "Version:ClassVer", &s); + if (NT_STATUS_IS_OK(status)) { + int cmp = strncasecmp_m(s, "4.0", 3); + if (cmp == 0) { + r->version = SPOOLSS_DRIVER_VERSION_2012; + } + if (strequal(s, "3.0")) { + r->version = SPOOLSS_DRIVER_VERSION_200X; + } + } + + status = gp_inifile_getstring_ext(ctx, "Version:Provider", &s); + if (NT_STATUS_IS_OK(status)) { + if (s != NULL) { + r->provider = talloc_strdup(mem_ctx, s); + if (r->provider == NULL) { + return NT_STATUS_NO_MEMORY; + } + } + } + + status = process_driver_driverver(ctx, r); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + r->printer_driver_attributes &= ~PRINTER_DRIVER_SANDBOX_ENABLED; + + status = gp_inifile_getstring(ctx, "Version:DriverIsolation", &s); + if (NT_STATUS_IS_OK(status)) { + int cmp = strncasecmp_m(s, "2", 1); + if (cmp == 0) { + r->printer_driver_attributes |= PRINTER_DRIVER_SANDBOX_ENABLED; + } + cmp = strncasecmp_m(s, "0", 1); + if (cmp == 0) { + r->printer_driver_attributes &= ~PRINTER_DRIVER_SANDBOX_ENABLED; + } + } + + status = find_manufacturer_name(ctx, mem_ctx, "Manufacturer", &r->manufacturer_name); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = find_manufacturer_url(ctx, mem_ctx, "OEM URLS", r->manufacturer_name, &r->manufacturer_url); + if (!NT_STATUS_IS_OK(status)) { + /* not critical */ + } + + status = gp_inifile_getbool(ctx, "PrinterPackageInstallation:PackageAware", &ok); + if (NT_STATUS_IS_OK(status)) { + if (ok) { + r->printer_driver_attributes |= PRINTER_DRIVER_PACKAGE_AWARE; + } + } + + key = talloc_asprintf(mem_ctx, "%s.%s:%s", + "PrinterPackageInstallation", short_environment, "PackageAware"); + if (key == NULL) { + return NT_STATUS_NO_MEMORY; + } + + status = gp_inifile_getbool(ctx, key, &ok); + if (NT_STATUS_IS_OK(status)) { + if (ok) { + r->printer_driver_attributes |= PRINTER_DRIVER_PACKAGE_AWARE; + } + } + + key = talloc_asprintf(mem_ctx, "%s.%s:%s", + "PrinterPackageInstallation", short_environment, "CoreDriverDependencies"); + if (key == NULL) { + return NT_STATUS_NO_MEMORY; + } + + status = gp_inifile_getstring(ctx, key, &s); + if (NT_STATUS_IS_OK(status)) { + char **list; + r->core_driver_dependencies = talloc_zero(mem_ctx, struct spoolss_StringArray); + if (r->core_driver_dependencies == NULL) { + return NT_STATUS_NO_MEMORY; + } + + list = str_list_make_v3(r->core_driver_dependencies, s, ","); + if (list == NULL) { + return NT_STATUS_NO_MEMORY; + } + r->core_driver_dependencies->string = const_str_list(list); + } + + key = talloc_asprintf(mem_ctx, "%s.%s:%s", + "PrinterPackageInstallation", short_environment, "InboxVersionRequired"); + if (key == NULL) { + return NT_STATUS_NO_MEMORY; + } + + status = gp_inifile_getstring(ctx, key, &s); + if (NT_STATUS_IS_OK(status)) { + if (strequal(s, "UseDriverVer")) { + r->min_inbox_driver_ver_date = r->driver_date; + r->min_inbox_driver_ver_version = r->driver_version; + } + } + + return NT_STATUS_OK; +} + +/**************************************************************** + parse the a printer inf file +****************************************************************/ + +NTSTATUS driver_inf_parse(TALLOC_CTX *mem_ctx, + const char *core_driver_inf, + const char *filename, + const char *environment, + const char *driver_name, + struct spoolss_AddDriverInfo8 *r, + const char **source_disk_name) +{ + NTSTATUS status; + struct inf_context *inf_ctx; + + if (!filename || !environment) { + return NT_STATUS_INVALID_PARAMETER; + } + + status = init_inf_context(mem_ctx, + filename, + core_driver_inf, + &inf_ctx); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = setup_driver_by_name(mem_ctx, inf_ctx, + filename, + environment, + driver_name, + r, + source_disk_name); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return NT_STATUS_OK; +} + +NTSTATUS driver_inf_list(TALLOC_CTX *mem_ctx, + const char *core_driver_inf, + const char *filename, + const char *environment, + uint32_t *count, + struct spoolss_AddDriverInfo8 **_r) +{ + NTSTATUS status; + const char *short_environment; + size_t d, num_devices = 0; + const char **devices = NULL; + const char **device_values = NULL; + struct inf_context *inf_ctx; + + if (!filename || !environment) { + return NT_STATUS_INVALID_PARAMETER; + } + + short_environment = spoolss_get_short_filesys_environment(environment); + if (short_environment == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + status = init_inf_context(mem_ctx, + filename, + core_driver_inf, + &inf_ctx); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = enum_devices_in_toc(inf_ctx->ctx, mem_ctx, + &num_devices, + &devices, + &device_values); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + for (d = 0; d < num_devices; d++) { + + struct spoolss_AddDriverInfo8 r; + const char *source_disk_name; + + ZERO_STRUCT(r); + + status = setup_driver_by_name(mem_ctx, inf_ctx, filename, + environment, devices[d], &r, + &source_disk_name); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + ADD_TO_ARRAY(mem_ctx, struct spoolss_AddDriverInfo8, r, _r, count); + } + + return NT_STATUS_OK; +} |