diff options
Diffstat (limited to 'source3/libsmb')
43 files changed, 23062 insertions, 568 deletions
diff --git a/source3/libsmb/.cvsignore b/source3/libsmb/.cvsignore new file mode 100644 index 0000000000..07da2225c7 --- /dev/null +++ b/source3/libsmb/.cvsignore @@ -0,0 +1,3 @@ +*.po +*.po32 + diff --git a/source3/libsmb/asn1.c b/source3/libsmb/asn1.c new file mode 100644 index 0000000000..b4ad3ad0b8 --- /dev/null +++ b/source3/libsmb/asn1.c @@ -0,0 +1,408 @@ +/* + Unix SMB/CIFS implementation. + simple SPNEGO routines + Copyright (C) Andrew Tridgell 2001 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/* free an asn1 structure */ +void asn1_free(ASN1_DATA *data) +{ + SAFE_FREE(data->data); +} + +/* write to the ASN1 buffer, advancing the buffer pointer */ +BOOL asn1_write(ASN1_DATA *data, const void *p, int len) +{ + if (data->has_error) return False; + if (data->length < data->ofs+len) { + uint8 *newp; + newp = Realloc(data->data, data->ofs+len); + if (!newp) { + SAFE_FREE(data->data); + data->has_error = True; + return False; + } + data->data = newp; + data->length = data->ofs+len; + } + memcpy(data->data + data->ofs, p, len); + data->ofs += len; + return True; +} + +/* useful fn for writing a uint8 */ +BOOL asn1_write_uint8(ASN1_DATA *data, uint8 v) +{ + return asn1_write(data, &v, 1); +} + +/* push a tag onto the asn1 data buffer. Used for nested structures */ +BOOL asn1_push_tag(ASN1_DATA *data, uint8 tag) +{ + struct nesting *nesting; + + asn1_write_uint8(data, tag); + nesting = (struct nesting *)malloc(sizeof(struct nesting)); + if (!nesting) { + data->has_error = True; + return False; + } + + nesting->start = data->ofs; + nesting->next = data->nesting; + data->nesting = nesting; + return asn1_write_uint8(data, 0xff); +} + +/* pop a tag */ +BOOL asn1_pop_tag(ASN1_DATA *data) +{ + struct nesting *nesting; + size_t len; + + nesting = data->nesting; + + if (!nesting) { + data->has_error = True; + return False; + } + len = data->ofs - (nesting->start+1); + /* yes, this is ugly. We don't know in advance how many bytes the length + of a tag will take, so we assumed 1 byte. If we were wrong then we + need to correct our mistake */ + if (len > 255) { + data->data[nesting->start] = 0x82; + if (!asn1_write_uint8(data, 0)) return False; + if (!asn1_write_uint8(data, 0)) return False; + memmove(data->data+nesting->start+3, data->data+nesting->start+1, len); + data->data[nesting->start+1] = len>>8; + data->data[nesting->start+2] = len&0xff; + } else if (len > 127) { + data->data[nesting->start] = 0x81; + if (!asn1_write_uint8(data, 0)) return False; + memmove(data->data+nesting->start+2, data->data+nesting->start+1, len); + data->data[nesting->start+1] = len; + } else { + data->data[nesting->start] = len; + } + + data->nesting = nesting->next; + free(nesting); + return True; +} + + +/* write an integer */ +BOOL asn1_write_Integer(ASN1_DATA *data, int i) +{ + if (!asn1_push_tag(data, ASN1_INTEGER)) return False; + do { + asn1_write_uint8(data, i); + i = i >> 8; + } while (i); + return asn1_pop_tag(data); +} + +/* write an object ID to a ASN1 buffer */ +BOOL asn1_write_OID(ASN1_DATA *data, const char *OID) +{ + unsigned v, v2; + const char *p = (const char *)OID; + char *newp; + + if (!asn1_push_tag(data, ASN1_OID)) + return False; + v = strtol(p, &newp, 10); + p = newp; + v2 = strtol(p, &newp, 10); + p = newp; + if (!asn1_write_uint8(data, 40*v + v2)) + return False; + + while (*p) { + v = strtol(p, &newp, 10); + p = newp; + if (v >= (1<<28)) asn1_write_uint8(data, 0x80 | ((v>>28)&0xff)); + if (v >= (1<<21)) asn1_write_uint8(data, 0x80 | ((v>>21)&0xff)); + if (v >= (1<<14)) asn1_write_uint8(data, 0x80 | ((v>>14)&0xff)); + if (v >= (1<<7)) asn1_write_uint8(data, 0x80 | ((v>>7)&0xff)); + if (!asn1_write_uint8(data, v&0x7f)) + return False; + } + return asn1_pop_tag(data); +} + +/* write an octet string */ +BOOL asn1_write_OctetString(ASN1_DATA *data, const void *p, size_t length) +{ + asn1_push_tag(data, ASN1_OCTET_STRING); + asn1_write(data, p, length); + asn1_pop_tag(data); + return !data->has_error; +} + +/* write a general string */ +BOOL asn1_write_GeneralString(ASN1_DATA *data, const char *s) +{ + asn1_push_tag(data, ASN1_GENERAL_STRING); + asn1_write(data, s, strlen(s)); + asn1_pop_tag(data); + return !data->has_error; +} + +/* write a BOOLEAN */ +BOOL asn1_write_BOOLEAN(ASN1_DATA *data, BOOL v) +{ + asn1_write_uint8(data, ASN1_BOOLEAN); + asn1_write_uint8(data, v); + return !data->has_error; +} + +/* check a BOOLEAN */ +BOOL asn1_check_BOOLEAN(ASN1_DATA *data, BOOL v) +{ + uint8 b = 0; + + asn1_read_uint8(data, &b); + if (b != ASN1_BOOLEAN) { + data->has_error = True; + return False; + } + asn1_read_uint8(data, &b); + if (b != v) { + data->has_error = True; + return False; + } + return !data->has_error; +} + + +/* load a ASN1_DATA structure with a lump of data, ready to be parsed */ +BOOL asn1_load(ASN1_DATA *data, DATA_BLOB blob) +{ + ZERO_STRUCTP(data); + data->data = memdup(blob.data, blob.length); + if (!data->data) { + data->has_error = True; + return False; + } + data->length = blob.length; + return True; +} + +/* read from a ASN1 buffer, advancing the buffer pointer */ +BOOL asn1_read(ASN1_DATA *data, void *p, int len) +{ + if (data->ofs + len > data->length) { + data->has_error = True; + return False; + } + memcpy(p, data->data + data->ofs, len); + data->ofs += len; + return True; +} + +/* read a uint8 from a ASN1 buffer */ +BOOL asn1_read_uint8(ASN1_DATA *data, uint8 *v) +{ + return asn1_read(data, v, 1); +} + +/* start reading a nested asn1 structure */ +BOOL asn1_start_tag(ASN1_DATA *data, uint8 tag) +{ + uint8 b; + struct nesting *nesting; + + asn1_read_uint8(data, &b); + if (b != tag) { + data->has_error = True; + return False; + } + nesting = (struct nesting *)malloc(sizeof(struct nesting)); + if (!nesting) { + data->has_error = True; + return False; + } + + asn1_read_uint8(data, &b); + if (b & 0x80) { + int n = b & 0x7f; + if (n > 2) { + data->has_error = True; + return False; + } + asn1_read_uint8(data, &b); + nesting->taglen = b; + if (n == 2) { + asn1_read_uint8(data, &b); + nesting->taglen = (nesting->taglen << 8) | b; + } + } else { + nesting->taglen = b; + } + nesting->start = data->ofs; + nesting->next = data->nesting; + data->nesting = nesting; + return !data->has_error; +} + + +/* stop reading a tag */ +BOOL asn1_end_tag(ASN1_DATA *data) +{ + struct nesting *nesting; + + /* make sure we read it all */ + if (asn1_tag_remaining(data) != 0) { + data->has_error = True; + return False; + } + + nesting = data->nesting; + + if (!nesting) { + data->has_error = True; + return False; + } + + data->nesting = nesting->next; + free(nesting); + return True; +} + +/* work out how many bytes are left in this nested tag */ +int asn1_tag_remaining(ASN1_DATA *data) +{ + if (!data->nesting) { + data->has_error = True; + return -1; + } + return data->nesting->taglen - (data->ofs - data->nesting->start); +} + +/* read an object ID from a ASN1 buffer */ +BOOL asn1_read_OID(ASN1_DATA *data, char **OID) +{ + uint8 b; + pstring oid; + fstring el; + + if (!asn1_start_tag(data, ASN1_OID)) return False; + asn1_read_uint8(data, &b); + + oid[0] = 0; + snprintf(el, sizeof(el), "%u", b/40); + pstrcat(oid, el); + snprintf(el, sizeof(el), " %u", b%40); + pstrcat(oid, el); + + while (asn1_tag_remaining(data) > 0) { + unsigned v = 0; + do { + asn1_read_uint8(data, &b); + v = (v<<7) | (b&0x7f); + } while (!data->has_error && b & 0x80); + snprintf(el, sizeof(el), " %u", v); + pstrcat(oid, el); + } + + asn1_end_tag(data); + + *OID = strdup(oid); + + return !data->has_error; +} + +/* check that the next object ID is correct */ +BOOL asn1_check_OID(ASN1_DATA *data, char *OID) +{ + char *id; + + if (!asn1_read_OID(data, &id)) return False; + + if (strcmp(id, OID) != 0) { + data->has_error = True; + return False; + } + free(id); + return True; +} + +/* read a GeneralString from a ASN1 buffer */ +BOOL asn1_read_GeneralString(ASN1_DATA *data, char **s) +{ + int len; + if (!asn1_start_tag(data, ASN1_GENERAL_STRING)) return False; + len = asn1_tag_remaining(data); + *s = malloc(len+1); + if (! *s) { + data->has_error = True; + return False; + } + asn1_read(data, *s, len); + (*s)[len] = 0; + asn1_end_tag(data); + return !data->has_error; +} + +/* read a octet string blob */ +BOOL asn1_read_OctetString(ASN1_DATA *data, DATA_BLOB *blob) +{ + int len; + if (!asn1_start_tag(data, ASN1_OCTET_STRING)) return False; + len = asn1_tag_remaining(data); + *blob = data_blob(NULL, len); + asn1_read(data, blob->data, len); + asn1_end_tag(data); + return !data->has_error; +} + +/* read an interger */ +BOOL asn1_read_Integer(ASN1_DATA *data, int *i) +{ + uint8 b; + *i = 0; + + if (!asn1_start_tag(data, ASN1_INTEGER)) return False; + while (asn1_tag_remaining(data)>0) { + *i = (*i << 8) + asn1_read_uint8(data, &b); + } + return asn1_end_tag(data); + +} + +/* check a enumarted value is correct */ +BOOL asn1_check_enumerated(ASN1_DATA *data, int v) +{ + uint8 b; + if (!asn1_start_tag(data, ASN1_ENUMERATED)) return False; + asn1_read_uint8(data, &b); + asn1_end_tag(data); + return !data->has_error && (v == b); +} + +/* check a enumarted value is correct */ +BOOL asn1_write_enumerated(ASN1_DATA *data, uint8 v) +{ + if (!asn1_push_tag(data, ASN1_ENUMERATED)) return False; + asn1_write_uint8(data, v); + asn1_pop_tag(data); + return !data->has_error; +} diff --git a/source3/libsmb/cli_dfs.c b/source3/libsmb/cli_dfs.c new file mode 100644 index 0000000000..312275926c --- /dev/null +++ b/source3/libsmb/cli_dfs.c @@ -0,0 +1,253 @@ +/* + Unix SMB/CIFS implementation. + RPC pipe client + Copyright (C) Tim Potter 2000-2001, + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/* Opens a SMB connection to the netdfs pipe */ + +struct cli_state *cli_dfs_initialise(struct cli_state *cli, char *system_name, + struct ntuser_creds *creds) +{ + return cli_pipe_initialise(cli, system_name, PIPE_NETDFS, creds); +} + +/* Query DFS support */ + +NTSTATUS cli_dfs_exist(struct cli_state *cli, TALLOC_CTX *mem_ctx, + BOOL *dfs_exists) +{ + prs_struct qbuf, rbuf; + DFS_Q_DFS_EXIST q; + DFS_R_DFS_EXIST r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_dfs_q_dfs_exist(&q); + + if (!dfs_io_q_dfs_exist("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, DFS_EXIST, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarshall response */ + + if (!dfs_io_r_dfs_exist("", &r, &rbuf, 0)) { + goto done; + } + + /* Return result */ + + *dfs_exists = (r.status != 0); + + result = NT_STATUS_OK; + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +NTSTATUS cli_dfs_add(struct cli_state *cli, TALLOC_CTX *mem_ctx, + char *entrypath, char *servername, char *sharename, + char *comment, uint32 flags) +{ + prs_struct qbuf, rbuf; + DFS_Q_DFS_ADD q; + DFS_R_DFS_ADD r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_dfs_q_dfs_add(&q, entrypath, servername, sharename, comment, + flags); + + if (!dfs_io_q_dfs_add("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, DFS_ADD, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarshall response */ + + if (!dfs_io_r_dfs_add("", &r, &rbuf, 0)) { + goto done; + } + + /* Return result */ + + result = werror_to_ntstatus(r.status); + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +NTSTATUS cli_dfs_remove(struct cli_state *cli, TALLOC_CTX *mem_ctx, + char *entrypath, char *servername, char *sharename) +{ + prs_struct qbuf, rbuf; + DFS_Q_DFS_REMOVE q; + DFS_R_DFS_REMOVE r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_dfs_q_dfs_remove(&q, entrypath, servername, sharename); + + if (!dfs_io_q_dfs_remove("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, DFS_REMOVE, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarshall response */ + + if (!dfs_io_r_dfs_remove("", &r, &rbuf, 0)) { + goto done; + } + + /* Return result */ + + result = werror_to_ntstatus(r.status); + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +NTSTATUS cli_dfs_get_info(struct cli_state *cli, TALLOC_CTX *mem_ctx, + char *entrypath, char *servername, char *sharename, + uint32 info_level, DFS_INFO_CTR *ctr) + +{ + prs_struct qbuf, rbuf; + DFS_Q_DFS_GET_INFO q; + DFS_R_DFS_GET_INFO r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_dfs_q_dfs_get_info(&q, entrypath, servername, sharename, + info_level); + + if (!dfs_io_q_dfs_get_info("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, DFS_GET_INFO, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarshall response */ + + if (!dfs_io_r_dfs_get_info("", &r, &rbuf, 0)) { + goto done; + } + + /* Return result */ + + result = werror_to_ntstatus(r.status); + *ctr = r.ctr; + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/* Enumerate dfs shares */ + +NTSTATUS cli_dfs_enum(struct cli_state *cli, TALLOC_CTX *mem_ctx, + uint32 info_level, DFS_INFO_CTR *ctr) +{ + prs_struct qbuf, rbuf; + DFS_Q_DFS_ENUM q; + DFS_R_DFS_ENUM r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_dfs_q_dfs_enum(&q, info_level, ctr); + + if (!dfs_io_q_dfs_enum("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, DFS_ENUM, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarshall response */ + + r.ctr = ctr; + + if (!dfs_io_r_dfs_enum("", &r, &rbuf, 0)) { + goto done; + } + + /* Return result */ + + result = werror_to_ntstatus(r.status); + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} diff --git a/source3/libsmb/cli_lsarpc.c b/source3/libsmb/cli_lsarpc.c new file mode 100644 index 0000000000..3216854608 --- /dev/null +++ b/source3/libsmb/cli_lsarpc.c @@ -0,0 +1,1167 @@ +/* + Unix SMB/CIFS implementation. + RPC pipe client + Copyright (C) Tim Potter 2000-2001, + Copyright (C) Andrew Tridgell 1992-1997,2000, + Copyright (C) Luke Kenneth Casson Leighton 1996-1997,2000, + Copyright (C) Paul Ashton 1997,2000, + Copyright (C) Elrond 2000. + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/** @defgroup lsa LSA - Local Security Architecture + * @ingroup rpc_client + * + * @{ + **/ + +/** + * @file cli_lsarpc.c + * + * RPC client routines for the LSA RPC pipe. LSA means "local + * security authority", which is half of a password database. + **/ + +/** Opens a SMB connection and connects to the LSARPC pipe. + * + * @param cli Uninitialised client handle. + * @param system_name NETBIOS name of the machine to connect to. + * @param creds User credentials to connect as. + * @returns Initialised client handle. + */ +struct cli_state *cli_lsa_initialise(struct cli_state *cli, char *system_name, + struct ntuser_creds *creds) +{ + return cli_pipe_initialise(cli, system_name, PIPE_LSARPC, creds); +} + +/** Open a LSA policy handle + * + * @param cli Handle on an initialised SMB connection */ + +NTSTATUS cli_lsa_open_policy(struct cli_state *cli, TALLOC_CTX *mem_ctx, + BOOL sec_qos, uint32 des_access, POLICY_HND *pol) +{ + prs_struct qbuf, rbuf; + LSA_Q_OPEN_POL q; + LSA_R_OPEN_POL r; + LSA_SEC_QOS qos; + NTSTATUS result; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Initialise input parameters */ + + if (sec_qos) { + init_lsa_sec_qos(&qos, 2, 1, 0); + init_q_open_pol(&q, '\\', 0, des_access, &qos); + } else { + init_q_open_pol(&q, '\\', 0, des_access, NULL); + } + + /* Marshall data and send request */ + + if (!lsa_io_q_open_pol("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, LSA_OPENPOLICY, &qbuf, &rbuf)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Unmarshall response */ + + if (!lsa_io_r_open_pol("", &r, &rbuf, 0)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Return output parameters */ + + if (NT_STATUS_IS_OK(result = r.status)) { + *pol = r.pol; +#ifdef __INSURE__ + pol->marker = malloc(1); +#endif + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/** Open a LSA policy handle + * + * @param cli Handle on an initialised SMB connection + */ + +NTSTATUS cli_lsa_open_policy2(struct cli_state *cli, TALLOC_CTX *mem_ctx, + BOOL sec_qos, uint32 des_access, POLICY_HND *pol) +{ + prs_struct qbuf, rbuf; + LSA_Q_OPEN_POL2 q; + LSA_R_OPEN_POL2 r; + LSA_SEC_QOS qos; + NTSTATUS result; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Initialise input parameters */ + + if (sec_qos) { + init_lsa_sec_qos(&qos, 2, 1, 0); + init_q_open_pol2(&q, cli->srv_name_slash, 0, des_access, + &qos); + } else { + init_q_open_pol2(&q, cli->srv_name_slash, 0, des_access, + NULL); + } + + /* Marshall data and send request */ + + if (!lsa_io_q_open_pol2("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, LSA_OPENPOLICY2, &qbuf, &rbuf)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Unmarshall response */ + + if (!lsa_io_r_open_pol2("", &r, &rbuf, 0)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Return output parameters */ + + if (NT_STATUS_IS_OK(result = r.status)) { + *pol = r.pol; +#ifdef __INSURE__ + pol->marker = (char *)malloc(1); +#endif + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/** Close a LSA policy handle */ + +NTSTATUS cli_lsa_close(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *pol) +{ + prs_struct qbuf, rbuf; + LSA_Q_CLOSE q; + LSA_R_CLOSE r; + NTSTATUS result; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_lsa_q_close(&q, pol); + + if (!lsa_io_q_close("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, LSA_CLOSE, &qbuf, &rbuf)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Unmarshall response */ + + if (!lsa_io_r_close("", &r, &rbuf, 0)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Return output parameters */ + + if (NT_STATUS_IS_OK(result = r.status)) { +#ifdef __INSURE__ + SAFE_FREE(pol->marker); +#endif + *pol = r.pol; + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/** Lookup a list of sids */ + +NTSTATUS cli_lsa_lookup_sids(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *pol, int num_sids, DOM_SID *sids, + char ***domains, char ***names, uint32 **types, int *num_names) +{ + prs_struct qbuf, rbuf; + LSA_Q_LOOKUP_SIDS q; + LSA_R_LOOKUP_SIDS r; + DOM_R_REF ref; + LSA_TRANS_NAME_ENUM t_names; + NTSTATUS result; + int i; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_q_lookup_sids(mem_ctx, &q, pol, num_sids, sids, 1); + + if (!lsa_io_q_lookup_sids("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, LSA_LOOKUPSIDS, &qbuf, &rbuf)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Unmarshall response */ + + ZERO_STRUCT(ref); + ZERO_STRUCT(t_names); + + r.dom_ref = &ref; + r.names = &t_names; + + if (!lsa_io_r_lookup_sids("", &r, &rbuf, 0)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + result = r.status; + + if (!NT_STATUS_IS_OK(result) && + NT_STATUS_V(result) != NT_STATUS_V(NT_STATUS_FILES_OPEN)) { + /* An actual error occured */ + + goto done; + } + + + /* Return output parameters */ + + if (r.mapped_count == 0) { + result = NT_STATUS_NONE_MAPPED; + goto done; + } + + (*num_names) = r.mapped_count; + result = NT_STATUS_OK; + + if (!((*domains) = (char **)talloc(mem_ctx, sizeof(char *) * r.mapped_count))) { + DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n")); + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (!((*names) = (char **)talloc(mem_ctx, sizeof(char *) * r.mapped_count))) { + DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n")); + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (!((*types) = (uint32 *)talloc(mem_ctx, sizeof(uint32) * r.mapped_count))) { + DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n")); + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + for (i = 0; i < r.mapped_count; i++) { + fstring name, dom_name; + uint32 dom_idx = t_names.name[i].domain_idx; + + /* Translate optimised name through domain index array */ + + if (dom_idx != 0xffffffff) { + + rpcstr_pull_unistr2_fstring( + dom_name, &ref.ref_dom[dom_idx].uni_dom_name); + rpcstr_pull_unistr2_fstring( + name, &t_names.uni_name[i]); + + (*names)[i] = talloc_strdup(mem_ctx, name); + (*domains)[i] = talloc_strdup(mem_ctx, dom_name); + (*types)[i] = t_names.name[i].sid_name_use; + + if (((*names)[i] == NULL) || ((*domains)[i] == NULL)) { + DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n")); + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + } else { + (*names)[i] = NULL; + (*types)[i] = SID_NAME_UNKNOWN; + } + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/** Lookup a list of names */ + +NTSTATUS cli_lsa_lookup_names(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *pol, int num_names, const char **names, + DOM_SID **sids, uint32 **types, int *num_sids) +{ + prs_struct qbuf, rbuf; + LSA_Q_LOOKUP_NAMES q; + LSA_R_LOOKUP_NAMES r; + DOM_R_REF ref; + NTSTATUS result; + int i; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_q_lookup_names(mem_ctx, &q, pol, num_names, names); + + if (!lsa_io_q_lookup_names("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, LSA_LOOKUPNAMES, &qbuf, &rbuf)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Unmarshall response */ + + ZERO_STRUCT(ref); + r.dom_ref = &ref; + + if (!lsa_io_r_lookup_names("", &r, &rbuf, 0)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + result = r.status; + + if (!NT_STATUS_IS_OK(result)) { + /* An actual error occured */ + + goto done; + } + + + /* Return output parameters */ + + if (r.mapped_count == 0) { + result = NT_STATUS_NONE_MAPPED; + goto done; + } + + (*num_sids) = r.mapped_count; + result = NT_STATUS_OK; + + if (!((*sids = (DOM_SID *)talloc(mem_ctx, sizeof(DOM_SID) * r.mapped_count)))) { + DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n")); + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (!((*types = (uint32 *)talloc(mem_ctx, sizeof(uint32) * r.mapped_count)))) { + DEBUG(0, ("cli_lsa_lookup_sids(): out of memory\n")); + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + for (i = 0; i < r.mapped_count; i++) { + DOM_RID2 *t_rids = r.dom_rid; + uint32 dom_idx = t_rids[i].rid_idx; + uint32 dom_rid = t_rids[i].rid; + DOM_SID *sid = &(*sids)[i]; + + /* Translate optimised sid through domain index array */ + + if (dom_idx != 0xffffffff) { + + sid_copy(sid, &ref.ref_dom[dom_idx].ref_dom.sid); + + if (dom_rid != 0xffffffff) { + sid_append_rid(sid, dom_rid); + } + + (*types)[i] = t_rids[i].type; + } else { + ZERO_STRUCTP(sid); + (*types)[i] = SID_NAME_UNKNOWN; + } + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/** Query info policy + * + * @param domain_sid - returned remote server's domain sid */ + +NTSTATUS cli_lsa_query_info_policy(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *pol, uint16 info_class, + fstring domain_name, DOM_SID *domain_sid) +{ + prs_struct qbuf, rbuf; + LSA_Q_QUERY_INFO q; + LSA_R_QUERY_INFO r; + NTSTATUS result; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_q_query(&q, pol, info_class); + + if (!lsa_io_q_query("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, LSA_QUERYINFOPOLICY, &qbuf, &rbuf)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Unmarshall response */ + + if (!lsa_io_r_query("", &r, &rbuf, 0)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (!NT_STATUS_IS_OK(result = r.status)) { + goto done; + } + + /* Return output parameters */ + + ZERO_STRUCTP(domain_sid); + domain_name[0] = '\0'; + + switch (info_class) { + + case 3: + if (r.dom.id3.buffer_dom_name != 0) { + unistr2_to_ascii(domain_name, + &r.dom.id3. + uni_domain_name, + sizeof (fstring) - 1); + } + + if (r.dom.id3.buffer_dom_sid != 0) { + *domain_sid = r.dom.id3.dom_sid.sid; + } + + break; + + case 5: + + if (r.dom.id5.buffer_dom_name != 0) { + unistr2_to_ascii(domain_name, &r.dom.id5. + uni_domain_name, + sizeof (fstring) - 1); + } + + if (r.dom.id5.buffer_dom_sid != 0) { + *domain_sid = r.dom.id5.dom_sid.sid; + } + + break; + + default: + DEBUG(3, ("unknown info class %d\n", info_class)); + break; + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/** Enumerate list of trusted domains */ + +NTSTATUS cli_lsa_enum_trust_dom(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *pol, uint32 *enum_ctx, + uint32 *num_domains, char ***domain_names, + DOM_SID **domain_sids) +{ + prs_struct qbuf, rbuf; + LSA_Q_ENUM_TRUST_DOM q; + LSA_R_ENUM_TRUST_DOM r; + NTSTATUS result; + int i; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_q_enum_trust_dom(&q, pol, *enum_ctx, 0xffffffff); + + if (!lsa_io_q_enum_trust_dom("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, LSA_ENUMTRUSTDOM, &qbuf, &rbuf)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Unmarshall response */ + + if (!lsa_io_r_enum_trust_dom("", &r, &rbuf, 0)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + result = r.status; + + if (!NT_STATUS_IS_OK(result) && + NT_STATUS_V(result) != NT_STATUS_V(NT_STATUS_NO_MORE_ENTRIES)) { + + /* An actual error ocured */ + + goto done; + } + + result = NT_STATUS_OK; + + /* Return output parameters */ + + if (r.num_domains) { + + /* Allocate memory for trusted domain names and sids */ + + *domain_names = (char **)talloc(mem_ctx, sizeof(char *) * + r.num_domains); + + if (!*domain_names) { + DEBUG(0, ("cli_lsa_enum_trust_dom(): out of memory\n")); + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + *domain_sids = (DOM_SID *)talloc(mem_ctx, sizeof(DOM_SID) * + r.num_domains); + if (!domain_sids) { + DEBUG(0, ("cli_lsa_enum_trust_dom(): out of memory\n")); + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Copy across names and sids */ + + for (i = 0; i < r.num_domains; i++) { + fstring tmp; + + unistr2_to_ascii(tmp, &r.uni_domain_name[i], + sizeof(tmp) - 1); + (*domain_names)[i] = talloc_strdup(mem_ctx, tmp); + sid_copy(&(*domain_sids)[i], &r.domain_sid[i].sid); + } + } + + *num_domains = r.num_domains; + *enum_ctx = r.enum_context; + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/** Enumerate privileges*/ + +NTSTATUS cli_lsa_enum_privilege(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *pol, uint32 *enum_context, uint32 pref_max_length, + uint32 *count, char ***privs_name, uint32 **privs_high, uint32 **privs_low) +{ + prs_struct qbuf, rbuf; + LSA_Q_ENUM_PRIVS q; + LSA_R_ENUM_PRIVS r; + NTSTATUS result; + int i; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_q_enum_privs(&q, pol, *enum_context, pref_max_length); + + if (!lsa_io_q_enum_privs("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, LSA_ENUM_PRIVS, &qbuf, &rbuf)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Unmarshall response */ + + if (!lsa_io_r_enum_privs("", &r, &rbuf, 0)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (!NT_STATUS_IS_OK(result = r.status)) { + goto done; + } + + /* Return output parameters */ + + *enum_context = r.enum_context; + *count = r.count; + + if (!((*privs_name = (char **)talloc(mem_ctx, sizeof(char *) * r.count)))) { + DEBUG(0, ("(cli_lsa_enum_privilege): out of memory\n")); + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (!((*privs_high = (uint32 *)talloc(mem_ctx, sizeof(uint32) * r.count)))) { + DEBUG(0, ("(cli_lsa_enum_privilege): out of memory\n")); + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (!((*privs_low = (uint32 *)talloc(mem_ctx, sizeof(uint32) * r.count)))) { + DEBUG(0, ("(cli_lsa_enum_privilege): out of memory\n")); + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + for (i = 0; i < r.count; i++) { + fstring name; + + rpcstr_pull_unistr2_fstring( name, &r.privs[i].name); + + (*privs_name)[i] = talloc_strdup(mem_ctx, name); + + (*privs_high)[i] = r.privs[i].luid_high; + (*privs_low)[i] = r.privs[i].luid_low; + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/** Get privilege name */ + +NTSTATUS cli_lsa_get_dispname(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *pol, char *name, uint16 lang_id, uint16 lang_id_sys, + fstring description, uint16 *lang_id_desc) +{ + prs_struct qbuf, rbuf; + LSA_Q_PRIV_GET_DISPNAME q; + LSA_R_PRIV_GET_DISPNAME r; + NTSTATUS result; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_lsa_priv_get_dispname(&q, pol, name, lang_id, lang_id_sys); + + if (!lsa_io_q_priv_get_dispname("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, LSA_PRIV_GET_DISPNAME, &qbuf, &rbuf)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Unmarshall response */ + + if (!lsa_io_r_priv_get_dispname("", &r, &rbuf, 0)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (!NT_STATUS_IS_OK(result = r.status)) { + goto done; + } + + /* Return output parameters */ + + rpcstr_pull_unistr2_fstring(description , &r.desc); + *lang_id_desc = r.lang_id; + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/** Enumerate list of SIDs */ + +NTSTATUS cli_lsa_enum_sids(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *pol, uint32 *enum_ctx, uint32 pref_max_length, + uint32 *num_sids, DOM_SID **sids) +{ + prs_struct qbuf, rbuf; + LSA_Q_ENUM_ACCOUNTS q; + LSA_R_ENUM_ACCOUNTS r; + NTSTATUS result; + int i; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_lsa_q_enum_accounts(&q, pol, *enum_ctx, pref_max_length); + + if (!lsa_io_q_enum_accounts("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, LSA_ENUM_ACCOUNTS, &qbuf, &rbuf)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Unmarshall response */ + + if (!lsa_io_r_enum_accounts("", &r, &rbuf, 0)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + result = r.status; + + if (!NT_STATUS_IS_OK(result = r.status)) { + goto done; + } + + if (r.sids.num_entries==0) + goto done; + + /* Return output parameters */ + + *sids = (DOM_SID *)talloc(mem_ctx, sizeof(DOM_SID) * r.sids.num_entries); + if (!*sids) { + DEBUG(0, ("(cli_lsa_enum_sids): out of memory\n")); + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Copy across names and sids */ + + for (i = 0; i < r.sids.num_entries; i++) { + sid_copy(&(*sids)[i], &r.sids.sid[i].sid); + } + + *num_sids= r.sids.num_entries; + *enum_ctx = r.enum_context; + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/** Open a LSA user handle + * + * @param cli Handle on an initialised SMB connection */ + +NTSTATUS cli_lsa_open_account(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *dom_pol, DOM_SID *sid, uint32 des_access, + POLICY_HND *user_pol) +{ + prs_struct qbuf, rbuf; + LSA_Q_OPENACCOUNT q; + LSA_R_OPENACCOUNT r; + NTSTATUS result; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Initialise input parameters */ + + init_lsa_q_open_account(&q, dom_pol, sid, des_access); + + /* Marshall data and send request */ + + if (!lsa_io_q_open_account("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, LSA_OPENACCOUNT, &qbuf, &rbuf)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Unmarshall response */ + + if (!lsa_io_r_open_account("", &r, &rbuf, 0)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Return output parameters */ + + if (NT_STATUS_IS_OK(result = r.status)) { + *user_pol = r.pol; + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/** Enumerate user privileges + * + * @param cli Handle on an initialised SMB connection */ + +NTSTATUS cli_lsa_enum_privsaccount(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *pol, uint32 *count, LUID_ATTR **set) +{ + prs_struct qbuf, rbuf; + LSA_Q_ENUMPRIVSACCOUNT q; + LSA_R_ENUMPRIVSACCOUNT r; + NTSTATUS result; + int i; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Initialise input parameters */ + + init_lsa_q_enum_privsaccount(&q, pol); + + /* Marshall data and send request */ + + if (!lsa_io_q_enum_privsaccount("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, LSA_ENUMPRIVSACCOUNT, &qbuf, &rbuf)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Unmarshall response */ + + if (!lsa_io_r_enum_privsaccount("", &r, &rbuf, 0)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Return output parameters */ + + if (!NT_STATUS_IS_OK(result = r.status)) { + goto done; + } + + if (r.count == 0) + goto done; + + if (!((*set = (LUID_ATTR *)talloc(mem_ctx, sizeof(LUID_ATTR) * r.count)))) { + DEBUG(0, ("(cli_lsa_enum_privsaccount): out of memory\n")); + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + for (i=0; i<r.count; i++) { + (*set)[i].luid.low = r.set.set[i].luid.low; + (*set)[i].luid.high = r.set.set[i].luid.high; + (*set)[i].attr = r.set.set[i].attr; + } + + *count=r.count; + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/** Get a privilege value given its name */ + +NTSTATUS cli_lsa_lookupprivvalue(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *pol, char *name, LUID *luid) +{ + prs_struct qbuf, rbuf; + LSA_Q_LOOKUPPRIVVALUE q; + LSA_R_LOOKUPPRIVVALUE r; + NTSTATUS result; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_lsa_q_lookupprivvalue(&q, pol, name); + + if (!lsa_io_q_lookupprivvalue("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, LSA_LOOKUPPRIVVALUE, &qbuf, &rbuf)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Unmarshall response */ + + if (!lsa_io_r_lookupprivvalue("", &r, &rbuf, 0)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (!NT_STATUS_IS_OK(result = r.status)) { + goto done; + } + + /* Return output parameters */ + + (*luid).low=r.luid.low; + (*luid).high=r.luid.high; + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/** Query LSA security object */ + +NTSTATUS cli_lsa_query_secobj(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *pol, uint32 sec_info, + SEC_DESC_BUF **psdb) +{ + prs_struct qbuf, rbuf; + LSA_Q_QUERY_SEC_OBJ q; + LSA_R_QUERY_SEC_OBJ r; + NTSTATUS result; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_q_query_sec_obj(&q, pol, sec_info); + + if (!lsa_io_q_query_sec_obj("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, LSA_QUERYSECOBJ, &qbuf, &rbuf)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Unmarshall response */ + + if (!lsa_io_r_query_sec_obj("", &r, &rbuf, 0)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (!NT_STATUS_IS_OK(result = r.status)) { + goto done; + } + + /* Return output parameters */ + + if (psdb) + *psdb = r.buf; + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +#if 0 + +/** An example of how to use the routines in this file. Fetch a DOMAIN + sid. Does complete cli setup / teardown anonymously. */ + +BOOL fetch_domain_sid( char *domain, char *remote_machine, DOM_SID *psid) +{ + extern pstring global_myname; + struct cli_state cli; + NTSTATUS result; + POLICY_HND lsa_pol; + BOOL ret = False; + + ZERO_STRUCT(cli); + if(cli_initialise(&cli) == False) { + DEBUG(0,("fetch_domain_sid: unable to initialize client connection.\n")); + return False; + } + + if(!resolve_name( remote_machine, &cli.dest_ip, 0x20)) { + DEBUG(0,("fetch_domain_sid: Can't resolve address for %s\n", remote_machine)); + goto done; + } + + if (!cli_connect(&cli, remote_machine, &cli.dest_ip)) { + DEBUG(0,("fetch_domain_sid: unable to connect to SMB server on \ +machine %s. Error was : %s.\n", remote_machine, cli_errstr(&cli) )); + goto done; + } + + if (!attempt_netbios_session_request(&cli, global_myname, remote_machine, &cli.dest_ip)) { + DEBUG(0,("fetch_domain_sid: machine %s rejected the NetBIOS session request.\n", + remote_machine)); + goto done; + } + + cli.protocol = PROTOCOL_NT1; + + if (!cli_negprot(&cli)) { + DEBUG(0,("fetch_domain_sid: machine %s rejected the negotiate protocol. \ +Error was : %s.\n", remote_machine, cli_errstr(&cli) )); + goto done; + } + + if (cli.protocol != PROTOCOL_NT1) { + DEBUG(0,("fetch_domain_sid: machine %s didn't negotiate NT protocol.\n", + remote_machine)); + goto done; + } + + /* + * Do an anonymous session setup. + */ + + if (!cli_session_setup(&cli, "", "", 0, "", 0, "")) { + DEBUG(0,("fetch_domain_sid: machine %s rejected the session setup. \ +Error was : %s.\n", remote_machine, cli_errstr(&cli) )); + goto done; + } + + if (!(cli.sec_mode & 1)) { + DEBUG(0,("fetch_domain_sid: machine %s isn't in user level security mode\n", + remote_machine)); + goto done; + } + + if (!cli_send_tconX(&cli, "IPC$", "IPC", "", 1)) { + DEBUG(0,("fetch_domain_sid: machine %s rejected the tconX on the IPC$ share. \ +Error was : %s.\n", remote_machine, cli_errstr(&cli) )); + goto done; + } + + /* Fetch domain sid */ + + if (!cli_nt_session_open(&cli, PIPE_LSARPC)) { + DEBUG(0, ("fetch_domain_sid: Error connecting to SAM pipe\n")); + goto done; + } + + result = cli_lsa_open_policy(&cli, cli.mem_ctx, True, SEC_RIGHTS_QUERY_VALUE, &lsa_pol); + if (!NT_STATUS_IS_OK(result)) { + DEBUG(0, ("fetch_domain_sid: Error opening lsa policy handle. %s\n", + nt_errstr(result) )); + goto done; + } + + result = cli_lsa_query_info_policy(&cli, cli.mem_ctx, &lsa_pol, 5, domain, psid); + if (!NT_STATUS_IS_OK(result)) { + DEBUG(0, ("fetch_domain_sid: Error querying lsa policy handle. %s\n", + nt_errstr(result) )); + goto done; + } + + ret = True; + + done: + + cli_shutdown(&cli); + return ret; +} + +#endif + +/** @} **/ diff --git a/source3/libsmb/cli_netlogon.c b/source3/libsmb/cli_netlogon.c new file mode 100644 index 0000000000..125590b6d3 --- /dev/null +++ b/source3/libsmb/cli_netlogon.c @@ -0,0 +1,673 @@ +/* + Unix SMB/CIFS implementation. + NT Domain Authentication SMB / MSRPC client + Copyright (C) Andrew Tridgell 1992-2000 + Copyright (C) Luke Kenneth Casson Leighton 1996-2000 + Copyright (C) Tim Potter 2001 + Copyright (C) Paul Ashton 1997. + Copyright (C) Jeremy Allison 1998. + Copyright (C) Andrew Bartlett 2001. + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/* Opens a SMB connection to the netlogon pipe */ + +struct cli_state *cli_netlogon_initialise(struct cli_state *cli, + char *system_name, + struct ntuser_creds *creds) +{ + return cli_pipe_initialise(cli, system_name, PIPE_NETLOGON, creds); +} + +/* LSA Request Challenge. Sends our challenge to server, then gets + server response. These are used to generate the credentials. */ + +NTSTATUS new_cli_net_req_chal(struct cli_state *cli, DOM_CHAL *clnt_chal, + DOM_CHAL *srv_chal) +{ + prs_struct qbuf, rbuf; + NET_Q_REQ_CHAL q; + NET_R_REQ_CHAL r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + extern pstring global_myname; + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, cli->mem_ctx, MARSHALL); + prs_init(&rbuf, 0, cli->mem_ctx, UNMARSHALL); + + /* create and send a MSRPC command with api NET_REQCHAL */ + + DEBUG(4,("cli_net_req_chal: LSA Request Challenge from %s to %s: %s\n", + cli->desthost, global_myname, credstr(clnt_chal->data))); + + /* store the parameters */ + init_q_req_chal(&q, cli->srv_name_slash, global_myname, clnt_chal); + + /* Marshall data and send request */ + + if (!net_io_q_req_chal("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, NET_REQCHAL, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarhall response */ + + if (!net_io_r_req_chal("", &r, &rbuf, 0)) { + goto done; + } + + result = r.status; + + /* Return result */ + + if (NT_STATUS_IS_OK(result)) { + memcpy(srv_chal, r.srv_chal.data, sizeof(srv_chal->data)); + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/**************************************************************************** +LSA Authenticate 2 + +Send the client credential, receive back a server credential. +Ensure that the server credential returned matches the session key +encrypt of the server challenge originally received. JRA. +****************************************************************************/ + +NTSTATUS new_cli_net_auth2(struct cli_state *cli, + uint16 sec_chan, + uint32 neg_flags, DOM_CHAL *srv_chal) +{ + prs_struct qbuf, rbuf; + NET_Q_AUTH_2 q; + NET_R_AUTH_2 r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + extern pstring global_myname; + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, cli->mem_ctx, MARSHALL); + prs_init(&rbuf, 0, cli->mem_ctx, UNMARSHALL); + + /* create and send a MSRPC command with api NET_AUTH2 */ + + DEBUG(4,("cli_net_auth2: srv:%s acct:%s sc:%x mc: %s chal %s neg: %x\n", + cli->srv_name_slash, cli->mach_acct, sec_chan, global_myname, + credstr(cli->clnt_cred.challenge.data), neg_flags)); + + /* store the parameters */ + init_q_auth_2(&q, cli->srv_name_slash, cli->mach_acct, + sec_chan, global_myname, &cli->clnt_cred.challenge, + neg_flags); + + /* turn parameters into data stream */ + + if (!net_io_q_auth_2("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, NET_AUTH2, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarshall response */ + + if (!net_io_r_auth_2("", &r, &rbuf, 0)) { + goto done; + } + + result = r.status; + + if (NT_STATUS_IS_OK(result)) { + UTIME zerotime; + + /* + * Check the returned value using the initial + * server received challenge. + */ + + zerotime.time = 0; + if (cred_assert( &r.srv_chal, cli->sess_key, srv_chal, + zerotime) == 0) { + + /* + * Server replied with bad credential. Fail. + */ + DEBUG(0,("cli_net_auth2: server %s replied with bad credential (bad machine \ +password ?).\n", cli->desthost )); + result = NT_STATUS_ACCESS_DENIED; + goto done; + } + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/* Initialize domain session credentials */ + +NTSTATUS new_cli_nt_setup_creds(struct cli_state *cli, + uint16 sec_chan, + const unsigned char mach_pwd[16]) +{ + DOM_CHAL clnt_chal; + DOM_CHAL srv_chal; + UTIME zerotime; + NTSTATUS result; + + /******************* Request Challenge ********************/ + + generate_random_buffer(clnt_chal.data, 8, False); + + /* send a client challenge; receive a server challenge */ + result = new_cli_net_req_chal(cli, &clnt_chal, &srv_chal); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(0,("cli_nt_setup_creds: request challenge failed\n")); + return result; + } + + /**************** Long-term Session key **************/ + + /* calculate the session key */ + cred_session_key(&clnt_chal, &srv_chal, mach_pwd, + cli->sess_key); + memset((char *)cli->sess_key+8, '\0', 8); + + /******************* Authenticate 2 ********************/ + + /* calculate auth-2 credentials */ + zerotime.time = 0; + cred_create(cli->sess_key, &clnt_chal, zerotime, + &cli->clnt_cred.challenge); + + /* + * Send client auth-2 challenge. + * Receive an auth-2 challenge response and check it. + */ + + result = new_cli_net_auth2(cli, sec_chan, 0x000001ff, + &srv_chal); + if (!NT_STATUS_IS_OK(result)) { + DEBUG(0,("cli_nt_setup_creds: auth2 challenge failed %s\n", + nt_errstr(result))); + } + + return result; +} + +/* Logon Control 2 */ + +NTSTATUS cli_netlogon_logon_ctrl2(struct cli_state *cli, TALLOC_CTX *mem_ctx, + uint32 query_level) +{ + prs_struct qbuf, rbuf; + NET_Q_LOGON_CTRL2 q; + NET_R_LOGON_CTRL2 r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Initialise input parameters */ + + init_net_q_logon_ctrl2(&q, cli->srv_name_slash, query_level); + + /* Marshall data and send request */ + + if (!net_io_q_logon_ctrl2("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, NET_LOGON_CTRL2, &qbuf, &rbuf)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Unmarshall response */ + + if (!net_io_r_logon_ctrl2("", &r, &rbuf, 0)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + result = r.status; + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/**************************************************************************** +Generate the next creds to use. Yuck - this is a cut&paste from another +file. They should be combined at some stage. )-: +****************************************************************************/ + +static void gen_next_creds( struct cli_state *cli, DOM_CRED *new_clnt_cred) +{ + /* + * Create the new client credentials. + */ + + cli->clnt_cred.timestamp.time = time(NULL); + + memcpy(new_clnt_cred, &cli->clnt_cred, sizeof(*new_clnt_cred)); + + /* Calculate the new credentials. */ + cred_create(cli->sess_key, &(cli->clnt_cred.challenge), + new_clnt_cred->timestamp, &(new_clnt_cred->challenge)); + +} + +/* Sam synchronisation */ + +NTSTATUS cli_netlogon_sam_sync(struct cli_state *cli, TALLOC_CTX *mem_ctx, DOM_CRED *ret_creds, + uint32 database_id, uint32 *num_deltas, + SAM_DELTA_HDR **hdr_deltas, + SAM_DELTA_CTR **deltas) +{ + prs_struct qbuf, rbuf; + NET_Q_SAM_SYNC q; + NET_R_SAM_SYNC r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + DOM_CRED clnt_creds; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Initialise input parameters */ + + gen_next_creds(cli, &clnt_creds); + + init_net_q_sam_sync(&q, cli->srv_name_slash, cli->clnt_name_slash + 2, + &clnt_creds, ret_creds, database_id); + + /* Marshall data and send request */ + + if (!net_io_q_sam_sync("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, NET_SAM_SYNC, &qbuf, &rbuf)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Unmarshall response */ + + if (!net_io_r_sam_sync("", cli->sess_key, &r, &rbuf, 0)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Return results */ + + result = r.status; + *num_deltas = r.num_deltas2; + *hdr_deltas = r.hdr_deltas; + *deltas = r.deltas; + + memcpy(ret_creds, &r.srv_creds, sizeof(*ret_creds)); + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/* Sam synchronisation */ + +NTSTATUS cli_netlogon_sam_deltas(struct cli_state *cli, TALLOC_CTX *mem_ctx, + uint32 database_id, UINT64_S seqnum, + uint32 *num_deltas, + SAM_DELTA_HDR **hdr_deltas, + SAM_DELTA_CTR **deltas) +{ + prs_struct qbuf, rbuf; + NET_Q_SAM_DELTAS q; + NET_R_SAM_DELTAS r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + DOM_CRED clnt_creds; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Initialise input parameters */ + + gen_next_creds(cli, &clnt_creds); + + init_net_q_sam_deltas(&q, cli->srv_name_slash, + cli->clnt_name_slash + 2, &clnt_creds, + database_id, seqnum); + + /* Marshall data and send request */ + + if (!net_io_q_sam_deltas("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, NET_SAM_DELTAS, &qbuf, &rbuf)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Unmarshall response */ + + if (!net_io_r_sam_deltas("", cli->sess_key, &r, &rbuf, 0)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Return results */ + + result = r.status; + *num_deltas = r.num_deltas2; + *hdr_deltas = r.hdr_deltas; + *deltas = r.deltas; + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/* Logon domain user */ + +NTSTATUS cli_netlogon_sam_logon(struct cli_state *cli, TALLOC_CTX *mem_ctx, + char *username, char *password, + int logon_type) +{ + prs_struct qbuf, rbuf; + NET_Q_SAM_LOGON q; + NET_R_SAM_LOGON r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + DOM_CRED clnt_creds, dummy_rtn_creds; + extern pstring global_myname; + NET_ID_INFO_CTR ctr; + NET_USER_INFO_3 user; + int validation_level = 3; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Initialise input parameters */ + + gen_next_creds(cli, &clnt_creds); + + q.validation_level = validation_level; + + memset(&dummy_rtn_creds, '\0', sizeof(dummy_rtn_creds)); + dummy_rtn_creds.timestamp.time = time(NULL); + + ctr.switch_value = logon_type; + + switch (logon_type) { + case INTERACTIVE_LOGON_TYPE: { + unsigned char lm_owf_user_pwd[16], nt_owf_user_pwd[16]; + + nt_lm_owf_gen(password, nt_owf_user_pwd, lm_owf_user_pwd); + + init_id_info1(&ctr.auth.id1, lp_workgroup(), + 0, /* param_ctrl */ + 0xdead, 0xbeef, /* LUID? */ + username, cli->clnt_name_slash, + cli->sess_key, lm_owf_user_pwd, + nt_owf_user_pwd); + + break; + } + case NET_LOGON_TYPE: { + uint8 chal[8]; + unsigned char local_lm_response[24]; + unsigned char local_nt_response[24]; + + generate_random_buffer(chal, 8, False); + + SMBencrypt(password, chal, local_lm_response); + SMBNTencrypt(password, chal, local_nt_response); + + init_id_info2(&ctr.auth.id2, lp_workgroup(), + 0, /* param_ctrl */ + 0xdead, 0xbeef, /* LUID? */ + username, cli->clnt_name_slash, chal, + local_lm_response, 24, local_nt_response, 24); + break; + } + default: + DEBUG(0, ("switch value %d not supported\n", + ctr.switch_value)); + goto done; + } + + init_sam_info(&q.sam_id, cli->srv_name_slash, global_myname, + &clnt_creds, &dummy_rtn_creds, logon_type, + &ctr); + + /* Marshall data and send request */ + + if (!net_io_q_sam_logon("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, NET_SAMLOGON, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarshall response */ + + r.user = &user; + + if (!net_io_r_sam_logon("", &r, &rbuf, 0)) { + goto done; + } + + /* Return results */ + + result = r.status; + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + + +/** + * Logon domain user with an 'network' SAM logon + * + * @param info3 Pointer to a NET_USER_INFO_3 already allocated by the caller. + **/ + +NTSTATUS cli_netlogon_sam_network_logon(struct cli_state *cli, TALLOC_CTX *mem_ctx, + const char *username, const char *domain, const char *workstation, + const uint8 chal[8], + DATA_BLOB lm_response, DATA_BLOB nt_response, + NET_USER_INFO_3 *info3) + +{ + prs_struct qbuf, rbuf; + NET_Q_SAM_LOGON q; + NET_R_SAM_LOGON r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + DOM_CRED clnt_creds, dummy_rtn_creds; + NET_ID_INFO_CTR ctr; + extern pstring global_myname; + int validation_level = 3; + char *workstation_name_slash; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + workstation_name_slash = talloc_asprintf(mem_ctx, "\\\\%s", workstation); + + if (!workstation_name_slash) { + DEBUG(0, ("talloc_asprintf failed!\n")); + return NT_STATUS_NO_MEMORY; + } + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Initialise input parameters */ + + gen_next_creds(cli, &clnt_creds); + + q.validation_level = validation_level; + + memset(&dummy_rtn_creds, '\0', sizeof(dummy_rtn_creds)); + dummy_rtn_creds.timestamp.time = time(NULL); + + ctr.switch_value = NET_LOGON_TYPE; + + init_id_info2(&ctr.auth.id2, domain, + 0, /* param_ctrl */ + 0xdead, 0xbeef, /* LUID? */ + username, workstation_name_slash, (const uchar*)chal, + lm_response.data, lm_response.length, nt_response.data, nt_response.length); + + init_sam_info(&q.sam_id, cli->srv_name_slash, global_myname, + &clnt_creds, &dummy_rtn_creds, NET_LOGON_TYPE, + &ctr); + + /* Marshall data and send request */ + + if (!net_io_q_sam_logon("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, NET_SAMLOGON, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarshall response */ + + r.user = info3; + + if (!net_io_r_sam_logon("", &r, &rbuf, 0)) { + goto done; + } + + /* Return results */ + + result = r.status; + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/*************************************************************************** +LSA Server Password Set. +****************************************************************************/ + +NTSTATUS cli_net_srv_pwset(struct cli_state *cli, TALLOC_CTX *mem_ctx, + char* machine_name, uint8 hashed_mach_pwd[16]) +{ + prs_struct rbuf; + prs_struct qbuf; + DOM_CRED new_clnt_cred; + NET_Q_SRV_PWSET q_s; + uint16 sec_chan_type = 2; + NTSTATUS nt_status; + char *mach_acct; + + gen_next_creds( cli, &new_clnt_cred); + + prs_init(&qbuf , 1024, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* create and send a MSRPC command with api NET_SRV_PWSET */ + + mach_acct = talloc_asprintf(mem_ctx, "%s$", machine_name); + + if (!mach_acct) { + DEBUG(0,("talloc_asprintf failed!\n")); + nt_status = NT_STATUS_NO_MEMORY; + goto done; + } + + DEBUG(4,("cli_net_srv_pwset: srv:%s acct:%s sc: %d mc: %s clnt %s %x\n", + cli->srv_name_slash, mach_acct, sec_chan_type, machine_name, + credstr(new_clnt_cred.challenge.data), new_clnt_cred.timestamp.time)); + + /* store the parameters */ + init_q_srv_pwset(&q_s, cli->srv_name_slash, cli->sess_key, + mach_acct, sec_chan_type, machine_name, + &new_clnt_cred, (char *)hashed_mach_pwd); + + /* turn parameters into data stream */ + if(!net_io_q_srv_pwset("", &q_s, &qbuf, 0)) { + DEBUG(0,("cli_net_srv_pwset: Error : failed to marshall NET_Q_SRV_PWSET struct.\n")); + nt_status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* send the data on \PIPE\ */ + if (rpc_api_pipe_req(cli, NET_SRVPWSET, &qbuf, &rbuf)) + { + NET_R_SRV_PWSET r_s; + + if (!net_io_r_srv_pwset("", &r_s, &rbuf, 0)) { + nt_status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + nt_status = r_s.status; + + if (!NT_STATUS_IS_OK(r_s.status)) + { + /* report error code */ + DEBUG(0,("cli_net_srv_pwset: %s\n", nt_errstr(nt_status))); + goto done; + } + + /* Update the credentials. */ + if (!clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred), &(r_s.srv_cred))) + { + /* + * Server replied with bad credential. Fail. + */ + DEBUG(0,("cli_net_srv_pwset: server %s replied with bad credential (bad machine \ +password ?).\n", cli->desthost )); + nt_status = NT_STATUS_UNSUCCESSFUL; + } + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return nt_status; +} + diff --git a/source3/libsmb/cli_pipe_util.c b/source3/libsmb/cli_pipe_util.c new file mode 100644 index 0000000000..de1c832e44 --- /dev/null +++ b/source3/libsmb/cli_pipe_util.c @@ -0,0 +1,82 @@ +/* + Unix SMB/CIFS implementation. + RPC pipe client utility functions + Copyright (C) Tim Potter 2001, + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/** \defgroup rpc_client RPC Client routines + */ + +/* Opens a SMB connection to a named pipe */ + +struct cli_state *cli_pipe_initialise(struct cli_state *cli, char *system_name, + char *pipe_name, + struct ntuser_creds *creds) +{ + struct in_addr dest_ip; + struct nmb_name calling, called; + fstring dest_host; + extern pstring global_myname; + struct ntuser_creds anon; + + /* Initialise cli_state information */ + + if (!cli_initialise(cli)) { + return NULL; + } + + if (!creds) { + ZERO_STRUCT(anon); + anon.pwd.null_pwd = 1; + creds = &anon; + } + + cli_init_creds(cli, creds); + + /* Establish a SMB connection */ + + if (!resolve_srv_name(system_name, dest_host, &dest_ip)) { + return NULL; + } + + make_nmb_name(&called, dns_to_netbios_name(dest_host), 0x20); + make_nmb_name(&calling, dns_to_netbios_name(global_myname), 0); + + if (!cli_establish_connection(cli, dest_host, &dest_ip, &calling, + &called, "IPC$", "IPC", False, True)) { + return NULL; + } + + /* Open a NT session thingy */ + + if (!cli_nt_session_open(cli, pipe_name)) { + cli_shutdown(cli); + return NULL; + } + + return cli; +} + +/* Shut down a SMB connection to the SAMR pipe */ + +void cli_pipe_shutdown(struct cli_state *cli) +{ + if (cli->fd != -1) cli_ulogoff(cli); + cli_shutdown(cli); +} diff --git a/source3/libsmb/cli_reg.c b/source3/libsmb/cli_reg.c new file mode 100644 index 0000000000..c09ccabb29 --- /dev/null +++ b/source3/libsmb/cli_reg.c @@ -0,0 +1,111 @@ +/* + Unix SMB/CIFS implementation. + RPC Pipe client + + Copyright (C) Andrew Tridgell 1992-1998, + Copyright (C) Luke Kenneth Casson Leighton 1996-1998, + Copyright (C) Paul Ashton 1997-1998. + Copyright (C) Jeremy Allison 1999. + Copyright (C) Simo Sorce 2001 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/* Opens a SMB connection to the WINREG pipe */ + +struct cli_state *cli_winreg_initialise(struct cli_state *cli, + char *system_name, + struct ntuser_creds *creds) +{ + return cli_pipe_initialise(cli, system_name, PIPE_WINREG, creds); +} + +/* Shutdown a server */ + +NTSTATUS cli_reg_shutdown(struct cli_state * cli, TALLOC_CTX *mem_ctx, + const char *msg, uint32 timeout, uint16 flags) +{ + prs_struct qbuf; + prs_struct rbuf; + REG_Q_SHUTDOWN q_s; + REG_R_SHUTDOWN r_s; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + + if (msg == NULL) return NT_STATUS_INVALID_PARAMETER; + + ZERO_STRUCT (q_s); + ZERO_STRUCT (r_s); + + prs_init(&qbuf , MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_reg_q_shutdown(&q_s, msg, timeout, flags); + + if (!reg_io_q_shutdown("", &q_s, &qbuf, 0) || + !rpc_api_pipe_req(cli, REG_SHUTDOWN, &qbuf, &rbuf)) + goto done; + + /* Unmarshall response */ + + if(reg_io_r_shutdown("", &r_s, &rbuf, 0)) + result = r_s.status; + +done: + prs_mem_free(&rbuf); + prs_mem_free(&qbuf); + + return result; +} + + +/* Abort a server shutdown */ + +NTSTATUS cli_reg_abort_shutdown(struct cli_state * cli, TALLOC_CTX *mem_ctx) +{ + prs_struct rbuf; + prs_struct qbuf; + REG_Q_ABORT_SHUTDOWN q_s; + REG_R_ABORT_SHUTDOWN r_s; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + + ZERO_STRUCT (q_s); + ZERO_STRUCT (r_s); + + prs_init(&qbuf , MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_reg_q_abort_shutdown(&q_s); + + if (!reg_io_q_abort_shutdown("", &q_s, &qbuf, 0) || + !rpc_api_pipe_req(cli, REG_ABORT_SHUTDOWN, &qbuf, &rbuf)) + goto done; + + /* Unmarshall response */ + + if (reg_io_r_abort_shutdown("", &r_s, &rbuf, 0)) + result = r_s.status; + +done: + prs_mem_free(&rbuf); + prs_mem_free(&qbuf ); + + return result; +} diff --git a/source3/libsmb/cli_samr.c b/source3/libsmb/cli_samr.c new file mode 100644 index 0000000000..85a7375f99 --- /dev/null +++ b/source3/libsmb/cli_samr.c @@ -0,0 +1,1274 @@ +/* + Unix SMB/CIFS implementation. + RPC pipe client + Copyright (C) Tim Potter 2000-2001, + Copyright (C) Andrew Tridgell 1992-1997,2000, + Copyright (C) Luke Kenneth Casson Leighton 1996-1997,2000, + Copyright (C) Paul Ashton 1997,2000, + Copyright (C) Elrond 2000. + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/* Opens a SMB connection to the SAMR pipe */ + +struct cli_state *cli_samr_initialise(struct cli_state *cli, char *system_name, + struct ntuser_creds *creds) +{ + return cli_pipe_initialise(cli, system_name, PIPE_SAMR, creds); +} + +/* Connect to SAMR database */ + +NTSTATUS cli_samr_connect(struct cli_state *cli, TALLOC_CTX *mem_ctx, + uint32 access_mask, POLICY_HND *connect_pol) +{ + prs_struct qbuf, rbuf; + SAMR_Q_CONNECT q; + SAMR_R_CONNECT r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_samr_q_connect(&q, cli->desthost, access_mask); + + if (!samr_io_q_connect("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SAMR_CONNECT, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarshall response */ + + if (!samr_io_r_connect("", &r, &rbuf, 0)) { + goto done; + } + + /* Return output parameters */ + + if (NT_STATUS_IS_OK(result = r.status)) { + *connect_pol = r.connect_pol; +#ifdef __INSURE__ + connect_pol->marker = malloc(1); +#endif + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/* Close SAMR handle */ + +NTSTATUS cli_samr_close(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *connect_pol) +{ + prs_struct qbuf, rbuf; + SAMR_Q_CLOSE_HND q; + SAMR_R_CLOSE_HND r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_samr_q_close_hnd(&q, connect_pol); + + if (!samr_io_q_close_hnd("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SAMR_CLOSE_HND, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarshall response */ + + if (!samr_io_r_close_hnd("", &r, &rbuf, 0)) { + goto done; + } + + /* Return output parameters */ + + if (NT_STATUS_IS_OK(result = r.status)) { +#ifdef __INSURE__ + SAFE_FREE(connect_pol->marker); +#endif + *connect_pol = r.pol; + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/* Open handle on a domain */ + +NTSTATUS cli_samr_open_domain(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *connect_pol, uint32 access_mask, + const DOM_SID *domain_sid, POLICY_HND *domain_pol) +{ + prs_struct qbuf, rbuf; + SAMR_Q_OPEN_DOMAIN q; + SAMR_R_OPEN_DOMAIN r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_samr_q_open_domain(&q, connect_pol, access_mask, domain_sid); + + if (!samr_io_q_open_domain("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SAMR_OPEN_DOMAIN, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarshall response */ + + if (!samr_io_r_open_domain("", &r, &rbuf, 0)) { + goto done; + } + + /* Return output parameters */ + + if (NT_STATUS_IS_OK(result = r.status)) { + *domain_pol = r.domain_pol; +#ifdef __INSURE__ + domain_pol->marker = malloc(1); +#endif + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/* Open handle on a user */ + +NTSTATUS cli_samr_open_user(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *domain_pol, uint32 access_mask, + uint32 user_rid, POLICY_HND *user_pol) +{ + prs_struct qbuf, rbuf; + SAMR_Q_OPEN_USER q; + SAMR_R_OPEN_USER r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_samr_q_open_user(&q, domain_pol, access_mask, user_rid); + + if (!samr_io_q_open_user("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SAMR_OPEN_USER, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarshall response */ + + if (!samr_io_r_open_user("", &r, &rbuf, 0)) { + goto done; + } + + /* Return output parameters */ + + if (NT_STATUS_IS_OK(result = r.status)) { + *user_pol = r.user_pol; +#ifdef __INSURE__ + user_pol->marker = malloc(1); +#endif + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/* Open handle on a group */ + +NTSTATUS cli_samr_open_group(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *domain_pol, uint32 access_mask, + uint32 group_rid, POLICY_HND *group_pol) +{ + prs_struct qbuf, rbuf; + SAMR_Q_OPEN_GROUP q; + SAMR_R_OPEN_GROUP r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_samr_q_open_group(&q, domain_pol, access_mask, group_rid); + + if (!samr_io_q_open_group("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SAMR_OPEN_GROUP, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarshall response */ + + if (!samr_io_r_open_group("", &r, &rbuf, 0)) { + goto done; + } + + /* Return output parameters */ + + if (NT_STATUS_IS_OK(result = r.status)) { + *group_pol = r.pol; +#ifdef __INSURE__ + group_pol->marker = malloc(1); +#endif + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/* Query user info */ + +NTSTATUS cli_samr_query_userinfo(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *user_pol, uint16 switch_value, + SAM_USERINFO_CTR **ctr) +{ + prs_struct qbuf, rbuf; + SAMR_Q_QUERY_USERINFO q; + SAMR_R_QUERY_USERINFO r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_samr_q_query_userinfo(&q, user_pol, switch_value); + + if (!samr_io_q_query_userinfo("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SAMR_QUERY_USERINFO, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarshall response */ + + if (!samr_io_r_query_userinfo("", &r, &rbuf, 0)) { + goto done; + } + + /* Return output parameters */ + + result = r.status; + *ctr = r.ctr; + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/* Query group info */ + +NTSTATUS cli_samr_query_groupinfo(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *group_pol, uint32 info_level, + GROUP_INFO_CTR *ctr) +{ + prs_struct qbuf, rbuf; + SAMR_Q_QUERY_GROUPINFO q; + SAMR_R_QUERY_GROUPINFO r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_samr_q_query_groupinfo(&q, group_pol, info_level); + + if (!samr_io_q_query_groupinfo("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SAMR_QUERY_GROUPINFO, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarshall response */ + + r.ctr = ctr; + + if (!samr_io_r_query_groupinfo("", &r, &rbuf, 0)) { + goto done; + } + + /* Return output parameters */ + + result = r.status; + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/* Query user groups */ + +NTSTATUS cli_samr_query_usergroups(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *user_pol, uint32 *num_groups, + DOM_GID **gid) +{ + prs_struct qbuf, rbuf; + SAMR_Q_QUERY_USERGROUPS q; + SAMR_R_QUERY_USERGROUPS r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_samr_q_query_usergroups(&q, user_pol); + + if (!samr_io_q_query_usergroups("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SAMR_QUERY_USERGROUPS, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarshall response */ + + if (!samr_io_r_query_usergroups("", &r, &rbuf, 0)) { + goto done; + } + + /* Return output parameters */ + + if (NT_STATUS_IS_OK(result = r.status)) { + *num_groups = r.num_entries; + *gid = r.gid; + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/* Query user aliases */ + +NTSTATUS cli_samr_query_useraliases(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *user_pol, uint32 num_sids, DOM_SID2 *sid, + uint32 *num_aliases, uint32 **als_rids) +{ + prs_struct qbuf, rbuf; + SAMR_Q_QUERY_USERALIASES q; + SAMR_R_QUERY_USERALIASES r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + uint ptr=1; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_samr_q_query_useraliases(&q, user_pol, num_sids, &ptr, sid); + + if (!samr_io_q_query_useraliases("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SAMR_QUERY_USERALIASES, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarshall response */ + + if (!samr_io_r_query_useraliases("", &r, &rbuf, 0)) { + goto done; + } + + /* Return output parameters */ + + if (NT_STATUS_IS_OK(result = r.status)) { + *num_aliases = r.num_entries; + *als_rids = r.rid; + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/* Query user groups */ + +NTSTATUS cli_samr_query_groupmem(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *group_pol, uint32 *num_mem, + uint32 **rid, uint32 **attr) +{ + prs_struct qbuf, rbuf; + SAMR_Q_QUERY_GROUPMEM q; + SAMR_R_QUERY_GROUPMEM r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_samr_q_query_groupmem(&q, group_pol); + + if (!samr_io_q_query_groupmem("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SAMR_QUERY_GROUPMEM, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarshall response */ + + if (!samr_io_r_query_groupmem("", &r, &rbuf, 0)) { + goto done; + } + + /* Return output parameters */ + + if (NT_STATUS_IS_OK(result = r.status)) { + *num_mem = r.num_entries; + *rid = r.rid; + *attr = r.attr; + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/* Enumerate domain groups */ + +NTSTATUS cli_samr_enum_dom_groups(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *pol, uint32 *start_idx, + uint32 size, struct acct_info **dom_groups, + uint32 *num_dom_groups) +{ + prs_struct qbuf, rbuf; + SAMR_Q_ENUM_DOM_GROUPS q; + SAMR_R_ENUM_DOM_GROUPS r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + uint32 name_idx, i; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_samr_q_enum_dom_groups(&q, pol, *start_idx, size); + + if (!samr_io_q_enum_dom_groups("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SAMR_ENUM_DOM_GROUPS, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarshall response */ + + if (!samr_io_r_enum_dom_groups("", &r, &rbuf, 0)) { + goto done; + } + + /* Return output parameters */ + + result = r.status; + + if (!NT_STATUS_IS_OK(result) && + NT_STATUS_V(result) != NT_STATUS_V(STATUS_MORE_ENTRIES)) { + goto done; + } + + *num_dom_groups = r.num_entries2; + + if (!((*dom_groups) = (struct acct_info *) + talloc(mem_ctx, sizeof(struct acct_info) * *num_dom_groups))) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + memset(*dom_groups, 0, sizeof(struct acct_info) * *num_dom_groups); + + name_idx = 0; + + for (i = 0; i < *num_dom_groups; i++) { + + (*dom_groups)[i].rid = r.sam[i].rid; + + if (r.sam[i].hdr_name.buffer) { + unistr2_to_ascii((*dom_groups)[i].acct_name, + &r.uni_grp_name[name_idx], + sizeof(fstring) - 1); + name_idx++; + } + + *start_idx = r.next_idx; + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/* Enumerate domain groups */ + +NTSTATUS cli_samr_enum_als_groups(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *pol, uint32 *start_idx, + uint32 size, struct acct_info **dom_groups, + uint32 *num_dom_groups) +{ + prs_struct qbuf, rbuf; + SAMR_Q_ENUM_DOM_ALIASES q; + SAMR_R_ENUM_DOM_ALIASES r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + uint32 name_idx, i; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_samr_q_enum_dom_aliases(&q, pol, *start_idx, size); + + if (!samr_io_q_enum_dom_aliases("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SAMR_ENUM_DOM_ALIASES, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarshall response */ + + if (!samr_io_r_enum_dom_aliases("", &r, &rbuf, 0)) { + goto done; + } + + /* Return output parameters */ + + result = r.status; + + if (!NT_STATUS_IS_OK(result) && + NT_STATUS_V(result) != NT_STATUS_V(STATUS_MORE_ENTRIES)) { + goto done; + } + + *num_dom_groups = r.num_entries2; + + if (!((*dom_groups) = (struct acct_info *) + talloc(mem_ctx, sizeof(struct acct_info) * *num_dom_groups))) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + memset(*dom_groups, 0, sizeof(struct acct_info) * *num_dom_groups); + + name_idx = 0; + + for (i = 0; i < *num_dom_groups; i++) { + + (*dom_groups)[i].rid = r.sam[i].rid; + + if (r.sam[i].hdr_name.buffer) { + unistr2_to_ascii((*dom_groups)[i].acct_name, + &r.uni_grp_name[name_idx], + sizeof(fstring) - 1); + name_idx++; + } + + *start_idx = r.next_idx; + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/* Query alias members */ + +NTSTATUS cli_samr_query_aliasmem(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *alias_pol, uint32 *num_mem, + DOM_SID **sids) +{ + prs_struct qbuf, rbuf; + SAMR_Q_QUERY_ALIASMEM q; + SAMR_R_QUERY_ALIASMEM r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + uint32 i; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_samr_q_query_aliasmem(&q, alias_pol); + + if (!samr_io_q_query_aliasmem("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SAMR_QUERY_ALIASMEM, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarshall response */ + + if (!samr_io_r_query_aliasmem("", &r, &rbuf, 0)) { + goto done; + } + + /* Return output parameters */ + + if (!NT_STATUS_IS_OK(result = r.status)) { + goto done; + } + + *num_mem = r.num_sids; + + if (!(*sids = talloc(mem_ctx, sizeof(DOM_SID) * *num_mem))) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + for (i = 0; i < *num_mem; i++) { + (*sids)[i] = r.sid[i].sid; + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/* Open handle on an alias */ + +NTSTATUS cli_samr_open_alias(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *domain_pol, uint32 access_mask, + uint32 alias_rid, POLICY_HND *alias_pol) +{ + prs_struct qbuf, rbuf; + SAMR_Q_OPEN_ALIAS q; + SAMR_R_OPEN_ALIAS r; + NTSTATUS result; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_samr_q_open_alias(&q, domain_pol, access_mask, alias_rid); + + if (!samr_io_q_open_alias("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SAMR_OPEN_ALIAS, &qbuf, &rbuf)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Unmarshall response */ + + if (!samr_io_r_open_alias("", &r, &rbuf, 0)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Return output parameters */ + + if (NT_STATUS_IS_OK(result = r.status)) { + *alias_pol = r.pol; +#ifdef __INSURE__ + alias_pol->marker = malloc(1); +#endif + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/* Query domain info */ + +NTSTATUS cli_samr_query_dom_info(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *domain_pol, uint16 switch_value, + SAM_UNK_CTR *ctr) +{ + prs_struct qbuf, rbuf; + SAMR_Q_QUERY_DOMAIN_INFO q; + SAMR_R_QUERY_DOMAIN_INFO r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_samr_q_query_dom_info(&q, domain_pol, switch_value); + + if (!samr_io_q_query_dom_info("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SAMR_QUERY_DOMAIN_INFO, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarshall response */ + + r.ctr = ctr; + + if (!samr_io_r_query_dom_info("", &r, &rbuf, 0)) { + goto done; + } + + /* Return output parameters */ + + if (!NT_STATUS_IS_OK(result = r.status)) { + goto done; + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/* Query display info */ + +NTSTATUS cli_samr_query_dispinfo(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *domain_pol, uint32 *start_idx, + uint16 switch_value, uint32 *num_entries, + uint32 max_entries, SAM_DISPINFO_CTR *ctr) +{ + prs_struct qbuf, rbuf; + SAMR_Q_QUERY_DISPINFO q; + SAMR_R_QUERY_DISPINFO r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_samr_q_query_dispinfo(&q, domain_pol, switch_value, + *start_idx, max_entries); + + if (!samr_io_q_query_dispinfo("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SAMR_QUERY_DISPINFO, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarshall response */ + + r.ctr = ctr; + + if (!samr_io_r_query_dispinfo("", &r, &rbuf, 0)) { + goto done; + } + + /* Return output parameters */ + + result = r.status; + + if (!NT_STATUS_IS_OK(result) && + NT_STATUS_V(result) != NT_STATUS_V(STATUS_MORE_ENTRIES)) { + goto done; + } + + *num_entries = r.num_entries; + *start_idx += r.num_entries; /* No next_idx in this structure! */ + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/* Lookup rids. Note that NT4 seems to crash if more than ~1000 rids are + looked up in one packet. */ + +NTSTATUS cli_samr_lookup_rids(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *domain_pol, uint32 flags, + uint32 num_rids, uint32 *rids, + uint32 *num_names, char ***names, + uint32 **name_types) +{ + prs_struct qbuf, rbuf; + SAMR_Q_LOOKUP_RIDS q; + SAMR_R_LOOKUP_RIDS r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + uint32 i; + + if (num_rids > 1000) { + DEBUG(2, ("cli_samr_lookup_rids: warning: NT4 can crash if " + "more than ~1000 rids are looked up at once.\n")); + } + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_samr_q_lookup_rids(mem_ctx, &q, domain_pol, flags, + num_rids, rids); + + if (!samr_io_q_lookup_rids("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SAMR_LOOKUP_RIDS, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarshall response */ + + if (!samr_io_r_lookup_rids("", &r, &rbuf, 0)) { + goto done; + } + + /* Return output parameters */ + + if (!NT_STATUS_IS_OK(result = r.status)) { + goto done; + } + + if (r.num_names1 == 0) { + *num_names = 0; + *names = NULL; + goto done; + } + + *num_names = r.num_names1; + *names = talloc(mem_ctx, sizeof(char *) * r.num_names1); + *name_types = talloc(mem_ctx, sizeof(uint32) * r.num_names1); + + for (i = 0; i < r.num_names1; i++) { + fstring tmp; + + unistr2_to_ascii(tmp, &r.uni_name[i], sizeof(tmp) - 1); + (*names)[i] = talloc_strdup(mem_ctx, tmp); + (*name_types)[i] = r.type[i]; + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/* Lookup names */ + +NTSTATUS cli_samr_lookup_names(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *domain_pol, uint32 flags, + uint32 num_names, char **names, + uint32 *num_rids, uint32 **rids, + uint32 **rid_types) +{ + prs_struct qbuf, rbuf; + SAMR_Q_LOOKUP_NAMES q; + SAMR_R_LOOKUP_NAMES r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + uint32 i; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_samr_q_lookup_names(mem_ctx, &q, domain_pol, flags, + num_names, names); + + if (!samr_io_q_lookup_names("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SAMR_LOOKUP_NAMES, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarshall response */ + + if (!samr_io_r_lookup_names("", &r, &rbuf, 0)) { + goto done; + } + + /* Return output parameters */ + + if (!NT_STATUS_IS_OK(result = r.status)) { + goto done; + } + + if (r.num_rids1 == 0) { + *num_rids = 0; + goto done; + } + + *num_rids = r.num_rids1; + *rids = talloc(mem_ctx, sizeof(uint32) * r.num_rids1); + *rid_types = talloc(mem_ctx, sizeof(uint32) * r.num_rids1); + + for (i = 0; i < r.num_rids1; i++) { + (*rids)[i] = r.rids[i]; + (*rid_types)[i] = r.types[i]; + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/* Create a domain user */ + +NTSTATUS cli_samr_create_dom_user(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *domain_pol, const char *acct_name, + uint32 acb_info, uint32 unknown, + POLICY_HND *user_pol, uint32 *rid) +{ + prs_struct qbuf, rbuf; + SAMR_Q_CREATE_USER q; + SAMR_R_CREATE_USER r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_samr_q_create_user(&q, domain_pol, acct_name, acb_info, unknown); + + if (!samr_io_q_create_user("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SAMR_CREATE_USER, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarshall response */ + + if (!samr_io_r_create_user("", &r, &rbuf, 0)) { + goto done; + } + + /* Return output parameters */ + + if (!NT_STATUS_IS_OK(result = r.status)) { + goto done; + } + + if (user_pol) + *user_pol = r.user_pol; + + if (rid) + *rid = r.user_rid; + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/* Set userinfo */ + +NTSTATUS cli_samr_set_userinfo(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *user_pol, uint16 switch_value, + uchar sess_key[16], SAM_USERINFO_CTR *ctr) +{ + prs_struct qbuf, rbuf; + SAMR_Q_SET_USERINFO q; + SAMR_R_SET_USERINFO r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + q.ctr = ctr; + + init_samr_q_set_userinfo(&q, user_pol, sess_key, switch_value, + ctr->info.id); + + if (!samr_io_q_set_userinfo("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SAMR_SET_USERINFO, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarshall response */ + + if (!samr_io_r_set_userinfo("", &r, &rbuf, 0)) { + goto done; + } + + /* Return output parameters */ + + if (!NT_STATUS_IS_OK(result = r.status)) { + goto done; + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/* Set userinfo2 */ + +NTSTATUS cli_samr_set_userinfo2(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *user_pol, uint16 switch_value, + uchar sess_key[16], SAM_USERINFO_CTR *ctr) +{ + prs_struct qbuf, rbuf; + SAMR_Q_SET_USERINFO2 q; + SAMR_R_SET_USERINFO2 r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_samr_q_set_userinfo2(&q, user_pol, sess_key, switch_value, ctr); + + if (!samr_io_q_set_userinfo2("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SAMR_SET_USERINFO2, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarshall response */ + + if (!samr_io_r_set_userinfo2("", &r, &rbuf, 0)) { + goto done; + } + + /* Return output parameters */ + + if (!NT_STATUS_IS_OK(result = r.status)) { + goto done; + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/* Delete domain user */ + +NTSTATUS cli_samr_delete_dom_user(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *user_pol) +{ + prs_struct qbuf, rbuf; + SAMR_Q_DELETE_DOM_USER q; + SAMR_R_DELETE_DOM_USER r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_samr_q_delete_dom_user(&q, user_pol); + + if (!samr_io_q_delete_dom_user("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SAMR_DELETE_DOM_USER, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarshall response */ + + if (!samr_io_r_delete_dom_user("", &r, &rbuf, 0)) { + goto done; + } + + /* Return output parameters */ + + result = r.status; + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/* Query user security object */ + +NTSTATUS cli_samr_query_sec_obj(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *user_pol, uint16 switch_value, + TALLOC_CTX *ctx, SEC_DESC_BUF **sec_desc_buf) +{ + prs_struct qbuf, rbuf; + SAMR_Q_QUERY_SEC_OBJ q; + SAMR_R_QUERY_SEC_OBJ r; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Marshall data and send request */ + + init_samr_q_query_sec_obj(&q, user_pol, switch_value); + + if (!samr_io_q_query_sec_obj("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SAMR_QUERY_SEC_OBJECT, &qbuf, &rbuf)) { + goto done; + } + + /* Unmarshall response */ + + if (!samr_io_r_query_sec_obj("", &r, &rbuf, 0)) { + goto done; + } + + /* Return output parameters */ + + result = r.status; + *sec_desc_buf=dup_sec_desc_buf(ctx, r.buf); + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} diff --git a/source3/libsmb/cli_spoolss.c b/source3/libsmb/cli_spoolss.c new file mode 100644 index 0000000000..cf356ef815 --- /dev/null +++ b/source3/libsmb/cli_spoolss.c @@ -0,0 +1,1590 @@ +/* + Unix SMB/CIFS implementation. + RPC pipe client + + Copyright (C) Gerald Carter 2001-2002, + Copyright (C) Tim Potter 2000-2002, + Copyright (C) Andrew Tridgell 1994-2000, + Copyright (C) Luke Kenneth Casson Leighton 1996-2000, + Copyright (C) Jean-Francois Micouleau 1999-2000. + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/** @defgroup spoolss SPOOLSS - NT printing routines + * @ingroup rpc_client + * + * @{ + **/ + +/** Opens a SMB connection and connects to the SPOOLSS pipe. + * + * @param cli Uninitialised client handle. + * @param system_name NETBIOS name of the machine to connect to. + * @param creds User credentials to connect as. + * @returns Initialised client handle. + */ +struct cli_state *cli_spoolss_initialise(struct cli_state *cli, + char *system_name, + struct ntuser_creds *creds) +{ + return cli_pipe_initialise(cli, system_name, PIPE_SPOOLSS, creds); +} + +/********************************************************************** + Initialize a new spoolss buff for use by a client rpc +**********************************************************************/ +static void init_buffer(NEW_BUFFER *buffer, uint32 size, TALLOC_CTX *ctx) +{ + buffer->ptr = (size != 0); + buffer->size = size; + buffer->string_at_end = size; + prs_init(&buffer->prs, size, ctx, MARSHALL); + buffer->struct_start = prs_offset(&buffer->prs); +} + +/********************************************************************* + Decode various spoolss rpc's and info levels + ********************************************************************/ + +/********************************************************************** +**********************************************************************/ +static void decode_printer_info_0(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer, + uint32 returned, PRINTER_INFO_0 **info) +{ + uint32 i; + PRINTER_INFO_0 *inf; + + inf=(PRINTER_INFO_0 *)talloc(mem_ctx, returned*sizeof(PRINTER_INFO_0)); + + buffer->prs.data_offset=0; + + for (i=0; i<returned; i++) { + smb_io_printer_info_0("", buffer, &inf[i], 0); + } + + *info=inf; +} + +/********************************************************************** +**********************************************************************/ +static void decode_printer_info_1(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer, + uint32 returned, PRINTER_INFO_1 **info) +{ + uint32 i; + PRINTER_INFO_1 *inf; + + inf=(PRINTER_INFO_1 *)talloc(mem_ctx, returned*sizeof(PRINTER_INFO_1)); + + buffer->prs.data_offset=0; + + for (i=0; i<returned; i++) { + smb_io_printer_info_1("", buffer, &inf[i], 0); + } + + *info=inf; +} + +/********************************************************************** +**********************************************************************/ +static void decode_printer_info_2(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer, + uint32 returned, PRINTER_INFO_2 **info) +{ + uint32 i; + PRINTER_INFO_2 *inf; + + inf=(PRINTER_INFO_2 *)talloc(mem_ctx, returned*sizeof(PRINTER_INFO_2)); + + buffer->prs.data_offset=0; + + for (i=0; i<returned; i++) { + /* a little initialization as we go */ + inf[i].secdesc = NULL; + smb_io_printer_info_2("", buffer, &inf[i], 0); + } + + *info=inf; +} + +/********************************************************************** +**********************************************************************/ +static void decode_printer_info_3(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer, + uint32 returned, PRINTER_INFO_3 **info) +{ + uint32 i; + PRINTER_INFO_3 *inf; + + inf=(PRINTER_INFO_3 *)talloc(mem_ctx, returned*sizeof(PRINTER_INFO_3)); + + buffer->prs.data_offset=0; + + for (i=0; i<returned; i++) { + inf[i].secdesc = NULL; + smb_io_printer_info_3("", buffer, &inf[i], 0); + } + + *info=inf; +} + +/********************************************************************** +**********************************************************************/ +static void decode_port_info_1(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer, + uint32 returned, PORT_INFO_1 **info) +{ + uint32 i; + PORT_INFO_1 *inf; + + inf=(PORT_INFO_1*)talloc(mem_ctx, returned*sizeof(PORT_INFO_1)); + + prs_set_offset(&buffer->prs, 0); + + for (i=0; i<returned; i++) { + smb_io_port_info_1("", buffer, &(inf[i]), 0); + } + + *info=inf; +} + +/********************************************************************** +**********************************************************************/ +static void decode_port_info_2(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer, + uint32 returned, PORT_INFO_2 **info) +{ + uint32 i; + PORT_INFO_2 *inf; + + inf=(PORT_INFO_2*)talloc(mem_ctx, returned*sizeof(PORT_INFO_2)); + + prs_set_offset(&buffer->prs, 0); + + for (i=0; i<returned; i++) { + smb_io_port_info_2("", buffer, &(inf[i]), 0); + } + + *info=inf; +} + +/********************************************************************** +**********************************************************************/ +static void decode_printer_driver_1(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer, + uint32 returned, DRIVER_INFO_1 **info) +{ + uint32 i; + DRIVER_INFO_1 *inf; + + inf=(DRIVER_INFO_1 *)talloc(mem_ctx, returned*sizeof(DRIVER_INFO_1)); + + buffer->prs.data_offset=0; + + for (i=0; i<returned; i++) { + smb_io_printer_driver_info_1("", buffer, &(inf[i]), 0); + } + + *info=inf; +} + +/********************************************************************** +**********************************************************************/ +static void decode_printer_driver_2(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer, + uint32 returned, DRIVER_INFO_2 **info) +{ + uint32 i; + DRIVER_INFO_2 *inf; + + inf=(DRIVER_INFO_2 *)talloc(mem_ctx, returned*sizeof(DRIVER_INFO_2)); + + buffer->prs.data_offset=0; + + for (i=0; i<returned; i++) { + smb_io_printer_driver_info_2("", buffer, &(inf[i]), 0); + } + + *info=inf; +} + +/********************************************************************** +**********************************************************************/ +static void decode_printer_driver_3(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer, + uint32 returned, DRIVER_INFO_3 **info) +{ + uint32 i; + DRIVER_INFO_3 *inf; + + inf=(DRIVER_INFO_3 *)talloc(mem_ctx, returned*sizeof(DRIVER_INFO_3)); + + buffer->prs.data_offset=0; + + for (i=0; i<returned; i++) { + smb_io_printer_driver_info_3("", buffer, &(inf[i]), 0); + } + + *info=inf; +} + +/********************************************************************** +**********************************************************************/ +static void decode_printerdriverdir_1 (TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer, + uint32 returned, DRIVER_DIRECTORY_1 **info +) +{ + DRIVER_DIRECTORY_1 *inf; + + inf=(DRIVER_DIRECTORY_1 *)talloc(mem_ctx, sizeof(DRIVER_DIRECTORY_1)); + + prs_set_offset(&buffer->prs, 0); + + smb_io_driverdir_1("", buffer, inf, 0); + + *info=inf; +} + +/** Return a handle to the specified printer or print server. + * + * @param cli Pointer to client state structure which is open + * on the SPOOLSS pipe. + * + * @param mem_ctx Pointer to an initialised talloc context. + * + * @param printername The name of the printer or print server to be + * opened in UNC format. + * + * @param datatype Specifies the default data type for the printer. + * + * @param access_required The access rights requested on the printer or + * print server. + * + * @param station The UNC name of the requesting workstation. + * + * @param username The name of the user requesting the open. + * + * @param pol Returned policy handle. + */ + +/********************************************************************************* + Win32 API - OpenPrinter() + ********************************************************************************/ + +WERROR cli_spoolss_open_printer_ex(struct cli_state *cli, TALLOC_CTX *mem_ctx, + char *printername, char *datatype, uint32 access_required, + char *station, char *username, POLICY_HND *pol) +{ + prs_struct qbuf, rbuf; + SPOOL_Q_OPEN_PRINTER_EX q; + SPOOL_R_OPEN_PRINTER_EX r; + WERROR result = W_ERROR(ERRgeneral); + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Initialise input parameters */ + + make_spoolss_q_open_printer_ex(&q, printername, datatype, + access_required, station, username); + + /* Marshall data and send request */ + + if (!spoolss_io_q_open_printer_ex("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SPOOLSS_OPENPRINTEREX, &qbuf, &rbuf)) + goto done; + + /* Unmarshall response */ + + if (!spoolss_io_r_open_printer_ex("", &r, &rbuf, 0)) + goto done; + + /* Return output parameters */ + + result = r.status; + + if (W_ERROR_IS_OK(result)) + *pol = r.handle; + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/** Close a printer handle + * + * @param cli Pointer to client state structure which is open + * on the SPOOLSS pipe. + * + * @param mem_ctx Pointer to an initialised talloc context. + * + * @param pol Policy handle of printer or print server to close. + */ +/********************************************************************************* + Win32 API - ClosePrinter() + ********************************************************************************/ + +WERROR cli_spoolss_close_printer(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *pol) +{ + prs_struct qbuf, rbuf; + SPOOL_Q_CLOSEPRINTER q; + SPOOL_R_CLOSEPRINTER r; + WERROR result = W_ERROR(ERRgeneral); + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Initialise input parameters */ + + make_spoolss_q_closeprinter(&q, pol); + + /* Marshall data and send request */ + + if (!spoolss_io_q_closeprinter("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SPOOLSS_CLOSEPRINTER, &qbuf, &rbuf)) + goto done; + + /* Unmarshall response */ + + if (!spoolss_io_r_closeprinter("", &r, &rbuf, 0)) + goto done; + + /* Return output parameters */ + + result = r.status; + + if (W_ERROR_IS_OK(result)) + *pol = r.handle; + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/** Enumerate printers on a print server. + * + * @param cli Pointer to client state structure which is open + * on the SPOOLSS pipe. + * @param mem_ctx Pointer to an initialised talloc context. + * + * @param offered Buffer size offered in the request. + * @param needed Number of bytes needed to complete the request. + * may be NULL. + * + * @param flags Selected from PRINTER_ENUM_* flags. + * @param level Request information level. + * + * @param num_printers Pointer to number of printers returned. May be + * NULL. + * @param ctr Return structure for printer information. May + * be NULL. + */ +/********************************************************************************* + Win32 API - EnumPrinters() + ********************************************************************************/ + +WERROR cli_spoolss_enum_printers(struct cli_state *cli, TALLOC_CTX *mem_ctx, + uint32 offered, uint32 *needed, + uint32 flags, uint32 level, + uint32 *num_printers, PRINTER_INFO_CTR *ctr) +{ + prs_struct qbuf, rbuf; + SPOOL_Q_ENUMPRINTERS q; + SPOOL_R_ENUMPRINTERS r; + NEW_BUFFER buffer; + WERROR result = W_ERROR(ERRgeneral); + fstring server; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + fstrcpy (server, cli->desthost); + strupper (server); + + /* Initialise input parameters */ + + init_buffer(&buffer, offered, mem_ctx); + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + make_spoolss_q_enumprinters(&q, flags, server, level, &buffer, + offered); + + /* Marshall data and send request */ + + if (!spoolss_io_q_enumprinters("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SPOOLSS_ENUMPRINTERS, &qbuf, &rbuf)) + goto done; + + /* Unmarshall response */ + + if (spoolss_io_r_enumprinters("", &r, &rbuf, 0)) { + if (needed) + *needed = r.needed; + } + + result = r.status; + + /* Return output parameters */ + + if (!W_ERROR_IS_OK(r.status)) + goto done; + + if (num_printers) + *num_printers = r.returned; + + if (!ctr) + goto done; + + switch (level) { + case 0: + decode_printer_info_0(mem_ctx, r.buffer, r.returned, + &ctr->printers_0); + break; + case 1: + decode_printer_info_1(mem_ctx, r.buffer, r.returned, + &ctr->printers_1); + break; + case 2: + decode_printer_info_2(mem_ctx, r.buffer, r.returned, + &ctr->printers_2); + break; + case 3: + decode_printer_info_3(mem_ctx, r.buffer, r.returned, + &ctr->printers_3); + break; + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/********************************************************************************* + Win32 API - EnumPorts() + ********************************************************************************/ +/** Enumerate printer ports on a print server. + * + * @param cli Pointer to client state structure which is open + * on the SPOOLSS pipe. + * @param mem_ctx Pointer to an initialised talloc context. + * + * @param offered Buffer size offered in the request. + * @param needed Number of bytes needed to complete the request. + * May be NULL. + * + * @param level Requested information level. + * + * @param num_ports Pointer to number of ports returned. May be NULL. + * @param ctr Pointer to structure holding port information. + * May be NULL. + */ + +WERROR cli_spoolss_enum_ports(struct cli_state *cli, TALLOC_CTX *mem_ctx, + uint32 offered, uint32 *needed, + uint32 level, int *num_ports, PORT_INFO_CTR *ctr) +{ + prs_struct qbuf, rbuf; + SPOOL_Q_ENUMPORTS q; + SPOOL_R_ENUMPORTS r; + NEW_BUFFER buffer; + WERROR result = W_ERROR(ERRgeneral); + fstring server; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + slprintf (server, sizeof(fstring)-1, "\\\\%s", cli->desthost); + strupper (server); + + /* Initialise input parameters */ + + init_buffer(&buffer, offered, mem_ctx); + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + make_spoolss_q_enumports(&q, server, level, &buffer, offered); + + /* Marshall data and send request */ + + if (!spoolss_io_q_enumports("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SPOOLSS_ENUMPORTS, &qbuf, &rbuf)) + goto done; + + /* Unmarshall response */ + + if (spoolss_io_r_enumports("", &r, &rbuf, 0)) { + if (needed) + *needed = r.needed; + } + + result = r.status; + + /* Return output parameters */ + + if (!W_ERROR_IS_OK(result)) + goto done; + + if (num_ports) + *num_ports = r.returned; + + if (!ctr) + goto done; + + switch (level) { + case 1: + decode_port_info_1(mem_ctx, r.buffer, r.returned, + &ctr->port.info_1); + break; + case 2: + decode_port_info_2(mem_ctx, r.buffer, r.returned, + &ctr->port.info_2); + break; + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/********************************************************************************* + Win32 API - GetPrinter() + ********************************************************************************/ + +WERROR cli_spoolss_getprinter(struct cli_state *cli, TALLOC_CTX *mem_ctx, + uint32 offered, uint32 *needed, + POLICY_HND *pol, uint32 level, + PRINTER_INFO_CTR *ctr) +{ + prs_struct qbuf, rbuf; + SPOOL_Q_GETPRINTER q; + SPOOL_R_GETPRINTER r; + NEW_BUFFER buffer; + WERROR result = W_ERROR(ERRgeneral); + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise input parameters */ + + init_buffer(&buffer, offered, mem_ctx); + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + make_spoolss_q_getprinter(mem_ctx, &q, pol, level, &buffer, offered); + + /* Marshall data and send request */ + + if (!spoolss_io_q_getprinter("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SPOOLSS_GETPRINTER, &qbuf, &rbuf)) + goto done; + + /* Unmarshall response */ + + if (!spoolss_io_r_getprinter("", &r, &rbuf, 0)) + goto done; + + if (needed) + *needed = r.needed; + + /* Return output parameters */ + + result = r.status; + + if (NT_STATUS_IS_OK(result)) { + switch (level) { + case 0: + decode_printer_info_0(mem_ctx, r.buffer, 1, &ctr->printers_0); + break; + case 1: + decode_printer_info_1(mem_ctx, r.buffer, 1, &ctr->printers_1); + break; + case 2: + decode_printer_info_2(mem_ctx, r.buffer, 1, &ctr->printers_2); + break; + case 3: + decode_printer_info_3(mem_ctx, r.buffer, 1, &ctr->printers_3); + break; + } + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/********************************************************************************* + Win32 API - SetPrinter() + ********************************************************************************/ +/** Set printer info + * + * @param cli Pointer to client state structure which is open + * on the SPOOLSS pipe. + * @param mem_ctx Pointer to an initialised talloc context. + * + * @param pol Policy handle on printer to set info. + * @param level Information level to set. + * @param ctr Pointer to structure holding printer information. + * @param command Specifies the action performed. See + * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/prntspol_13ua.asp + * for details. + * + */ + +WERROR cli_spoolss_setprinter(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *pol, uint32 level, + PRINTER_INFO_CTR *ctr, uint32 command) +{ + prs_struct qbuf, rbuf; + SPOOL_Q_SETPRINTER q; + SPOOL_R_SETPRINTER r; + WERROR result = W_ERROR(ERRgeneral); + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise input parameters */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + make_spoolss_q_setprinter(mem_ctx, &q, pol, level, ctr, command); + + /* Marshall data and send request */ + + if (!spoolss_io_q_setprinter("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SPOOLSS_SETPRINTER, &qbuf, &rbuf)) + goto done; + + /* Unmarshall response */ + + if (!spoolss_io_r_setprinter("", &r, &rbuf, 0)) + goto done; + + result = r.status; + +done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/********************************************************************************* + Win32 API - GetPrinterDriver() + ********************************************************************************/ +/** Get installed printer drivers for a given printer + * + * @param cli Pointer to client state structure which is open + * on the SPOOLSS pipe. + * + * @param mem_ctx Pointer to an initialised talloc context. + * + * @param offered Buffer size offered in the request. + * @param needed Number of bytes needed to complete the request. + * may be NULL. + * + * @param pol Pointer to an open policy handle for the printer + * opened with cli_spoolss_open_printer_ex(). + * @param level Requested information level. + * @param env The print environment or archictecture. This is + * "Windows NT x86" for NT4. + * @param ctr Returned printer driver information. + */ + +WERROR cli_spoolss_getprinterdriver(struct cli_state *cli, + TALLOC_CTX *mem_ctx, + uint32 offered, uint32 *needed, + POLICY_HND *pol, uint32 level, + char *env, PRINTER_DRIVER_CTR *ctr) +{ + prs_struct qbuf, rbuf; + SPOOL_Q_GETPRINTERDRIVER2 q; + SPOOL_R_GETPRINTERDRIVER2 r; + NEW_BUFFER buffer; + WERROR result = W_ERROR(ERRgeneral); + fstring server; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + fstrcpy (server, cli->desthost); + strupper (server); + + /* Initialise input parameters */ + + init_buffer(&buffer, offered, mem_ctx); + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + make_spoolss_q_getprinterdriver2(&q, pol, env, level, 2, 2, + &buffer, offered); + + /* Marshall data and send request */ + + if (!spoolss_io_q_getprinterdriver2 ("", &q, &qbuf, 0) || + !rpc_api_pipe_req (cli, SPOOLSS_GETPRINTERDRIVER2, &qbuf, &rbuf)) + goto done; + + /* Unmarshall response */ + + if (spoolss_io_r_getprinterdriver2 ("", &r, &rbuf, 0)) { + if (needed) + *needed = r.needed; + } + + result = r.status; + + /* Return output parameters */ + + if (!W_ERROR_IS_OK(result)) + goto done; + + if (!ctr) + goto done; + + switch (level) { + case 1: + decode_printer_driver_1(mem_ctx, r.buffer, 1, &ctr->info1); + break; + case 2: + decode_printer_driver_2(mem_ctx, r.buffer, 1, &ctr->info2); + break; + case 3: + decode_printer_driver_3(mem_ctx, r.buffer, 1, &ctr->info3); + break; + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/********************************************************************************* + Win32 API - EnumPrinterDrivers() + ********************************************************************************/ +/********************************************************************** + * Get installed printer drivers for a given printer + */ +WERROR cli_spoolss_enumprinterdrivers (struct cli_state *cli, + TALLOC_CTX *mem_ctx, + uint32 offered, uint32 *needed, + uint32 level, char *env, + uint32 *num_drivers, + PRINTER_DRIVER_CTR *ctr) +{ + prs_struct qbuf, rbuf; + SPOOL_Q_ENUMPRINTERDRIVERS q; + SPOOL_R_ENUMPRINTERDRIVERS r; + NEW_BUFFER buffer; + WERROR result = W_ERROR(ERRgeneral); + fstring server; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + slprintf (server, sizeof(fstring)-1, "\\\\%s", cli->desthost); + strupper (server); + + /* Initialise input parameters */ + + init_buffer(&buffer, offered, mem_ctx); + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Write the request */ + + make_spoolss_q_enumprinterdrivers(&q, server, env, level, &buffer, + offered); + + /* Marshall data and send request */ + + if (!spoolss_io_q_enumprinterdrivers ("", &q, &qbuf, 0) || + !rpc_api_pipe_req (cli, SPOOLSS_ENUMPRINTERDRIVERS, &qbuf, &rbuf)) + goto done; + + /* Unmarshall response */ + + if (!spoolss_io_r_enumprinterdrivers ("", &r, &rbuf, 0)) + goto done; + + if (needed) + *needed = r.needed; + + if (num_drivers) + *num_drivers = r.returned; + + result = r.status; + + /* Return output parameters */ + + if (W_ERROR_IS_OK(result) && (r.returned != 0)) { + *num_drivers = r.returned; + + switch (level) { + case 1: + decode_printer_driver_1(mem_ctx, r.buffer, r.returned, &ctr->info1); + break; + case 2: + decode_printer_driver_2(mem_ctx, r.buffer, r.returned, &ctr->info2); + break; + case 3: + decode_printer_driver_3(mem_ctx, r.buffer, r.returned, &ctr->info3); + break; + } + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + + +/********************************************************************************* + Win32 API - GetPrinterDriverDirectory() + ********************************************************************************/ +/********************************************************************** + * Get installed printer drivers for a given printer + */ +WERROR cli_spoolss_getprinterdriverdir (struct cli_state *cli, + TALLOC_CTX *mem_ctx, + uint32 offered, uint32 *needed, + uint32 level, char *env, + DRIVER_DIRECTORY_CTR *ctr) +{ + prs_struct qbuf, rbuf; + SPOOL_Q_GETPRINTERDRIVERDIR q; + SPOOL_R_GETPRINTERDRIVERDIR r; + NEW_BUFFER buffer; + WERROR result = W_ERROR(ERRgeneral); + fstring server; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + slprintf (server, sizeof(fstring)-1, "\\\\%s", cli->desthost); + strupper (server); + + /* Initialise input parameters */ + + init_buffer(&buffer, offered, mem_ctx); + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Write the request */ + + make_spoolss_q_getprinterdriverdir(&q, server, env, level, &buffer, + offered); + + /* Marshall data and send request */ + + if (!spoolss_io_q_getprinterdriverdir ("", &q, &qbuf, 0) || + !rpc_api_pipe_req (cli, SPOOLSS_GETPRINTERDRIVERDIRECTORY, + &qbuf, &rbuf)) + goto done; + + /* Unmarshall response */ + + if (spoolss_io_r_getprinterdriverdir ("", &r, &rbuf, 0)) { + if (needed) + *needed = r.needed; + } + + /* Return output parameters */ + + result = r.status; + + if (W_ERROR_IS_OK(result)) { + switch (level) { + case 1: + decode_printerdriverdir_1(mem_ctx, r.buffer, 1, + &ctr->info1); + break; + } + } + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/********************************************************************************* + Win32 API - AddPrinterDriver() + ********************************************************************************/ +/********************************************************************** + * Install a printer driver + */ +WERROR cli_spoolss_addprinterdriver (struct cli_state *cli, + TALLOC_CTX *mem_ctx, uint32 level, + PRINTER_DRIVER_CTR *ctr) +{ + prs_struct qbuf, rbuf; + SPOOL_Q_ADDPRINTERDRIVER q; + SPOOL_R_ADDPRINTERDRIVER r; + WERROR result = W_ERROR(ERRgeneral); + fstring server; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + slprintf (server, sizeof(fstring)-1, "\\\\%s", cli->desthost); + strupper (server); + + /* Initialise input parameters */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Write the request */ + + make_spoolss_q_addprinterdriver (mem_ctx, &q, server, level, ctr); + + /* Marshall data and send request */ + + if (!spoolss_io_q_addprinterdriver ("", &q, &qbuf, 0) || + !rpc_api_pipe_req (cli, SPOOLSS_ADDPRINTERDRIVER, &qbuf, &rbuf)) + goto done; + + /* Unmarshall response */ + + if (!spoolss_io_r_addprinterdriver ("", &r, &rbuf, 0)) + goto done; + + /* Return output parameters */ + + result = r.status; + +done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/********************************************************************************* + Win32 API - AddPrinter() + ********************************************************************************/ +/********************************************************************** + * Install a printer + */ +WERROR cli_spoolss_addprinterex (struct cli_state *cli, TALLOC_CTX *mem_ctx, + uint32 level, PRINTER_INFO_CTR*ctr) +{ + prs_struct qbuf, rbuf; + SPOOL_Q_ADDPRINTEREX q; + SPOOL_R_ADDPRINTEREX r; + WERROR result = W_ERROR(ERRgeneral); + fstring server, + client, + user; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + slprintf (client, sizeof(fstring)-1, "\\\\%s", cli->desthost); + strupper (client); + slprintf (server, sizeof(fstring)-1, "\\\\%s", cli->desthost); + strupper (server); + fstrcpy (user, cli->user_name); + + /* Initialise input parameters */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Write the request */ + + make_spoolss_q_addprinterex (mem_ctx, &q, server, client, user, + level, ctr); + + /* Marshall data and send request */ + + if (!spoolss_io_q_addprinterex ("", &q, &qbuf, 0) || + !rpc_api_pipe_req (cli, SPOOLSS_ADDPRINTEREX, &qbuf, &rbuf)) + goto done; + + /* Unmarshall response */ + + if (!spoolss_io_r_addprinterex ("", &r, &rbuf, 0)) + goto done; + + /* Return output parameters */ + + result = r.status; + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/********************************************************************************* + Win32 API - DeltePrinterDriver() + ********************************************************************************/ +/********************************************************************** + * Delete a Printer Driver from the server (does not remove + * the driver files + */ +WERROR cli_spoolss_deleteprinterdriver (struct cli_state *cli, + TALLOC_CTX *mem_ctx, char *arch, + char *driver) +{ + prs_struct qbuf, rbuf; + SPOOL_Q_DELETEPRINTERDRIVER q; + SPOOL_R_DELETEPRINTERDRIVER r; + WERROR result = W_ERROR(ERRgeneral); + fstring server; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + + /* Initialise input parameters */ + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + slprintf (server, sizeof(fstring)-1, "\\\\%s", cli->desthost); + strupper (server); + + /* Write the request */ + + make_spoolss_q_deleteprinterdriver(mem_ctx, &q, server, arch, driver); + + /* Marshall data and send request */ + + if (!spoolss_io_q_deleteprinterdriver ("", &q, &qbuf, 0) || + !rpc_api_pipe_req (cli,SPOOLSS_DELETEPRINTERDRIVER , &qbuf, &rbuf)) + goto done; + + /* Unmarshall response */ + + if (!spoolss_io_r_deleteprinterdriver ("", &r, &rbuf, 0)) + goto done; + + /* Return output parameters */ + + result = r.status; + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/********************************************************************************* + Win32 API - GetPrinterProcessorDirectory() + ********************************************************************************/ + +WERROR cli_spoolss_getprintprocessordirectory(struct cli_state *cli, + TALLOC_CTX *mem_ctx, + uint32 offered, uint32 *needed, + char *name, char *environment, + fstring procdir) +{ + prs_struct qbuf, rbuf; + SPOOL_Q_GETPRINTPROCESSORDIRECTORY q; + SPOOL_R_GETPRINTPROCESSORDIRECTORY r; + int level = 1; + WERROR result = W_ERROR(ERRgeneral); + NEW_BUFFER buffer; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Initialise input parameters */ + + init_buffer(&buffer, offered, mem_ctx); + + make_spoolss_q_getprintprocessordirectory( + &q, name, environment, level, &buffer, offered); + + /* Marshall data and send request */ + + if (!spoolss_io_q_getprintprocessordirectory("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SPOOLSS_GETPRINTPROCESSORDIRECTORY, + &qbuf, &rbuf)) + goto done; + + /* Unmarshall response */ + + if (!spoolss_io_r_getprintprocessordirectory("", &r, &rbuf, 0)) + goto done; + + /* Return output parameters */ + + result = r.status; + + if (needed) + *needed = r.needed; + + if (W_ERROR_IS_OK(result)) + fstrcpy(procdir, "Not implemented!"); + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/** Add a form to a printer. + * + * @param cli Pointer to client state structure which is open + * on the SPOOLSS pipe. + * @param mem_ctx Pointer to an initialised talloc context. + * + * @param handle Policy handle opened with cli_spoolss_open_printer_ex + * or cli_spoolss_addprinterex. + * @param level Form info level to add - should always be 1. + * @param form A pointer to the form to be added. + * + */ + +WERROR cli_spoolss_addform(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *handle, uint32 level, FORM *form) +{ + prs_struct qbuf, rbuf; + SPOOL_Q_ADDFORM q; + SPOOL_R_ADDFORM r; + WERROR result = W_ERROR(ERRgeneral); + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Initialise input parameters */ + + make_spoolss_q_addform(&q, handle, level, form); + + /* Marshall data and send request */ + + if (!spoolss_io_q_addform("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SPOOLSS_ADDFORM, &qbuf, &rbuf)) + goto done; + + /* Unmarshall response */ + + if (!spoolss_io_r_addform("", &r, &rbuf, 0)) + goto done; + + /* Return output parameters */ + + result = r.status; + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/** Set a form on a printer. + * + * @param cli Pointer to client state structure which is open + * on the SPOOLSS pipe. + * @param mem_ctx Pointer to an initialised talloc context. + * + * @param handle Policy handle opened with cli_spoolss_open_printer_ex + * or cli_spoolss_addprinterex. + * @param level Form info level to set - should always be 1. + * @param form A pointer to the form to be set. + * + */ + +WERROR cli_spoolss_setform(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *handle, uint32 level, char *form_name, + FORM *form) +{ + prs_struct qbuf, rbuf; + SPOOL_Q_SETFORM q; + SPOOL_R_SETFORM r; + WERROR result = W_ERROR(ERRgeneral); + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Initialise input parameters */ + + make_spoolss_q_setform(&q, handle, level, form_name, form); + + /* Marshall data and send request */ + + if (!spoolss_io_q_setform("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SPOOLSS_SETFORM, &qbuf, &rbuf)) + goto done; + + /* Unmarshall response */ + + if (!spoolss_io_r_setform("", &r, &rbuf, 0)) + goto done; + + /* Return output parameters */ + + result = r.status; + + if (!W_ERROR_IS_OK(result)) + goto done; + + + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/** Get a form on a printer. + * + * @param cli Pointer to client state structure which is open + * on the SPOOLSS pipe. + * @param mem_ctx Pointer to an initialised talloc context. + * + * @param handle Policy handle opened with cli_spoolss_open_printer_ex + * or cli_spoolss_addprinterex. + * @param formname Name of the form to get + * @param level Form info level to get - should always be 1. + * + */ + +WERROR cli_spoolss_getform(struct cli_state *cli, TALLOC_CTX *mem_ctx, + uint32 offered, uint32 *needed, + POLICY_HND *handle, char *formname, uint32 level, + FORM_1 *form) +{ + prs_struct qbuf, rbuf; + SPOOL_Q_GETFORM q; + SPOOL_R_GETFORM r; + WERROR result = W_ERROR(ERRgeneral); + NEW_BUFFER buffer; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + init_buffer(&buffer, offered, mem_ctx); + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Initialise input parameters */ + + make_spoolss_q_getform(&q, handle, formname, level, &buffer, offered); + + /* Marshall data and send request */ + + if (!spoolss_io_q_getform("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SPOOLSS_GETFORM, &qbuf, &rbuf)) + goto done; + + /* Unmarshall response */ + + if (!spoolss_io_r_getform("", &r, &rbuf, 0)) + goto done; + + /* Return output parameters */ + + result = r.status; + + if (needed) + *needed = r.needed; + + if (W_ERROR_IS_OK(result)) + smb_io_form_1("", r.buffer, form, 0); + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/** Delete a form on a printer. + * + * @param cli Pointer to client state structure which is open + * on the SPOOLSS pipe. + * @param mem_ctx Pointer to an initialised talloc context. + * + * @param handle Policy handle opened with cli_spoolss_open_printer_ex + * or cli_spoolss_addprinterex. + * @param form The name of the form to delete. + * + */ + +WERROR cli_spoolss_deleteform(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *handle, char *form_name) +{ + prs_struct qbuf, rbuf; + SPOOL_Q_DELETEFORM q; + SPOOL_R_DELETEFORM r; + WERROR result = W_ERROR(ERRgeneral); + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Initialise input parameters */ + + make_spoolss_q_deleteform(&q, handle, form_name); + + /* Marshall data and send request */ + + if (!spoolss_io_q_deleteform("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SPOOLSS_DELETEFORM, &qbuf, &rbuf)) + goto done; + + /* Unmarshall response */ + + if (!spoolss_io_r_deleteform("", &r, &rbuf, 0)) + goto done; + + /* Return output parameters */ + + result = r.status; + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +static void decode_forms_1(TALLOC_CTX *mem_ctx, NEW_BUFFER *buffer, + uint32 num_forms, FORM_1 **forms) +{ + int i; + + *forms = (FORM_1 *)talloc(mem_ctx, num_forms * sizeof(FORM_1)); + buffer->prs.data_offset = 0; + + for (i = 0; i < num_forms; i++) + smb_io_form_1("", buffer, &((*forms)[i]), 0); +} + +/** Enumerate forms + * + * @param cli Pointer to client state structure which is open + * on the SPOOLSS pipe. + * @param mem_ctx Pointer to an initialised talloc context. + * + * @param offered Buffer size offered in the request. + * @param needed Number of bytes needed to complete the request. + * may be NULL. + * or cli_spoolss_addprinterex. + * @param level Form info level to get - should always be 1. + * @param handle Open policy handle + * + */ + +WERROR cli_spoolss_enumforms(struct cli_state *cli, TALLOC_CTX *mem_ctx, + uint32 offered, uint32 *needed, + POLICY_HND *handle, int level, uint32 *num_forms, + FORM_1 **forms) +{ + prs_struct qbuf, rbuf; + SPOOL_Q_ENUMFORMS q; + SPOOL_R_ENUMFORMS r; + WERROR result = W_ERROR(ERRgeneral); + NEW_BUFFER buffer; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + init_buffer(&buffer, offered, mem_ctx); + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Initialise input parameters */ + + make_spoolss_q_enumforms(&q, handle, level, &buffer, offered); + + /* Marshall data and send request */ + + if (!spoolss_io_q_enumforms("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SPOOLSS_ENUMFORMS, &qbuf, &rbuf)) + goto done; + + /* Unmarshall response */ + + if (!spoolss_io_r_enumforms("", &r, &rbuf, 0)) + goto done; + + /* Return output parameters */ + + result = r.status; + + if (needed) + *needed = r.needed; + + if (num_forms) + *num_forms = r.numofforms; + + decode_forms_1(mem_ctx, r.buffer, *num_forms, forms); + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/********************************************************************************* + Win32 API - SetPrinterData() + ********************************************************************************/ + +WERROR cli_spoolss_setprinterdata (struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *pol, char* valname, char* value) +{ + prs_struct qbuf, rbuf; + SPOOL_Q_SETPRINTERDATA q; + SPOOL_R_SETPRINTERDATA r; + WERROR result = W_ERROR(ERRgeneral); + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise input parameters */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + + /* write the request */ + make_spoolss_q_setprinterdata(&q, mem_ctx, pol, valname, value); + + /* Marshall data and send request */ + if (!spoolss_io_q_setprinterdata ("", &q, &qbuf, 0) || + !rpc_api_pipe_req (cli, SPOOLSS_SETPRINTERDATA, &qbuf, &rbuf)) + goto done; + + /* Unmarshall response */ + if (spoolss_io_r_setprinterdata ("", &r, &rbuf, 0)) + goto done; + + result = r.status; + +done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/* Enumerate jobs */ + +WERROR cli_spoolss_enumjobs(struct cli_state *cli, TALLOC_CTX *mem_ctx, + uint32 offered, uint32 *needed, + POLICY_HND *hnd, uint32 firstjob, uint32 numofjobs, + uint32 level) +{ + prs_struct qbuf, rbuf; + SPOOL_Q_ENUMJOBS q; + SPOOL_R_ENUMJOBS r; + WERROR result = W_ERROR(ERRgeneral); + NEW_BUFFER buffer; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + init_buffer(&buffer, offered, mem_ctx); + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Initialise input parameters */ + + make_spoolss_q_enumjobs(&q, hnd, firstjob, numofjobs, level, &buffer, + offered); + + /* Marshall data and send request */ + + if (!spoolss_io_q_enumjobs("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SPOOLSS_ENUMJOBS, &qbuf, &rbuf)) + goto done; + + /* Unmarshall response */ + + if (!spoolss_io_r_enumjobs("", &r, &rbuf, 0)) + goto done; + + /* Return output parameters */ + + result = r.status; + + if (needed) + *needed = r.needed; + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} + +/** @} **/ diff --git a/source3/libsmb/cli_srvsvc.c b/source3/libsmb/cli_srvsvc.c new file mode 100644 index 0000000000..9d33149540 --- /dev/null +++ b/source3/libsmb/cli_srvsvc.c @@ -0,0 +1,79 @@ +/* + Unix SMB/CIFS implementation. + NT Domain Authentication SMB / MSRPC client + Copyright (C) Andrew Tridgell 1994-2000 + Copyright (C) Luke Kenneth Casson Leighton 1996-2000 + Copyright (C) Tim Potter 2001 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/* Opens a SMB connection to the svrsvc pipe */ + +struct cli_state *cli_svrsvc_initialise(struct cli_state *cli, + char *system_name, + struct ntuser_creds *creds) +{ + return cli_pipe_initialise(cli, system_name, PIPE_SRVSVC, creds); +} + +NTSTATUS cli_srvsvc_net_srv_get_info(struct cli_state *cli, + TALLOC_CTX *mem_ctx, + uint32 switch_value, SRV_INFO_CTR *ctr) +{ + prs_struct qbuf, rbuf; + SRV_Q_NET_SRV_GET_INFO q; + SRV_R_NET_SRV_GET_INFO r; + NTSTATUS result; + + ZERO_STRUCT(q); + ZERO_STRUCT(r); + + /* Initialise parse structures */ + + prs_init(&qbuf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + /* Initialise input parameters */ + + init_srv_q_net_srv_get_info(&q, cli->srv_name_slash, switch_value); + + /* Marshall data and send request */ + + if (!srv_io_q_net_srv_get_info("", &q, &qbuf, 0) || + !rpc_api_pipe_req(cli, SRV_NET_SRV_GET_INFO, &qbuf, &rbuf)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* Unmarshall response */ + + r.ctr = ctr; + + if (!srv_io_r_net_srv_get_info("", &r, &rbuf, 0)) { + result = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + result = werror_to_ntstatus(r.status); + + done: + prs_mem_free(&qbuf); + prs_mem_free(&rbuf); + + return result; +} diff --git a/source3/libsmb/cli_wkssvc.c b/source3/libsmb/cli_wkssvc.c new file mode 100644 index 0000000000..2a84e6b698 --- /dev/null +++ b/source3/libsmb/cli_wkssvc.c @@ -0,0 +1,112 @@ +/* + Unix SMB/CIFS implementation. + NT Domain Authentication SMB / MSRPC client + Copyright (C) Andrew Tridgell 1994-2000 + Copyright (C) Luke Kenneth Casson Leighton 1996-2000 + Copyright (C) Tim Potter 2001 + Copytight (C) Rafal Szczesniak 2002 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/** + * Opens a SMB connection to the wkssvc pipe + * + * @param cli client structure (not yet initialised) + * @param system_name called rpc server name + * @param creds user credentials + * + * @return client structure with opened pipe + **/ + +struct cli_state *cli_wkssvc_initialise(struct cli_state *cli, + char *system_name, + struct ntuser_creds *creds) +{ + return cli_pipe_initialise(cli, system_name, PIPE_WKSSVC, creds); +} + + +/** + * WksQueryInfo rpc call (like query for server's capabilities) + * + * @param initialised client structure with \PIPE\wkssvc opened + * @param mem_ctx memory context assigned to this rpc binding + * @param wks100 WksQueryInfo structure + * + * @return NTSTATUS of rpc call + */ + +NTSTATUS cli_wks_query_info(struct cli_state *cli, TALLOC_CTX *mem_ctx, + WKS_INFO_100 *wks100) +{ + prs_struct buf; + prs_struct rbuf; + WKS_Q_QUERY_INFO q_o; + WKS_R_QUERY_INFO r_o; + NTSTATUS nt_status; + + if (cli == NULL || wks100 == NULL) + return NT_STATUS_UNSUCCESSFUL; + + /* init rpc parse structures */ + prs_init(&buf, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL); + prs_init(&rbuf, 0, mem_ctx, UNMARSHALL); + + DEBUG(4, ("WksQueryInfo\n")); + + /* init query structure with rpc call arguments */ + init_wks_q_query_info(&q_o, cli->desthost, 100); + + /* marshall data */ + if (!wks_io_q_query_info("", &q_o, &buf, 0)) { + prs_mem_free(&buf); + prs_mem_free(&rbuf); + return NT_STATUS_UNSUCCESSFUL; + } + + /* actual rpc call over \PIPE\wkssvc */ + if (!rpc_api_pipe_req(cli, WKS_QUERY_INFO, &buf, &rbuf)) { + prs_mem_free(&buf); + prs_mem_free(&rbuf); + return NT_STATUS_UNSUCCESSFUL; + } + + prs_mem_free(&buf); + + r_o.wks100 = wks100; + + /* get call results from response buffer */ + if (!wks_io_r_query_info("", &r_o, &rbuf, 0)) { + prs_mem_free(&rbuf); + return NT_STATUS_UNSUCCESSFUL; + } + + /* check returnet status code */ + if (NT_STATUS_IS_ERR(r_o.status)) { + /* report the error */ + DEBUG(0,("WKS_R_QUERY_INFO: %s\n", nt_errstr(r_o.status))); + prs_mem_free(&rbuf); + return r_o.status; + } + + /* do clean up */ + prs_mem_free(&rbuf); + + return nt_status; +} + diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c new file mode 100644 index 0000000000..8ddd116679 --- /dev/null +++ b/source3/libsmb/cliconnect.c @@ -0,0 +1,1348 @@ +/* + Unix SMB/CIFS implementation. + client connect/disconnect routines + Copyright (C) Andrew Tridgell 1994-1998 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#define NO_SYSLOG + +#include "includes.h" + + +static const struct { + int prot; + const char *name; + } +prots[] = + { + {PROTOCOL_CORE,"PC NETWORK PROGRAM 1.0"}, + {PROTOCOL_COREPLUS,"MICROSOFT NETWORKS 1.03"}, + {PROTOCOL_LANMAN1,"MICROSOFT NETWORKS 3.0"}, + {PROTOCOL_LANMAN1,"LANMAN1.0"}, + {PROTOCOL_LANMAN2,"LM1.2X002"}, + {PROTOCOL_LANMAN2,"Samba"}, + {PROTOCOL_NT1,"NT LANMAN 1.0"}, + {PROTOCOL_NT1,"NT LM 0.12"}, + {-1,NULL} + }; + + +/**************************************************************************** +do an old lanman2 style session setup +****************************************************************************/ +static BOOL cli_session_setup_lanman2(struct cli_state *cli, char *user, + char *pass, int passlen) +{ + fstring pword; + char *p; + + if (passlen > sizeof(pword)-1) { + return False; + } + + /* if in share level security then don't send a password now */ + if (!(cli->sec_mode & 1)) { + passlen = 0; + } + + if (passlen > 0 && (cli->sec_mode & 2) && passlen != 24) { + /* Encrypted mode needed, and non encrypted password supplied. */ + passlen = 24; + clistr_push(cli, pword, pass, -1, STR_TERMINATE); + SMBencrypt((uchar *)pword,cli->secblob.data,(uchar *)pword); + } else if ((cli->sec_mode & 2) && passlen == 24) { + /* Encrypted mode needed, and encrypted password supplied. */ + memcpy(pword, pass, passlen); + } else if (passlen > 0) { + /* Plaintext mode needed, assume plaintext supplied. */ + passlen = clistr_push(cli, pword, pass, -1, STR_TERMINATE); + } + + /* send a session setup command */ + memset(cli->outbuf,'\0',smb_size); + set_message(cli->outbuf,10, 0, True); + SCVAL(cli->outbuf,smb_com,SMBsesssetupX); + cli_setup_packet(cli); + + SCVAL(cli->outbuf,smb_vwv0,0xFF); + SSVAL(cli->outbuf,smb_vwv2,cli->max_xmit); + SSVAL(cli->outbuf,smb_vwv3,2); + SSVAL(cli->outbuf,smb_vwv4,1); + SIVAL(cli->outbuf,smb_vwv5,cli->sesskey); + SSVAL(cli->outbuf,smb_vwv7,passlen); + + p = smb_buf(cli->outbuf); + memcpy(p,pword,passlen); + p += passlen; + p += clistr_push(cli, p, user, -1, STR_TERMINATE); + cli_setup_bcc(cli, p); + + cli_send_smb(cli); + if (!cli_receive_smb(cli)) + return False; + + show_msg(cli->inbuf); + + if (cli_is_error(cli)) { + return False; + } + + /* use the returned vuid from now on */ + cli->vuid = SVAL(cli->inbuf,smb_uid); + fstrcpy(cli->user_name, user); + + return True; +} + + +/**************************************************************************** +work out suitable capabilities to offer the server +****************************************************************************/ +static uint32 cli_session_setup_capabilities(struct cli_state *cli) +{ + uint32 capabilities = CAP_NT_SMBS; + + if (!cli->force_dos_errors) { + capabilities |= CAP_STATUS32; + } + + if (cli->use_level_II_oplocks) { + capabilities |= CAP_LEVEL_II_OPLOCKS; + } + + if (cli->capabilities & CAP_UNICODE) { + capabilities |= CAP_UNICODE; + } + + return capabilities; +} + + +/**************************************************************************** +do a NT1 guest session setup +****************************************************************************/ +static BOOL cli_session_setup_guest(struct cli_state *cli) +{ + char *p; + uint32 capabilities = cli_session_setup_capabilities(cli); + + set_message(cli->outbuf,13,0,True); + SCVAL(cli->outbuf,smb_com,SMBsesssetupX); + cli_setup_packet(cli); + + SCVAL(cli->outbuf,smb_vwv0,0xFF); + SSVAL(cli->outbuf,smb_vwv2,CLI_BUFFER_SIZE); + SSVAL(cli->outbuf,smb_vwv3,2); + SSVAL(cli->outbuf,smb_vwv4,cli->pid); + SIVAL(cli->outbuf,smb_vwv5,cli->sesskey); + SSVAL(cli->outbuf,smb_vwv7,0); + SSVAL(cli->outbuf,smb_vwv8,0); + SIVAL(cli->outbuf,smb_vwv11,capabilities); + p = smb_buf(cli->outbuf); + p += clistr_push(cli, p, "", -1, STR_TERMINATE); /* username */ + p += clistr_push(cli, p, "", -1, STR_TERMINATE); /* workgroup */ + p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE); + p += clistr_push(cli, p, "Samba", -1, STR_TERMINATE); + cli_setup_bcc(cli, p); + + cli_send_smb(cli); + if (!cli_receive_smb(cli)) + return False; + + show_msg(cli->inbuf); + + if (cli_is_error(cli)) { + return False; + } + + cli->vuid = SVAL(cli->inbuf,smb_uid); + + p = smb_buf(cli->inbuf); + p += clistr_pull(cli, cli->server_os, p, sizeof(fstring), -1, STR_TERMINATE); + p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), -1, STR_TERMINATE); + p += clistr_pull(cli, cli->server_domain, p, sizeof(fstring), -1, STR_TERMINATE); + + fstrcpy(cli->user_name, ""); + + return True; +} + + +/**************************************************************************** +do a NT1 plaintext session setup +****************************************************************************/ +static BOOL cli_session_setup_plaintext(struct cli_state *cli, char *user, + char *pass, char *workgroup) +{ + uint32 capabilities = cli_session_setup_capabilities(cli); + fstring pword; + int passlen; + char *p; + + passlen = clistr_push(cli, pword, pass, sizeof(pword), STR_TERMINATE|STR_ASCII); + + set_message(cli->outbuf,13,0,True); + SCVAL(cli->outbuf,smb_com,SMBsesssetupX); + cli_setup_packet(cli); + + SCVAL(cli->outbuf,smb_vwv0,0xFF); + SSVAL(cli->outbuf,smb_vwv2,CLI_BUFFER_SIZE); + SSVAL(cli->outbuf,smb_vwv3,2); + SSVAL(cli->outbuf,smb_vwv4,cli->pid); + SIVAL(cli->outbuf,smb_vwv5,cli->sesskey); + SSVAL(cli->outbuf,smb_vwv7,passlen); + SSVAL(cli->outbuf,smb_vwv8,0); + SIVAL(cli->outbuf,smb_vwv11,capabilities); + p = smb_buf(cli->outbuf); + memcpy(p, pword, passlen); + p += passlen; + p += clistr_push(cli, p, user, -1, STR_TERMINATE); /* username */ + p += clistr_push(cli, p, workgroup, -1, STR_TERMINATE); /* workgroup */ + p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE); + p += clistr_push(cli, p, "Samba", -1, STR_TERMINATE); + cli_setup_bcc(cli, p); + + cli_send_smb(cli); + if (!cli_receive_smb(cli)) + return False; + + show_msg(cli->inbuf); + + if (cli_is_error(cli)) { + return False; + } + + cli->vuid = SVAL(cli->inbuf,smb_uid); + p = smb_buf(cli->inbuf); + p += clistr_pull(cli, cli->server_os, p, sizeof(fstring), -1, STR_TERMINATE); + p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), -1, STR_TERMINATE); + p += clistr_pull(cli, cli->server_domain, p, sizeof(fstring), -1, STR_TERMINATE); + fstrcpy(cli->user_name, user); + + return True; +} + + +/**************************************************************************** +do a NT1 NTLM/LM encrypted session setup +****************************************************************************/ +static BOOL cli_session_setup_nt1(struct cli_state *cli, char *user, + char *pass, int passlen, + char *ntpass, int ntpasslen, + char *workgroup) +{ + uint32 capabilities = cli_session_setup_capabilities(cli); + fstring pword, ntpword; + char *p; + + if (passlen > sizeof(pword)-1 || ntpasslen > sizeof(ntpword)-1) { + return False; + } + + if (passlen != 24) { + /* non encrypted password supplied. Ignore ntpass. */ + passlen = 24; + ntpasslen = 24; + clistr_push(cli, pword, + pass?pass:"", sizeof(pword), STR_TERMINATE|STR_ASCII); + clistr_push(cli, ntpword, + pass?pass:"", sizeof(ntpword), STR_TERMINATE|STR_ASCII); + SMBencrypt((uchar *)pword,cli->secblob.data,(uchar *)pword); + SMBNTencrypt((uchar *)ntpword,cli->secblob.data,(uchar *)ntpword); + } else { + memcpy(pword, pass, passlen); + memcpy(ntpword, ntpass, ntpasslen); + } + + /* send a session setup command */ + memset(cli->outbuf,'\0',smb_size); + + set_message(cli->outbuf,13,0,True); + SCVAL(cli->outbuf,smb_com,SMBsesssetupX); + cli_setup_packet(cli); + + SCVAL(cli->outbuf,smb_vwv0,0xFF); + SSVAL(cli->outbuf,smb_vwv2,CLI_BUFFER_SIZE); + SSVAL(cli->outbuf,smb_vwv3,2); + SSVAL(cli->outbuf,smb_vwv4,cli->pid); + SIVAL(cli->outbuf,smb_vwv5,cli->sesskey); + SSVAL(cli->outbuf,smb_vwv7,passlen); + SSVAL(cli->outbuf,smb_vwv8,ntpasslen); + SIVAL(cli->outbuf,smb_vwv11,capabilities); + p = smb_buf(cli->outbuf); + memcpy(p,pword,passlen); p += passlen; + memcpy(p,ntpword,ntpasslen); p += ntpasslen; + p += clistr_push(cli, p, user, -1, STR_TERMINATE|STR_UPPER); + p += clistr_push(cli, p, workgroup, -1, STR_TERMINATE|STR_UPPER); + p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE); + p += clistr_push(cli, p, "Samba", -1, STR_TERMINATE); + cli_setup_bcc(cli, p); + + cli_send_smb(cli); + if (!cli_receive_smb(cli)) + return False; + + show_msg(cli->inbuf); + + if (cli_is_error(cli)) { + return False; + } + + /* use the returned vuid from now on */ + cli->vuid = SVAL(cli->inbuf,smb_uid); + + p = smb_buf(cli->inbuf); + p += clistr_pull(cli, cli->server_os, p, sizeof(fstring), -1, STR_TERMINATE); + p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), -1, STR_TERMINATE); + p += clistr_pull(cli, cli->server_domain, p, sizeof(fstring), -1, STR_TERMINATE); + + fstrcpy(cli->user_name, user); + + return True; +} + + +/**************************************************************************** +send a extended security session setup blob, returning a reply blob +****************************************************************************/ +static DATA_BLOB cli_session_setup_blob(struct cli_state *cli, DATA_BLOB blob) +{ + uint32 capabilities = cli_session_setup_capabilities(cli); + char *p; + DATA_BLOB blob2; + uint32 len; + + blob2 = data_blob(NULL, 0); + + capabilities |= CAP_EXTENDED_SECURITY; + + /* send a session setup command */ + memset(cli->outbuf,'\0',smb_size); + + set_message(cli->outbuf,12,0,True); + SCVAL(cli->outbuf,smb_com,SMBsesssetupX); + cli_setup_packet(cli); + + SCVAL(cli->outbuf,smb_vwv0,0xFF); + SSVAL(cli->outbuf,smb_vwv2,CLI_BUFFER_SIZE); + SSVAL(cli->outbuf,smb_vwv3,2); + SSVAL(cli->outbuf,smb_vwv4,1); + SIVAL(cli->outbuf,smb_vwv5,0); + SSVAL(cli->outbuf,smb_vwv7,blob.length); + SIVAL(cli->outbuf,smb_vwv10,capabilities); + p = smb_buf(cli->outbuf); + memcpy(p, blob.data, blob.length); + p += blob.length; + p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE); + p += clistr_push(cli, p, "Samba", -1, STR_TERMINATE); + cli_setup_bcc(cli, p); + + cli_send_smb(cli); + if (!cli_receive_smb(cli)) + return blob2; + + show_msg(cli->inbuf); + + if (cli_is_error(cli) && !NT_STATUS_EQUAL(cli_nt_error(cli), + NT_STATUS_MORE_PROCESSING_REQUIRED)) { + return blob2; + } + + /* use the returned vuid from now on */ + cli->vuid = SVAL(cli->inbuf,smb_uid); + + p = smb_buf(cli->inbuf); + + blob2 = data_blob(p, SVAL(cli->inbuf, smb_vwv3)); + + p += blob2.length; + p += clistr_pull(cli, cli->server_os, p, sizeof(fstring), -1, STR_TERMINATE); + + /* w2k with kerberos doesn't properly null terminate this field */ + len = smb_buflen(cli->inbuf) - PTR_DIFF(p, smb_buf(cli->inbuf)); + p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), len, 0); + + return blob2; +} + + +#ifdef HAVE_KRB5 +/**************************************************************************** +do a spnego/kerberos encrypted session setup +****************************************************************************/ +static BOOL cli_session_setup_kerberos(struct cli_state *cli, char *principal, char *workgroup) +{ + DATA_BLOB blob2, negTokenTarg; + + DEBUG(2,("Doing kerberos session setup\n")); + + /* generate the encapsulated kerberos5 ticket */ + negTokenTarg = spnego_gen_negTokenTarg(cli, principal); + + if (!negTokenTarg.data) return False; + +#if 0 + file_save("negTokenTarg.dat", negTokenTarg.data, negTokenTarg.length); +#endif + + blob2 = cli_session_setup_blob(cli, negTokenTarg); + + /* we don't need this blob for kerberos */ + data_blob_free(&blob2); + + data_blob_free(&negTokenTarg); + + return !cli_is_error(cli); +} +#endif + +/**************************************************************************** +do a spnego/NTLMSSP encrypted session setup +****************************************************************************/ +static BOOL cli_session_setup_ntlmssp(struct cli_state *cli, char *user, + char *pass, char *workgroup) +{ + const char *mechs[] = {OID_NTLMSSP, NULL}; + DATA_BLOB msg1; + DATA_BLOB blob, chal1, chal2, auth; + uint8 challenge[8]; + uint8 nthash[24], lmhash[24], sess_key[16]; + uint32 neg_flags; + + neg_flags = NTLMSSP_NEGOTIATE_UNICODE | + NTLMSSP_NEGOTIATE_LM_KEY | + NTLMSSP_NEGOTIATE_NTLM; + + memset(sess_key, 0, 16); + + /* generate the ntlmssp negotiate packet */ + msrpc_gen(&blob, "CddB", + "NTLMSSP", + NTLMSSP_NEGOTIATE, + neg_flags, + sess_key, 16); + + /* and wrap it in a SPNEGO wrapper */ + msg1 = gen_negTokenTarg(mechs, blob); + data_blob_free(&blob); + + /* now send that blob on its way */ + blob = cli_session_setup_blob(cli, msg1); + + data_blob_free(&msg1); + + if (!NT_STATUS_EQUAL(cli_nt_error(cli), NT_STATUS_MORE_PROCESSING_REQUIRED)) { + return False; + } + +#if 0 + file_save("chal.dat", blob.data, blob.length); +#endif + + /* the server gives us back two challenges */ + if (!spnego_parse_challenge(blob, &chal1, &chal2)) { + DEBUG(3,("Failed to parse challenges\n")); + return False; + } + + data_blob_free(&blob); + + /* encrypt the password with the challenge */ + memcpy(challenge, chal1.data + 24, 8); + SMBencrypt((unsigned char *)pass, challenge,lmhash); + SMBNTencrypt((unsigned char *)pass, challenge,nthash); + +#if 0 + file_save("nthash.dat", nthash, 24); + file_save("lmhash.dat", lmhash, 24); + file_save("chal1.dat", chal1.data, chal1.length); +#endif + + data_blob_free(&chal1); + data_blob_free(&chal2); + + /* this generates the actual auth packet */ + msrpc_gen(&blob, "CdBBUUUBd", + "NTLMSSP", + NTLMSSP_AUTH, + lmhash, 24, + nthash, 24, + workgroup, + user, + cli->calling.name, + sess_key, 16, + neg_flags); + + /* wrap it in SPNEGO */ + auth = spnego_gen_auth(blob); + + data_blob_free(&blob); + + /* now send the auth packet and we should be done */ + blob = cli_session_setup_blob(cli, auth); + + data_blob_free(&auth); + data_blob_free(&blob); + + return !cli_is_error(cli); +} + + +/**************************************************************************** +do a spnego encrypted session setup +****************************************************************************/ +static BOOL cli_session_setup_spnego(struct cli_state *cli, char *user, + char *pass, char *workgroup) +{ + char *principal; + char *OIDs[ASN1_MAX_OIDS]; + uint8 guid[16]; + int i; + BOOL got_kerberos_mechanism = False; + + DEBUG(2,("Doing spnego session setup (blob length=%d)\n", cli->secblob.length)); + + /* the server might not even do spnego */ + if (cli->secblob.length == 16) { + DEBUG(3,("server didn't supply a full spnego negprot\n")); + goto ntlmssp; + } + +#if 0 + file_save("negprot.dat", cli->secblob.data, cli->secblob.length); +#endif + + /* the server sent us the first part of the SPNEGO exchange in the negprot + reply */ + if (!spnego_parse_negTokenInit(cli->secblob, guid, OIDs, &principal)) { + return False; + } + + /* make sure the server understands kerberos */ + for (i=0;OIDs[i];i++) { + DEBUG(3,("got OID=%s\n", OIDs[i])); + if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 || + strcmp(OIDs[i], OID_KERBEROS5) == 0) { + got_kerberos_mechanism = True; + } + free(OIDs[i]); + } + DEBUG(3,("got principal=%s\n", principal)); + + fstrcpy(cli->user_name, user); + +#ifdef HAVE_KRB5 + if (got_kerberos_mechanism && cli->use_kerberos) { + return cli_session_setup_kerberos(cli, principal, workgroup); + } +#endif + + free(principal); + +ntlmssp: + + return cli_session_setup_ntlmssp(cli, user, pass, workgroup); +} + + +/**************************************************************************** + Send a session setup. The username and workgroup is in UNIX character + format and must be converted to DOS codepage format before sending. If the + password is in plaintext, the same should be done. +****************************************************************************/ +BOOL cli_session_setup(struct cli_state *cli, + char *user, + char *pass, int passlen, + char *ntpass, int ntpasslen, + char *workgroup) +{ + char *p; + fstring user2; + + /* allow for workgroups as part of the username */ + fstrcpy(user2, user); + if ((p=strchr_m(user2,'\\')) || (p=strchr_m(user2,'/')) || + (p=strchr_m(user2,*lp_winbind_separator()))) { + *p = 0; + user = p+1; + workgroup = user2; + } + + if (cli->protocol < PROTOCOL_LANMAN1) + return True; + + /* now work out what sort of session setup we are going to + do. I have split this into separate functions to make the + flow a bit easier to understand (tridge) */ + + /* if its an older server then we have to use the older request format */ + if (cli->protocol < PROTOCOL_NT1) { + return cli_session_setup_lanman2(cli, user, pass, passlen); + } + + /* if no user is supplied then we have to do an anonymous connection. + passwords are ignored */ + if (!user || !*user) { + return cli_session_setup_guest(cli); + } + + /* if the server is share level then send a plaintext null + password at this point. The password is sent in the tree + connect */ + if ((cli->sec_mode & 1) == 0) { + return cli_session_setup_plaintext(cli, user, "", workgroup); + } + + /* if the server doesn't support encryption then we have to use plaintext. The + second password is ignored */ + if ((cli->sec_mode & 2) == 0) { + return cli_session_setup_plaintext(cli, user, pass, workgroup); + } + + /* if the server supports extended security then use SPNEGO */ + if (cli->capabilities & CAP_EXTENDED_SECURITY) { + return cli_session_setup_spnego(cli, user, pass, workgroup); + } + + /* otherwise do a NT1 style session setup */ + return cli_session_setup_nt1(cli, user, + pass, passlen, ntpass, ntpasslen, + workgroup); +} + +/**************************************************************************** + Send a uloggoff. +*****************************************************************************/ + +BOOL cli_ulogoff(struct cli_state *cli) +{ + memset(cli->outbuf,'\0',smb_size); + set_message(cli->outbuf,2,0,True); + SCVAL(cli->outbuf,smb_com,SMBulogoffX); + cli_setup_packet(cli); + SSVAL(cli->outbuf,smb_vwv0,0xFF); + SSVAL(cli->outbuf,smb_vwv2,0); /* no additional info */ + + cli_send_smb(cli); + if (!cli_receive_smb(cli)) + return False; + + return !cli_is_error(cli); +} + +/**************************************************************************** +send a tconX +****************************************************************************/ +BOOL cli_send_tconX(struct cli_state *cli, + const char *share, const char *dev, const char *pass, int passlen) +{ + fstring fullshare, pword, dos_pword; + char *p; + memset(cli->outbuf,'\0',smb_size); + memset(cli->inbuf,'\0',smb_size); + + fstrcpy(cli->share, share); + + /* in user level security don't send a password now */ + if (cli->sec_mode & 1) { + passlen = 1; + pass = ""; + } + + if ((cli->sec_mode & 2) && *pass && passlen != 24) { + /* + * Non-encrypted passwords - convert to DOS codepage before encryption. + */ + passlen = 24; + clistr_push(cli, dos_pword, pass, -1, STR_TERMINATE); + SMBencrypt((uchar *)dos_pword,cli->secblob.data,(uchar *)pword); + } else { + if((cli->sec_mode & 3) == 0) { + /* + * Non-encrypted passwords - convert to DOS codepage before using. + */ + passlen = clistr_push(cli, pword, pass, -1, STR_TERMINATE); + } else { + memcpy(pword, pass, passlen); + } + } + + if (cli->port == 445) { + slprintf(fullshare, sizeof(fullshare)-1, + "%s", share); + } else { + slprintf(fullshare, sizeof(fullshare)-1, + "\\\\%s\\%s", cli->desthost, share); + } + + set_message(cli->outbuf,4, 0, True); + SCVAL(cli->outbuf,smb_com,SMBtconX); + cli_setup_packet(cli); + + SSVAL(cli->outbuf,smb_vwv0,0xFF); + SSVAL(cli->outbuf,smb_vwv3,passlen); + + p = smb_buf(cli->outbuf); + memcpy(p,pword,passlen); + p += passlen; + p += clistr_push(cli, p, fullshare, -1, STR_TERMINATE |STR_UPPER); + fstrcpy(p, dev); p += strlen(dev)+1; + + cli_setup_bcc(cli, p); + + cli_send_smb(cli); + if (!cli_receive_smb(cli)) + return False; + + if (cli_is_error(cli)) { + return False; + } + + clistr_pull(cli, cli->dev, smb_buf(cli->inbuf), sizeof(fstring), -1, STR_TERMINATE|STR_ASCII); + + if (strcasecmp(share,"IPC$")==0) { + fstrcpy(cli->dev, "IPC"); + } + + if (cli->protocol >= PROTOCOL_NT1 && + smb_buflen(cli->inbuf) == 3) { + /* almost certainly win95 - enable bug fixes */ + cli->win95 = True; + } + + cli->cnum = SVAL(cli->inbuf,smb_tid); + return True; +} + + +/**************************************************************************** +send a tree disconnect +****************************************************************************/ +BOOL cli_tdis(struct cli_state *cli) +{ + memset(cli->outbuf,'\0',smb_size); + set_message(cli->outbuf,0,0,True); + SCVAL(cli->outbuf,smb_com,SMBtdis); + SSVAL(cli->outbuf,smb_tid,cli->cnum); + cli_setup_packet(cli); + + cli_send_smb(cli); + if (!cli_receive_smb(cli)) + return False; + + return !cli_is_error(cli); +} + + +/**************************************************************************** +send a negprot command +****************************************************************************/ +void cli_negprot_send(struct cli_state *cli) +{ + char *p; + int numprots; + + memset(cli->outbuf,'\0',smb_size); + + /* setup the protocol strings */ + set_message(cli->outbuf,0,0,True); + + p = smb_buf(cli->outbuf); + for (numprots=0; + prots[numprots].name && prots[numprots].prot<=cli->protocol; + numprots++) { + *p++ = 2; + p += clistr_push(cli, p, prots[numprots].name, -1, STR_TERMINATE); + } + + SCVAL(cli->outbuf,smb_com,SMBnegprot); + cli_setup_bcc(cli, p); + cli_setup_packet(cli); + + SCVAL(smb_buf(cli->outbuf),0,2); + + cli_send_smb(cli); +} + + +/**************************************************************************** +send a negprot command +****************************************************************************/ +BOOL cli_negprot(struct cli_state *cli) +{ + char *p; + int numprots; + int plength; + + memset(cli->outbuf,'\0',smb_size); + + /* setup the protocol strings */ + for (plength=0,numprots=0; + prots[numprots].name && prots[numprots].prot<=cli->protocol; + numprots++) + plength += strlen(prots[numprots].name)+2; + + set_message(cli->outbuf,0,plength,True); + + p = smb_buf(cli->outbuf); + for (numprots=0; + prots[numprots].name && prots[numprots].prot<=cli->protocol; + numprots++) { + *p++ = 2; + p += clistr_push(cli, p, prots[numprots].name, -1, STR_TERMINATE); + } + + SCVAL(cli->outbuf,smb_com,SMBnegprot); + cli_setup_packet(cli); + + SCVAL(smb_buf(cli->outbuf),0,2); + + cli_send_smb(cli); + if (!cli_receive_smb(cli)) + return False; + + show_msg(cli->inbuf); + + if (cli_is_error(cli) || + ((int)SVAL(cli->inbuf,smb_vwv0) >= numprots)) { + return(False); + } + + cli->protocol = prots[SVAL(cli->inbuf,smb_vwv0)].prot; + + if (cli->protocol >= PROTOCOL_NT1) { + /* NT protocol */ + cli->sec_mode = CVAL(cli->inbuf,smb_vwv1); + cli->max_mux = SVAL(cli->inbuf, smb_vwv1+1); + cli->max_xmit = IVAL(cli->inbuf,smb_vwv3+1); + cli->sesskey = IVAL(cli->inbuf,smb_vwv7+1); + cli->serverzone = SVALS(cli->inbuf,smb_vwv15+1); + cli->serverzone *= 60; + /* this time arrives in real GMT */ + cli->servertime = interpret_long_date(cli->inbuf+smb_vwv11+1); + cli->secblob = data_blob(smb_buf(cli->inbuf),smb_buflen(cli->inbuf)); + cli->capabilities = IVAL(cli->inbuf,smb_vwv9+1); + if (cli->capabilities & CAP_RAW_MODE) { + cli->readbraw_supported = True; + cli->writebraw_supported = True; + } + /* work out if they sent us a workgroup */ + if (!(cli->capabilities & CAP_EXTENDED_SECURITY) && + smb_buflen(cli->inbuf) > 8) { + clistr_pull(cli, cli->server_domain, + smb_buf(cli->inbuf)+8, sizeof(cli->server_domain), + smb_buflen(cli->inbuf)-8, STR_UNICODE|STR_NOALIGN); + } + } else if (cli->protocol >= PROTOCOL_LANMAN1) { + cli->sec_mode = SVAL(cli->inbuf,smb_vwv1); + cli->max_xmit = SVAL(cli->inbuf,smb_vwv2); + cli->sesskey = IVAL(cli->inbuf,smb_vwv6); + cli->serverzone = SVALS(cli->inbuf,smb_vwv10); + cli->serverzone *= 60; + /* this time is converted to GMT by make_unix_date */ + cli->servertime = make_unix_date(cli->inbuf+smb_vwv8); + cli->readbraw_supported = ((SVAL(cli->inbuf,smb_vwv5) & 0x1) != 0); + cli->writebraw_supported = ((SVAL(cli->inbuf,smb_vwv5) & 0x2) != 0); + cli->secblob = data_blob(smb_buf(cli->inbuf),smb_buflen(cli->inbuf)); + } else { + /* the old core protocol */ + cli->sec_mode = 0; + cli->serverzone = TimeDiff(time(NULL)); + } + + cli->max_xmit = MIN(cli->max_xmit, CLI_BUFFER_SIZE); + + /* a way to force ascii SMB */ + if (getenv("CLI_FORCE_ASCII")) { + cli->capabilities &= ~CAP_UNICODE; + } + + return True; +} + + +/**************************************************************************** + send a session request. see rfc1002.txt 4.3 and 4.3.2 +****************************************************************************/ +BOOL cli_session_request(struct cli_state *cli, + struct nmb_name *calling, struct nmb_name *called) +{ + char *p; + int len = 4; + extern pstring user_socket_options; + + /* 445 doesn't have session request */ + if (cli->port == 445) return True; + + /* send a session request (RFC 1002) */ + memcpy(&(cli->calling), calling, sizeof(*calling)); + memcpy(&(cli->called ), called , sizeof(*called )); + + /* put in the destination name */ + p = cli->outbuf+len; + name_mangle(cli->called .name, p, cli->called .name_type); + len += name_len(p); + + /* and my name */ + p = cli->outbuf+len; + name_mangle(cli->calling.name, p, cli->calling.name_type); + len += name_len(p); + + /* setup the packet length */ + _smb_setlen(cli->outbuf,len); + SCVAL(cli->outbuf,0,0x81); + +#ifdef WITH_SSL +retry: +#endif /* WITH_SSL */ + + cli_send_smb(cli); + DEBUG(5,("Sent session request\n")); + + if (!cli_receive_smb(cli)) + return False; + + if (CVAL(cli->inbuf,0) == 0x84) { + /* C. Hoch 9/14/95 Start */ + /* For information, here is the response structure. + * We do the byte-twiddling to for portability. + struct RetargetResponse{ + unsigned char type; + unsigned char flags; + int16 length; + int32 ip_addr; + int16 port; + }; + */ + int port = (CVAL(cli->inbuf,8)<<8)+CVAL(cli->inbuf,9); + /* SESSION RETARGET */ + putip((char *)&cli->dest_ip,cli->inbuf+4); + + cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip, port, LONG_CONNECT_TIMEOUT); + if (cli->fd == -1) + return False; + + DEBUG(3,("Retargeted\n")); + + set_socket_options(cli->fd,user_socket_options); + + /* Try again */ + { + static int depth; + BOOL ret; + if (depth > 4) { + DEBUG(0,("Retarget recursion - failing\n")); + return False; + } + depth++; + ret = cli_session_request(cli, calling, called); + depth--; + return ret; + } + } /* C. Hoch 9/14/95 End */ + +#ifdef WITH_SSL + if (CVAL(cli->inbuf,0) == 0x83 && CVAL(cli->inbuf,4) == 0x8e){ /* use ssl */ + if (!sslutil_fd_is_ssl(cli->fd)){ + if (sslutil_connect(cli->fd) == 0) + goto retry; + } + } +#endif /* WITH_SSL */ + + if (CVAL(cli->inbuf,0) != 0x82) { + /* This is the wrong place to put the error... JRA. */ + cli->rap_error = CVAL(cli->inbuf,4); + return False; + } + return(True); +} + +/**************************************************************************** +open the client sockets +****************************************************************************/ +BOOL cli_connect(struct cli_state *cli, const char *host, struct in_addr *ip) +{ + extern pstring user_socket_options; + int name_type = 0x20; + char *p; + + /* reasonable default hostname */ + if (!host) host = "*SMBSERVER"; + + fstrcpy(cli->desthost, host); + + /* allow hostnames of the form NAME#xx and do a netbios lookup */ + if ((p = strchr(cli->desthost, '#'))) { + name_type = strtol(p+1, NULL, 16); + *p = 0; + } + + if (!ip || is_zero_ip(*ip)) { + if (!resolve_name(cli->desthost, &cli->dest_ip, name_type)) { + return False; + } + if (ip) *ip = cli->dest_ip; + } else { + cli->dest_ip = *ip; + } + + if (getenv("LIBSMB_PROG")) { + cli->fd = sock_exec(getenv("LIBSMB_PROG")); + } else { + /* try 445 first, then 139 */ + int port = cli->port?cli->port:445; + cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip, + port, cli->timeout); + if (cli->fd == -1 && cli->port == 0) { + port = 139; + cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip, + port, cli->timeout); + } + if (cli->fd != -1) cli->port = port; + } + if (cli->fd == -1) { + DEBUG(1,("Error connecting to %s (%s)\n", + inet_ntoa(*ip),strerror(errno))); + return False; + } + + set_socket_options(cli->fd,user_socket_options); + + return True; +} + +/**************************************************************************** +establishes a connection right up to doing tconX, password in cache. +****************************************************************************/ +BOOL cli_establish_connection(struct cli_state *cli, + char *dest_host, struct in_addr *dest_ip, + struct nmb_name *calling, struct nmb_name *called, + char *service, char *service_type, + BOOL do_shutdown, BOOL do_tcon) +{ + DEBUG(5,("cli_establish_connection: %s connecting to %s (%s) - %s [%s]\n", + nmb_namestr(calling), nmb_namestr(called), inet_ntoa(*dest_ip), + cli->user_name, cli->domain)); + + /* establish connection */ + + if ((!cli->initialised)) + { + return False; + } + + /* cli_establish_connection() can't handle spnego yet. Once we get rid of + pwd_cache and other horrors we can get rid of this */ + cli->use_spnego = False; + + if (cli->fd == -1) + { + if (!cli_connect(cli, dest_host, dest_ip)) + { + DEBUG(1,("cli_establish_connection: failed to connect to %s (%s)\n", + nmb_namestr(called), inet_ntoa(*dest_ip))); + return False; + } + } + + if (!cli_session_request(cli, calling, called)) + { + DEBUG(1,("failed session request\n")); + if (do_shutdown) + cli_shutdown(cli); + return False; + } + + if (!cli_negprot(cli)) + { + DEBUG(1,("failed negprot\n")); + if (do_shutdown) + cli_shutdown(cli); + return False; + } + + if (cli->pwd.cleartext || cli->pwd.null_pwd) + { + fstring passwd; + int pass_len; + + if (cli->pwd.null_pwd) + { + /* attempt null session */ + passwd[0] = 0; + pass_len = 1; + } + else + { + /* attempt clear-text session */ + pwd_get_cleartext(&(cli->pwd), passwd); + pass_len = strlen(passwd); + } + + /* attempt clear-text session */ + if (!cli_session_setup(cli, cli->user_name, + passwd, pass_len, + NULL, 0, + cli->domain)) + { + DEBUG(1,("failed session setup\n")); + if (do_shutdown) + { + cli_shutdown(cli); + } + return False; + } + if (do_tcon) + { + if (!cli_send_tconX(cli, service, service_type, + (char*)passwd, strlen(passwd))) + { + DEBUG(1,("failed tcon_X\n")); + if (do_shutdown) + { + cli_shutdown(cli); + } + return False; + } + } + } + else + { + /* attempt encrypted session */ + unsigned char nt_sess_pwd[24]; + unsigned char lm_sess_pwd[24]; + + /* creates (storing a copy of) and then obtains a 24 byte password OWF */ + pwd_make_lm_nt_owf(&(cli->pwd), cli->secblob.data); + pwd_get_lm_nt_owf(&(cli->pwd), lm_sess_pwd, nt_sess_pwd); + + /* attempt encrypted session */ + if (!cli_session_setup(cli, cli->user_name, + (char*)lm_sess_pwd, sizeof(lm_sess_pwd), + (char*)nt_sess_pwd, sizeof(nt_sess_pwd), + cli->domain)) + { + DEBUG(1,("failed session setup\n")); + if (do_shutdown) + cli_shutdown(cli); + return False; + } + + DEBUG(1,("session setup ok\n")); + + if (*cli->server_domain || *cli->server_os || *cli->server_type) + { + DEBUG(1,("Domain=[%s] OS=[%s] Server=[%s]\n", + cli->server_domain, + cli->server_os, + cli->server_type)); + } + + if (do_tcon) + { + if (!cli_send_tconX(cli, service, service_type, + (char*)nt_sess_pwd, sizeof(nt_sess_pwd))) + { + DEBUG(1,("failed tcon_X\n")); + if (do_shutdown) + cli_shutdown(cli); + return False; + } + } + } + + if (do_shutdown) + cli_shutdown(cli); + + return True; +} + +/* Initialise client credentials for authenticated pipe access */ + +static void init_creds(struct ntuser_creds *creds, char* username, + char* domain, char* password, int pass_len) +{ + ZERO_STRUCTP(creds); + + pwd_set_cleartext(&creds->pwd, password); + + fstrcpy(creds->user_name, username); + fstrcpy(creds->domain, domain); + + if (!*username) { + creds->pwd.null_pwd = True; + } +} + +/**************************************************************************** +establishes a connection right up to doing tconX, password specified. +****************************************************************************/ +NTSTATUS cli_full_connection(struct cli_state **output_cli, + const char *my_name, const char *dest_host, + struct in_addr *dest_ip, int port, + char *service, char *service_type, + char *user, char *domain, + char *password, int pass_len) +{ + struct ntuser_creds creds; + NTSTATUS nt_status; + struct nmb_name calling; + struct nmb_name called; + struct cli_state *cli; + struct in_addr ip; + + if (!output_cli) + DEBUG(0, ("output_cli is NULL!?!")); + + *output_cli = NULL; + + make_nmb_name(&calling, my_name, 0x0); + make_nmb_name(&called , dest_host, 0x20); + +again: + + if (!(cli = cli_initialise(NULL))) + return NT_STATUS_NO_MEMORY; + + if (cli_set_port(cli, port) != port) { + cli_shutdown(cli); + return NT_STATUS_UNSUCCESSFUL; + } + + ip = *dest_ip; + + DEBUG(3,("Connecting to host=%s share=%s\n", dest_host, service)); + + if (!cli_connect(cli, dest_host, &ip)) { + DEBUG(1,("cli_establish_connection: failed to connect to %s (%s)\n", + nmb_namestr(&called), inet_ntoa(*dest_ip))); + cli_shutdown(cli); + return NT_STATUS_UNSUCCESSFUL; + } + + if (!cli_session_request(cli, &calling, &called)) { + char *p; + DEBUG(1,("session request to %s failed (%s)\n", + called.name, cli_errstr(cli))); + cli_shutdown(cli); + if ((p=strchr(called.name, '.'))) { + *p = 0; + goto again; + } + if (strcmp(called.name, "*SMBSERVER")) { + make_nmb_name(&called , "*SMBSERVER", 0x20); + goto again; + } + return NT_STATUS_UNSUCCESSFUL; + } + + if (!cli_negprot(cli)) { + DEBUG(1,("failed negprot\n")); + nt_status = NT_STATUS_UNSUCCESSFUL; + cli_shutdown(cli); + return nt_status; + } + + if (!cli_session_setup(cli, user, password, pass_len, password, pass_len, + domain)) { + DEBUG(1,("failed session setup\n")); + nt_status = cli_nt_error(cli); + cli_shutdown(cli); + if (NT_STATUS_IS_OK(nt_status)) + nt_status = NT_STATUS_UNSUCCESSFUL; + return nt_status; + } + + if (service) { + if (!cli_send_tconX(cli, service, service_type, + (char*)password, pass_len)) { + DEBUG(1,("failed tcon_X\n")); + nt_status = cli_nt_error(cli); + cli_shutdown(cli); + if (NT_STATUS_IS_OK(nt_status)) + nt_status = NT_STATUS_UNSUCCESSFUL; + return nt_status; + } + } + + init_creds(&creds, user, domain, password, pass_len); + cli_init_creds(cli, &creds); + + *output_cli = cli; + return NT_STATUS_OK; +} + +/**************************************************************************** + Attempt a NetBIOS session request, falling back to *SMBSERVER if needed. +****************************************************************************/ + +BOOL attempt_netbios_session_request(struct cli_state *cli, char *srchost, char *desthost, + struct in_addr *pdest_ip) +{ + struct nmb_name calling, called; + + make_nmb_name(&calling, srchost, 0x0); + + /* + * If the called name is an IP address + * then use *SMBSERVER immediately. + */ + + if(is_ipaddress(desthost)) + make_nmb_name(&called, "*SMBSERVER", 0x20); + else + make_nmb_name(&called, desthost, 0x20); + + if (!cli_session_request(cli, &calling, &called)) { + struct nmb_name smbservername; + + make_nmb_name(&smbservername , "*SMBSERVER", 0x20); + + /* + * If the name wasn't *SMBSERVER then + * try with *SMBSERVER if the first name fails. + */ + + if (nmb_name_equal(&called, &smbservername)) { + + /* + * The name used was *SMBSERVER, don't bother with another name. + */ + + DEBUG(0,("attempt_netbios_session_request: %s rejected the session for name *SMBSERVER \ +with error %s.\n", desthost, cli_errstr(cli) )); + cli_shutdown(cli); + return False; + } + + cli_shutdown(cli); + + if (!cli_initialise(cli) || + !cli_connect(cli, desthost, pdest_ip) || + !cli_session_request(cli, &calling, &smbservername)) { + DEBUG(0,("attempt_netbios_session_request: %s rejected the session for \ +name *SMBSERVER with error %s\n", desthost, cli_errstr(cli) )); + cli_shutdown(cli); + return False; + } + } + + return True; +} + + diff --git a/source3/libsmb/clidgram.c b/source3/libsmb/clidgram.c new file mode 100644 index 0000000000..8f4bdf7be6 --- /dev/null +++ b/source3/libsmb/clidgram.c @@ -0,0 +1,267 @@ +/* + Unix SMB/CIFS implementation. + client dgram calls + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) Richard Sharpe 2001 + Copyright (C) John Terpstra 2001 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/* + * cli_send_mailslot, send a mailslot for client code ... + */ + +int cli_send_mailslot(int dgram_sock, BOOL unique, char *mailslot, + char *buf, int len, + const char *srcname, int src_type, + const char *dstname, int dest_type, + struct in_addr dest_ip, struct in_addr src_ip, + int dest_port, int src_port) +{ + struct packet_struct p; + struct dgram_packet *dgram = &p.packet.dgram; + char *ptr, *p2; + char tmp[4]; + + memset((char *)&p, '\0', sizeof(p)); + + /* + * Next, build the DGRAM ... + */ + + /* DIRECT GROUP or UNIQUE datagram. */ + dgram->header.msg_type = unique ? 0x10 : 0x11; + dgram->header.flags.node_type = M_NODE; + dgram->header.flags.first = True; + dgram->header.flags.more = False; + dgram->header.dgm_id = ((unsigned)time(NULL)%(unsigned)0x7FFF) + ((unsigned)sys_getpid()%(unsigned)100); + dgram->header.source_ip.s_addr = src_ip.s_addr; + dgram->header.source_port = ntohs(src_port); + dgram->header.dgm_length = 0; /* Let build_dgram() handle this. */ + dgram->header.packet_offset = 0; + + make_nmb_name(&dgram->source_name,srcname,src_type); + make_nmb_name(&dgram->dest_name,dstname,dest_type); + + ptr = &dgram->data[0]; + + /* Setup the smb part. */ + ptr -= 4; /* XXX Ugliness because of handling of tcp SMB length. */ + memcpy(tmp,ptr,4); + set_message(ptr,17,17 + len,True); + memcpy(ptr,tmp,4); + + SCVAL(ptr,smb_com,SMBtrans); + SSVAL(ptr,smb_vwv1,len); + SSVAL(ptr,smb_vwv11,len); + SSVAL(ptr,smb_vwv12,70 + strlen(mailslot)); + SSVAL(ptr,smb_vwv13,3); + SSVAL(ptr,smb_vwv14,1); + SSVAL(ptr,smb_vwv15,1); + SSVAL(ptr,smb_vwv16,2); + p2 = smb_buf(ptr); + pstrcpy(p2,mailslot); + p2 = skip_string(p2,1); + + memcpy(p2,buf,len); + p2 += len; + + dgram->datasize = PTR_DIFF(p2,ptr+4); /* +4 for tcp length. */ + + p.ip = dest_ip; + p.port = dest_port; + p.fd = dgram_sock; + p.timestamp = time(NULL); + p.packet_type = DGRAM_PACKET; + + DEBUG(4,("send_mailslot: Sending to mailslot %s from %s IP %s ", mailslot, + nmb_namestr(&dgram->source_name), inet_ntoa(src_ip))); + DEBUG(4,("to %s IP %s\n", nmb_namestr(&dgram->dest_name), inet_ntoa(dest_ip))); + + return send_packet(&p); + +} + +/* + * cli_get_response: Get a response ... + */ +int cli_get_response(int dgram_sock, BOOL unique, char *mailslot, char *buf, int bufsiz) +{ + struct packet_struct *packet; + + packet = receive_dgram_packet(dgram_sock, 5, mailslot); + + if (packet) { /* We got one, pull what we want out of the SMB data ... */ + + struct dgram_packet *dgram = &packet->packet.dgram; + + /* + * We should probably parse the SMB, but for now, we will pull what + * from fixed, known locations ... + */ + + /* Copy the data to buffer, respecting sizes ... */ + + memcpy(buf, &dgram->data[92], MIN(bufsiz, (dgram->datasize - 92))); + + } + else + return -1; + + return 0; + +} + +/* + * cli_get_backup_list: Send a get backup list request ... + */ + +static char cli_backup_list[1024]; + +int cli_get_backup_list(const char *myname, const char *send_to_name) +{ + char outbuf[15]; + char *p; + struct in_addr sendto_ip, my_ip; + int dgram_sock; + struct sockaddr_in sock_out; + socklen_t name_size; + + if (!resolve_name(send_to_name, &sendto_ip, 0x1d)) { + + DEBUG(0, ("Could not resolve name: %s<1D>\n", send_to_name)); + return False; + + } + + my_ip.s_addr = inet_addr("0.0.0.0"); + + if (!resolve_name(myname, &my_ip, 0x00)) { /* FIXME: Call others here */ + + DEBUG(0, ("Could not resolve name: %s<00>\n", myname)); + + } + + if ((dgram_sock = open_socket_out(SOCK_DGRAM, &sendto_ip, 138, LONG_CONNECT_TIMEOUT)) < 0) { + + DEBUG(4, ("open_sock_out failed ...")); + return False; + + } + + /* Make it a broadcast socket ... */ + + set_socket_options(dgram_sock, "SO_BROADCAST"); + + /* Make it non-blocking??? */ + + if (fcntl(dgram_sock, F_SETFL, O_NONBLOCK) < 0) { + + DEBUG(0, ("Unable to set non blocking on dgram sock\n")); + + } + + /* Now, bind a local addr to it ... Try port 138 first ... */ + + memset((char *)&sock_out, '\0', sizeof(sock_out)); + sock_out.sin_addr.s_addr = INADDR_ANY; + sock_out.sin_port = htons(138); + sock_out.sin_family = AF_INET; + + if (bind(dgram_sock, (struct sockaddr *)&sock_out, sizeof(sock_out)) < 0) { + + /* Try again on any port ... */ + + sock_out.sin_port = INADDR_ANY; + + if (bind(dgram_sock, (struct sockaddr *)&sock_out, sizeof(sock_out)) < 0) { + + DEBUG(4, ("failed to bind socket to address ...\n")); + return False; + + } + + } + + /* Now, figure out what socket name we were bound to. We want the port */ + + name_size = sizeof(sock_out); + + getsockname(dgram_sock, (struct sockaddr *)&sock_out, &name_size); + + DEBUG(5, ("Socket bound to IP:%s, port: %d\n", inet_ntoa(sock_out.sin_addr), ntohs(sock_out.sin_port))); + + /* Now, build the request */ + + memset(cli_backup_list, '\0', sizeof(cli_backup_list)); + memset(outbuf, '\0', sizeof(outbuf)); + + p = outbuf; + + SCVAL(p, 0, ANN_GetBackupListReq); + p++; + + SCVAL(p, 0, 1); /* Count pointer ... */ + p++; + + SIVAL(p, 0, 1); /* The sender's token ... */ + p += 4; + + cli_send_mailslot(dgram_sock, True, "\\MAILSLOT\\BROWSE", outbuf, + PTR_DIFF(p, outbuf), myname, 0, send_to_name, + 0x1d, sendto_ip, my_ip, 138, sock_out.sin_port); + + /* We should check the error and return if we got one */ + + /* Now, get the response ... */ + + cli_get_response(dgram_sock, True, "\\MAILSLOT\\BROWSE", cli_backup_list, sizeof(cli_backup_list)); + + /* Should check the response here ... FIXME */ + + close(dgram_sock); + + return True; + +} + +/* + * cli_get_backup_server: Get the backup list and retrieve a server from it + */ + +int cli_get_backup_server(char *my_name, char *target, char *servername, int namesize) +{ + + /* Get the backup list first. We could pull this from the cache later */ + + cli_get_backup_list(my_name, target); /* FIXME: Check the response */ + + if (!cli_backup_list[0]) { /* Empty list ... try again */ + + cli_get_backup_list(my_name, target); + + } + + strncpy(servername, cli_backup_list, MIN(16, namesize)); + + return True; + +} + + + diff --git a/source3/libsmb/clientgen.c b/source3/libsmb/clientgen.c new file mode 100644 index 0000000000..ba7a327344 --- /dev/null +++ b/source3/libsmb/clientgen.c @@ -0,0 +1,280 @@ +/* + Unix SMB/CIFS implementation. + SMB client generic functions + Copyright (C) Andrew Tridgell 1994-1998 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#define NO_SYSLOG + +#include "includes.h" + +/* + * Change the port number used to call on + */ +int cli_set_port(struct cli_state *cli, int port) +{ + cli->port = port; + return port; +} + +/**************************************************************************** +recv an smb +****************************************************************************/ +BOOL cli_receive_smb(struct cli_state *cli) +{ + BOOL ret; + + /* fd == -1 causes segfaults -- Tom (tom@ninja.nl) */ + if (cli->fd == -1) return False; + + again: + ret = client_receive_smb(cli->fd,cli->inbuf,cli->timeout); + + if (ret) { + /* it might be an oplock break request */ + if (!(CVAL(cli->inbuf, smb_flg) & FLAG_REPLY) && + CVAL(cli->inbuf,smb_com) == SMBlockingX && + SVAL(cli->inbuf,smb_vwv6) == 0 && + SVAL(cli->inbuf,smb_vwv7) == 0) { + if (cli->oplock_handler) { + int fnum = SVAL(cli->inbuf,smb_vwv2); + unsigned char level = CVAL(cli->inbuf,smb_vwv3+1); + if (!cli->oplock_handler(cli, fnum, level)) return False; + } + /* try to prevent loops */ + SCVAL(cli->inbuf,smb_com,0xFF); + goto again; + } + } + + /* If the server is not responding, note that now */ + + if (!ret) { + close(cli->fd); + cli->fd = -1; + } + + return ret; +} + +/**************************************************************************** + send an smb to a fd. +****************************************************************************/ + +BOOL cli_send_smb(struct cli_state *cli) +{ + size_t len; + size_t nwritten=0; + ssize_t ret; + + /* fd == -1 causes segfaults -- Tom (tom@ninja.nl) */ + if (cli->fd == -1) return False; + + len = smb_len(cli->outbuf) + 4; + + while (nwritten < len) { + ret = write_socket(cli->fd,cli->outbuf+nwritten,len - nwritten); + if (ret <= 0) { + close(cli->fd); + cli->fd = -1; + DEBUG(0,("Error writing %d bytes to client. %d\n", + (int)len,(int)ret)); + return False; + } + nwritten += ret; + } + + return True; +} + +/**************************************************************************** +setup basics in a outgoing packet +****************************************************************************/ +void cli_setup_packet(struct cli_state *cli) +{ + cli->rap_error = 0; + SSVAL(cli->outbuf,smb_pid,cli->pid); + SSVAL(cli->outbuf,smb_uid,cli->vuid); + SSVAL(cli->outbuf,smb_mid,cli->mid); + if (cli->protocol > PROTOCOL_CORE) { + uint16 flags2; + SCVAL(cli->outbuf,smb_flg,0x8); + flags2 = FLAGS2_LONG_PATH_COMPONENTS; + if (cli->capabilities & CAP_UNICODE) { + flags2 |= FLAGS2_UNICODE_STRINGS; + } + if (cli->capabilities & CAP_STATUS32) { + flags2 |= FLAGS2_32_BIT_ERROR_CODES; + } + if (cli->use_spnego) { + flags2 |= FLAGS2_EXTENDED_SECURITY; + } + SSVAL(cli->outbuf,smb_flg2, flags2); + } +} + +/**************************************************************************** +setup the bcc length of the packet from a pointer to the end of the data +****************************************************************************/ +void cli_setup_bcc(struct cli_state *cli, void *p) +{ + set_message_bcc(cli->outbuf, PTR_DIFF(p, smb_buf(cli->outbuf))); +} + + + +/**************************************************************************** +initialise credentials of a client structure +****************************************************************************/ +void cli_init_creds(struct cli_state *cli, const struct ntuser_creds *usr) +{ + /* copy_nt_creds(&cli->usr, usr); */ + safe_strcpy(cli->domain , usr->domain , sizeof(usr->domain )-1); + safe_strcpy(cli->user_name, usr->user_name, sizeof(usr->user_name)-1); + memcpy(&cli->pwd, &usr->pwd, sizeof(usr->pwd)); + cli->ntlmssp_flags = usr->ntlmssp_flags; + cli->ntlmssp_cli_flgs = usr != NULL ? usr->ntlmssp_flags : 0; + + DEBUG(10,("cli_init_creds: user %s domain %s flgs: %x\nntlmssp_cli_flgs:%x\n", + cli->user_name, cli->domain, + cli->ntlmssp_flags,cli->ntlmssp_cli_flgs)); +} + + +/**************************************************************************** +initialise a client structure +****************************************************************************/ +struct cli_state *cli_initialise(struct cli_state *cli) +{ + BOOL alloced_cli = False; + + /* Check the effective uid - make sure we are not setuid */ + if (is_setuid_root()) { + DEBUG(0,("libsmb based programs must *NOT* be setuid root.\n")); + return NULL; + } + + if (!cli) { + cli = (struct cli_state *)malloc(sizeof(*cli)); + if (!cli) + return NULL; + ZERO_STRUCTP(cli); + alloced_cli = True; + } + + if (cli->initialised) { + cli_shutdown(cli); + } + + ZERO_STRUCTP(cli); + + cli->port = 0; + cli->fd = -1; + cli->cnum = -1; + cli->pid = (uint16)sys_getpid(); + cli->mid = 1; + cli->vuid = UID_FIELD_INVALID; + cli->protocol = PROTOCOL_NT1; + cli->timeout = 20000; /* Timeout is in milliseconds. */ + cli->bufsize = CLI_BUFFER_SIZE+4; + cli->max_xmit = cli->bufsize; + cli->outbuf = (char *)malloc(cli->bufsize); + cli->inbuf = (char *)malloc(cli->bufsize); + cli->oplock_handler = cli_oplock_ack; + cli->use_spnego = True; + + /* Set the CLI_FORCE_DOSERR environment variable to test + client routines using DOS errors instead of STATUS32 + ones. This intended only as a temporary hack. */ + if (getenv("CLI_FORCE_DOSERR")) { + cli->force_dos_errors = True; + } + + if (!cli->outbuf || !cli->inbuf) + goto error; + + if ((cli->mem_ctx = talloc_init_named("cli based talloc")) == NULL) + goto error; + + memset(cli->outbuf, 0, cli->bufsize); + memset(cli->inbuf, 0, cli->bufsize); + + cli->nt_pipe_fnum = 0; + + cli->initialised = 1; + cli->allocated = alloced_cli; + + return cli; + + /* Clean up after malloc() error */ + + error: + + SAFE_FREE(cli->inbuf); + SAFE_FREE(cli->outbuf); + + if (alloced_cli) + SAFE_FREE(cli); + + return NULL; +} + +/**************************************************************************** +shutdown a client structure +****************************************************************************/ +void cli_shutdown(struct cli_state *cli) +{ + BOOL allocated; + SAFE_FREE(cli->outbuf); + SAFE_FREE(cli->inbuf); + + data_blob_free(&cli->secblob); + + if (cli->mem_ctx) + talloc_destroy(cli->mem_ctx); + +#ifdef WITH_SSL + if (cli->fd != -1) + sslutil_disconnect(cli->fd); +#endif /* WITH_SSL */ + if (cli->fd != -1) + close(cli->fd); + allocated = cli->allocated; + ZERO_STRUCTP(cli); + if (allocated) { + free(cli); + } +} + + +/**************************************************************************** +set socket options on a open connection +****************************************************************************/ +void cli_sockopt(struct cli_state *cli, char *options) +{ + set_socket_options(cli->fd, options); +} + +/**************************************************************************** +set the PID to use for smb messages. Return the old pid. +****************************************************************************/ +uint16 cli_setpid(struct cli_state *cli, uint16 pid) +{ + uint16 ret = cli->pid; + cli->pid = pid; + return ret; +} diff --git a/source3/libsmb/clierror.c b/source3/libsmb/clierror.c new file mode 100644 index 0000000000..591c04db22 --- /dev/null +++ b/source3/libsmb/clierror.c @@ -0,0 +1,281 @@ +/* + Unix SMB/CIFS implementation. + client error handling routines + Copyright (C) Andrew Tridgell 1994-1998 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#define NO_SYSLOG + +#include "includes.h" + +/***************************************************** + RAP error codes - a small start but will be extended. + + XXX: Perhaps these should move into a common function because they're + duplicated in clirap2.c + +*******************************************************/ + +static const struct +{ + int err; + char *message; +} rap_errmap[] = +{ + {5, "RAP5: User has insufficient privilege" }, + {50, "RAP50: Not supported by server" }, + {65, "RAP65: Access denied" }, + {86, "RAP86: The specified password is invalid" }, + {2220, "RAP2220: Group does not exist" }, + {2221, "RAP2221: User does not exist" }, + {2226, "RAP2226: Operation only permitted on a Primary Domain Controller" }, + {2237, "RAP2237: User is not in group" }, + {2242, "RAP2242: The password of this user has expired." }, + {2243, "RAP2243: The password of this user cannot change." }, + {2244, "RAP2244: This password cannot be used now (password history conflict)." }, + {2245, "RAP2245: The password is shorter than required." }, + {2246, "RAP2246: The password of this user is too recent to change."}, + + /* these really shouldn't be here ... */ + {0x80, "Not listening on called name"}, + {0x81, "Not listening for calling name"}, + {0x82, "Called name not present"}, + {0x83, "Called name present, but insufficient resources"}, + + {0, NULL} +}; + +/**************************************************************************** + return a description of an SMB error +****************************************************************************/ +static char *cli_smb_errstr(struct cli_state *cli) +{ + return smb_dos_errstr(cli->inbuf); +} + +/*************************************************************************** + Return an error message - either an NT error, SMB error or a RAP error. + Note some of the NT errors are actually warnings or "informational" errors + in which case they can be safely ignored. +****************************************************************************/ + +char *cli_errstr(struct cli_state *cli) +{ + static fstring cli_error_message; + uint32 flgs2 = SVAL(cli->inbuf,smb_flg2), errnum; + uint8 errclass; + int i; + + if (!cli->initialised) { + fstrcpy(cli_error_message, "[Programmer's error] cli_errstr called on unitialized cli_stat struct!\n"); + return cli_error_message; + } + + /* Case #1: RAP error */ + if (cli->rap_error) { + for (i = 0; rap_errmap[i].message != NULL; i++) { + if (rap_errmap[i].err == cli->rap_error) { + return rap_errmap[i].message; + } + } + + slprintf(cli_error_message, sizeof(cli_error_message) - 1, "RAP code %d", + cli->rap_error); + + return cli_error_message; + } + + /* Case #2: 32-bit NT errors */ + if (flgs2 & FLAGS2_32_BIT_ERROR_CODES) { + NTSTATUS status = NT_STATUS(IVAL(cli->inbuf,smb_rcls)); + + return nt_errstr(status); + } + + cli_dos_error(cli, &errclass, &errnum); + + /* Case #3: SMB error */ + + return cli_smb_errstr(cli); +} + + +/* Return the 32-bit NT status code from the last packet */ +NTSTATUS cli_nt_error(struct cli_state *cli) +{ + int flgs2 = SVAL(cli->inbuf,smb_flg2); + + if (!(flgs2 & FLAGS2_32_BIT_ERROR_CODES)) { + int class = CVAL(cli->inbuf,smb_rcls); + int code = SVAL(cli->inbuf,smb_err); + return dos_to_ntstatus(class, code); + } + + return NT_STATUS(IVAL(cli->inbuf,smb_rcls)); +} + + +/* Return the DOS error from the last packet - an error class and an error + code. */ +void cli_dos_error(struct cli_state *cli, uint8 *eclass, uint32 *ecode) +{ + int flgs2; + char rcls; + int code; + + if(!cli->initialised) return; + + flgs2 = SVAL(cli->inbuf,smb_flg2); + + if (flgs2 & FLAGS2_32_BIT_ERROR_CODES) { + NTSTATUS ntstatus = NT_STATUS(IVAL(cli->inbuf, smb_rcls)); + ntstatus_to_dos(ntstatus, eclass, ecode); + return; + } + + rcls = CVAL(cli->inbuf,smb_rcls); + code = SVAL(cli->inbuf,smb_err); + + if (eclass) *eclass = rcls; + if (ecode) *ecode = code; +} + +/* Return a UNIX errno from a dos error class, error number tuple */ + +int cli_errno_from_dos(uint8 eclass, uint32 num) +{ + if (eclass == ERRDOS) { + switch (num) { + case ERRbadfile: return ENOENT; + case ERRbadpath: return ENOTDIR; + case ERRnoaccess: return EACCES; + case ERRfilexists: return EEXIST; + case ERRrename: return EEXIST; + case ERRbadshare: return EBUSY; + case ERRlock: return EBUSY; + case ERRinvalidname: return ENOENT; + case ERRnosuchshare: return ENODEV; + } + } + + if (eclass == ERRSRV) { + switch (num) { + case ERRbadpw: return EPERM; + case ERRaccess: return EACCES; + case ERRnoresource: return ENOMEM; + case ERRinvdevice: return ENODEV; + case ERRinvnetname: return ENODEV; + } + } + + /* for other cases */ + return EINVAL; +} + +/* Return a UNIX errno from a NT status code */ +static struct { + NTSTATUS status; + int error; +} nt_errno_map[] = { + {NT_STATUS_ACCESS_VIOLATION, EACCES}, + {NT_STATUS_NO_SUCH_FILE, ENOENT}, + {NT_STATUS_NO_SUCH_DEVICE, ENODEV}, + {NT_STATUS_INVALID_HANDLE, EBADF}, + {NT_STATUS_NO_MEMORY, ENOMEM}, + {NT_STATUS_ACCESS_DENIED, EACCES}, + {NT_STATUS_OBJECT_NAME_NOT_FOUND, ENOENT}, + {NT_STATUS_SHARING_VIOLATION, EBUSY}, + {NT_STATUS_OBJECT_PATH_INVALID, ENOTDIR}, + {NT_STATUS_OBJECT_NAME_COLLISION, EEXIST}, + {NT_STATUS_PATH_NOT_COVERED, ENOENT}, + {NT_STATUS(0), 0} +}; + +int cli_errno_from_nt(NTSTATUS status) +{ + int i; + DEBUG(10,("cli_errno_from_nt: 32 bit codes: code=%08x\n", NT_STATUS_V(status))); + + /* Status codes without this bit set are not errors */ + + if (!(NT_STATUS_V(status) & 0xc0000000)) + return 0; + + for (i=0;nt_errno_map[i].error;i++) { + if (NT_STATUS_V(nt_errno_map[i].status) == + NT_STATUS_V(status)) return nt_errno_map[i].error; + } + + /* for all other cases - a default code */ + return EINVAL; +} + +/* Return a UNIX errno appropriate for the error received in the last + packet. */ + +int cli_errno(struct cli_state *cli) +{ + NTSTATUS status; + + if (cli_is_dos_error(cli)) { + uint8 eclass; + uint32 ecode; + + cli_dos_error(cli, &eclass, &ecode); + return cli_errno_from_dos(eclass, ecode); + } + + status = cli_nt_error(cli); + + return cli_errno_from_nt(status); +} + +/* Return true if the last packet was in error */ + +BOOL cli_is_error(struct cli_state *cli) +{ + uint32 flgs2 = SVAL(cli->inbuf,smb_flg2), rcls = 0; + + if (flgs2 & FLAGS2_32_BIT_ERROR_CODES) { + /* Return error is error bits are set */ + rcls = IVAL(cli->inbuf, smb_rcls); + return (rcls & 0xF0000000) == 0xC0000000; + } + + /* Return error if error class in non-zero */ + + rcls = CVAL(cli->inbuf, smb_rcls); + return rcls != 0; +} + +/* Return true if the last error was an NT error */ + +BOOL cli_is_nt_error(struct cli_state *cli) +{ + uint32 flgs2 = SVAL(cli->inbuf,smb_flg2); + + return cli_is_error(cli) && (flgs2 & FLAGS2_32_BIT_ERROR_CODES); +} + +/* Return true if the last error was a DOS error */ + +BOOL cli_is_dos_error(struct cli_state *cli) +{ + uint32 flgs2 = SVAL(cli->inbuf,smb_flg2); + + return cli_is_error(cli) && !(flgs2 & FLAGS2_32_BIT_ERROR_CODES); +} diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c new file mode 100644 index 0000000000..05843ac5de --- /dev/null +++ b/source3/libsmb/clifile.c @@ -0,0 +1,1051 @@ +/* + Unix SMB/CIFS implementation. + client file operations + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) Jeremy Allison 2001-2002 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#define NO_SYSLOG + +#include "includes.h" + +/**************************************************************************** + Hard/Symlink a file (UNIX extensions). +****************************************************************************/ + +static BOOL cli_link_internal(struct cli_state *cli, const char *fname_src, const char *fname_dst, BOOL hard_link) +{ + int data_len = 0; + int param_len = 0; + uint16 setup = TRANSACT2_SETPATHINFO; + char param[sizeof(pstring)+6]; + pstring data; + char *rparam=NULL, *rdata=NULL; + char *p; + + memset(param, 0, sizeof(param)); + SSVAL(param,0,hard_link ? SMB_SET_FILE_UNIX_HLINK : SMB_SET_FILE_UNIX_LINK); + p = ¶m[6]; + + p += clistr_push(cli, p, fname_src, -1, STR_TERMINATE); + param_len = PTR_DIFF(p, param); + + p = data; + p += clistr_push(cli, p, fname_dst, -1, STR_TERMINATE); + data_len = PTR_DIFF(p, data); + + if (!cli_send_trans(cli, SMBtrans2, + NULL, /* name */ + -1, 0, /* fid, flags */ + &setup, 1, 0, /* setup, length, max */ + param, param_len, 2, /* param, length, max */ + (char *)&data, data_len, cli->max_xmit /* data, length, max */ + )) { + return False; + } + + if (!cli_receive_trans(cli, SMBtrans2, + &rparam, ¶m_len, + &rdata, &data_len)) { + return False; + } + + SAFE_FREE(rdata); + SAFE_FREE(rparam); + + return True; +} + +/**************************************************************************** + Map standard UNIX permissions onto wire representations. +****************************************************************************/ + +uint32 unix_perms_to_wire(mode_t perms) +{ + uint ret = 0; + + ret |= ((perms & S_IXOTH) ? UNIX_X_OTH : 0); + ret |= ((perms & S_IWOTH) ? UNIX_W_OTH : 0); + ret |= ((perms & S_IROTH) ? UNIX_R_OTH : 0); + ret |= ((perms & S_IXGRP) ? UNIX_X_GRP : 0); + ret |= ((perms & S_IWGRP) ? UNIX_W_GRP : 0); + ret |= ((perms & S_IRGRP) ? UNIX_R_GRP : 0); + ret |= ((perms & S_IXUSR) ? UNIX_X_USR : 0); + ret |= ((perms & S_IWUSR) ? UNIX_W_USR : 0); + ret |= ((perms & S_IRUSR) ? UNIX_R_USR : 0); +#ifdef S_ISVTX + ret |= ((perms & S_ISVTX) ? UNIX_STICKY : 0); +#endif +#ifdef S_ISGID + ret |= ((perms & S_ISGID) ? UNIX_SET_GID : 0); +#endif +#ifdef S_ISUID + ret |= ((perms & S_ISVTX) ? UNIX_SET_UID : 0); +#endif + return ret; +} + +/**************************************************************************** + Symlink a file (UNIX extensions). +****************************************************************************/ + +BOOL cli_unix_symlink(struct cli_state *cli, const char *fname_src, const char *fname_dst) +{ + return cli_link_internal(cli, fname_src, fname_dst, False); +} + +/**************************************************************************** + Hard a file (UNIX extensions). +****************************************************************************/ + +BOOL cli_unix_hardlink(struct cli_state *cli, const char *fname_src, const char *fname_dst) +{ + return cli_link_internal(cli, fname_src, fname_dst, True); +} + +/**************************************************************************** + Chmod or chown a file internal (UNIX extensions). +****************************************************************************/ + +static BOOL cli_unix_chmod_chown_internal(struct cli_state *cli, const char *fname, uint32 mode, uint32 uid, uint32 gid) +{ + int data_len = 0; + int param_len = 0; + uint16 setup = TRANSACT2_SETPATHINFO; + char param[sizeof(pstring)+6]; + char data[100]; + char *rparam=NULL, *rdata=NULL; + char *p; + + memset(param, 0, sizeof(param)); + memset(data, 0, sizeof(data)); + SSVAL(param,0,SMB_SET_FILE_UNIX_BASIC); + p = ¶m[6]; + + p += clistr_push(cli, p, fname, -1, STR_TERMINATE); + param_len = PTR_DIFF(p, param); + + SIVAL(data,40,uid); + SIVAL(data,48,gid); + SIVAL(data,84,mode); + + data_len = 100; + + if (!cli_send_trans(cli, SMBtrans2, + NULL, /* name */ + -1, 0, /* fid, flags */ + &setup, 1, 0, /* setup, length, max */ + param, param_len, 2, /* param, length, max */ + (char *)&data, data_len, cli->max_xmit /* data, length, max */ + )) { + return False; + } + + if (!cli_receive_trans(cli, SMBtrans2, + &rparam, ¶m_len, + &rdata, &data_len)) { + return False; + } + + SAFE_FREE(rdata); + SAFE_FREE(rparam); + + return True; +} + +/**************************************************************************** + chmod a file (UNIX extensions). +****************************************************************************/ + +BOOL cli_unix_chmod(struct cli_state *cli, const char *fname, mode_t mode) +{ + return cli_unix_chmod_chown_internal(cli, fname, + unix_perms_to_wire(mode), SMB_UID_NO_CHANGE, SMB_GID_NO_CHANGE); +} + +/**************************************************************************** + chown a file (UNIX extensions). +****************************************************************************/ + +BOOL cli_unix_chown(struct cli_state *cli, const char *fname, uid_t uid, gid_t gid) +{ + return cli_unix_chmod_chown_internal(cli, fname, SMB_MODE_NO_CHANGE, (uint32)uid, (uint32)gid); +} + +/**************************************************************************** + Rename a file. +****************************************************************************/ + +BOOL cli_rename(struct cli_state *cli, const char *fname_src, const char *fname_dst) +{ + char *p; + + memset(cli->outbuf,'\0',smb_size); + memset(cli->inbuf,'\0',smb_size); + + set_message(cli->outbuf,1, 0, True); + + SCVAL(cli->outbuf,smb_com,SMBmv); + SSVAL(cli->outbuf,smb_tid,cli->cnum); + cli_setup_packet(cli); + + SSVAL(cli->outbuf,smb_vwv0,aSYSTEM | aHIDDEN | aDIR); + + p = smb_buf(cli->outbuf); + *p++ = 4; + p += clistr_push(cli, p, fname_src, -1, STR_TERMINATE); + *p++ = 4; + p += clistr_push(cli, p, fname_dst, -1, STR_TERMINATE); + + cli_setup_bcc(cli, p); + + cli_send_smb(cli); + if (!cli_receive_smb(cli)) + return False; + + if (cli_is_error(cli)) + return False; + + return True; +} + +/**************************************************************************** + Delete a file. +****************************************************************************/ + +BOOL cli_unlink(struct cli_state *cli, const char *fname) +{ + char *p; + + memset(cli->outbuf,'\0',smb_size); + memset(cli->inbuf,'\0',smb_size); + + set_message(cli->outbuf,1, 0,True); + + SCVAL(cli->outbuf,smb_com,SMBunlink); + SSVAL(cli->outbuf,smb_tid,cli->cnum); + cli_setup_packet(cli); + + SSVAL(cli->outbuf,smb_vwv0,aSYSTEM | aHIDDEN); + + p = smb_buf(cli->outbuf); + *p++ = 4; + p += clistr_push(cli, p, fname, -1, STR_TERMINATE); + + cli_setup_bcc(cli, p); + cli_send_smb(cli); + if (!cli_receive_smb(cli)) { + return False; + } + + if (cli_is_error(cli)) { + return False; + } + + return True; +} + +/**************************************************************************** + Create a directory. +****************************************************************************/ + +BOOL cli_mkdir(struct cli_state *cli, const char *dname) +{ + char *p; + + memset(cli->outbuf,'\0',smb_size); + memset(cli->inbuf,'\0',smb_size); + + set_message(cli->outbuf,0, 0,True); + + SCVAL(cli->outbuf,smb_com,SMBmkdir); + SSVAL(cli->outbuf,smb_tid,cli->cnum); + cli_setup_packet(cli); + + p = smb_buf(cli->outbuf); + *p++ = 4; + p += clistr_push(cli, p, dname, -1, STR_TERMINATE); + + cli_setup_bcc(cli, p); + + cli_send_smb(cli); + if (!cli_receive_smb(cli)) { + return False; + } + + if (cli_is_error(cli)) { + return False; + } + + return True; +} + +/**************************************************************************** + Remove a directory. +****************************************************************************/ + +BOOL cli_rmdir(struct cli_state *cli, const char *dname) +{ + char *p; + + memset(cli->outbuf,'\0',smb_size); + memset(cli->inbuf,'\0',smb_size); + + set_message(cli->outbuf,0, 0, True); + + SCVAL(cli->outbuf,smb_com,SMBrmdir); + SSVAL(cli->outbuf,smb_tid,cli->cnum); + cli_setup_packet(cli); + + p = smb_buf(cli->outbuf); + *p++ = 4; + p += clistr_push(cli, p, dname, -1, STR_TERMINATE); + + cli_setup_bcc(cli, p); + + cli_send_smb(cli); + if (!cli_receive_smb(cli)) { + return False; + } + + if (cli_is_error(cli)) { + return False; + } + + return True; +} + +/**************************************************************************** + Set or clear the delete on close flag. +****************************************************************************/ + +int cli_nt_delete_on_close(struct cli_state *cli, int fnum, BOOL flag) +{ + int data_len = 1; + int param_len = 6; + uint16 setup = TRANSACT2_SETFILEINFO; + pstring param; + unsigned char data; + char *rparam=NULL, *rdata=NULL; + + memset(param, 0, param_len); + SSVAL(param,0,fnum); + SSVAL(param,2,SMB_SET_FILE_DISPOSITION_INFO); + + data = flag ? 1 : 0; + + if (!cli_send_trans(cli, SMBtrans2, + NULL, /* name */ + -1, 0, /* fid, flags */ + &setup, 1, 0, /* setup, length, max */ + param, param_len, 2, /* param, length, max */ + (char *)&data, data_len, cli->max_xmit /* data, length, max */ + )) { + return False; + } + + if (!cli_receive_trans(cli, SMBtrans2, + &rparam, ¶m_len, + &rdata, &data_len)) { + return False; + } + + SAFE_FREE(rdata); + SAFE_FREE(rparam); + + return True; +} + +/**************************************************************************** + Open a file - exposing the full horror of the NT API :-). + Used in smbtorture. +****************************************************************************/ + +int cli_nt_create_full(struct cli_state *cli, const char *fname, uint32 DesiredAccess, + uint32 FileAttributes, uint32 ShareAccess, + uint32 CreateDisposition, uint32 CreateOptions) +{ + char *p; + int len; + + memset(cli->outbuf,'\0',smb_size); + memset(cli->inbuf,'\0',smb_size); + + set_message(cli->outbuf,24,0,True); + + SCVAL(cli->outbuf,smb_com,SMBntcreateX); + SSVAL(cli->outbuf,smb_tid,cli->cnum); + cli_setup_packet(cli); + + SSVAL(cli->outbuf,smb_vwv0,0xFF); + if (cli->use_oplocks) + SIVAL(cli->outbuf,smb_ntcreate_Flags, REQUEST_OPLOCK|REQUEST_BATCH_OPLOCK); + else + SIVAL(cli->outbuf,smb_ntcreate_Flags, 0); + SIVAL(cli->outbuf,smb_ntcreate_RootDirectoryFid, 0x0); + SIVAL(cli->outbuf,smb_ntcreate_DesiredAccess, DesiredAccess); + SIVAL(cli->outbuf,smb_ntcreate_FileAttributes, FileAttributes); + SIVAL(cli->outbuf,smb_ntcreate_ShareAccess, ShareAccess); + SIVAL(cli->outbuf,smb_ntcreate_CreateDisposition, CreateDisposition); + SIVAL(cli->outbuf,smb_ntcreate_CreateOptions, CreateOptions); + SIVAL(cli->outbuf,smb_ntcreate_ImpersonationLevel, 0x02); + + p = smb_buf(cli->outbuf); + /* this alignment and termination is critical for netapp filers. Don't change */ + p += clistr_align_out(cli, p, 0); + len = clistr_push(cli, p, fname, -1, 0); + p += len; + SSVAL(cli->outbuf,smb_ntcreate_NameLength, len); + /* sigh. this copes with broken netapp filer behaviour */ + p += clistr_push(cli, p, "", -1, STR_TERMINATE); + + cli_setup_bcc(cli, p); + + cli_send_smb(cli); + if (!cli_receive_smb(cli)) { + return -1; + } + + if (cli_is_error(cli)) { + return -1; + } + + return SVAL(cli->inbuf,smb_vwv2 + 1); +} + +/**************************************************************************** + Open a file. +****************************************************************************/ + +int cli_nt_create(struct cli_state *cli, const char *fname, uint32 DesiredAccess) +{ + return cli_nt_create_full(cli, fname, DesiredAccess, 0, + FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_EXISTS_OPEN, 0x0); +} + +/**************************************************************************** + Open a file + WARNING: if you open with O_WRONLY then getattrE won't work! +****************************************************************************/ + +int cli_open(struct cli_state *cli, const char *fname, int flags, int share_mode) +{ + char *p; + unsigned openfn=0; + unsigned accessmode=0; + + if (flags & O_CREAT) + openfn |= (1<<4); + if (!(flags & O_EXCL)) { + if (flags & O_TRUNC) + openfn |= (1<<1); + else + openfn |= (1<<0); + } + + accessmode = (share_mode<<4); + + if ((flags & O_ACCMODE) == O_RDWR) { + accessmode |= 2; + } else if ((flags & O_ACCMODE) == O_WRONLY) { + accessmode |= 1; + } + +#if defined(O_SYNC) + if ((flags & O_SYNC) == O_SYNC) { + accessmode |= (1<<14); + } +#endif /* O_SYNC */ + + if (share_mode == DENY_FCB) { + accessmode = 0xFF; + } + + memset(cli->outbuf,'\0',smb_size); + memset(cli->inbuf,'\0',smb_size); + + set_message(cli->outbuf,15,0,True); + + SCVAL(cli->outbuf,smb_com,SMBopenX); + SSVAL(cli->outbuf,smb_tid,cli->cnum); + cli_setup_packet(cli); + + SSVAL(cli->outbuf,smb_vwv0,0xFF); + SSVAL(cli->outbuf,smb_vwv2,0); /* no additional info */ + SSVAL(cli->outbuf,smb_vwv3,accessmode); + SSVAL(cli->outbuf,smb_vwv4,aSYSTEM | aHIDDEN); + SSVAL(cli->outbuf,smb_vwv5,0); + SSVAL(cli->outbuf,smb_vwv8,openfn); + + if (cli->use_oplocks) { + /* if using oplocks then ask for a batch oplock via + core and extended methods */ + SCVAL(cli->outbuf,smb_flg, CVAL(cli->outbuf,smb_flg)| + FLAG_REQUEST_OPLOCK|FLAG_REQUEST_BATCH_OPLOCK); + SSVAL(cli->outbuf,smb_vwv2,SVAL(cli->outbuf,smb_vwv2) | 6); + } + + p = smb_buf(cli->outbuf); + p += clistr_push(cli, p, fname, -1, STR_TERMINATE); + + cli_setup_bcc(cli, p); + + cli_send_smb(cli); + if (!cli_receive_smb(cli)) { + return -1; + } + + if (cli_is_error(cli)) { + return -1; + } + + return SVAL(cli->inbuf,smb_vwv2); +} + +/**************************************************************************** + Close a file. +****************************************************************************/ + +BOOL cli_close(struct cli_state *cli, int fnum) +{ + memset(cli->outbuf,'\0',smb_size); + memset(cli->inbuf,'\0',smb_size); + + set_message(cli->outbuf,3,0,True); + + SCVAL(cli->outbuf,smb_com,SMBclose); + SSVAL(cli->outbuf,smb_tid,cli->cnum); + cli_setup_packet(cli); + + SSVAL(cli->outbuf,smb_vwv0,fnum); + SIVALS(cli->outbuf,smb_vwv1,-1); + + cli_send_smb(cli); + if (!cli_receive_smb(cli)) { + return False; + } + + return !cli_is_error(cli); +} + + +/**************************************************************************** + send a lock with a specified locktype + this is used for testing LOCKING_ANDX_CANCEL_LOCK +****************************************************************************/ +NTSTATUS cli_locktype(struct cli_state *cli, int fnum, + uint32 offset, uint32 len, int timeout, unsigned char locktype) +{ + char *p; + int saved_timeout = cli->timeout; + + memset(cli->outbuf,'\0',smb_size); + memset(cli->inbuf,'\0', smb_size); + + set_message(cli->outbuf,8,0,True); + + SCVAL(cli->outbuf,smb_com,SMBlockingX); + SSVAL(cli->outbuf,smb_tid,cli->cnum); + cli_setup_packet(cli); + + SCVAL(cli->outbuf,smb_vwv0,0xFF); + SSVAL(cli->outbuf,smb_vwv2,fnum); + SCVAL(cli->outbuf,smb_vwv3,locktype); + SIVALS(cli->outbuf, smb_vwv4, timeout); + SSVAL(cli->outbuf,smb_vwv6,0); + SSVAL(cli->outbuf,smb_vwv7,1); + + p = smb_buf(cli->outbuf); + SSVAL(p, 0, cli->pid); + SIVAL(p, 2, offset); + SIVAL(p, 6, len); + + p += 10; + + cli_setup_bcc(cli, p); + + cli_send_smb(cli); + + if (timeout != 0) { + cli->timeout = (timeout == -1) ? 0x7FFFFFFF : (timeout + 2*1000); + } + + if (!cli_receive_smb(cli)) { + cli->timeout = saved_timeout; + return NT_STATUS_UNSUCCESSFUL; + } + + cli->timeout = saved_timeout; + + return cli_nt_error(cli); +} + + +/**************************************************************************** + Lock a file. +****************************************************************************/ + +BOOL cli_lock(struct cli_state *cli, int fnum, + uint32 offset, uint32 len, int timeout, enum brl_type lock_type) +{ + char *p; + int saved_timeout = cli->timeout; + + memset(cli->outbuf,'\0',smb_size); + memset(cli->inbuf,'\0', smb_size); + + set_message(cli->outbuf,8,0,True); + + SCVAL(cli->outbuf,smb_com,SMBlockingX); + SSVAL(cli->outbuf,smb_tid,cli->cnum); + cli_setup_packet(cli); + + SCVAL(cli->outbuf,smb_vwv0,0xFF); + SSVAL(cli->outbuf,smb_vwv2,fnum); + SCVAL(cli->outbuf,smb_vwv3,(lock_type == READ_LOCK? 1 : 0)); + SIVALS(cli->outbuf, smb_vwv4, timeout); + SSVAL(cli->outbuf,smb_vwv6,0); + SSVAL(cli->outbuf,smb_vwv7,1); + + p = smb_buf(cli->outbuf); + SSVAL(p, 0, cli->pid); + SIVAL(p, 2, offset); + SIVAL(p, 6, len); + + p += 10; + + cli_setup_bcc(cli, p); + + cli_send_smb(cli); + + if (timeout != 0) { + cli->timeout = (timeout == -1) ? 0x7FFFFFFF : (timeout + 2*1000); + } + + if (!cli_receive_smb(cli)) { + cli->timeout = saved_timeout; + return False; + } + + cli->timeout = saved_timeout; + + if (cli_is_error(cli)) { + return False; + } + + return True; +} + +/**************************************************************************** + Unlock a file. +****************************************************************************/ + +BOOL cli_unlock(struct cli_state *cli, int fnum, uint32 offset, uint32 len) +{ + char *p; + + memset(cli->outbuf,'\0',smb_size); + memset(cli->inbuf,'\0',smb_size); + + set_message(cli->outbuf,8,0,True); + + SCVAL(cli->outbuf,smb_com,SMBlockingX); + SSVAL(cli->outbuf,smb_tid,cli->cnum); + cli_setup_packet(cli); + + SCVAL(cli->outbuf,smb_vwv0,0xFF); + SSVAL(cli->outbuf,smb_vwv2,fnum); + SCVAL(cli->outbuf,smb_vwv3,0); + SIVALS(cli->outbuf, smb_vwv4, 0); + SSVAL(cli->outbuf,smb_vwv6,1); + SSVAL(cli->outbuf,smb_vwv7,0); + + p = smb_buf(cli->outbuf); + SSVAL(p, 0, cli->pid); + SIVAL(p, 2, offset); + SIVAL(p, 6, len); + p += 10; + cli_setup_bcc(cli, p); + cli_send_smb(cli); + if (!cli_receive_smb(cli)) { + return False; + } + + if (cli_is_error(cli)) { + return False; + } + + return True; +} + +/**************************************************************************** + Lock a file with 64 bit offsets. +****************************************************************************/ + +BOOL cli_lock64(struct cli_state *cli, int fnum, + SMB_BIG_UINT offset, SMB_BIG_UINT len, int timeout, enum brl_type lock_type) +{ + char *p; + int saved_timeout = cli->timeout; + int ltype; + + if (! (cli->capabilities & CAP_LARGE_FILES)) { + return cli_lock(cli, fnum, offset, len, timeout, lock_type); + } + + ltype = (lock_type == READ_LOCK? 1 : 0); + ltype |= LOCKING_ANDX_LARGE_FILES; + + memset(cli->outbuf,'\0',smb_size); + memset(cli->inbuf,'\0', smb_size); + + set_message(cli->outbuf,8,0,True); + + SCVAL(cli->outbuf,smb_com,SMBlockingX); + SSVAL(cli->outbuf,smb_tid,cli->cnum); + cli_setup_packet(cli); + + SCVAL(cli->outbuf,smb_vwv0,0xFF); + SSVAL(cli->outbuf,smb_vwv2,fnum); + SCVAL(cli->outbuf,smb_vwv3,ltype); + SIVALS(cli->outbuf, smb_vwv4, timeout); + SSVAL(cli->outbuf,smb_vwv6,0); + SSVAL(cli->outbuf,smb_vwv7,1); + + p = smb_buf(cli->outbuf); + SIVAL(p, 0, cli->pid); + SOFF_T_R(p, 4, offset); + SOFF_T_R(p, 12, len); + p += 20; + + cli_setup_bcc(cli, p); + cli_send_smb(cli); + + if (timeout != 0) { + cli->timeout = (timeout == -1) ? 0x7FFFFFFF : (timeout + 5*1000); + } + + if (!cli_receive_smb(cli)) { + cli->timeout = saved_timeout; + return False; + } + + cli->timeout = saved_timeout; + + if (cli_is_error(cli)) { + return False; + } + + return True; +} + +/**************************************************************************** + Unlock a file with 64 bit offsets. +****************************************************************************/ + +BOOL cli_unlock64(struct cli_state *cli, int fnum, SMB_BIG_UINT offset, SMB_BIG_UINT len) +{ + char *p; + + if (! (cli->capabilities & CAP_LARGE_FILES)) { + return cli_unlock(cli, fnum, offset, len); + } + + memset(cli->outbuf,'\0',smb_size); + memset(cli->inbuf,'\0',smb_size); + + set_message(cli->outbuf,8,0,True); + + SCVAL(cli->outbuf,smb_com,SMBlockingX); + SSVAL(cli->outbuf,smb_tid,cli->cnum); + cli_setup_packet(cli); + + SCVAL(cli->outbuf,smb_vwv0,0xFF); + SSVAL(cli->outbuf,smb_vwv2,fnum); + SCVAL(cli->outbuf,smb_vwv3,LOCKING_ANDX_LARGE_FILES); + SIVALS(cli->outbuf, smb_vwv4, 0); + SSVAL(cli->outbuf,smb_vwv6,1); + SSVAL(cli->outbuf,smb_vwv7,0); + + p = smb_buf(cli->outbuf); + SIVAL(p, 0, cli->pid); + SOFF_T_R(p, 4, offset); + SOFF_T_R(p, 12, len); + p += 20; + cli_setup_bcc(cli, p); + cli_send_smb(cli); + if (!cli_receive_smb(cli)) { + return False; + } + + if (cli_is_error(cli)) { + return False; + } + + return True; +} + + +/**************************************************************************** + Do a SMBgetattrE call. +****************************************************************************/ + +BOOL cli_getattrE(struct cli_state *cli, int fd, + uint16 *attr, size_t *size, + time_t *c_time, time_t *a_time, time_t *m_time) +{ + memset(cli->outbuf,'\0',smb_size); + memset(cli->inbuf,'\0',smb_size); + + set_message(cli->outbuf,1,0,True); + + SCVAL(cli->outbuf,smb_com,SMBgetattrE); + SSVAL(cli->outbuf,smb_tid,cli->cnum); + cli_setup_packet(cli); + + SSVAL(cli->outbuf,smb_vwv0,fd); + + cli_send_smb(cli); + if (!cli_receive_smb(cli)) { + return False; + } + + if (cli_is_error(cli)) { + return False; + } + + if (size) { + *size = IVAL(cli->inbuf, smb_vwv6); + } + + if (attr) { + *attr = SVAL(cli->inbuf,smb_vwv10); + } + + if (c_time) { + *c_time = make_unix_date3(cli->inbuf+smb_vwv0); + } + + if (a_time) { + *a_time = make_unix_date3(cli->inbuf+smb_vwv2); + } + + if (m_time) { + *m_time = make_unix_date3(cli->inbuf+smb_vwv4); + } + + return True; +} + +/**************************************************************************** + Do a SMBgetatr call +****************************************************************************/ + +BOOL cli_getatr(struct cli_state *cli, const char *fname, + uint16 *attr, size_t *size, time_t *t) +{ + char *p; + + memset(cli->outbuf,'\0',smb_size); + memset(cli->inbuf,'\0',smb_size); + + set_message(cli->outbuf,0,0,True); + + SCVAL(cli->outbuf,smb_com,SMBgetatr); + SSVAL(cli->outbuf,smb_tid,cli->cnum); + cli_setup_packet(cli); + + p = smb_buf(cli->outbuf); + *p++ = 4; + p += clistr_push(cli, p, fname, -1, STR_TERMINATE); + + cli_setup_bcc(cli, p); + + cli_send_smb(cli); + if (!cli_receive_smb(cli)) { + return False; + } + + if (cli_is_error(cli)) { + return False; + } + + if (size) { + *size = IVAL(cli->inbuf, smb_vwv3); + } + + if (t) { + *t = make_unix_date3(cli->inbuf+smb_vwv1); + } + + if (attr) { + *attr = SVAL(cli->inbuf,smb_vwv0); + } + + + return True; +} + +/**************************************************************************** + Do a SMBsetatr call. +****************************************************************************/ + +BOOL cli_setatr(struct cli_state *cli, const char *fname, uint16 attr, time_t t) +{ + char *p; + + memset(cli->outbuf,'\0',smb_size); + memset(cli->inbuf,'\0',smb_size); + + set_message(cli->outbuf,8,0,True); + + SCVAL(cli->outbuf,smb_com,SMBsetatr); + SSVAL(cli->outbuf,smb_tid,cli->cnum); + cli_setup_packet(cli); + + SSVAL(cli->outbuf,smb_vwv0, attr); + put_dos_date3(cli->outbuf,smb_vwv1, t); + + p = smb_buf(cli->outbuf); + *p++ = 4; + p += clistr_push(cli, p, fname, -1, STR_TERMINATE); + *p++ = 4; + + cli_setup_bcc(cli, p); + + cli_send_smb(cli); + if (!cli_receive_smb(cli)) { + return False; + } + + if (cli_is_error(cli)) { + return False; + } + + return True; +} + +/**************************************************************************** + Check for existance of a dir. +****************************************************************************/ + +BOOL cli_chkpath(struct cli_state *cli, const char *path) +{ + pstring path2; + char *p; + + safe_strcpy(path2,path,sizeof(pstring)); + trim_string(path2,NULL,"\\"); + if (!*path2) *path2 = '\\'; + + memset(cli->outbuf,'\0',smb_size); + set_message(cli->outbuf,0,0,True); + SCVAL(cli->outbuf,smb_com,SMBchkpth); + SSVAL(cli->outbuf,smb_tid,cli->cnum); + cli_setup_packet(cli); + p = smb_buf(cli->outbuf); + *p++ = 4; + p += clistr_push(cli, p, path2, -1, STR_TERMINATE); + + cli_setup_bcc(cli, p); + + cli_send_smb(cli); + if (!cli_receive_smb(cli)) { + return False; + } + + if (cli_is_error(cli)) return False; + + return True; +} + +/**************************************************************************** + Query disk space. +****************************************************************************/ + +BOOL cli_dskattr(struct cli_state *cli, int *bsize, int *total, int *avail) +{ + memset(cli->outbuf,'\0',smb_size); + set_message(cli->outbuf,0,0,True); + SCVAL(cli->outbuf,smb_com,SMBdskattr); + SSVAL(cli->outbuf,smb_tid,cli->cnum); + cli_setup_packet(cli); + + cli_send_smb(cli); + if (!cli_receive_smb(cli)) { + return False; + } + + *bsize = SVAL(cli->inbuf,smb_vwv1)*SVAL(cli->inbuf,smb_vwv2); + *total = SVAL(cli->inbuf,smb_vwv0); + *avail = SVAL(cli->inbuf,smb_vwv3); + + return True; +} + +/**************************************************************************** + Create and open a temporary file. +****************************************************************************/ + +int cli_ctemp(struct cli_state *cli, const char *path, char **tmp_path) +{ + int len; + char *p; + + memset(cli->outbuf,'\0',smb_size); + memset(cli->inbuf,'\0',smb_size); + + set_message(cli->outbuf,3,0,True); + + SCVAL(cli->outbuf,smb_com,SMBctemp); + SSVAL(cli->outbuf,smb_tid,cli->cnum); + cli_setup_packet(cli); + + SSVAL(cli->outbuf,smb_vwv0,0); + SIVALS(cli->outbuf,smb_vwv1,-1); + + p = smb_buf(cli->outbuf); + *p++ = 4; + p += clistr_push(cli, p, path, -1, STR_TERMINATE); + + cli_setup_bcc(cli, p); + + cli_send_smb(cli); + if (!cli_receive_smb(cli)) { + return -1; + } + + if (cli_is_error(cli)) { + return -1; + } + + /* despite the spec, the result has a -1, followed by + length, followed by name */ + p = smb_buf(cli->inbuf); + p += 4; + len = smb_buflen(cli->inbuf) - 4; + if (len <= 0) return -1; + + if (tmp_path) { + pstring path2; + clistr_pull(cli, path2, p, + sizeof(path2), len, STR_ASCII); + *tmp_path = strdup(path2); + } + + return SVAL(cli->inbuf,smb_vwv0); +} diff --git a/source3/libsmb/clikrb5.c b/source3/libsmb/clikrb5.c new file mode 100644 index 0000000000..685c4a25e0 --- /dev/null +++ b/source3/libsmb/clikrb5.c @@ -0,0 +1,145 @@ +/* + Unix SMB/CIFS implementation. + simple kerberos5 routines for active directory + Copyright (C) Andrew Tridgell 2001 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +#ifdef HAVE_KRB5 +/* + we can't use krb5_mk_req because w2k wants the service to be in a particular format +*/ +static krb5_error_code krb5_mk_req2(krb5_context context, + krb5_auth_context *auth_context, + const krb5_flags ap_req_options, + const char *principal, + krb5_ccache ccache, + krb5_data *outbuf) +{ + krb5_error_code retval; + krb5_principal server; + krb5_creds * credsp; + krb5_creds creds; + krb5_data in_data; + + retval = krb5_parse_name(context, principal, &server); + if (retval) { + DEBUG(1,("Failed to parse principal %s\n", principal)); + return retval; + } + + /* obtain ticket & session key */ + memset((char *)&creds, 0, sizeof(creds)); + if ((retval = krb5_copy_principal(context, server, &creds.server))) { + DEBUG(1,("krb5_copy_principal failed (%s)\n", + error_message(retval))); + goto cleanup_princ; + } + + if ((retval = krb5_cc_get_principal(context, ccache, &creds.client))) { + DEBUG(1,("krb5_cc_get_principal failed (%s)\n", + error_message(retval))); + goto cleanup_creds; + } + + if ((retval = krb5_get_credentials(context, 0, + ccache, &creds, &credsp))) { + DEBUG(1,("krb5_get_credentials failed for %s (%s)\n", + principal, error_message(retval))); + goto cleanup_creds; + } + + in_data.length = 0; + retval = krb5_mk_req_extended(context, auth_context, ap_req_options, + &in_data, credsp, outbuf); + if (retval) { + DEBUG(1,("krb5_mk_req_extended failed (%s)\n", + error_message(retval))); + } + + krb5_free_creds(context, credsp); + +cleanup_creds: + krb5_free_cred_contents(context, &creds); + +cleanup_princ: + krb5_free_principal(context, server); + + return retval; +} + +/* + get a kerberos5 ticket for the given service +*/ +DATA_BLOB krb5_get_ticket(char *principal) +{ + krb5_error_code retval; + krb5_data packet; + krb5_ccache ccdef; + krb5_context context; + krb5_auth_context auth_context = NULL; + DATA_BLOB ret; + krb5_enctype enc_types[] = {ENCTYPE_DES_CBC_MD5, ENCTYPE_NULL}; + + retval = krb5_init_context(&context); + if (retval) { + DEBUG(1,("krb5_init_context failed (%s)\n", + error_message(retval))); + goto failed; + } + + if ((retval = krb5_cc_default(context, &ccdef))) { + DEBUG(1,("krb5_cc_default failed (%s)\n", + error_message(retval))); + goto failed; + } + + if ((retval = krb5_set_default_tgs_ktypes(context, enc_types))) { + DEBUG(1,("krb5_set_default_tgs_ktypes failed (%s)\n", + error_message(retval))); + goto failed; + } + + if ((retval = krb5_mk_req2(context, + &auth_context, + 0, + principal, + ccdef, &packet))) { + goto failed; + } + + ret = data_blob(packet.data, packet.length); +/* Hmm, heimdal dooesn't have this - what's the correct call? */ +/* krb5_free_data_contents(context, &packet); */ + krb5_free_context(context); + return ret; + +failed: + krb5_free_context(context); + return data_blob(NULL, 0); +} + + +#else /* HAVE_KRB5 */ + /* this saves a few linking headaches */ + DATA_BLOB krb5_get_ticket(char *principal) + { + DEBUG(0,("NO KERBEROS SUPPORT\n")); + return data_blob(NULL, 0); + } +#endif diff --git a/source3/libsmb/clilist.c b/source3/libsmb/clilist.c new file mode 100644 index 0000000000..8b28e05a47 --- /dev/null +++ b/source3/libsmb/clilist.c @@ -0,0 +1,464 @@ +/* + Unix SMB/CIFS implementation. + client directory list routines + Copyright (C) Andrew Tridgell 1994-1998 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#define NO_SYSLOG + +#include "includes.h" + + +/**************************************************************************** +interpret a long filename structure - this is mostly guesses at the moment +The length of the structure is returned +The structure of a long filename depends on the info level. 260 is used +by NT and 2 is used by OS/2 +****************************************************************************/ +static int interpret_long_filename(struct cli_state *cli, + int level,char *p,file_info *finfo) +{ + extern file_info def_finfo; + file_info finfo2; + int len; + char *base = p; + + if (!finfo) finfo = &finfo2; + + memcpy(finfo,&def_finfo,sizeof(*finfo)); + + switch (level) + { + case 1: /* OS/2 understands this */ + /* these dates are converted to GMT by + make_unix_date */ + finfo->ctime = make_unix_date2(p+4); + finfo->atime = make_unix_date2(p+8); + finfo->mtime = make_unix_date2(p+12); + finfo->size = IVAL(p,16); + finfo->mode = CVAL(p,24); + len = CVAL(p, 26); + p += 27; + p += clistr_align_in(cli, p, 0); + p += clistr_pull(cli, finfo->name, p, + sizeof(finfo->name), + len, + STR_TERMINATE); + return PTR_DIFF(p, base); + + case 2: /* this is what OS/2 uses mostly */ + /* these dates are converted to GMT by + make_unix_date */ + finfo->ctime = make_unix_date2(p+4); + finfo->atime = make_unix_date2(p+8); + finfo->mtime = make_unix_date2(p+12); + finfo->size = IVAL(p,16); + finfo->mode = CVAL(p,24); + len = CVAL(p, 30); + p += 31; + /* check for unisys! */ + p += clistr_pull(cli, finfo->name, p, + sizeof(finfo->name), + len, + STR_NOALIGN); + return PTR_DIFF(p, base) + 1; + + case 260: /* NT uses this, but also accepts 2 */ + { + int namelen, slen; + p += 4; /* next entry offset */ + p += 4; /* fileindex */ + + /* these dates appear to arrive in a + weird way. It seems to be localtime + plus the serverzone given in the + initial connect. This is GMT when + DST is not in effect and one hour + from GMT otherwise. Can this really + be right?? + + I suppose this could be called + kludge-GMT. Is is the GMT you get + by using the current DST setting on + a different localtime. It will be + cheap to calculate, I suppose, as + no DST tables will be needed */ + + finfo->ctime = interpret_long_date(p); p += 8; + finfo->atime = interpret_long_date(p); p += 8; + finfo->mtime = interpret_long_date(p); p += 8; p += 8; + finfo->size = IVAL(p,0); p += 8; + p += 8; /* alloc size */ + finfo->mode = CVAL(p,0); p += 4; + namelen = IVAL(p,0); p += 4; + p += 4; /* EA size */ + slen = SVAL(p, 0); + p += 2; + { + /* stupid NT bugs. grr */ + int flags = 0; + if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE; + clistr_pull(cli, finfo->short_name, p, + sizeof(finfo->short_name), + slen, flags); + } + p += 24; /* short name? */ + clistr_pull(cli, finfo->name, p, + sizeof(finfo->name), + namelen, 0); + return SVAL(base, 0); + } + } + + DEBUG(1,("Unknown long filename format %d\n",level)); + return(SVAL(p,0)); +} + + +/**************************************************************************** + do a directory listing, calling fn on each file found + ****************************************************************************/ +int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute, + void (*fn)(file_info *, const char *, void *), void *state) +{ + int max_matches = 512; + int info_level; + char *p, *p2; + pstring mask; + file_info finfo; + int i; + char *tdl, *dirlist = NULL; + int dirlist_len = 0; + int total_received = -1; + BOOL First = True; + int ff_searchcount=0; + int ff_eos=0; + int ff_lastname=0; + int ff_dir_handle=0; + int loop_count = 0; + char *rparam=NULL, *rdata=NULL; + int param_len, data_len; + uint16 setup; + pstring param; + + /* NT uses 260, OS/2 uses 2. Both accept 1. */ + info_level = (cli->capabilities&CAP_NT_SMBS)?260:1; + + pstrcpy(mask,Mask); + + while (ff_eos == 0) { + loop_count++; + if (loop_count > 200) { + DEBUG(0,("Error: Looping in FIND_NEXT??\n")); + break; + } + + if (First) { + setup = TRANSACT2_FINDFIRST; + SSVAL(param,0,attribute); /* attribute */ + SSVAL(param,2,max_matches); /* max count */ + SSVAL(param,4,4+2); /* resume required + close on end */ + SSVAL(param,6,info_level); + SIVAL(param,8,0); + p = param+12; + p += clistr_push(cli, param+12, mask, -1, + STR_TERMINATE); + } else { + setup = TRANSACT2_FINDNEXT; + SSVAL(param,0,ff_dir_handle); + SSVAL(param,2,max_matches); /* max count */ + SSVAL(param,4,info_level); + SIVAL(param,6,0); /* ff_resume_key */ + SSVAL(param,10,8+4+2); /* continue + resume required + close on end */ + p = param+12; + p += clistr_push(cli, param+12, mask, -1, + STR_TERMINATE); + } + + param_len = PTR_DIFF(p, param); + + if (!cli_send_trans(cli, SMBtrans2, + NULL, /* Name */ + -1, 0, /* fid, flags */ + &setup, 1, 0, /* setup, length, max */ + param, param_len, 10, /* param, length, max */ + NULL, 0, + cli->max_xmit /* data, length, max */ + )) { + break; + } + + if (!cli_receive_trans(cli, SMBtrans2, + &rparam, ¶m_len, + &rdata, &data_len) && + cli_is_dos_error(cli)) { + /* we need to work around a Win95 bug - sometimes + it gives ERRSRV/ERRerror temprarily */ + uint8 eclass; + uint32 ecode; + cli_dos_error(cli, &eclass, &ecode); + if (eclass != ERRSRV || ecode != ERRerror) break; + msleep(100); + continue; + } + + if (cli_is_error(cli) || !rdata || !rparam) + break; + + if (total_received == -1) total_received = 0; + + /* parse out some important return info */ + p = rparam; + if (First) { + ff_dir_handle = SVAL(p,0); + ff_searchcount = SVAL(p,2); + ff_eos = SVAL(p,4); + ff_lastname = SVAL(p,8); + } else { + ff_searchcount = SVAL(p,0); + ff_eos = SVAL(p,2); + ff_lastname = SVAL(p,6); + } + + if (ff_searchcount == 0) + break; + + /* point to the data bytes */ + p = rdata; + + /* we might need the lastname for continuations */ + if (ff_lastname > 0) { + switch(info_level) + { + case 260: + clistr_pull(cli, mask, p+ff_lastname, + sizeof(mask), + data_len-ff_lastname, + STR_TERMINATE); + break; + case 1: + clistr_pull(cli, mask, p+ff_lastname+1, + sizeof(mask), + -1, + STR_TERMINATE); + break; + } + } else { + pstrcpy(mask,""); + } + + /* and add them to the dirlist pool */ + tdl = Realloc(dirlist,dirlist_len + data_len); + + if (!tdl) { + DEBUG(0,("cli_list_new: Failed to expand dirlist\n")); + break; + } + else dirlist = tdl; + + /* put in a length for the last entry, to ensure we can chain entries + into the next packet */ + for (p2=p,i=0;i<(ff_searchcount-1);i++) + p2 += interpret_long_filename(cli,info_level,p2,NULL); + SSVAL(p2,0,data_len - PTR_DIFF(p2,p)); + + /* grab the data for later use */ + memcpy(dirlist+dirlist_len,p,data_len); + dirlist_len += data_len; + + total_received += ff_searchcount; + + SAFE_FREE(rdata); + SAFE_FREE(rparam); + + DEBUG(3,("received %d entries (eos=%d)\n", + ff_searchcount,ff_eos)); + + if (ff_searchcount > 0) loop_count = 0; + + First = False; + } + + for (p=dirlist,i=0;i<total_received;i++) { + p += interpret_long_filename(cli,info_level,p,&finfo); + fn(&finfo, Mask, state); + } + + /* free up the dirlist buffer */ + SAFE_FREE(dirlist); + return(total_received); +} + + + +/**************************************************************************** +interpret a short filename structure +The length of the structure is returned +****************************************************************************/ +static int interpret_short_filename(struct cli_state *cli, char *p,file_info *finfo) +{ + extern file_info def_finfo; + + *finfo = def_finfo; + + finfo->mode = CVAL(p,21); + + /* this date is converted to GMT by make_unix_date */ + finfo->ctime = make_unix_date(p+22); + finfo->mtime = finfo->atime = finfo->ctime; + finfo->size = IVAL(p,26); + clistr_pull(cli, finfo->name, p+30, sizeof(finfo->name), 12, STR_ASCII); + if (strcmp(finfo->name, "..") && strcmp(finfo->name, ".")) + fstrcpy(finfo->short_name,finfo->name); + + return(DIR_STRUCT_SIZE); +} + + +/**************************************************************************** + do a directory listing, calling fn on each file found + this uses the old SMBsearch interface. It is needed for testing Samba, + but should otherwise not be used + ****************************************************************************/ +int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute, + void (*fn)(file_info *, const char *, void *), void *state) +{ + char *p; + int received = 0; + BOOL first = True; + char status[21]; + int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE; + int num_received = 0; + int i; + char *tdl, *dirlist = NULL; + pstring mask; + + ZERO_ARRAY(status); + + pstrcpy(mask,Mask); + + while (1) { + memset(cli->outbuf,'\0',smb_size); + memset(cli->inbuf,'\0',smb_size); + + set_message(cli->outbuf,2,0,True); + + SCVAL(cli->outbuf,smb_com,SMBsearch); + + SSVAL(cli->outbuf,smb_tid,cli->cnum); + cli_setup_packet(cli); + + SSVAL(cli->outbuf,smb_vwv0,num_asked); + SSVAL(cli->outbuf,smb_vwv1,attribute); + + p = smb_buf(cli->outbuf); + *p++ = 4; + + p += clistr_push(cli, p, first?mask:"", -1, STR_TERMINATE); + *p++ = 5; + if (first) { + SSVAL(p,0,0); + p += 2; + } else { + SSVAL(p,0,21); + p += 2; + memcpy(p,status,21); + p += 21; + } + + cli_setup_bcc(cli, p); + cli_send_smb(cli); + if (!cli_receive_smb(cli)) break; + + received = SVAL(cli->inbuf,smb_vwv0); + if (received <= 0) break; + + first = False; + + tdl = Realloc(dirlist,(num_received + received)*DIR_STRUCT_SIZE); + + if (!tdl) { + DEBUG(0,("cli_list_old: failed to expand dirlist")); + SAFE_FREE(dirlist); + return 0; + } + else dirlist = tdl; + + p = smb_buf(cli->inbuf) + 3; + + memcpy(dirlist+num_received*DIR_STRUCT_SIZE, + p,received*DIR_STRUCT_SIZE); + + memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21); + + num_received += received; + + if (cli_is_error(cli)) break; + } + + if (!first) { + memset(cli->outbuf,'\0',smb_size); + memset(cli->inbuf,'\0',smb_size); + + set_message(cli->outbuf,2,0,True); + SCVAL(cli->outbuf,smb_com,SMBfclose); + SSVAL(cli->outbuf,smb_tid,cli->cnum); + cli_setup_packet(cli); + + SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */ + SSVAL(cli->outbuf, smb_vwv1, attribute); + + p = smb_buf(cli->outbuf); + *p++ = 4; + fstrcpy(p, ""); + p += strlen(p) + 1; + *p++ = 5; + SSVAL(p, 0, 21); + p += 2; + memcpy(p,status,21); + p += 21; + + cli_setup_bcc(cli, p); + cli_send_smb(cli); + if (!cli_receive_smb(cli)) { + DEBUG(0,("Error closing search: %s\n",cli_errstr(cli))); + } + } + + for (p=dirlist,i=0;i<num_received;i++) { + file_info finfo; + p += interpret_short_filename(cli, p,&finfo); + fn(&finfo, Mask, state); + } + + SAFE_FREE(dirlist); + return(num_received); +} + + +/**************************************************************************** + do a directory listing, calling fn on each file found + this auto-switches between old and new style + ****************************************************************************/ +int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute, + void (*fn)(file_info *, const char *, void *), void *state) +{ + if (cli->protocol <= PROTOCOL_LANMAN1) { + return cli_list_old(cli, Mask, attribute, fn, state); + } + return cli_list_new(cli, Mask, attribute, fn, state); +} diff --git a/source3/libsmb/climessage.c b/source3/libsmb/climessage.c new file mode 100644 index 0000000000..1587e6f4cd --- /dev/null +++ b/source3/libsmb/climessage.c @@ -0,0 +1,120 @@ +/* + Unix SMB/CIFS implementation. + client message handling routines + Copyright (C) Andrew Tridgell 1994-1998 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#define NO_SYSLOG + +#include "includes.h" + + +/**************************************************************************** +start a message sequence +****************************************************************************/ +BOOL cli_message_start(struct cli_state *cli, char *host, char *username, + int *grp) +{ + char *p; + + /* send a SMBsendstrt command */ + memset(cli->outbuf,'\0',smb_size); + set_message(cli->outbuf,0,0,True); + SCVAL(cli->outbuf,smb_com,SMBsendstrt); + SSVAL(cli->outbuf,smb_tid,cli->cnum); + cli_setup_packet(cli); + + p = smb_buf(cli->outbuf); + *p++ = 4; + p += clistr_push(cli, p, username, -1, STR_TERMINATE); + *p++ = 4; + p += clistr_push(cli, p, host, -1, STR_TERMINATE); + + cli_setup_bcc(cli, p); + + cli_send_smb(cli); + + if (!cli_receive_smb(cli)) { + return False; + } + + if (cli_is_error(cli)) return False; + + *grp = SVAL(cli->inbuf,smb_vwv0); + + return True; +} + + +/**************************************************************************** +send a message +****************************************************************************/ +BOOL cli_message_text(struct cli_state *cli, char *msg, int len, int grp) +{ + char *p; + + memset(cli->outbuf,'\0',smb_size); + set_message(cli->outbuf,1,0,True); + SCVAL(cli->outbuf,smb_com,SMBsendtxt); + SSVAL(cli->outbuf,smb_tid,cli->cnum); + cli_setup_packet(cli); + + SSVAL(cli->outbuf,smb_vwv0,grp); + + p = smb_buf(cli->outbuf); + *p++ = 1; + SSVAL(p,0,len); p += 2; + memcpy(p,msg,len); + p += len; + + cli_setup_bcc(cli, p); + cli_send_smb(cli); + + if (!cli_receive_smb(cli)) { + return False; + } + + if (cli_is_error(cli)) return False; + + return True; +} + +/**************************************************************************** +end a message +****************************************************************************/ +BOOL cli_message_end(struct cli_state *cli, int grp) +{ + memset(cli->outbuf,'\0',smb_size); + set_message(cli->outbuf,1,0,True); + SCVAL(cli->outbuf,smb_com,SMBsendend); + SSVAL(cli->outbuf,smb_tid,cli->cnum); + + SSVAL(cli->outbuf,smb_vwv0,grp); + + cli_setup_packet(cli); + + cli_send_smb(cli); + + if (!cli_receive_smb(cli)) { + return False; + } + + if (cli_is_error(cli)) return False; + + return True; +} + diff --git a/source3/libsmb/clioplock.c b/source3/libsmb/clioplock.c new file mode 100644 index 0000000000..0ffeb1926b --- /dev/null +++ b/source3/libsmb/clioplock.c @@ -0,0 +1,68 @@ +/* + Unix SMB/CIFS implementation. + SMB client oplock functions + Copyright (C) Andrew Tridgell 2001 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#define NO_SYSLOG + +#include "includes.h" + +/**************************************************************************** +send an ack for an oplock break request +****************************************************************************/ +BOOL cli_oplock_ack(struct cli_state *cli, int fnum, unsigned char level) +{ + char *oldbuf = cli->outbuf; + pstring buf; + BOOL ret; + + cli->outbuf = buf; + + memset(buf,'\0',smb_size); + set_message(buf,8,0,True); + + SCVAL(buf,smb_com,SMBlockingX); + SSVAL(buf,smb_tid, cli->cnum); + cli_setup_packet(cli); + SSVAL(buf,smb_vwv0,0xFF); + SSVAL(buf,smb_vwv1,0); + SSVAL(buf,smb_vwv2,fnum); + if (level == 1) + SSVAL(buf,smb_vwv3,0x102); /* levelII oplock break ack */ + else + SSVAL(buf,smb_vwv3,2); /* exclusive oplock break ack */ + SIVAL(buf,smb_vwv4,0); /* timoeut */ + SSVAL(buf,smb_vwv6,0); /* unlockcount */ + SSVAL(buf,smb_vwv7,0); /* lockcount */ + + ret = cli_send_smb(cli); + + cli->outbuf = oldbuf; + + return ret; +} + + +/**************************************************************************** +set the oplock handler for a connection +****************************************************************************/ +void cli_oplock_handler(struct cli_state *cli, + BOOL (*handler)(struct cli_state *, int, unsigned char)) +{ + cli->oplock_handler = handler; +} diff --git a/source3/libsmb/cliprint.c b/source3/libsmb/cliprint.c new file mode 100644 index 0000000000..92fbf02e91 --- /dev/null +++ b/source3/libsmb/cliprint.c @@ -0,0 +1,157 @@ +/* + Unix SMB/CIFS implementation. + client print routines + Copyright (C) Andrew Tridgell 1994-1998 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#define NO_SYSLOG + +#include "includes.h" + +/***************************************************************************** + Convert a character pointer in a cli_call_api() response to a form we can use. + This function contains code to prevent core dumps if the server returns + invalid data. +*****************************************************************************/ +static char *fix_char_ptr(unsigned int datap, unsigned int converter, + char *rdata, int rdrcnt) +{ + if (datap == 0) { /* turn NULL pointers into zero length strings */ + return ""; + } else { + unsigned int offset = datap - converter; + + if (offset >= rdrcnt) { + DEBUG(1,("bad char ptr: datap=%u, converter=%u rdrcnt=%d>", + datap, converter, rdrcnt)); + return "<ERROR>"; + } else { + return &rdata[offset]; + } + } +} + + +/**************************************************************************** +call fn() on each entry in a print queue +****************************************************************************/ +int cli_print_queue(struct cli_state *cli, + void (*fn)(struct print_job_info *)) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + int rdrcnt, rprcnt; + pstring param; + int result_code=0; + int i = -1; + + memset(param,'\0',sizeof(param)); + + p = param; + SSVAL(p,0,76); /* API function number 76 (DosPrintJobEnum) */ + p += 2; + pstrcpy(p,"zWrLeh"); /* parameter description? */ + p = skip_string(p,1); + pstrcpy(p,"WWzWWDDzz"); /* returned data format */ + p = skip_string(p,1); + pstrcpy(p,cli->share); /* name of queue */ + p = skip_string(p,1); + SSVAL(p,0,2); /* API function level 2, PRJINFO_2 data structure */ + SSVAL(p,2,1000); /* size of bytes of returned data buffer */ + p += 4; + pstrcpy(p,""); /* subformat */ + p = skip_string(p,1); + + DEBUG(4,("doing cli_print_queue for %s\n", cli->share)); + + if (cli_api(cli, + param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */ + NULL, 0, CLI_BUFFER_SIZE, /* data, length, maxlen */ + &rparam, &rprcnt, /* return params, length */ + &rdata, &rdrcnt)) { /* return data, length */ + int converter; + result_code = SVAL(rparam,0); + converter = SVAL(rparam,2); /* conversion factor */ + + if (result_code == 0) { + struct print_job_info job; + + p = rdata; + + for (i = 0; i < SVAL(rparam,4); ++i) { + job.id = SVAL(p,0); + job.priority = SVAL(p,2); + fstrcpy(job.user, + fix_char_ptr(SVAL(p,4), converter, + rdata, rdrcnt)); + job.t = make_unix_date3(p + 12); + job.size = IVAL(p,16); + fstrcpy(job.name,fix_char_ptr(SVAL(p,24), + converter, + rdata, rdrcnt)); + fn(&job); + p += 28; + } + } + } + + /* If any parameters or data were returned, free the storage. */ + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return i; +} + +/**************************************************************************** + cancel a print job + ****************************************************************************/ +int cli_printjob_del(struct cli_state *cli, int job) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + int rdrcnt,rprcnt, ret = -1; + pstring param; + + memset(param,'\0',sizeof(param)); + + p = param; + SSVAL(p,0,81); /* DosPrintJobDel() */ + p += 2; + pstrcpy(p,"W"); + p = skip_string(p,1); + pstrcpy(p,""); + p = skip_string(p,1); + SSVAL(p,0,job); + p += 2; + + if (cli_api(cli, + param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */ + NULL, 0, CLI_BUFFER_SIZE, /* data, length, maxlen */ + &rparam, &rprcnt, /* return params, length */ + &rdata, &rdrcnt)) { /* return data, length */ + ret = SVAL(rparam,0); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return ret; +} + + diff --git a/source3/libsmb/clirap.c b/source3/libsmb/clirap.c new file mode 100644 index 0000000000..a2b6c8bb8b --- /dev/null +++ b/source3/libsmb/clirap.c @@ -0,0 +1,738 @@ +/* + Unix SMB/CIFS implementation. + client RAP calls + Copyright (C) Andrew Tridgell 1994-1998 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#define NO_SYSLOG + +#include "includes.h" + + +/**************************************************************************** +Call a remote api on an arbitrary pipe. takes param, data and setup buffers. +****************************************************************************/ +BOOL cli_api_pipe(struct cli_state *cli, char *pipe_name, + uint16 *setup, uint32 setup_count, uint32 max_setup_count, + char *params, uint32 param_count, uint32 max_param_count, + char *data, uint32 data_count, uint32 max_data_count, + char **rparam, uint32 *rparam_count, + char **rdata, uint32 *rdata_count) +{ + cli_send_trans(cli, SMBtrans, + pipe_name, + 0,0, /* fid, flags */ + setup, setup_count, max_setup_count, + params, param_count, max_param_count, + data, data_count, max_data_count); + + return (cli_receive_trans(cli, SMBtrans, + rparam, (int *)rparam_count, + rdata, (int *)rdata_count)); +} + +/**************************************************************************** +call a remote api +****************************************************************************/ +BOOL cli_api(struct cli_state *cli, + char *param, int prcnt, int mprcnt, + char *data, int drcnt, int mdrcnt, + char **rparam, int *rprcnt, + char **rdata, int *rdrcnt) +{ + cli_send_trans(cli,SMBtrans, + PIPE_LANMAN, /* Name */ + 0,0, /* fid, flags */ + NULL,0,0, /* Setup, length, max */ + param, prcnt, mprcnt, /* Params, length, max */ + data, drcnt, mdrcnt /* Data, length, max */ + ); + + return (cli_receive_trans(cli,SMBtrans, + rparam, rprcnt, + rdata, rdrcnt)); +} + + +/**************************************************************************** +perform a NetWkstaUserLogon +****************************************************************************/ +BOOL cli_NetWkstaUserLogon(struct cli_state *cli,char *user, char *workstation) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + int rdrcnt,rprcnt; + pstring param; + + memset(param, 0, sizeof(param)); + + /* send a SMBtrans command with api NetWkstaUserLogon */ + p = param; + SSVAL(p,0,132); /* api number */ + p += 2; + pstrcpy(p,"OOWb54WrLh"); + p = skip_string(p,1); + pstrcpy(p,"WB21BWDWWDDDDDDDzzzD"); + p = skip_string(p,1); + SSVAL(p,0,1); + p += 2; + pstrcpy(p,user); + strupper(p); + p += 21; + p++; + p += 15; + p++; + pstrcpy(p, workstation); + strupper(p); + p += 16; + SSVAL(p, 0, CLI_BUFFER_SIZE); + p += 2; + SSVAL(p, 0, CLI_BUFFER_SIZE); + p += 2; + + if (cli_api(cli, + param, PTR_DIFF(p,param),1024, /* param, length, max */ + NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */ + &rparam, &rprcnt, /* return params, return size */ + &rdata, &rdrcnt /* return data, return size */ + )) { + cli->rap_error = rparam? SVAL(rparam,0) : -1; + p = rdata; + + if (cli->rap_error == 0) { + DEBUG(4,("NetWkstaUserLogon success\n")); + cli->privileges = SVAL(p, 24); + fstrcpy(cli->eff_name,p+2); + } else { + DEBUG(1,("NetwkstaUserLogon gave error %d\n", cli->rap_error)); + } + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + return (cli->rap_error == 0); +} + +/**************************************************************************** +call a NetShareEnum - try and browse available connections on a host +****************************************************************************/ +int cli_RNetShareEnum(struct cli_state *cli, void (*fn)(const char *, uint32, const char *, void *), void *state) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + int rdrcnt,rprcnt; + pstring param; + int count = -1; + + /* now send a SMBtrans command with api RNetShareEnum */ + p = param; + SSVAL(p,0,0); /* api number */ + p += 2; + pstrcpy(p,"WrLeh"); + p = skip_string(p,1); + pstrcpy(p,"B13BWz"); + p = skip_string(p,1); + SSVAL(p,0,1); + /* + * Win2k needs a *smaller* buffer than 0xFFFF here - + * it returns "out of server memory" with 0xFFFF !!! JRA. + */ + SSVAL(p,2,0xFFE0); + p += 4; + + if (cli_api(cli, + param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */ + NULL, 0, 0xFFE0, /* data, length, maxlen - Win2k needs a small buffer here too ! */ + &rparam, &rprcnt, /* return params, length */ + &rdata, &rdrcnt)) /* return data, length */ + { + int res = rparam? SVAL(rparam,0) : -1; + + if (res == 0 || res == ERRmoredata) { + int converter=SVAL(rparam,2); + int i; + + count=SVAL(rparam,4); + p = rdata; + + for (i=0;i<count;i++,p+=20) { + char *sname = p; + int type = SVAL(p,14); + int comment_offset = IVAL(p,16) & 0xFFFF; + char *cmnt = comment_offset?(rdata+comment_offset-converter):""; + pstring s1, s2; + + pull_ascii_pstring(s1, sname); + pull_ascii_pstring(s2, cmnt); + + fn(s1, type, s2, state); + } + } else { + DEBUG(4,("NetShareEnum res=%d\n", res)); + } + } else { + DEBUG(4,("NetShareEnum failed\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return count; +} + + +/**************************************************************************** +call a NetServerEnum for the specified workgroup and servertype mask. This +function then calls the specified callback function for each name returned. + +The callback function takes 4 arguments: the machine name, the server type, +the comment and a state pointer. +****************************************************************************/ +BOOL cli_NetServerEnum(struct cli_state *cli, char *workgroup, uint32 stype, + void (*fn)(const char *, uint32, const char *, void *), + void *state) +{ + char *rparam = NULL; + char *rdata = NULL; + int rdrcnt,rprcnt; + char *p; + pstring param; + int uLevel = 1; + int count = -1; + + /* send a SMBtrans command with api NetServerEnum */ + p = param; + SSVAL(p,0,0x68); /* api number */ + p += 2; + pstrcpy(p,"WrLehDz"); + p = skip_string(p,1); + + pstrcpy(p,"B16BBDz"); + + p = skip_string(p,1); + SSVAL(p,0,uLevel); + SSVAL(p,2,CLI_BUFFER_SIZE); + p += 4; + SIVAL(p,0,stype); + p += 4; + + p += push_pstring(p, workgroup); + + if (cli_api(cli, + param, PTR_DIFF(p,param), 8, /* params, length, max */ + NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */ + &rparam, &rprcnt, /* return params, return size */ + &rdata, &rdrcnt /* return data, return size */ + )) { + int res = rparam? SVAL(rparam,0) : -1; + + if (res == 0 || res == ERRmoredata) { + int i; + int converter=SVAL(rparam,2); + + count=SVAL(rparam,4); + p = rdata; + + for (i = 0;i < count;i++, p += 26) { + char *sname = p; + int comment_offset = (IVAL(p,22) & 0xFFFF)-converter; + char *cmnt = comment_offset?(rdata+comment_offset):""; + pstring s1, s2; + + if (comment_offset < 0 || comment_offset > rdrcnt) continue; + + stype = IVAL(p,18) & ~SV_TYPE_LOCAL_LIST_ONLY; + + pull_ascii_pstring(s1, sname); + pull_ascii_pstring(s2, cmnt); + fn(s1, stype, s2, state); + } + } + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return(count > 0); +} + + + +/**************************************************************************** +Send a SamOEMChangePassword command +****************************************************************************/ +BOOL cli_oem_change_password(struct cli_state *cli, const char *user, const char *new_password, + const char *old_password) +{ + char param[16+sizeof(fstring)]; + char data[532]; + char *p = param; + fstring upper_case_old_pw; + fstring upper_case_new_pw; + unsigned char old_pw_hash[16]; + unsigned char new_pw_hash[16]; + int data_len; + int param_len = 0; + char *rparam = NULL; + char *rdata = NULL; + int rprcnt, rdrcnt; + pstring dos_new_password; + + if (strlen(user) >= sizeof(fstring)-1) { + DEBUG(0,("cli_oem_change_password: user name %s is too long.\n", user)); + return False; + } + + SSVAL(p,0,214); /* SamOEMChangePassword command. */ + p += 2; + pstrcpy(p, "zsT"); + p = skip_string(p,1); + pstrcpy(p, "B516B16"); + p = skip_string(p,1); + pstrcpy(p,user); + p = skip_string(p,1); + SSVAL(p,0,532); + p += 2; + + param_len = PTR_DIFF(p,param); + + /* + * Get the Lanman hash of the old password, we + * use this as the key to make_oem_passwd_hash(). + */ + memset(upper_case_old_pw, '\0', sizeof(upper_case_old_pw)); + clistr_push(cli, upper_case_old_pw, old_password, -1,STR_TERMINATE|STR_UPPER|STR_ASCII); + E_P16((uchar *)upper_case_old_pw, old_pw_hash); + + clistr_push(cli, dos_new_password, new_password, -1, STR_TERMINATE|STR_ASCII); + + if (!make_oem_passwd_hash( data, dos_new_password, old_pw_hash, False)) + return False; + + /* + * Now place the old password hash in the data. + */ + memset(upper_case_new_pw, '\0', sizeof(upper_case_new_pw)); + clistr_push(cli, upper_case_new_pw, new_password, -1, STR_TERMINATE|STR_UPPER|STR_ASCII); + + E_P16((uchar *)upper_case_new_pw, new_pw_hash); + + E_old_pw_hash( new_pw_hash, old_pw_hash, (uchar *)&data[516]); + + data_len = 532; + + if (cli_send_trans(cli,SMBtrans, + PIPE_LANMAN, /* name */ + 0,0, /* fid, flags */ + NULL,0,0, /* setup, length, max */ + param,param_len,2, /* param, length, max */ + data,data_len,0 /* data, length, max */ + ) == False) { + DEBUG(0,("cli_oem_change_password: Failed to send password change for user %s\n", + user )); + return False; + } + + if (cli_receive_trans(cli,SMBtrans, + &rparam, &rprcnt, + &rdata, &rdrcnt)) { + if (rparam) + cli->rap_error = SVAL(rparam,0); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return (cli->rap_error == 0); +} + + +/**************************************************************************** +send a qpathinfo call +****************************************************************************/ +BOOL cli_qpathinfo(struct cli_state *cli, const char *fname, + time_t *c_time, time_t *a_time, time_t *m_time, + size_t *size, uint16 *mode) +{ + int data_len = 0; + int param_len = 0; + int rparam_len, rdata_len; + uint16 setup = TRANSACT2_QPATHINFO; + pstring param; + char *rparam=NULL, *rdata=NULL; + int count=8; + BOOL ret; + time_t (*date_fn)(void *); + char *p; + + p = param; + memset(p, 0, 6); + SSVAL(p, 0, SMB_INFO_STANDARD); + p += 6; + p += clistr_push(cli, p, fname, sizeof(pstring)-6, STR_TERMINATE); + + param_len = PTR_DIFF(p, param); + + do { + ret = (cli_send_trans(cli, SMBtrans2, + NULL, /* Name */ + -1, 0, /* fid, flags */ + &setup, 1, 0, /* setup, length, max */ + param, param_len, 10, /* param, length, max */ + NULL, data_len, cli->max_xmit /* data, length, max */ + ) && + cli_receive_trans(cli, SMBtrans2, + &rparam, &rparam_len, + &rdata, &rdata_len)); + if (!cli_is_dos_error(cli)) break; + if (!ret) { + /* we need to work around a Win95 bug - sometimes + it gives ERRSRV/ERRerror temprarily */ + uint8 eclass; + uint32 ecode; + cli_dos_error(cli, &eclass, &ecode); + if (eclass != ERRSRV || ecode != ERRerror) break; + msleep(100); + } + } while (count-- && ret==False); + + if (!ret || !rdata || rdata_len < 22) { + return False; + } + + if (cli->win95) { + date_fn = make_unix_date; + } else { + date_fn = make_unix_date2; + } + + if (c_time) { + *c_time = date_fn(rdata+0); + } + if (a_time) { + *a_time = date_fn(rdata+4); + } + if (m_time) { + *m_time = date_fn(rdata+8); + } + if (size) { + *size = IVAL(rdata, 12); + } + if (mode) { + *mode = SVAL(rdata,l1_attrFile); + } + + SAFE_FREE(rdata); + SAFE_FREE(rparam); + return True; +} + +/**************************************************************************** +send a qpathinfo call with the SMB_QUERY_FILE_ALL_INFO info level +****************************************************************************/ +BOOL cli_qpathinfo2(struct cli_state *cli, const char *fname, + time_t *c_time, time_t *a_time, time_t *m_time, + time_t *w_time, size_t *size, uint16 *mode, + SMB_INO_T *ino) +{ + int data_len = 0; + int param_len = 0; + uint16 setup = TRANSACT2_QPATHINFO; + pstring param; + char *rparam=NULL, *rdata=NULL; + char *p; + + p = param; + memset(p, 0, 6); + SSVAL(p, 0, SMB_QUERY_FILE_ALL_INFO); + p += 6; + p += clistr_push(cli, p, fname, sizeof(pstring)-6, STR_TERMINATE); + + param_len = PTR_DIFF(p, param); + + if (!cli_send_trans(cli, SMBtrans2, + NULL, /* name */ + -1, 0, /* fid, flags */ + &setup, 1, 0, /* setup, length, max */ + param, param_len, 10, /* param, length, max */ + NULL, data_len, cli->max_xmit /* data, length, max */ + )) { + return False; + } + + if (!cli_receive_trans(cli, SMBtrans2, + &rparam, ¶m_len, + &rdata, &data_len)) { + return False; + } + + if (!rdata || data_len < 22) { + return False; + } + + if (c_time) { + *c_time = interpret_long_date(rdata+0) - cli->serverzone; + } + if (a_time) { + *a_time = interpret_long_date(rdata+8) - cli->serverzone; + } + if (m_time) { + *m_time = interpret_long_date(rdata+16) - cli->serverzone; + } + if (w_time) { + *w_time = interpret_long_date(rdata+24) - cli->serverzone; + } + if (mode) { + *mode = SVAL(rdata, 32); + } + if (size) { + *size = IVAL(rdata, 48); + } + if (ino) { + *ino = IVAL(rdata, 64); + } + + SAFE_FREE(rdata); + SAFE_FREE(rparam); + return True; +} + + +/**************************************************************************** +send a qfileinfo QUERY_FILE_NAME_INFO call +****************************************************************************/ +BOOL cli_qfilename(struct cli_state *cli, int fnum, + pstring name) +{ + int data_len = 0; + int param_len = 0; + uint16 setup = TRANSACT2_QFILEINFO; + pstring param; + char *rparam=NULL, *rdata=NULL; + + param_len = 4; + memset(param, 0, param_len); + SSVAL(param, 0, fnum); + SSVAL(param, 2, SMB_QUERY_FILE_NAME_INFO); + + if (!cli_send_trans(cli, SMBtrans2, + NULL, /* name */ + -1, 0, /* fid, flags */ + &setup, 1, 0, /* setup, length, max */ + param, param_len, 2, /* param, length, max */ + NULL, data_len, cli->max_xmit /* data, length, max */ + )) { + return False; + } + + if (!cli_receive_trans(cli, SMBtrans2, + &rparam, ¶m_len, + &rdata, &data_len)) { + return False; + } + + if (!rdata || data_len < 4) { + return False; + } + + clistr_pull(cli, name, rdata+4, sizeof(pstring), IVAL(rdata, 0), STR_UNICODE); + + return True; +} + + +/**************************************************************************** +send a qfileinfo call +****************************************************************************/ +BOOL cli_qfileinfo(struct cli_state *cli, int fnum, + uint16 *mode, size_t *size, + time_t *c_time, time_t *a_time, time_t *m_time, + time_t *w_time, SMB_INO_T *ino) +{ + int data_len = 0; + int param_len = 0; + uint16 setup = TRANSACT2_QFILEINFO; + pstring param; + char *rparam=NULL, *rdata=NULL; + + /* if its a win95 server then fail this - win95 totally screws it + up */ + if (cli->win95) return False; + + param_len = 4; + + memset(param, 0, param_len); + SSVAL(param, 0, fnum); + SSVAL(param, 2, SMB_QUERY_FILE_ALL_INFO); + + if (!cli_send_trans(cli, SMBtrans2, + NULL, /* name */ + -1, 0, /* fid, flags */ + &setup, 1, 0, /* setup, length, max */ + param, param_len, 2, /* param, length, max */ + NULL, data_len, cli->max_xmit /* data, length, max */ + )) { + return False; + } + + if (!cli_receive_trans(cli, SMBtrans2, + &rparam, ¶m_len, + &rdata, &data_len)) { + return False; + } + + if (!rdata || data_len < 68) { + return False; + } + + if (c_time) { + *c_time = interpret_long_date(rdata+0) - cli->serverzone; + } + if (a_time) { + *a_time = interpret_long_date(rdata+8) - cli->serverzone; + } + if (m_time) { + *m_time = interpret_long_date(rdata+16) - cli->serverzone; + } + if (w_time) { + *w_time = interpret_long_date(rdata+24) - cli->serverzone; + } + if (mode) { + *mode = SVAL(rdata, 32); + } + if (size) { + *size = IVAL(rdata, 48); + } + if (ino) { + *ino = IVAL(rdata, 64); + } + + SAFE_FREE(rdata); + SAFE_FREE(rparam); + return True; +} + +/**************************************************************************** +send a qfileinfo call +****************************************************************************/ +BOOL cli_qfileinfo_test(struct cli_state *cli, int fnum, int level, char *outdata) +{ + int data_len = 0; + int param_len = 0; + uint16 setup = TRANSACT2_QFILEINFO; + pstring param; + char *rparam=NULL, *rdata=NULL; + + /* if its a win95 server then fail this - win95 totally screws it + up */ + if (cli->win95) return False; + + param_len = 4; + + memset(param, 0, param_len); + SSVAL(param, 0, fnum); + SSVAL(param, 2, level); + + if (!cli_send_trans(cli, SMBtrans2, + NULL, /* name */ + -1, 0, /* fid, flags */ + &setup, 1, 0, /* setup, length, max */ + param, param_len, 2, /* param, length, max */ + NULL, data_len, cli->max_xmit /* data, length, max */ + )) { + return False; + } + + if (!cli_receive_trans(cli, SMBtrans2, + &rparam, ¶m_len, + &rdata, &data_len)) { + return False; + } + + memcpy(outdata, rdata, data_len); + + SAFE_FREE(rdata); + SAFE_FREE(rparam); + return True; +} + + + +/**************************************************************************** +send a qpathinfo SMB_QUERY_FILE_ALT_NAME_INFO call +****************************************************************************/ +NTSTATUS cli_qpathinfo_alt_name(struct cli_state *cli, const char *fname, fstring alt_name) +{ + int data_len = 0; + int param_len = 0; + uint16 setup = TRANSACT2_QPATHINFO; + pstring param; + char *rparam=NULL, *rdata=NULL; + int count=8; + char *p; + BOOL ret; + int len; + + p = param; + memset(p, 0, 6); + SSVAL(p, 0, SMB_QUERY_FILE_ALT_NAME_INFO); + p += 6; + p += clistr_push(cli, p, fname, sizeof(pstring)-6, STR_TERMINATE); + + param_len = PTR_DIFF(p, param); + + do { + ret = (cli_send_trans(cli, SMBtrans2, + NULL, /* Name */ + -1, 0, /* fid, flags */ + &setup, 1, 0, /* setup, length, max */ + param, param_len, 10, /* param, length, max */ + NULL, data_len, cli->max_xmit /* data, length, max */ + ) && + cli_receive_trans(cli, SMBtrans2, + &rparam, ¶m_len, + &rdata, &data_len)); + if (!ret && cli_is_dos_error(cli)) { + /* we need to work around a Win95 bug - sometimes + it gives ERRSRV/ERRerror temprarily */ + uint8 eclass; + uint32 ecode; + cli_dos_error(cli, &eclass, &ecode); + if (eclass != ERRSRV || ecode != ERRerror) break; + msleep(100); + } + } while (count-- && ret==False); + + if (!ret || !rdata || data_len < 4) { + return NT_STATUS_UNSUCCESSFUL; + } + + len = IVAL(rdata, 0); + + if (len > data_len - 4) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + clistr_pull(cli, alt_name, rdata+4, sizeof(fstring), len, 0); + + SAFE_FREE(rdata); + SAFE_FREE(rparam); + + return NT_STATUS_OK; +} diff --git a/source3/libsmb/clirap2.c b/source3/libsmb/clirap2.c new file mode 100644 index 0000000000..00cd4b15f3 --- /dev/null +++ b/source3/libsmb/clirap2.c @@ -0,0 +1,1961 @@ +/* + Samba Unix/Linux SMB client library + More client RAP (SMB Remote Procedure Calls) functions + Copyright (C) 2001 Steve French (sfrench@us.ibm.com) + Copyright (C) 2001 Jim McDonough (jmcd@us.ibm.com) + + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/*****************************************************/ +/* */ +/* Additional RAP functionality */ +/* */ +/* RAP is the original SMB RPC, documented */ +/* by Microsoft and X/Open in the 1990s and */ +/* supported by most SMB/CIFS servers although */ +/* it is unlikely that any one implementation */ +/* supports all RAP command codes since some */ +/* are quite obsolete and a few are specific */ +/* to a particular network operating system */ +/* */ +/* Although it has largely been replaced */ +/* for complex remote admistration and management */ +/* (of servers) by the relatively newer */ +/* DCE/RPC based remote API (which better handles */ +/* large >64K data structures), there are many */ +/* important administrative and resource location */ +/* tasks and user tasks (e.g. password change) */ +/* that are performed via RAP. */ +/* */ +/* Although a few of the RAP calls are implemented */ +/* in the Samba client library already (clirap.c) */ +/* the new ones are in clirap2.c for easy patching */ +/* and integration and a corresponding header */ +/* file, rap.h, has been created. */ +/* */ +/* This is based on data from the CIFS spec */ +/* and the LAN Server and LAN Manager */ +/* Programming Reference books and published */ +/* RAP document and CIFS forum postings and */ +/* lots of trial and error */ +/* */ +/* Function names changed from API_ (as they are */ +/* in the CIFS specification) to RAP_ in order */ +/* to avoid confusion with other API calls */ +/* sent via DCE RPC */ +/* */ +/*****************************************************/ + +/*****************************************************/ +/* */ +/* cifsrap.c already includes support for: */ +/* */ +/* WshareEnum ( API number 0, level 1) */ +/* NetServerEnum2 (API num 104, level 1) */ +/* WWkstaUserLogon (132) */ +/* SamOEMchgPasswordUser2_P (214) */ +/* */ +/* cifsprint.c already includes support for: */ +/* */ +/* WPrintJobEnum (API num 76, level 2) */ +/* WPrintJobDel (API num 81) */ +/* */ +/*****************************************************/ + +#define NO_SYSLOG + +#include "includes.h" + +#define WORDSIZE 2 +#define DWORDSIZE 4 + +#define PUTBYTE(p,b) do {SCVAL(p,0,b); p++;} while(0) +#define GETBYTE(p,b) do {b = CVAL(p,0); p++;} while(0) +#define PUTWORD(p,w) do {SSVAL(p,0,w); p += WORDSIZE;} while(0) +#define GETWORD(p,w) do {w = SVAL(p,0); p += WORDSIZE;} while(0) +#define PUTDWORD(p,d) do {SIVAL(p,0,d); p += DWORDSIZE;} while(0) +#define GETDWORD(p,d) do {d = IVAL(p,0); p += DWORDSIZE;} while(0) +#define GETRES(p) p ? SVAL(p,0) : -1 +/* put string s at p with max len n and increment p past string */ +#define PUTSTRING(p,s,n) do {\ + push_ascii(p,s?s:"",n?n:256,STR_TERMINATE);\ + p = skip_string(p,1);\ + } while(0) +/* put string s and p, using fixed len l, and increment p by l */ +#define PUTSTRINGF(p,s,l) do {\ + push_ascii(p,s?s:"",l,STR_TERMINATE);\ + p += l;\ + } while (0) +/* put string pointer at p, supplying offset o from rdata r, store */ +/* dword offset at p, increment p by 4 and o by length of s. This */ +/* means on the first call, you must calc the offset yourself! */ +#define PUTSTRINGP(p,s,r,o) do {\ + if (s) {\ + push_ascii(r+o,s,strlen(s)+1,STR_TERMINATE);\ + PUTDWORD(p,o);\ + o += strlen(s) + 1;\ + } else PUTDWORD(p,0);\ + }while(0); +/* get asciiz string s from p, increment p past string */ +#define GETSTRING(p,s) do {\ + pull_ascii_pstring(s,p);\ + p = skip_string(p,1);\ + } while(0) +/* get fixed length l string s from p, increment p by l */ +#define GETSTRINGF(p,s,l) do {\ + pull_ascii_pstring(s,p);\ + p += l;\ + } while(0) +/* get string s from offset (obtained at p) from rdata r - converter c */ +#define GETSTRINGP(p,s,r,c) do {\ + uint32 off;\ + GETDWORD(p,off);\ + off &= 0x0000FFFF; /* mask the obsolete segment number from the offset */ \ + pull_ascii_pstring(s, off?(r+off-c):"");\ + } while(0) + +static char *make_header(char *param, uint16 apinum, char *reqfmt, char *datafmt) +{ + PUTWORD(param,apinum); + if (reqfmt) + PUTSTRING(param,reqfmt,0); + else + *param++ = (char) 0; + + if (datafmt) + PUTSTRING(param,datafmt,0); + else + *param++ = (char) 0; + + return param; +} + + +/**************************************************************************** + call a NetGroupDelete - delete user group from remote server +****************************************************************************/ +int cli_NetGroupDelete(struct cli_state *cli, const char *group_name ) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + int rdrcnt,rprcnt, res; + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetGroupDel_REQ) /* parm string */ + +1 /* no ret string */ + +RAP_GROUPNAME_LEN /* group to del */ + +WORDSIZE]; /* reserved word */ + + /* now send a SMBtrans command with api GroupDel */ + p = make_header(param, RAP_WGroupDel, RAP_NetGroupDel_REQ, NULL); + PUTSTRING(p, group_name, RAP_GROUPNAME_LEN); + PUTWORD(p,0); /* reserved word MBZ on input */ + + if (cli_api(cli, + param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */ + NULL, 0, 200, /* data, length, maxlen */ + &rparam, &rprcnt, /* return params, length */ + &rdata, &rdrcnt)) /* return data, length */ + { + res = GETRES(rparam); + + if (res == 0) { + /* nothing to do */ + } + else if ((res == 5) || (res == 65)) { + DEBUG(1, ("Access Denied\n")); + } + else if (res == 2220) { + DEBUG (1, ("Group does not exist\n")); + } + else { + DEBUG(4,("NetGroupDelete res=%d\n", res)); + } + } else { + res = -1; + DEBUG(4,("NetGroupDelete failed\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +/**************************************************************************** + call a NetGroupAdd - add user group to remote server +****************************************************************************/ +int cli_NetGroupAdd(struct cli_state *cli, RAP_GROUP_INFO_1 * grinfo ) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + int rdrcnt,rprcnt,res; + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetGroupAdd_REQ) /* req string */ + +sizeof(RAP_GROUP_INFO_L1) /* return string */ + +WORDSIZE /* info level */ + +WORDSIZE]; /* reserved word */ + + char data[1024]; + + /* offset into data of free format strings. Will be updated */ + /* by PUTSTRINGP macro and end up with total data length. */ + int soffset = RAP_GROUPNAME_LEN + 1 + DWORDSIZE; + + /* now send a SMBtrans command with api WGroupAdd */ + + p = make_header(param, RAP_WGroupAdd, + RAP_NetGroupAdd_REQ, RAP_GROUP_INFO_L1); + PUTWORD(p, 1); /* info level */ + PUTWORD(p, 0); /* reserved word 0 */ + + p = data; + PUTSTRINGF(p, grinfo->group_name, RAP_GROUPNAME_LEN); + PUTBYTE(p, 0); /* pad byte 0 */ + PUTSTRINGP(p, grinfo->comment, data, soffset); + + if (cli_api(cli, + param, sizeof(param), 1024, /* Param, length, maxlen */ + data, soffset, sizeof(data), /* data, length, maxlen */ + &rparam, &rprcnt, /* return params, length */ + &rdata, &rdrcnt)) /* return data, length */ + { + res = GETRES(rparam); + + if (res == 0) { + /* nothing to do */ + } else if ((res == 5) || (res == 65)) { + DEBUG(1, ("Access Denied\n")); + } + else if (res == 2223) { + DEBUG (1, ("Group already exists\n")); + } + else { + DEBUG(4,("NetGroupAdd res=%d\n", res)); + } + } else { + res = -1; + DEBUG(4,("NetGroupAdd failed\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +/**************************************************************************** +call a NetGroupEnum - try and list user groups on a different host +****************************************************************************/ +int cli_RNetGroupEnum(struct cli_state *cli, void (*fn)(const char *, const char *, void *), void *state) +{ + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetGroupEnum_REQ) /* parm string */ + +sizeof(RAP_GROUP_INFO_L1) /* return string */ + +WORDSIZE /* info level */ + +WORDSIZE]; /* buffer size */ + char *p; + char *rparam = NULL; + char *rdata = NULL; + int rprcnt, rdrcnt; + int res = -1; + + + memset(param, '\0', sizeof(param)); + p = make_header(param, RAP_WGroupEnum, + RAP_NetGroupEnum_REQ, RAP_GROUP_INFO_L1); + PUTWORD(p,1); /* Info level 1 */ /* add level 0 */ + PUTWORD(p,0xFFE0); /* Return buffer size */ + + if (cli_api(cli, + param, PTR_DIFF(p,param),8, + NULL, 0, 0xFFE0 /* data area size */, + &rparam, &rprcnt, + &rdata, &rdrcnt)) { + res = GETRES(rparam); + cli->rap_error = res; + if(cli->rap_error == 234) + DEBUG(1,("Not all group names were returned (such as those longer than 21 characters)\n")); + else if (cli->rap_error != 0) { + DEBUG(1,("NetGroupEnum gave error %d\n", cli->rap_error)); + } + } + + if (rdata) { + if (res == 0 || res == ERRmoredata) { + int i, converter, count; + + p = rparam + WORDSIZE; /* skip result */ + GETWORD(p, converter); + GETWORD(p, count); + + for (i=0,p=rdata;i<count;i++) { + pstring comment; + char groupname[RAP_GROUPNAME_LEN]; + + GETSTRINGF(p, groupname, RAP_GROUPNAME_LEN); + p++; /* pad byte */ + GETSTRINGP(p, comment, rdata, converter); + + fn(groupname, comment, cli); + } + } else { + DEBUG(4,("NetGroupEnum res=%d\n", res)); + } + } else { + DEBUG(4,("NetGroupEnum no data returned\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +int cli_NetGroupDelUser(struct cli_state * cli, const char *group_name, const char *user_name) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + int rdrcnt,rprcnt,res; + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetGroupDelUser_REQ) /* parm string */ + +1 /* no ret string */ + +RAP_GROUPNAME_LEN /* group name */ + +RAP_USERNAME_LEN]; /* user to del */ + + /* now send a SMBtrans command with api GroupMemberAdd */ + p = make_header(param, RAP_WGroupDelUser, RAP_NetGroupDelUser_REQ, NULL); + PUTSTRING(p,group_name,RAP_GROUPNAME_LEN); + PUTSTRING(p,user_name,RAP_USERNAME_LEN); + + if (cli_api(cli, + param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */ + NULL, 0, 200, /* data, length, maxlen */ + &rparam, &rprcnt, /* return params, length */ + &rdata, &rdrcnt)) /* return data, length */ + { + res = GETRES(rparam); + + switch(res) { + case 0: + break; + case 5: + case 65: + DEBUG(1, ("Access Denied\n")); + break; + case 50: + DEBUG(1, ("Not supported by server\n")); + break; + case 2220: + DEBUG(1, ("Group does not exist\n")); + break; + case 2221: + DEBUG(1, ("User does not exist\n")); + break; + case 2237: + DEBUG(1, ("User is not in group\n")); + break; + default: + DEBUG(4,("NetGroupDelUser res=%d\n", res)); + } + } else { + res = -1; + DEBUG(4,("NetGroupDelUser failed\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +int cli_NetGroupAddUser(struct cli_state * cli, const char *group_name, const char *user_name) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + int rdrcnt,rprcnt,res; + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetGroupAddUser_REQ) /* parm string */ + +1 /* no ret string */ + +RAP_GROUPNAME_LEN /* group name */ + +RAP_USERNAME_LEN]; /* user to add */ + + /* now send a SMBtrans command with api GroupMemberAdd */ + p = make_header(param, RAP_WGroupAddUser, RAP_NetGroupAddUser_REQ, NULL); + PUTSTRING(p,group_name,RAP_GROUPNAME_LEN); + PUTSTRING(p,user_name,RAP_USERNAME_LEN); + + if (cli_api(cli, + param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */ + NULL, 0, 200, /* data, length, maxlen */ + &rparam, &rprcnt, /* return params, length */ + &rdata, &rdrcnt)) /* return data, length */ + { + res = GETRES(rparam); + + switch(res) { + case 0: + break; + case 5: + case 65: + DEBUG(1, ("Access Denied\n")); + break; + case 50: + DEBUG(1, ("Not supported by server\n")); + break; + case 2220: + DEBUG(1, ("Group does not exist\n")); + break; + case 2221: + DEBUG(1, ("User does not exist\n")); + break; + default: + DEBUG(4,("NetGroupAddUser res=%d\n", res)); + } + } else { + res = -1; + DEBUG(4,("NetGroupAddUser failed\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + + +int cli_NetGroupGetUsers(struct cli_state * cli, const char *group_name, void (*fn)(const char *, void *), void *state ) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + int rdrcnt,rprcnt; + int res = -1; + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetGroupGetUsers_REQ)/* parm string */ + +sizeof(RAP_GROUP_USERS_INFO_0) /* return string */ + +RAP_GROUPNAME_LEN /* group name */ + +WORDSIZE /* info level */ + +WORDSIZE]; /* buffer size */ + + /* now send a SMBtrans command with api GroupGetUsers */ + p = make_header(param, RAP_WGroupGetUsers, + RAP_NetGroupGetUsers_REQ, RAP_GROUP_USERS_INFO_0); + PUTSTRING(p,group_name,RAP_GROUPNAME_LEN-1); + PUTWORD(p,0); /* info level 0 */ + PUTWORD(p,0xFFE0); /* return buffer size */ + + if (cli_api(cli, + param, PTR_DIFF(p,param),PTR_DIFF(p,param), + NULL, 0, CLI_BUFFER_SIZE, + &rparam, &rprcnt, + &rdata, &rdrcnt)) { + res = GETRES(rparam); + cli->rap_error = res; + if (res != 0) { + DEBUG(1,("NetGroupGetUsers gave error %d\n", res)); + } + } + if (rdata) { + if (res == 0 || res == ERRmoredata) { + int i, converter, count; + fstring username; + p = rparam +WORDSIZE; + GETWORD(p, converter); + GETWORD(p, count); + + for (i=0,p=rdata; i<count; i++) { + GETSTRINGF(p, username, RAP_USERNAME_LEN); + fn(username, state); + } + } else { + DEBUG(4,("NetGroupGetUsers res=%d\n", res)); + } + } else { + DEBUG(4,("NetGroupGetUsers no data returned\n")); + } + SAFE_FREE(rdata); + SAFE_FREE(rparam); + return res; +} + +int cli_NetUserGetGroups(struct cli_state * cli, const char *user_name, void (*fn)(const char *, void *), void *state ) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + int rdrcnt,rprcnt; + int res = -1; + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetUserGetGroups_REQ)/* parm string */ + +sizeof(RAP_GROUP_USERS_INFO_0) /* return string */ + +RAP_USERNAME_LEN /* user name */ + +WORDSIZE /* info level */ + +WORDSIZE]; /* buffer size */ + + /* now send a SMBtrans command with api GroupGetUsers */ + p = make_header(param, RAP_WUserGetGroups, + RAP_NetUserGetGroups_REQ, RAP_GROUP_USERS_INFO_0); + PUTSTRING(p,user_name,RAP_USERNAME_LEN-1); + PUTWORD(p,0); /* info level 0 */ + PUTWORD(p,0xFFE0); /* return buffer size */ + + if (cli_api(cli, + param, PTR_DIFF(p,param),PTR_DIFF(p,param), + NULL, 0, CLI_BUFFER_SIZE, + &rparam, &rprcnt, + &rdata, &rdrcnt)) { + res = GETRES(rparam); + cli->rap_error = res; + if (res != 0) { + DEBUG(1,("NetUserGetGroups gave error %d\n", res)); + } + } + if (rdata) { + if (res == 0 || res == ERRmoredata) { + int i, converter, count; + fstring groupname; + p = rparam +WORDSIZE; + GETWORD(p, converter); + GETWORD(p, count); + + for (i=0,p=rdata; i<count; i++) { + GETSTRINGF(p, groupname, RAP_USERNAME_LEN); + fn(groupname, state); + } + } else { + DEBUG(4,("NetUserGetGroups res=%d\n", res)); + } + } else { + DEBUG(4,("NetUserGetGroups no data returned\n")); + } + SAFE_FREE(rdata); + SAFE_FREE(rparam); + return res; +} + + +/**************************************************************************** + call a NetUserDelete - delete user from remote server +****************************************************************************/ +int cli_NetUserDelete(struct cli_state *cli, const char * user_name ) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + int rdrcnt,rprcnt, res; + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetGroupDel_REQ) /* parm string */ + +1 /* no ret string */ + +RAP_USERNAME_LEN /* user to del */ + +WORDSIZE]; /* reserved word */ + + /* now send a SMBtrans command with api UserDel */ + p = make_header(param, RAP_WUserDel, RAP_NetGroupDel_REQ, NULL); + PUTSTRING(p, user_name, RAP_USERNAME_LEN); + PUTWORD(p,0); /* reserved word MBZ on input */ + + if (cli_api(cli, + param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */ + NULL, 0, 200, /* data, length, maxlen */ + &rparam, &rprcnt, /* return params, length */ + &rdata, &rdrcnt)) /* return data, length */ + { + res = GETRES(rparam); + + if (res == 0) { + /* nothing to do */ + } + else if ((res == 5) || (res == 65)) { + DEBUG(1, ("Access Denied\n")); + } + else if (res == 2221) { + DEBUG (1, ("User does not exist\n")); + } + else { + DEBUG(4,("NetUserDelete res=%d\n", res)); + } + } else { + res = -1; + DEBUG(4,("NetUserDelete failed\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +/**************************************************************************** + call a NetUserAdd - add user to remote server +****************************************************************************/ +int cli_NetUserAdd(struct cli_state *cli, RAP_USER_INFO_1 * userinfo ) +{ + + + + char *rparam = NULL; + char *rdata = NULL; + char *p; + int rdrcnt,rprcnt,res; + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetUserAdd2_REQ) /* req string */ + +sizeof(RAP_USER_INFO_L1) /* data string */ + +WORDSIZE /* info level */ + +WORDSIZE /* buffer length */ + +WORDSIZE]; /* reserved */ + + char data[1024]; + /* offset into data of free format strings. Will be updated */ + /* by PUTSTRINGP macro and end up with total data length. */ + int soffset=RAP_USERNAME_LEN+1 /* user name + pad */ + + RAP_UPASSWD_LEN /* password */ + + DWORDSIZE /* password age */ + + WORDSIZE /* privilege */ + + DWORDSIZE /* home dir ptr */ + + DWORDSIZE /* comment ptr */ + + WORDSIZE /* flags */ + + DWORDSIZE; /* login script ptr*/ + + /* now send a SMBtrans command with api NetUserAdd */ + p = make_header(param, RAP_WUserAdd2, + RAP_NetUserAdd2_REQ, RAP_USER_INFO_L1); + PUTWORD(p, 1); /* info level */ + + PUTWORD(p, 0); /* pwencrypt */ + if(userinfo->passwrd) + PUTWORD(p,MIN(strlen(userinfo->passwrd), RAP_UPASSWD_LEN)); + else + PUTWORD(p, 0); /* password length */ + + p = data; + memset(data, '\0', soffset); + + PUTSTRINGF(p, userinfo->user_name, RAP_USERNAME_LEN); + PUTBYTE(p, 0); /* pad byte 0 */ + PUTSTRINGF(p, userinfo->passwrd, RAP_UPASSWD_LEN); + PUTDWORD(p, 0); /* pw age - n.a. on user add */ + PUTWORD(p, userinfo->priv); + PUTSTRINGP(p, userinfo->home_dir, data, soffset); + PUTSTRINGP(p, userinfo->comment, data, soffset); + PUTWORD(p, userinfo->userflags); + PUTSTRINGP(p, userinfo->logon_script, data, soffset); + + if (cli_api(cli, + param, sizeof(param), 1024, /* Param, length, maxlen */ + data, soffset, sizeof(data), /* data, length, maxlen */ + &rparam, &rprcnt, /* return params, length */ + &rdata, &rdrcnt)) /* return data, length */ + { + res = GETRES(rparam); + + if (res == 0) { + /* nothing to do */ + } + else if ((res == 5) || (res == 65)) { + DEBUG(1, ("Access Denied\n")); + } + else if (res == 2224) { + DEBUG (1, ("User already exists\n")); + } + else { + DEBUG(4,("NetUserAdd res=%d\n", res)); + } + } else { + res = -1; + DEBUG(4,("NetUserAdd failed\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +/**************************************************************************** +call a NetUserEnum - try and list users on a different host +****************************************************************************/ +int cli_RNetUserEnum(struct cli_state *cli, void (*fn)(const char *, const char *, const char *, const char *, void *), void *state) +{ + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetUserEnum_REQ) /* parm string */ + +sizeof(RAP_USER_INFO_L1) /* return string */ + +WORDSIZE /* info level */ + +WORDSIZE]; /* buffer size */ + char *p; + char *rparam = NULL; + char *rdata = NULL; + int rprcnt, rdrcnt; + int res = -1; + + + memset(param, '\0', sizeof(param)); + p = make_header(param, RAP_WUserEnum, + RAP_NetUserEnum_REQ, RAP_USER_INFO_L1); + PUTWORD(p,1); /* Info level 1 */ + PUTWORD(p,0xFF00); /* Return buffer size */ + +/* BB Fix handling of large numbers of users to be returned */ + if (cli_api(cli, + param, PTR_DIFF(p,param),8, + NULL, 0, CLI_BUFFER_SIZE, + &rparam, &rprcnt, + &rdata, &rdrcnt)) { + res = GETRES(rparam); + cli->rap_error = res; + if (cli->rap_error != 0) { + DEBUG(1,("NetUserEnum gave error %d\n", cli->rap_error)); + } + } + if (rdata) { + if (res == 0 || res == ERRmoredata) { + int i, converter, count; + char username[RAP_USERNAME_LEN]; + char userpw[RAP_UPASSWD_LEN]; + pstring comment, homedir, logonscript; + int pwage, priv, flags; + + p = rparam + WORDSIZE; /* skip result */ + GETWORD(p, converter); + GETWORD(p, count); + + for (i=0,p=rdata;i<count;i++) { + GETSTRINGF(p, username, RAP_USERNAME_LEN); + p++; /* pad byte */ + GETSTRINGF(p, userpw, RAP_UPASSWD_LEN); + GETDWORD(p, pwage); /* password age */ + GETWORD(p, priv); /* 0=guest, 1=user, 2=admin */ + GETSTRINGP(p, homedir, rdata, converter); + GETSTRINGP(p, comment, rdata, converter); + GETWORD(p, flags); + GETSTRINGP(p, logonscript, rdata, converter); + + fn(username, comment, homedir, logonscript, cli); + } + } else { + DEBUG(4,("NetUserEnum res=%d\n", res)); + } + } else { + DEBUG(4,("NetUserEnum no data returned\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +/**************************************************************************** + call a NetFileClose2 - close open file on another session to server +****************************************************************************/ +int cli_NetFileClose(struct cli_state *cli, uint32 file_id ) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + int rdrcnt,rprcnt; + char param[WORDSIZE /* api number */ + +sizeof(RAP_WFileClose2_REQ) /* req string */ + +1 /* no ret string */ + +DWORDSIZE]; /* file ID */ + int res = -1; + + /* now send a SMBtrans command with api RNetShareEnum */ + p = make_header(param, RAP_WFileClose2, RAP_WFileClose2_REQ, NULL); + PUTDWORD(p, file_id); + + if (cli_api(cli, + param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */ + NULL, 0, 200, /* data, length, maxlen */ + &rparam, &rprcnt, /* return params, length */ + &rdata, &rdrcnt)) /* return data, length */ + { + res = GETRES(rparam); + + if (res == 0) { + /* nothing to do */ + } else if (res == 2314){ + DEBUG(1, ("NetFileClose2 - attempt to close non-existant file open instance\n")); + } else { + DEBUG(4,("NetFileClose2 res=%d\n", res)); + } + } else { + res = -1; + DEBUG(4,("NetFileClose2 failed\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +/**************************************************************************** +call a NetFileGetInfo - get information about server file opened from other + workstation +****************************************************************************/ +int cli_NetFileGetInfo(struct cli_state *cli, uint32 file_id, void (*fn)(const char *, const char *, uint16, uint16, uint32)) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + int rdrcnt,rprcnt, res; + char param[WORDSIZE /* api number */ + +sizeof(RAP_WFileGetInfo2_REQ) /* req string */ + +sizeof(RAP_FILE_INFO_L3) /* return string */ + +DWORDSIZE /* file ID */ + +WORDSIZE /* info level */ + +WORDSIZE]; /* buffer size */ + + /* now send a SMBtrans command with api RNetShareEnum */ + p = make_header(param, RAP_WFileGetInfo2, + RAP_WFileGetInfo2_REQ, RAP_FILE_INFO_L3); + PUTDWORD(p, file_id); + PUTWORD(p, 3); /* info level */ + PUTWORD(p, 0x1000); /* buffer size */ + if (cli_api(cli, + param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */ + NULL, 0, 0x1000, /* data, length, maxlen */ + &rparam, &rprcnt, /* return params, length */ + &rdata, &rdrcnt)) /* return data, length */ + { + res = GETRES(rparam); + if (res == 0 || res == ERRmoredata) { + int converter,id, perms, locks; + pstring fpath, fuser; + + p = rparam + WORDSIZE; /* skip result */ + GETWORD(p, converter); + + p = rdata; + GETDWORD(p, id); + GETWORD(p, perms); + GETWORD(p, locks); + GETSTRINGP(p, fpath, rdata, converter); + GETSTRINGP(p, fuser, rdata, converter); + + fn(fpath, fuser, perms, locks, id); + } else { + DEBUG(4,("NetFileGetInfo2 res=%d\n", res)); + } + } else { + res = -1; + DEBUG(4,("NetFileGetInfo2 failed\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +/**************************************************************************** +* Call a NetFileEnum2 - list open files on an SMB server +* +* PURPOSE: Remotes a NetFileEnum API call to the current server or target +* server listing the files open via the network (and their +* corresponding open instance ids) +* +* Dependencies: none +* +* Parameters: +* cli - pointer to cli_state structure +* user - if present, return only files opened by this remote user +* base_path - if present, return only files opened below this +* base path +* fn - display function to invoke for each entry in the result +* +* +* Returns: +* True - success +* False - failure +* +****************************************************************************/ +int cli_NetFileEnum(struct cli_state *cli, char * user, char * base_path, void (*fn)(const char *, const char *, uint16, uint16, uint32)) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + int rdrcnt,rprcnt; + char param[WORDSIZE /* api number */ + +sizeof(RAP_WFileEnum2_REQ) /* req string */ + +sizeof(RAP_FILE_INFO_L3) /* return string */ + +256 /* base path (opt) */ + +RAP_USERNAME_LEN /* user name (opt) */ + +WORDSIZE /* info level */ + +WORDSIZE /* buffer size */ + +DWORDSIZE /* resume key ? */ + +DWORDSIZE]; /* resume key ? */ + int count = -1; + + /* now send a SMBtrans command with api RNetShareEnum */ + p = make_header(param, RAP_WFileEnum2, + RAP_WFileEnum2_REQ, RAP_FILE_INFO_L3); + + PUTSTRING(p, base_path, 256); + PUTSTRING(p, user, RAP_USERNAME_LEN); + PUTWORD(p, 3); /* info level */ + PUTWORD(p, 0xFF00); /* buffer size */ + PUTDWORD(p, 0); /* zero out the resume key */ + PUTDWORD(p, 0); /* or is this one the resume key? */ + + if (cli_api(cli, + param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */ + NULL, 0, 0xFF00, /* data, length, maxlen */ + &rparam, &rprcnt, /* return params, length */ + &rdata, &rdrcnt)) /* return data, length */ + { + int res = GETRES(rparam); + + if (res == 0 || res == ERRmoredata) { + int converter, i; + + p = rparam + WORDSIZE; /* skip result */ + GETWORD(p, converter); + GETWORD(p, count); + + p = rdata; + for (i=0; i<count; i++) { + int id, perms, locks; + pstring fpath, fuser; + + GETDWORD(p, id); + GETWORD(p, perms); + GETWORD(p, locks); + GETSTRINGP(p, fpath, rdata, converter); + GETSTRINGP(p, fuser, rdata, converter); + + fn(fpath, fuser, perms, locks, id); + } /* BB fix ERRmoredata case to send resume request */ + } else { + DEBUG(4,("NetFileEnum2 res=%d\n", res)); + } + } else { + DEBUG(4,("NetFileEnum2 failed\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return count; +} + +/**************************************************************************** + call a NetShareAdd - share/export directory on remote server +****************************************************************************/ +int cli_NetShareAdd(struct cli_state *cli, RAP_SHARE_INFO_2 * sinfo ) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + int rdrcnt,rprcnt,res; + char param[WORDSIZE /* api number */ + +sizeof(RAP_WShareAdd_REQ) /* req string */ + +sizeof(RAP_SHARE_INFO_L2) /* return string */ + +WORDSIZE /* info level */ + +WORDSIZE]; /* reserved word */ + char data[1024]; + /* offset to free format string section following fixed length data. */ + /* will be updated by PUTSTRINGP macro and will end up with total len */ + int soffset = RAP_SHARENAME_LEN + 1 /* share name + pad */ + + WORDSIZE /* share type */ + + DWORDSIZE /* comment pointer */ + + WORDSIZE /* permissions */ + + WORDSIZE /* max users */ + + WORDSIZE /* active users */ + + DWORDSIZE /* share path */ + + RAP_SPASSWD_LEN + 1; /* share password + pad */ + + memset(param,'\0',sizeof(param)); + /* now send a SMBtrans command with api RNetShareAdd */ + p = make_header(param, RAP_WshareAdd, + RAP_WShareAdd_REQ, RAP_SHARE_INFO_L2); + PUTWORD(p, 2); /* info level */ + PUTWORD(p, 0); /* reserved word 0 */ + + p = data; + PUTSTRINGF(p, sinfo->share_name, RAP_SHARENAME_LEN); + PUTBYTE(p, 0); /* pad byte 0 */ + + PUTWORD(p, sinfo->share_type); + PUTSTRINGP(p, sinfo->comment, data, soffset); + PUTWORD(p, sinfo->perms); + PUTWORD(p, sinfo->maximum_users); + PUTWORD(p, sinfo->active_users); + PUTSTRINGP(p, sinfo->path, data, soffset); + PUTSTRINGF(p, sinfo->password, RAP_SPASSWD_LEN); + SCVAL(p,-1,0x0A); /* required 0x0A at end of password */ + + if (cli_api(cli, + param, sizeof(param), 1024, /* Param, length, maxlen */ + data, soffset, sizeof(data), /* data, length, maxlen */ + &rparam, &rprcnt, /* return params, length */ + &rdata, &rdrcnt)) /* return data, length */ + { + res = rparam? SVAL(rparam,0) : -1; + + if (res == 0) { + /* nothing to do */ + } + else { + DEBUG(4,("NetShareAdd res=%d\n", res)); + } + } else { + res = -1; + DEBUG(4,("NetShareAdd failed\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} +/**************************************************************************** + call a NetShareDelete - unshare exported directory on remote server +****************************************************************************/ +int cli_NetShareDelete(struct cli_state *cli, const char * share_name ) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + int rdrcnt,rprcnt, res; + char param[WORDSIZE /* api number */ + +sizeof(RAP_WShareDel_REQ) /* req string */ + +1 /* no ret string */ + +RAP_SHARENAME_LEN /* share to del */ + +WORDSIZE]; /* reserved word */ + + + /* now send a SMBtrans command with api RNetShareDelete */ + p = make_header(param, RAP_WshareDel, RAP_WShareDel_REQ, NULL); + PUTSTRING(p,share_name,RAP_SHARENAME_LEN); + PUTWORD(p,0); /* reserved word MBZ on input */ + + if (cli_api(cli, + param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */ + NULL, 0, 200, /* data, length, maxlen */ + &rparam, &rprcnt, /* return params, length */ + &rdata, &rdrcnt)) /* return data, length */ + { + res = GETRES(rparam); + + if (res == 0) { + /* nothing to do */ + } + else { + DEBUG(4,("NetShareDelete res=%d\n", res)); + } + } else { + res = -1; + DEBUG(4,("NetShareDelete failed\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} +/************************************************************************* +* +* Function Name: cli_get_pdc_name +* +* PURPOSE: Remotes a NetServerEnum API call to the current server +* requesting the name of a server matching the server +* type of SV_TYPE_DOMAIN_CTRL (PDC). +* +* Dependencies: none +* +* Parameters: +* cli - pointer to cli_state structure +* workgroup - pointer to string containing name of domain +* pdc_name - pointer to string that will contain PDC name +* on successful return +* +* Returns: +* True - success +* False - failure +* +************************************************************************/ +BOOL cli_get_pdc_name(struct cli_state *cli, char *workgroup, char *pdc_name) +{ + char *rparam = NULL; + char *rdata = NULL; + int rdrcnt,rprcnt; + char *p; + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetServerEnum2_REQ) /* req string */ + +sizeof(RAP_SERVER_INFO_L1) /* return string */ + +WORDSIZE /* info level */ + +WORDSIZE /* buffer size */ + +DWORDSIZE /* server type */ + +RAP_MACHNAME_LEN]; /* workgroup */ + int count = -1; + + *pdc_name = '\0'; + + /* send a SMBtrans command with api NetServerEnum */ + p = make_header(param, RAP_NetServerEnum2, + RAP_NetServerEnum2_REQ, RAP_SERVER_INFO_L1); + PUTWORD(p, 1); /* info level */ + PUTWORD(p, CLI_BUFFER_SIZE); + PUTDWORD(p, SV_TYPE_DOMAIN_CTRL); + PUTSTRING(p, workgroup, RAP_MACHNAME_LEN); + + if (cli_api(cli, + param, PTR_DIFF(p,param), 8, /* params, length, max */ + NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */ + &rparam, &rprcnt, /* return params, return size */ + &rdata, &rdrcnt /* return data, return size */ + )) { + cli->rap_error = GETRES(rparam); + + /* + * We only really care to copy a name if the + * API succeeded and we got back a name. + */ + if (cli->rap_error == 0) { + p = rparam + WORDSIZE + WORDSIZE; /* skip result and converter */ + GETWORD(p, count); + p = rdata; + + if (count > 0) + GETSTRING(p, pdc_name); + } + else { + DEBUG(4,("cli_get_pdc_name: machine %s failed the NetServerEnum call. " + "Error was : %s.\n", cli->desthost, cli_errstr(cli) )); + } + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return(count > 0); +} + + +/************************************************************************* +* +* Function Name: cli_get_server_domain +* +* PURPOSE: Remotes a NetWkstaGetInfo API call to the current server +* requesting wksta_info_10 level information to determine +* the domain the server belongs to. On success, this +* routine sets the server_domain field in the cli_state structure +* to the server's domain name. +* +* Dependencies: none +* +* Parameters: +* cli - pointer to cli_state structure +* +* Returns: +* True - success +* False - failure +* +* Origins: samba 2.0.6 source/libsmb/clientgen.c cli_NetServerEnum() +* +************************************************************************/ +BOOL cli_get_server_domain(struct cli_state *cli) +{ + char *rparam = NULL; + char *rdata = NULL; + int rdrcnt,rprcnt; + char *p; + char param[WORDSIZE /* api number */ + +sizeof(RAP_WWkstaGetInfo_REQ) /* req string */ + +sizeof(RAP_WKSTA_INFO_L10) /* return string */ + +WORDSIZE /* info level */ + +WORDSIZE]; /* buffer size */ + int res = -1; + + /* send a SMBtrans command with api NetWkstaGetInfo */ + p = make_header(param, RAP_WWkstaGetInfo, + RAP_WWkstaGetInfo_REQ, RAP_WKSTA_INFO_L10); + PUTWORD(p, 10); /* info level */ + PUTWORD(p, CLI_BUFFER_SIZE); + + if (cli_api(cli, param, PTR_DIFF(p,param), 8, /* params, length, max */ + NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */ + &rparam, &rprcnt, /* return params, return size */ + &rdata, &rdrcnt)) { /* return data, return size */ + res = GETRES(rparam); + p = rdata; + + if (res == 0) { + int converter; + + p = rparam + WORDSIZE; + GETWORD(p, converter); + + p = rdata + DWORDSIZE + DWORDSIZE; /* skip computer & user names */ + GETSTRINGP(p, cli->server_domain, rdata, converter); + } + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return(res == 0); +} + + +/************************************************************************* +* +* Function Name: cli_get_server_type +* +* PURPOSE: Remotes a NetServerGetInfo API call to the current server +* requesting server_info_1 level information to retrieve +* the server type. +* +* Dependencies: none +* +* Parameters: +* cli - pointer to cli_state structure +* pstype - pointer to uint32 to contain returned server type +* +* Returns: +* True - success +* False - failure +* +* Origins: samba 2.0.6 source/libsmb/clientgen.c cli_NetServerEnum() +* +************************************************************************/ +BOOL cli_get_server_type(struct cli_state *cli, uint32 *pstype) +{ + char *rparam = NULL; + char *rdata = NULL; + int rdrcnt,rprcnt; + char *p; + char param[WORDSIZE /* api number */ + +sizeof(RAP_WserverGetInfo_REQ) /* req string */ + +sizeof(RAP_SERVER_INFO_L1) /* return string */ + +WORDSIZE /* info level */ + +WORDSIZE]; /* buffer size */ + int res = -1; + + /* send a SMBtrans command with api NetServerGetInfo */ + p = make_header(param, RAP_WserverGetInfo, + RAP_WserverGetInfo_REQ, RAP_SERVER_INFO_L1); + PUTWORD(p, 1); /* info level */ + PUTWORD(p, CLI_BUFFER_SIZE); + + if (cli_api(cli, + param, PTR_DIFF(p,param), 8, /* params, length, max */ + NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */ + &rparam, &rprcnt, /* return params, return size */ + &rdata, &rdrcnt /* return data, return size */ + )) { + + res = GETRES(rparam); + + if (res == 0 || res == ERRmoredata) { + p = rdata; + *pstype = IVAL(p,18) & ~SV_TYPE_LOCAL_LIST_ONLY; + } + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return(res == 0 || res == ERRmoredata); +} + + +/************************************************************************* +* +* Function Name: cli_ns_check_server_type +* +* PURPOSE: Remotes a NetServerEnum2 API call to the current server +* requesting server_info_0 level information of machines +* matching the given server type. If the returned server +* list contains the machine name contained in cli->desthost +* then we conclude the server type checks out. This routine +* is useful to retrieve list of server's of a certain +* type when all you have is a null session connection and +* can't remote API calls such as NetWkstaGetInfo or +* NetServerGetInfo. +* +* Dependencies: none +* +* Parameters: +* cli - pointer to cli_state structure +* workgroup - pointer to string containing domain +* stype - server type +* +* Returns: +* True - success +* False - failure +* +************************************************************************/ +BOOL cli_ns_check_server_type(struct cli_state *cli, char *workgroup, uint32 stype) +{ + char *rparam = NULL; + char *rdata = NULL; + int rdrcnt,rprcnt; + char *p; + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetServerEnum2_REQ) /* req string */ + +sizeof(RAP_SERVER_INFO_L0) /* return string */ + +WORDSIZE /* info level */ + +WORDSIZE /* buffer size */ + +DWORDSIZE /* server type */ + +RAP_MACHNAME_LEN]; /* workgroup */ + BOOL found_server = False; + int res = -1; + + /* send a SMBtrans command with api NetServerEnum */ + p = make_header(param, RAP_NetServerEnum2, + RAP_NetServerEnum2_REQ, RAP_SERVER_INFO_L0); + PUTWORD(p, 0); /* info level 0 */ + PUTWORD(p, CLI_BUFFER_SIZE); + PUTDWORD(p, stype); + PUTSTRING(p, workgroup, RAP_MACHNAME_LEN); + + if (cli_api(cli, + param, PTR_DIFF(p,param), 8, /* params, length, max */ + NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */ + &rparam, &rprcnt, /* return params, return size */ + &rdata, &rdrcnt /* return data, return size */ + )) { + + res = GETRES(rparam); + cli->rap_error = res; + + if (res == 0 || res == ERRmoredata) { + int i, converter, count; + + p = rparam + WORDSIZE; + GETWORD(p, converter); + GETWORD(p, count); + + p = rdata; + for (i = 0;i < count;i++, p += 16) { + char ret_server[RAP_MACHNAME_LEN]; + + GETSTRINGF(p, ret_server, RAP_MACHNAME_LEN); + if (strequal(ret_server, cli->desthost)) { + found_server = True; + break; + } + } + } + else { + DEBUG(4,("cli_ns_check_server_type: machine %s failed the NetServerEnum call. " + "Error was : %s.\n", cli->desthost, cli_errstr(cli) )); + } + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return found_server; + } + + +/**************************************************************************** + perform a NetWkstaUserLogoff +****************************************************************************/ +BOOL cli_NetWkstaUserLogoff(struct cli_state *cli,char *user, char *workstation) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + int rdrcnt,rprcnt; + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetWkstaUserLogoff_REQ) /* req string */ + +sizeof(RAP_USER_LOGOFF_INFO_L1) /* return string */ + +RAP_USERNAME_LEN+1 /* user name+pad */ + +RAP_MACHNAME_LEN /* wksta name */ + +WORDSIZE /* buffer size */ + +WORDSIZE]; /* buffer size? */ + fstring upperbuf; + + memset(param, 0, sizeof(param)); + + /* send a SMBtrans command with api NetWkstaUserLogoff */ + p = make_header(param, RAP_WWkstaUserLogoff, + RAP_NetWkstaUserLogoff_REQ, RAP_USER_LOGOFF_INFO_L1); + PUTDWORD(p, 0); /* Null pointer */ + PUTDWORD(p, 0); /* Null pointer */ + fstrcpy(upperbuf, user); + strupper(upperbuf); + PUTSTRINGF(p, upperbuf, RAP_USERNAME_LEN); + p++; /* strange format, but ok */ + fstrcpy(upperbuf, workstation); + strupper(upperbuf); + PUTSTRINGF(p, upperbuf, RAP_MACHNAME_LEN); + PUTWORD(p, CLI_BUFFER_SIZE); + PUTWORD(p, CLI_BUFFER_SIZE); + + if (cli_api(cli, + param, PTR_DIFF(p,param),1024, /* param, length, max */ + NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */ + &rparam, &rprcnt, /* return params, return size */ + &rdata, &rdrcnt /* return data, return size */ + )) { + cli->rap_error = GETRES(rparam); + + if (cli->rap_error != 0) { + DEBUG(4,("NetwkstaUserLogoff gave error %d\n", cli->rap_error)); + } + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + return (cli->rap_error == 0); +} + +int cli_NetPrintQEnum(struct cli_state *cli, + void (*qfn)(const char*,uint16,uint16,uint16,const char*,const char*,const char*,const char*,const char*,uint16,uint16), + void (*jfn)(uint16,const char*,const char*,const char*,const char*,uint16,uint16,const char*,uint,uint,const char*)) +{ + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetPrintQEnum_REQ) /* req string */ + +sizeof(RAP_PRINTQ_INFO_L2) /* return string */ + +WORDSIZE /* info level */ + +WORDSIZE /* buffer size */ + +sizeof(RAP_SMB_PRINT_JOB_L1)]; /* more ret data */ + char *p; + char *rparam = NULL; + char *rdata = NULL; + int rprcnt, rdrcnt; + int res = -1; + + + memset(param, '\0',sizeof(param)); + p = make_header(param, RAP_WPrintQEnum, + RAP_NetPrintQEnum_REQ, RAP_PRINTQ_INFO_L2); + PUTWORD(p,2); /* Info level 2 */ + PUTWORD(p,0xFFE0); /* Return buffer size */ + PUTSTRING(p, RAP_SMB_PRINT_JOB_L1, 0); + + if (cli_api(cli, + param, PTR_DIFF(p,param),1024, + NULL, 0, CLI_BUFFER_SIZE, + &rparam, &rprcnt, + &rdata, &rdrcnt)) { + res = GETRES(rparam); + cli->rap_error = res; + if (res != 0) { + DEBUG(1,("NetPrintQEnum gave error %d\n", res)); + } + } + + if (rdata) { + if (res == 0 || res == ERRmoredata) { + int i, converter, count; + + p = rparam + WORDSIZE; + GETWORD(p, converter); + GETWORD(p, count); + + p = rdata; + for (i=0;i<count;i++) { + pstring qname, sep_file, print_proc, dest, parms, comment; + uint16 jobcount, priority, start_time, until_time, status; + + GETSTRINGF(p, qname, RAP_SHARENAME_LEN); + p++; /* pad */ + GETWORD(p, priority); + GETWORD(p, start_time); + GETWORD(p, until_time); + GETSTRINGP(p, sep_file, rdata, converter); + GETSTRINGP(p, print_proc, rdata, converter); + GETSTRINGP(p, dest, rdata, converter); + GETSTRINGP(p, parms, rdata, converter); + GETSTRINGP(p, parms, comment, converter); + GETWORD(p, status); + GETWORD(p, jobcount); + + qfn(qname, priority, start_time, until_time, sep_file, print_proc, + dest, parms, comment, status, jobcount); + + if (jobcount) { + int j; + for (j=0;j<jobcount;j++) { + uint16 jid, pos, fsstatus; + pstring ownername, notifyname, datatype, jparms, jstatus, jcomment; + uint submitted, jsize; + + GETWORD(p, jid); + GETSTRINGF(p, ownername, RAP_USERNAME_LEN); + p++; /* pad byte */ + GETSTRINGF(p, notifyname, RAP_MACHNAME_LEN); + GETSTRINGF(p, datatype, RAP_DATATYPE_LEN); + GETSTRINGP(p, jparms, rdata, converter); + GETWORD(p, pos); + GETWORD(p, fsstatus); + GETSTRINGP(p, jstatus, rdata, converter); + GETDWORD(p, submitted); + GETDWORD(p, jsize); + GETSTRINGP(p, jcomment, rdata, converter); + + jfn(jid, ownername, notifyname, datatype, jparms, pos, fsstatus, + jstatus, submitted, jsize, jcomment); + } + } + } + } else { + DEBUG(4,("NetPrintQEnum res=%d\n", res)); + } + } else { + DEBUG(4,("NetPrintQEnum no data returned\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +int cli_NetPrintQGetInfo(struct cli_state *cli, const char *printer, + void (*qfn)(const char*,uint16,uint16,uint16,const char*,const char*,const char*,const char*,const char*,uint16,uint16), + void (*jfn)(uint16,const char*,const char*,const char*,const char*,uint16,uint16,const char*,uint,uint,const char*)) +{ + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetPrintQGetInfo_REQ) /* req string */ + +sizeof(RAP_PRINTQ_INFO_L2) /* return string */ + +RAP_SHARENAME_LEN /* printer name */ + +WORDSIZE /* info level */ + +WORDSIZE /* buffer size */ + +sizeof(RAP_SMB_PRINT_JOB_L1)]; /* more ret data */ + char *p; + char *rparam = NULL; + char *rdata = NULL; + int rprcnt, rdrcnt; + int res = -1; + + + memset(param, '\0',sizeof(param)); + p = make_header(param, RAP_WPrintQGetInfo, + RAP_NetPrintQGetInfo_REQ, RAP_PRINTQ_INFO_L2); + PUTSTRING(p, printer, RAP_SHARENAME_LEN-1); + PUTWORD(p, 2); /* Info level 2 */ + PUTWORD(p,0xFFE0); /* Return buffer size */ + PUTSTRING(p, RAP_SMB_PRINT_JOB_L1, 0); + + if (cli_api(cli, + param, PTR_DIFF(p,param),1024, + NULL, 0, CLI_BUFFER_SIZE, + &rparam, &rprcnt, + &rdata, &rdrcnt)) { + res = GETRES(rparam); + cli->rap_error = res; + if (res != 0) { + DEBUG(1,("NetPrintQGetInfo gave error %d\n", res)); + } + } + + if (rdata) { + if (res == 0 || res == ERRmoredata) { + int rsize, converter; + pstring qname, sep_file, print_proc, dest, parms, comment; + uint16 jobcount, priority, start_time, until_time, status; + + p = rparam + WORDSIZE; + GETWORD(p, converter); + GETWORD(p, rsize); + + p = rdata; + GETSTRINGF(p, qname, RAP_SHARENAME_LEN); + p++; /* pad */ + GETWORD(p, priority); + GETWORD(p, start_time); + GETWORD(p, until_time); + GETSTRINGP(p, sep_file, rdata, converter); + GETSTRINGP(p, print_proc, rdata, converter); + GETSTRINGP(p, dest, rdata, converter); + GETSTRINGP(p, parms, rdata, converter); + GETSTRINGP(p, comment, rdata, converter); + GETWORD(p, status); + GETWORD(p, jobcount); + qfn(qname, priority, start_time, until_time, sep_file, print_proc, + dest, parms, comment, status, jobcount); + if (jobcount) { + int j; + for (j=0;(j<jobcount)&&(PTR_DIFF(p,rdata)< rsize);j++) { + uint16 jid, pos, fsstatus; + pstring ownername, notifyname, datatype, jparms, jstatus, jcomment; + uint submitted, jsize; + + GETWORD(p, jid); + GETSTRINGF(p, ownername, RAP_USERNAME_LEN); + p++; /* pad byte */ + GETSTRINGF(p, notifyname, RAP_MACHNAME_LEN); + GETSTRINGF(p, datatype, RAP_DATATYPE_LEN); + GETSTRINGP(p, jparms, rdata, converter); + GETWORD(p, pos); + GETWORD(p, fsstatus); + GETSTRINGP(p, jstatus, rdata, converter); + GETDWORD(p, submitted); + GETDWORD(p, jsize); + GETSTRINGP(p, jcomment, rdata, converter); + + jfn(jid, ownername, notifyname, datatype, jparms, pos, fsstatus, + jstatus, submitted, jsize, jcomment); + } + } + } else { + DEBUG(4,("NetPrintQGetInfo res=%d\n", res)); + } + } else { + DEBUG(4,("NetPrintQGetInfo no data returned\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +/**************************************************************************** +call a NetServiceEnum - list running services on a different host +****************************************************************************/ +int cli_RNetServiceEnum(struct cli_state *cli, void (*fn)(const char *, const char *, void *), void *state) +{ + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetServiceEnum_REQ) /* parm string */ + +sizeof(RAP_SERVICE_INFO_L2) /* return string */ + +WORDSIZE /* info level */ + +WORDSIZE]; /* buffer size */ + char *p; + char *rparam = NULL; + char *rdata = NULL; + int rprcnt, rdrcnt; + int res = -1; + + + memset(param, '\0', sizeof(param)); + p = make_header(param, RAP_WServiceEnum, + RAP_NetServiceEnum_REQ, RAP_SERVICE_INFO_L2); + PUTWORD(p,2); /* Info level 2 */ + PUTWORD(p,0xFFE0); /* Return buffer size */ + + if (cli_api(cli, + param, PTR_DIFF(p,param),8, + NULL, 0, 0xFFE0 /* data area size */, + &rparam, &rprcnt, + &rdata, &rdrcnt)) { + res = GETRES(rparam); + cli->rap_error = res; + if(cli->rap_error == 234) + DEBUG(1,("Not all service names were returned (such as those longer than 15 characters)\n")); + else if (cli->rap_error != 0) { + DEBUG(1,("NetServiceEnum gave error %d\n", cli->rap_error)); + } + } + + if (rdata) { + if (res == 0 || res == ERRmoredata) { + int i, converter, count; + + p = rparam + WORDSIZE; /* skip result */ + GETWORD(p, converter); + GETWORD(p, count); + + for (i=0,p=rdata;i<count;i++) { + pstring comment; + char servicename[RAP_SRVCNAME_LEN]; + + GETSTRINGF(p, servicename, RAP_SRVCNAME_LEN); + p+=8; /* pass status words */ + GETSTRINGF(p, comment, RAP_SRVCCMNT_LEN); + + fn(servicename, comment, cli); /* BB add status too */ + } + } else { + DEBUG(4,("NetServiceEnum res=%d\n", res)); + } + } else { + DEBUG(4,("NetServiceEnum no data returned\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + + +/**************************************************************************** +call a NetSessionEnum - list workstations with sessions to an SMB server +****************************************************************************/ +int cli_NetSessionEnum(struct cli_state *cli, void (*fn)(char *, char *, uint16, uint16, uint16, uint, uint, uint, char *)) +{ + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetSessionEnum_REQ) /* parm string */ + +sizeof(RAP_SESSION_INFO_L2) /* return string */ + +WORDSIZE /* info level */ + +WORDSIZE]; /* buffer size */ + char *p; + char *rparam = NULL; + char *rdata = NULL; + int rprcnt, rdrcnt; + int res = -1; + + memset(param, '\0', sizeof(param)); + p = make_header(param, RAP_WsessionEnum, + RAP_NetSessionEnum_REQ, RAP_SESSION_INFO_L2); + PUTWORD(p,2); /* Info level 2 */ + PUTWORD(p,0xFF); /* Return buffer size */ + + if (cli_api(cli, + param, PTR_DIFF(p,param),8, + NULL, 0, CLI_BUFFER_SIZE, + &rparam, &rprcnt, + &rdata, &rdrcnt)) { + res = GETRES(rparam); + cli->rap_error = res; + if (res != 0) { + DEBUG(1,("NetSessionEnum gave error %d\n", res)); + } + } + + if (rdata) { + if (res == 0 || res == ERRmoredata) { + int i, converter, count; + + p = rparam + WORDSIZE; + GETWORD(p, converter); + GETWORD(p, count); + + for (i=0,p=rdata;i<count;i++) { + pstring wsname, username, clitype_name; + uint16 num_conns, num_opens, num_users; + uint sess_time, idle_time, user_flags; + + GETSTRINGP(p, wsname, rdata, converter); + GETSTRINGP(p, username, rdata, converter); + GETWORD(p, num_conns); + GETWORD(p, num_opens); + GETWORD(p, num_users); + GETDWORD(p, sess_time); + GETDWORD(p, idle_time); + GETDWORD(p, user_flags); + GETSTRINGP(p, clitype_name, rdata, converter); + + fn(wsname, username, num_conns, num_opens, num_users, sess_time, + idle_time, user_flags, clitype_name); + } + + } else { + DEBUG(4,("NetSessionEnum res=%d\n", res)); + } + } else { + DEBUG(4,("NetSesssionEnum no data returned\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +/**************************************************************************** + Call a NetSessionGetInfo - get information about other session to an SMB server. +****************************************************************************/ + +int cli_NetSessionGetInfo(struct cli_state *cli, const char *workstation, void (*fn)(const char *, const char *, uint16, uint16, uint16, uint, uint, uint, const char *)) +{ + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetSessionGetInfo_REQ) /* req string */ + +sizeof(RAP_SESSION_INFO_L2) /* return string */ + +RAP_MACHNAME_LEN /* wksta name */ + +WORDSIZE /* info level */ + +WORDSIZE]; /* buffer size */ + char *p; + char *rparam = NULL; + char *rdata = NULL; + int rprcnt, rdrcnt; + int res = -1; + + + memset(param, '\0', sizeof(param)); + p = make_header(param, RAP_WsessionGetInfo, + RAP_NetSessionGetInfo_REQ, RAP_SESSION_INFO_L2); + PUTSTRING(p, workstation, RAP_MACHNAME_LEN-1); + PUTWORD(p,2); /* Info level 2 */ + PUTWORD(p,0xFF); /* Return buffer size */ + + if (cli_api(cli, + param, PTR_DIFF(p,param),PTR_DIFF(p,param), + NULL, 0, CLI_BUFFER_SIZE, + &rparam, &rprcnt, + &rdata, &rdrcnt)) { + cli->rap_error = SVAL(rparam,0); + if (cli->rap_error != 0) { + DEBUG(1,("NetSessionGetInfo gave error %d\n", cli->rap_error)); + } + } + + if (rdata) { + res = GETRES(rparam); + + if (res == 0 || res == ERRmoredata) { + int rsize, converter; + pstring wsname, username, clitype_name; + uint16 num_conns, num_opens, num_users; + uint sess_time, idle_time, user_flags; + + p = rparam + WORDSIZE; + GETWORD(p, converter); + GETWORD(p, rsize); + + p = rdata; + GETSTRINGP(p, wsname, rdata, converter); + GETSTRINGP(p, username, rdata, converter); + GETWORD(p, num_conns); + GETWORD(p, num_opens); + GETWORD(p, num_users); + GETDWORD(p, sess_time); + GETDWORD(p, idle_time); + GETDWORD(p, user_flags); + GETSTRINGP(p, clitype_name, rdata, converter); + + fn(wsname, username, num_conns, num_opens, num_users, sess_time, + idle_time, user_flags, clitype_name); + } else { + DEBUG(4,("NetSessionGetInfo res=%d\n", res)); + } + } else { + DEBUG(4,("NetSessionGetInfo no data returned\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +/**************************************************************************** +call a NetSessionDel - close a session to an SMB server +****************************************************************************/ +int cli_NetSessionDel(struct cli_state *cli, const char *workstation) +{ + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetSessionDel_REQ) /* req string */ + +1 /* no return string */ + +RAP_MACHNAME_LEN /* workstation name */ + +WORDSIZE]; /* reserved (0) */ + char *p; + char *rparam = NULL; + char *rdata = NULL; + int rprcnt, rdrcnt; + int res; + + memset(param, '\0', sizeof(param)); + p = make_header(param, RAP_WsessionDel, RAP_NetSessionDel_REQ, NULL); + PUTSTRING(p, workstation, RAP_MACHNAME_LEN-1); + PUTWORD(p,0); /* reserved word of 0 */ + if (cli_api(cli, + param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */ + NULL, 0, 200, /* data, length, maxlen */ + &rparam, &rprcnt, /* return params, length */ + &rdata, &rdrcnt)) /* return data, length */ + { + res = GETRES(rparam); + cli->rap_error = res; + + if (res == 0) { + /* nothing to do */ + } + else { + DEBUG(4,("NetFileClose2 res=%d\n", res)); + } + } else { + res = -1; + DEBUG(4,("NetFileClose2 failed\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + + +int cli_NetConnectionEnum(struct cli_state *cli, const char *qualifier, void (*fn)(uint16 conid, uint16 contype, uint16 numopens, uint16 numusers, uint32 contime, const char *username, const char *netname)) +{ + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetConnectionEnum_REQ) /* req string */ + +sizeof(RAP_CONNECTION_INFO_L1) /* return string */ + +RAP_MACHNAME_LEN /* wksta name */ + +WORDSIZE /* info level */ + +WORDSIZE]; /* buffer size */ + char *p; + char *rparam = NULL; + char *rdata = NULL; + int rprcnt, rdrcnt; + int res = -1; + + memset(param, '\0', sizeof(param)); + p = make_header(param, RAP_WconnectionEnum, + RAP_NetConnectionEnum_REQ, RAP_CONNECTION_INFO_L1); + PUTSTRING(p, qualifier, RAP_MACHNAME_LEN-1);/* Workstation name */ + PUTWORD(p,1); /* Info level 1 */ + PUTWORD(p,0xFFE0); /* Return buffer size */ + + if (cli_api(cli, + param, PTR_DIFF(p,param),PTR_DIFF(p,param), + NULL, 0, CLI_BUFFER_SIZE, + &rparam, &rprcnt, + &rdata, &rdrcnt)) { + res = GETRES(rparam); + cli->rap_error = res; + if (res != 0) { + DEBUG(1,("NetConnectionEnum gave error %d\n", res)); + } + } + if (rdata) { + if (res == 0 || res == ERRmoredata) { + int i, converter, count; + + p = rparam + WORDSIZE; + GETWORD(p, converter); + GETWORD(p, count); + + for (i=0,p=rdata;i<count;i++) { + pstring netname, username; + uint16 conn_id, conn_type, num_opens, num_users; + uint conn_time; + + GETWORD(p,conn_id); + GETWORD(p,conn_type); + GETWORD(p,num_opens); + GETWORD(p,num_users); + GETDWORD(p,conn_time); + GETSTRINGP(p, username, rdata, converter); + GETSTRINGP(p, netname, rdata, converter); + + fn(conn_id, conn_type, num_opens, num_users, conn_time, + username, netname); + } + + } else { + DEBUG(4,("NetConnectionEnum res=%d\n", res)); + } + } else { + DEBUG(4,("NetConnectionEnum no data returned\n")); + } + SAFE_FREE(rdata); + SAFE_FREE(rparam); + return res; +} diff --git a/source3/libsmb/clireadwrite.c b/source3/libsmb/clireadwrite.c new file mode 100644 index 0000000000..0a9569fc69 --- /dev/null +++ b/source3/libsmb/clireadwrite.c @@ -0,0 +1,374 @@ +/* + Unix SMB/CIFS implementation. + client file read/write routines + Copyright (C) Andrew Tridgell 1994-1998 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#define NO_SYSLOG + +#include "includes.h" + +/**************************************************************************** +Issue a single SMBread and don't wait for a reply. +****************************************************************************/ + +static BOOL cli_issue_read(struct cli_state *cli, int fnum, off_t offset, + size_t size, int i) +{ + memset(cli->outbuf,'\0',smb_size); + memset(cli->inbuf,'\0',smb_size); + + set_message(cli->outbuf,10,0,True); + + SCVAL(cli->outbuf,smb_com,SMBreadX); + SSVAL(cli->outbuf,smb_tid,cli->cnum); + cli_setup_packet(cli); + + SCVAL(cli->outbuf,smb_vwv0,0xFF); + SSVAL(cli->outbuf,smb_vwv2,fnum); + SIVAL(cli->outbuf,smb_vwv3,offset); + SSVAL(cli->outbuf,smb_vwv5,size); + SSVAL(cli->outbuf,smb_vwv6,size); + SSVAL(cli->outbuf,smb_mid,cli->mid + i); + + return cli_send_smb(cli); +} + +/**************************************************************************** +Issue a single SMBreadraw and don't wait for a reply. +****************************************************************************/ + +static BOOL cli_issue_readraw(struct cli_state *cli, int fnum, off_t offset, + size_t size, int i) +{ + memset(cli->outbuf,'\0',smb_size); + memset(cli->inbuf,'\0',smb_size); + + set_message(cli->outbuf,10,0,True); + + SCVAL(cli->outbuf,smb_com,SMBreadbraw); + SSVAL(cli->outbuf,smb_tid,cli->cnum); + cli_setup_packet(cli); + + SSVAL(cli->outbuf,smb_vwv0,fnum); + SIVAL(cli->outbuf,smb_vwv1,offset); + SSVAL(cli->outbuf,smb_vwv2,size); + SSVAL(cli->outbuf,smb_vwv3,size); + SSVAL(cli->outbuf,smb_mid,cli->mid + i); + + return cli_send_smb(cli); +} + +/**************************************************************************** + Read size bytes at offset offset using SMBreadX. +****************************************************************************/ + +ssize_t cli_read(struct cli_state *cli, int fnum, char *buf, off_t offset, size_t size) +{ + char *p; + int size2; + int readsize; + ssize_t total = 0; + + if (size == 0) + return 0; + + /* + * Set readsize to the maximum size we can handle in one readX, + * rounded down to a multiple of 1024. + */ + + readsize = (cli->max_xmit - (smb_size+32)) & ~1023; + + while (total < size) { + readsize = MIN(readsize, size-total); + + /* Issue a read and receive a reply */ + + if (!cli_issue_read(cli, fnum, offset, readsize, 0)) + return -1; + + if (!cli_receive_smb(cli)) + return -1; + + /* Check for error. Make sure to check for DOS and NT + errors. */ + + if (cli_is_error(cli)) { + NTSTATUS status = NT_STATUS_OK; + uint8 eclass = 0; + uint32 ecode = 0; + + if (cli_is_nt_error(cli)) + status = cli_nt_error(cli); + else + cli_dos_error(cli, &eclass, &ecode); + + if ((eclass == ERRDOS && ecode == ERRmoredata) || + NT_STATUS_V(status) == NT_STATUS_V(STATUS_MORE_ENTRIES)) + return -1; + } + + size2 = SVAL(cli->inbuf, smb_vwv5); + + if (size2 > readsize) { + DEBUG(5,("server returned more than we wanted!\n")); + return -1; + } else if (size2 < 0) { + DEBUG(5,("read return < 0!\n")); + return -1; + } + + /* Copy data into buffer */ + + p = smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_vwv6); + memcpy(buf + total, p, size2); + + total += size2; + offset += size2; + + /* + * If the server returned less than we asked for we're at EOF. + */ + + if (size2 < readsize) + break; + } + + return total; +} + +/**************************************************************************** + Tester for the readraw call. +****************************************************************************/ + +ssize_t cli_readraw(struct cli_state *cli, int fnum, char *buf, off_t offset, size_t size) +{ + char *p; + int size2; + size_t readsize; + ssize_t total = 0; + + if (size == 0) + return 0; + + /* + * Set readsize to the maximum size we can handle in one readraw. + */ + + readsize = 0xFFFF; + + while (total < size) { + readsize = MIN(readsize, size-total); + + /* Issue a read and receive a reply */ + + if (!cli_issue_readraw(cli, fnum, offset, readsize, 0)) + return -1; + + if (!client_receive_smb(cli->fd, cli->inbuf, cli->timeout)) + return -1; + + size2 = smb_len(cli->inbuf); + + if (size2 > readsize) { + DEBUG(5,("server returned more than we wanted!\n")); + return -1; + } else if (size2 < 0) { + DEBUG(5,("read return < 0!\n")); + return -1; + } + + /* Copy data into buffer */ + + if (size2) { + p = cli->inbuf + 4; + memcpy(buf + total, p, size2); + } + + total += size2; + offset += size2; + + /* + * If the server returned less than we asked for we're at EOF. + */ + + if (size2 < readsize) + break; + } + + return total; +} + +/**************************************************************************** +issue a single SMBwrite and don't wait for a reply +****************************************************************************/ + +static BOOL cli_issue_write(struct cli_state *cli, int fnum, off_t offset, uint16 mode, char *buf, + size_t size, int i) +{ + char *p; + + if (size > cli->bufsize) { + cli->outbuf = realloc(cli->outbuf, size + 1024); + cli->inbuf = realloc(cli->inbuf, size + 1024); + if (cli->outbuf == NULL || cli->inbuf == NULL) + return False; + cli->bufsize = size + 1024; + } + + memset(cli->outbuf,'\0',smb_size); + memset(cli->inbuf,'\0',smb_size); + + if (size > 0xFFFF) + set_message(cli->outbuf,14,0,True); + else + set_message(cli->outbuf,12,0,True); + + SCVAL(cli->outbuf,smb_com,SMBwriteX); + SSVAL(cli->outbuf,smb_tid,cli->cnum); + cli_setup_packet(cli); + + SCVAL(cli->outbuf,smb_vwv0,0xFF); + SSVAL(cli->outbuf,smb_vwv2,fnum); + + SIVAL(cli->outbuf,smb_vwv3,offset); + SIVAL(cli->outbuf,smb_vwv5,(mode & 0x0008) ? 0xFFFFFFFF : 0); + SSVAL(cli->outbuf,smb_vwv7,mode); + + SSVAL(cli->outbuf,smb_vwv8,(mode & 0x0008) ? size : 0); + SSVAL(cli->outbuf,smb_vwv9,((size>>16)&1)); + SSVAL(cli->outbuf,smb_vwv10,size); + SSVAL(cli->outbuf,smb_vwv11, + smb_buf(cli->outbuf) - smb_base(cli->outbuf)); + + p = smb_base(cli->outbuf) + SVAL(cli->outbuf,smb_vwv11); + memcpy(p, buf, size); + cli_setup_bcc(cli, p+size); + + SSVAL(cli->outbuf,smb_mid,cli->mid + i); + + show_msg(cli->outbuf); + return cli_send_smb(cli); +} + +/**************************************************************************** + write to a file + write_mode: 0x0001 disallow write cacheing + 0x0002 return bytes remaining + 0x0004 use raw named pipe protocol + 0x0008 start of message mode named pipe protocol +****************************************************************************/ + +ssize_t cli_write(struct cli_state *cli, + int fnum, uint16 write_mode, + char *buf, off_t offset, size_t size) +{ + int bwritten = 0; + int issued = 0; + int received = 0; + int mpx = MAX(cli->max_mux-1, 1); + int block = (cli->max_xmit - (smb_size+32)) & ~1023; + int blocks = (size + (block-1)) / block; + + while (received < blocks) { + + while ((issued - received < mpx) && (issued < blocks)) { + int bsent = issued * block; + int size1 = MIN(block, size - bsent); + + if (!cli_issue_write(cli, fnum, offset + bsent, + write_mode, + buf + bsent, + size1, issued)) + return -1; + issued++; + } + + if (!cli_receive_smb(cli)) + return bwritten; + + received++; + + if (cli_is_error(cli)) + break; + + bwritten += SVAL(cli->inbuf, smb_vwv2); + bwritten += (((int)(SVAL(cli->inbuf, smb_vwv4)))>>16); + } + + while (received < issued && cli_receive_smb(cli)) + received++; + + return bwritten; +} + +/**************************************************************************** + write to a file using a SMBwrite and not bypassing 0 byte writes +****************************************************************************/ + +ssize_t cli_smbwrite(struct cli_state *cli, + int fnum, char *buf, off_t offset, size_t size1) +{ + char *p; + ssize_t total = 0; + + do { + size_t size = MIN(size1, cli->max_xmit - 48); + + memset(cli->outbuf,'\0',smb_size); + memset(cli->inbuf,'\0',smb_size); + + set_message(cli->outbuf,5, 0,True); + + SCVAL(cli->outbuf,smb_com,SMBwrite); + SSVAL(cli->outbuf,smb_tid,cli->cnum); + cli_setup_packet(cli); + + SSVAL(cli->outbuf,smb_vwv0,fnum); + SSVAL(cli->outbuf,smb_vwv1,size); + SIVAL(cli->outbuf,smb_vwv2,offset); + SSVAL(cli->outbuf,smb_vwv4,0); + + p = smb_buf(cli->outbuf); + *p++ = 1; + SSVAL(p, 0, size); p += 2; + memcpy(p, buf, size); p += size; + + cli_setup_bcc(cli, p); + + if (!cli_send_smb(cli)) + return -1; + + if (!cli_receive_smb(cli)) + return -1; + + if (cli_is_error(cli)) + return -1; + + size = SVAL(cli->inbuf,smb_vwv0); + if (size == 0) + break; + + size1 -= size; + total += size; + offset += size; + + } while (size1); + + return total; +} diff --git a/source3/libsmb/clisecdesc.c b/source3/libsmb/clisecdesc.c new file mode 100644 index 0000000000..5de67b1e05 --- /dev/null +++ b/source3/libsmb/clisecdesc.c @@ -0,0 +1,131 @@ +/* + Unix SMB/CIFS implementation. + client security descriptor functions + Copyright (C) Andrew Tridgell 2000 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/**************************************************************************** + query the security descriptor for a open file + ****************************************************************************/ +SEC_DESC *cli_query_secdesc(struct cli_state *cli, int fnum, + TALLOC_CTX *mem_ctx) +{ + char param[8]; + char *rparam=NULL, *rdata=NULL; + int rparam_count=0, rdata_count=0; + prs_struct pd; + SEC_DESC *psd = NULL; + + SIVAL(param, 0, fnum); + SSVAL(param, 4, 0x7); + + if (!cli_send_nt_trans(cli, + NT_TRANSACT_QUERY_SECURITY_DESC, + 0, + NULL, 0, 0, + param, 8, 4, + NULL, 0, 0x10000)) { + DEBUG(1,("Failed to send NT_TRANSACT_QUERY_SECURITY_DESC\n")); + goto cleanup; + } + + + if (!cli_receive_nt_trans(cli, + &rparam, &rparam_count, + &rdata, &rdata_count)) { + DEBUG(1,("Failed to recv NT_TRANSACT_QUERY_SECURITY_DESC\n")); + goto cleanup; + } + + prs_init(&pd, rdata_count, mem_ctx, UNMARSHALL); + prs_append_data(&pd, rdata, rdata_count); + pd.data_offset = 0; + + if (!sec_io_desc("sd data", &psd, &pd, 1)) { + DEBUG(1,("Failed to parse secdesc\n")); + goto cleanup; + } + + cleanup: + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + prs_mem_free(&pd); + return psd; +} + +/**************************************************************************** + set the security descriptor for a open file + ****************************************************************************/ +BOOL cli_set_secdesc(struct cli_state *cli, int fnum, SEC_DESC *sd) +{ + char param[8]; + char *rparam=NULL, *rdata=NULL; + int rparam_count=0, rdata_count=0; + TALLOC_CTX *mem_ctx; + prs_struct pd; + BOOL ret = False; + + if ((mem_ctx = talloc_init()) == NULL) { + DEBUG(0,("talloc_init failed.\n")); + goto cleanup; + } + + prs_init(&pd, 0, mem_ctx, MARSHALL); + prs_give_memory(&pd, NULL, 0, True); + + if (!sec_io_desc("sd data", &sd, &pd, 1)) { + DEBUG(1,("Failed to marshall secdesc\n")); + goto cleanup; + } + + SIVAL(param, 0, fnum); + SSVAL(param, 4, 0x7); + + if (!cli_send_nt_trans(cli, + NT_TRANSACT_SET_SECURITY_DESC, + 0, + NULL, 0, 0, + param, 8, 0, + pd.data_p, pd.data_offset, 0)) { + DEBUG(1,("Failed to send NT_TRANSACT_SET_SECURITY_DESC\n")); + goto cleanup; + } + + + if (!cli_receive_nt_trans(cli, + &rparam, &rparam_count, + &rdata, &rdata_count)) { + DEBUG(1,("NT_TRANSACT_SET_SECURITY_DESC failed\n")); + goto cleanup; + } + + ret = True; + + cleanup: + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + talloc_destroy(mem_ctx); + + prs_mem_free(&pd); + return ret; +} diff --git a/source3/libsmb/clispnego.c b/source3/libsmb/clispnego.c new file mode 100644 index 0000000000..a4fcfa5d9a --- /dev/null +++ b/source3/libsmb/clispnego.c @@ -0,0 +1,622 @@ +/* + Unix SMB/CIFS implementation. + simple kerberos5/SPNEGO routines + Copyright (C) Andrew Tridgell 2001 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/* + generate a negTokenInit packet given a GUID, a list of supported + OIDs (the mechanisms) and a principal name string +*/ +DATA_BLOB spnego_gen_negTokenInit(uint8 guid[16], + const char *OIDs[], + const char *principal) +{ + int i; + ASN1_DATA data; + DATA_BLOB ret; + + memset(&data, 0, sizeof(data)); + + asn1_write(&data, guid, 16); + asn1_push_tag(&data,ASN1_APPLICATION(0)); + asn1_write_OID(&data,OID_SPNEGO); + asn1_push_tag(&data,ASN1_CONTEXT(0)); + asn1_push_tag(&data,ASN1_SEQUENCE(0)); + + asn1_push_tag(&data,ASN1_CONTEXT(0)); + asn1_push_tag(&data,ASN1_SEQUENCE(0)); + for (i=0; OIDs[i]; i++) { + asn1_write_OID(&data,OIDs[i]); + } + asn1_pop_tag(&data); + asn1_pop_tag(&data); + + asn1_push_tag(&data, ASN1_CONTEXT(3)); + asn1_push_tag(&data, ASN1_SEQUENCE(0)); + asn1_push_tag(&data, ASN1_CONTEXT(0)); + asn1_write_GeneralString(&data,principal); + asn1_pop_tag(&data); + asn1_pop_tag(&data); + asn1_pop_tag(&data); + + asn1_pop_tag(&data); + asn1_pop_tag(&data); + + asn1_pop_tag(&data); + + if (data.has_error) { + DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data.ofs)); + asn1_free(&data); + } + + ret = data_blob(data.data, data.length); + asn1_free(&data); + + return ret; +} + + +/* + parse a negTokenInit packet giving a GUID, a list of supported + OIDs (the mechanisms) and a principal name string +*/ +BOOL spnego_parse_negTokenInit(DATA_BLOB blob, + uint8 guid[16], + char *OIDs[ASN1_MAX_OIDS], + char **principal) +{ + int i; + BOOL ret; + ASN1_DATA data; + + asn1_load(&data, blob); + + asn1_read(&data, guid, 16); + asn1_start_tag(&data,ASN1_APPLICATION(0)); + asn1_check_OID(&data,OID_SPNEGO); + asn1_start_tag(&data,ASN1_CONTEXT(0)); + asn1_start_tag(&data,ASN1_SEQUENCE(0)); + + asn1_start_tag(&data,ASN1_CONTEXT(0)); + asn1_start_tag(&data,ASN1_SEQUENCE(0)); + for (i=0; asn1_tag_remaining(&data) > 0 && i < ASN1_MAX_OIDS; i++) { + char *oid = NULL; + asn1_read_OID(&data,&oid); + OIDs[i] = oid; + } + OIDs[i] = NULL; + asn1_end_tag(&data); + asn1_end_tag(&data); + + asn1_start_tag(&data, ASN1_CONTEXT(3)); + asn1_start_tag(&data, ASN1_SEQUENCE(0)); + asn1_start_tag(&data, ASN1_CONTEXT(0)); + asn1_read_GeneralString(&data,principal); + asn1_end_tag(&data); + asn1_end_tag(&data); + asn1_end_tag(&data); + + asn1_end_tag(&data); + asn1_end_tag(&data); + + asn1_end_tag(&data); + + ret = !data.has_error; + asn1_free(&data); + return ret; +} + + +/* + generate a negTokenTarg packet given a list of OIDs and a security blob +*/ +DATA_BLOB gen_negTokenTarg(const char *OIDs[], DATA_BLOB blob) +{ + int i; + ASN1_DATA data; + DATA_BLOB ret; + + memset(&data, 0, sizeof(data)); + + asn1_push_tag(&data, ASN1_APPLICATION(0)); + asn1_write_OID(&data,OID_SPNEGO); + asn1_push_tag(&data, ASN1_CONTEXT(0)); + asn1_push_tag(&data, ASN1_SEQUENCE(0)); + + asn1_push_tag(&data, ASN1_CONTEXT(0)); + asn1_push_tag(&data, ASN1_SEQUENCE(0)); + for (i=0; OIDs[i]; i++) { + asn1_write_OID(&data,OIDs[i]); + } + asn1_pop_tag(&data); + asn1_pop_tag(&data); + + asn1_push_tag(&data, ASN1_CONTEXT(2)); + asn1_write_OctetString(&data,blob.data,blob.length); + asn1_pop_tag(&data); + + asn1_pop_tag(&data); + asn1_pop_tag(&data); + + asn1_pop_tag(&data); + + if (data.has_error) { + DEBUG(1,("Failed to build negTokenTarg at offset %d\n", (int)data.ofs)); + asn1_free(&data); + } + + ret = data_blob(data.data, data.length); + asn1_free(&data); + + return ret; +} + + +/* + parse a negTokenTarg packet giving a list of OIDs and a security blob +*/ +BOOL parse_negTokenTarg(DATA_BLOB blob, char *OIDs[ASN1_MAX_OIDS], DATA_BLOB *secblob) +{ + int i; + ASN1_DATA data; + + asn1_load(&data, blob); + asn1_start_tag(&data, ASN1_APPLICATION(0)); + asn1_check_OID(&data,OID_SPNEGO); + asn1_start_tag(&data, ASN1_CONTEXT(0)); + asn1_start_tag(&data, ASN1_SEQUENCE(0)); + + asn1_start_tag(&data, ASN1_CONTEXT(0)); + asn1_start_tag(&data, ASN1_SEQUENCE(0)); + for (i=0; asn1_tag_remaining(&data) > 0 && i < ASN1_MAX_OIDS; i++) { + char *oid = NULL; + asn1_read_OID(&data,&oid); + OIDs[i] = oid; + } + OIDs[i] = NULL; + asn1_end_tag(&data); + asn1_end_tag(&data); + + asn1_start_tag(&data, ASN1_CONTEXT(2)); + asn1_read_OctetString(&data,secblob); + asn1_end_tag(&data); + + asn1_end_tag(&data); + asn1_end_tag(&data); + + asn1_end_tag(&data); + + if (data.has_error) { + DEBUG(1,("Failed to parse negTokenTarg at offset %d\n", (int)data.ofs)); + asn1_free(&data); + return False; + } + + asn1_free(&data); + return True; +} + +/* + generate a krb5 GSS-API wrapper packet given a ticket +*/ +DATA_BLOB spnego_gen_krb5_wrap(DATA_BLOB ticket) +{ + ASN1_DATA data; + DATA_BLOB ret; + + memset(&data, 0, sizeof(data)); + + asn1_push_tag(&data, ASN1_APPLICATION(0)); + asn1_write_OID(&data, OID_KERBEROS5); + asn1_write_BOOLEAN(&data, 0); + asn1_write(&data, ticket.data, ticket.length); + asn1_pop_tag(&data); + + if (data.has_error) { + DEBUG(1,("Failed to build krb5 wrapper at offset %d\n", (int)data.ofs)); + asn1_free(&data); + } + + ret = data_blob(data.data, data.length); + asn1_free(&data); + + return ret; +} + +/* + parse a krb5 GSS-API wrapper packet giving a ticket +*/ +BOOL spnego_parse_krb5_wrap(DATA_BLOB blob, DATA_BLOB *ticket) +{ + BOOL ret; + ASN1_DATA data; + int data_remaining; + + asn1_load(&data, blob); + asn1_start_tag(&data, ASN1_APPLICATION(0)); + asn1_check_OID(&data, OID_KERBEROS5); + asn1_check_BOOLEAN(&data, 0); + + data_remaining = asn1_tag_remaining(&data); + + if (data_remaining < 1) { + data.has_error = True; + } else { + + *ticket = data_blob(data.data, data_remaining); + asn1_read(&data, ticket->data, ticket->length); + } + + asn1_end_tag(&data); + + ret = !data.has_error; + + asn1_free(&data); + + return ret; +} + + +/* + generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY + kerberos session setup +*/ +DATA_BLOB spnego_gen_negTokenTarg(struct cli_state *cli, char *principal) +{ + DATA_BLOB tkt, tkt_wrapped, targ; + const char *krb_mechs[] = {OID_KERBEROS5_OLD, OID_NTLMSSP, NULL}; + + /* get a kerberos ticket for the service */ + tkt = krb5_get_ticket(principal); + + /* wrap that up in a nice GSS-API wrapping */ + tkt_wrapped = spnego_gen_krb5_wrap(tkt); + + /* and wrap that in a shiny SPNEGO wrapper */ + targ = gen_negTokenTarg(krb_mechs, tkt_wrapped); + + data_blob_free(&tkt_wrapped); + data_blob_free(&tkt); + + return targ; +} + + +/* + parse a spnego NTLMSSP challenge packet giving two security blobs +*/ +BOOL spnego_parse_challenge(DATA_BLOB blob, + DATA_BLOB *chal1, DATA_BLOB *chal2) +{ + BOOL ret; + ASN1_DATA data; + + ZERO_STRUCTP(chal1); + ZERO_STRUCTP(chal2); + + asn1_load(&data, blob); + asn1_start_tag(&data,ASN1_CONTEXT(1)); + asn1_start_tag(&data,ASN1_SEQUENCE(0)); + + asn1_start_tag(&data,ASN1_CONTEXT(0)); + asn1_check_enumerated(&data,1); + asn1_end_tag(&data); + + asn1_start_tag(&data,ASN1_CONTEXT(1)); + asn1_check_OID(&data, OID_NTLMSSP); + asn1_end_tag(&data); + + asn1_start_tag(&data,ASN1_CONTEXT(2)); + asn1_read_OctetString(&data, chal1); + asn1_end_tag(&data); + + /* the second challenge is optional (XP doesn't send it) */ + if (asn1_tag_remaining(&data)) { + asn1_start_tag(&data,ASN1_CONTEXT(3)); + asn1_read_OctetString(&data, chal2); + asn1_end_tag(&data); + } + + asn1_end_tag(&data); + asn1_end_tag(&data); + + ret = !data.has_error; + asn1_free(&data); + return ret; +} + + +/* + generate a spnego NTLMSSP challenge packet given two security blobs + The second challenge is optional +*/ +BOOL spnego_gen_challenge(DATA_BLOB *blob, + DATA_BLOB *chal1, DATA_BLOB *chal2) +{ + ASN1_DATA data; + + ZERO_STRUCT(data); + + asn1_push_tag(&data,ASN1_CONTEXT(1)); + asn1_push_tag(&data,ASN1_SEQUENCE(0)); + + asn1_push_tag(&data,ASN1_CONTEXT(0)); + asn1_write_enumerated(&data,1); + asn1_pop_tag(&data); + + asn1_push_tag(&data,ASN1_CONTEXT(1)); + asn1_write_OID(&data, OID_NTLMSSP); + asn1_pop_tag(&data); + + asn1_push_tag(&data,ASN1_CONTEXT(2)); + asn1_write_OctetString(&data, chal1->data, chal1->length); + asn1_pop_tag(&data); + + /* the second challenge is optional (XP doesn't send it) */ + if (chal2) { + asn1_push_tag(&data,ASN1_CONTEXT(3)); + asn1_write_OctetString(&data, chal2->data, chal2->length); + asn1_pop_tag(&data); + } + + asn1_pop_tag(&data); + asn1_pop_tag(&data); + + if (data.has_error) { + return False; + } + + *blob = data_blob(data.data, data.length); + asn1_free(&data); + return True; +} + +/* + generate a SPNEGO NTLMSSP auth packet. This will contain the encrypted passwords +*/ +DATA_BLOB spnego_gen_auth(DATA_BLOB blob) +{ + ASN1_DATA data; + DATA_BLOB ret; + + memset(&data, 0, sizeof(data)); + + asn1_push_tag(&data, ASN1_CONTEXT(1)); + asn1_push_tag(&data, ASN1_SEQUENCE(0)); + asn1_push_tag(&data, ASN1_CONTEXT(2)); + asn1_write_OctetString(&data,blob.data,blob.length); + asn1_pop_tag(&data); + asn1_pop_tag(&data); + asn1_pop_tag(&data); + + ret = data_blob(data.data, data.length); + + asn1_free(&data); + + return ret; +} + +/* + parse a SPNEGO NTLMSSP auth packet. This contains the encrypted passwords +*/ +BOOL spnego_parse_auth(DATA_BLOB blob, DATA_BLOB *auth) +{ + ASN1_DATA data; + + asn1_load(&data, blob); + asn1_start_tag(&data, ASN1_CONTEXT(1)); + asn1_start_tag(&data, ASN1_SEQUENCE(0)); + asn1_start_tag(&data, ASN1_CONTEXT(2)); + asn1_read_OctetString(&data,auth); + asn1_end_tag(&data); + asn1_end_tag(&data); + asn1_end_tag(&data); + + if (data.has_error) { + DEBUG(3,("spnego_parse_auth failed at %d\n", (int)data.ofs)); + asn1_free(&data); + return False; + } + + asn1_free(&data); + return True; +} + + +/* + this is a tiny msrpc packet generator. I am only using this to + avoid tying this code to a particular varient of our rpc code. This + generator is not general enough for all our rpc needs, its just + enough for the spnego/ntlmssp code + + format specifiers are: + + U = unicode string (input is unix string) + B = data blob (pointer + length) + b = data blob in header (pointer + length) + d = word (4 bytes) + C = constant ascii string + */ +BOOL msrpc_gen(DATA_BLOB *blob, + const char *format, ...) +{ + int i, n; + va_list ap; + char *s; + uint8 *b; + int head_size=0, data_size=0; + int head_ofs, data_ofs; + + /* first scan the format to work out the header and body size */ + va_start(ap, format); + for (i=0; format[i]; i++) { + switch (format[i]) { + case 'U': + s = va_arg(ap, char *); + head_size += 8; + data_size += str_charnum(s) * 2; + break; + case 'B': + b = va_arg(ap, uint8 *); + head_size += 8; + data_size += va_arg(ap, int); + break; + case 'b': + b = va_arg(ap, uint8 *); + head_size += va_arg(ap, int); + break; + case 'd': + n = va_arg(ap, int); + head_size += 4; + break; + case 'C': + s = va_arg(ap, char *); + head_size += str_charnum(s) + 1; + break; + } + } + va_end(ap); + + /* allocate the space, then scan the format again to fill in the values */ + *blob = data_blob(NULL, head_size + data_size); + + head_ofs = 0; + data_ofs = head_size; + + va_start(ap, format); + for (i=0; format[i]; i++) { + switch (format[i]) { + case 'U': + s = va_arg(ap, char *); + n = str_charnum(s); + SSVAL(blob->data, head_ofs, n*2); head_ofs += 2; + SSVAL(blob->data, head_ofs, n*2); head_ofs += 2; + SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4; + push_string(NULL, blob->data+data_ofs, s, n*2, STR_UNICODE|STR_NOALIGN); + data_ofs += n*2; + break; + case 'B': + b = va_arg(ap, uint8 *); + n = va_arg(ap, int); + SSVAL(blob->data, head_ofs, n); head_ofs += 2; + SSVAL(blob->data, head_ofs, n); head_ofs += 2; + SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4; + memcpy(blob->data+data_ofs, b, n); + data_ofs += n; + break; + case 'd': + n = va_arg(ap, int); + SIVAL(blob->data, head_ofs, n); head_ofs += 4; + break; + case 'b': + b = va_arg(ap, uint8 *); + n = va_arg(ap, int); + memcpy(blob->data + head_ofs, b, n); + head_ofs += n; + break; + case 'C': + s = va_arg(ap, char *); + head_ofs += push_string(NULL, blob->data+head_ofs, s, -1, + STR_ASCII|STR_TERMINATE); + break; + } + } + va_end(ap); + + return True; +} + + +/* + this is a tiny msrpc packet parser. This the the partner of msrpc_gen + + format specifiers are: + + U = unicode string (input is unix string) + B = data blob + b = data blob in header + d = word (4 bytes) + C = constant ascii string + */ +BOOL msrpc_parse(DATA_BLOB *blob, + const char *format, ...) +{ + int i; + va_list ap; + char **ps, *s; + DATA_BLOB *b; + int head_ofs = 0; + uint16 len1, len2; + uint32 ptr; + uint32 *v; + pstring p; + + va_start(ap, format); + for (i=0; format[i]; i++) { + switch (format[i]) { + case 'U': + len1 = SVAL(blob->data, head_ofs); head_ofs += 2; + len2 = SVAL(blob->data, head_ofs); head_ofs += 2; + ptr = IVAL(blob->data, head_ofs); head_ofs += 4; + /* make sure its in the right format - be strict */ + if (len1 != len2 || (len1&1) || ptr + len1 > blob->length) { + return False; + } + ps = va_arg(ap, char **); + pull_string(NULL, p, blob->data + ptr, -1, len1, + STR_UNICODE|STR_NOALIGN); + (*ps) = strdup(p); + break; + case 'B': + len1 = SVAL(blob->data, head_ofs); head_ofs += 2; + len2 = SVAL(blob->data, head_ofs); head_ofs += 2; + ptr = IVAL(blob->data, head_ofs); head_ofs += 4; + /* make sure its in the right format - be strict */ + if (len1 != len2 || ptr + len1 > blob->length) { + return False; + } + b = (DATA_BLOB *)va_arg(ap, void *); + *b = data_blob(blob->data + ptr, len1); + break; + case 'b': + b = (DATA_BLOB *)va_arg(ap, void *); + len1 = va_arg(ap, unsigned); + *b = data_blob(blob->data + head_ofs, len1); + head_ofs += len1; + break; + case 'd': + v = va_arg(ap, uint32 *); + *v = IVAL(blob->data, head_ofs); head_ofs += 4; + break; + case 'C': + s = va_arg(ap, char *); + head_ofs += pull_string(NULL, p, blob->data+head_ofs, -1, + blob->length - head_ofs, + STR_ASCII|STR_TERMINATE); + if (strcmp(s, p) != 0) { + return False; + } + break; + } + } + va_end(ap); + + return True; +} diff --git a/source3/libsmb/clistr.c b/source3/libsmb/clistr.c new file mode 100644 index 0000000000..3c9964368e --- /dev/null +++ b/source3/libsmb/clistr.c @@ -0,0 +1,43 @@ +/* + Unix SMB/CIFS implementation. + client string routines + Copyright (C) Andrew Tridgell 2001 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +int clistr_push(struct cli_state *cli, void *dest, const char *src, int dest_len, int flags) +{ + return push_string(cli->outbuf, dest, src, dest_len, flags); +} + +int clistr_pull(struct cli_state *cli, char *dest, const void *src, int dest_len, int src_len, + int flags) +{ + return pull_string(cli->inbuf, dest, src, dest_len, src_len, flags); +} + + +int clistr_align_out(struct cli_state *cli, const void *p, int flags) +{ + return align_string(cli->outbuf, p, flags); +} + +int clistr_align_in(struct cli_state *cli, const void *p, int flags) +{ + return align_string(cli->inbuf, p, flags); +} diff --git a/source3/libsmb/clitrans.c b/source3/libsmb/clitrans.c new file mode 100644 index 0000000000..3d862a1796 --- /dev/null +++ b/source3/libsmb/clitrans.c @@ -0,0 +1,468 @@ +/* + Unix SMB/CIFS implementation. + client transaction calls + Copyright (C) Andrew Tridgell 1994-1998 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#define NO_SYSLOG + +#include "includes.h" + + +/**************************************************************************** + send a SMB trans or trans2 request + ****************************************************************************/ +BOOL cli_send_trans(struct cli_state *cli, int trans, + const char *pipe_name, + int fid, int flags, + uint16 *setup, int lsetup, int msetup, + char *param, int lparam, int mparam, + char *data, int ldata, int mdata) +{ + int i; + int this_ldata,this_lparam; + int tot_data=0,tot_param=0; + char *outdata,*outparam; + char *p; + int pipe_name_len=0; + + this_lparam = MIN(lparam,cli->max_xmit - (500+lsetup*2)); /* hack */ + this_ldata = MIN(ldata,cli->max_xmit - (500+lsetup*2+this_lparam)); + + memset(cli->outbuf,'\0',smb_size); + set_message(cli->outbuf,14+lsetup,0,True); + SCVAL(cli->outbuf,smb_com,trans); + SSVAL(cli->outbuf,smb_tid, cli->cnum); + cli_setup_packet(cli); + + if (pipe_name) { + pipe_name_len = clistr_push(cli, smb_buf(cli->outbuf), pipe_name, -1, STR_TERMINATE); + } + + outparam = smb_buf(cli->outbuf)+(trans==SMBtrans ? pipe_name_len : 3); + outdata = outparam+this_lparam; + + /* primary request */ + SSVAL(cli->outbuf,smb_tpscnt,lparam); /* tpscnt */ + SSVAL(cli->outbuf,smb_tdscnt,ldata); /* tdscnt */ + SSVAL(cli->outbuf,smb_mprcnt,mparam); /* mprcnt */ + SSVAL(cli->outbuf,smb_mdrcnt,mdata); /* mdrcnt */ + SCVAL(cli->outbuf,smb_msrcnt,msetup); /* msrcnt */ + SSVAL(cli->outbuf,smb_flags,flags); /* flags */ + SIVAL(cli->outbuf,smb_timeout,0); /* timeout */ + SSVAL(cli->outbuf,smb_pscnt,this_lparam); /* pscnt */ + SSVAL(cli->outbuf,smb_psoff,smb_offset(outparam,cli->outbuf)); /* psoff */ + SSVAL(cli->outbuf,smb_dscnt,this_ldata); /* dscnt */ + SSVAL(cli->outbuf,smb_dsoff,smb_offset(outdata,cli->outbuf)); /* dsoff */ + SCVAL(cli->outbuf,smb_suwcnt,lsetup); /* suwcnt */ + for (i=0;i<lsetup;i++) /* setup[] */ + SSVAL(cli->outbuf,smb_setup+i*2,setup[i]); + p = smb_buf(cli->outbuf); + if (trans != SMBtrans) { + *p++ = 0; /* put in a null smb_name */ + *p++ = 'D'; *p++ = ' '; /* observed in OS/2 */ + } + if (this_lparam) /* param[] */ + memcpy(outparam,param,this_lparam); + if (this_ldata) /* data[] */ + memcpy(outdata,data,this_ldata); + cli_setup_bcc(cli, outdata+this_ldata); + + show_msg(cli->outbuf); + cli_send_smb(cli); + + if (this_ldata < ldata || this_lparam < lparam) { + /* receive interim response */ + if (!cli_receive_smb(cli) || + cli_is_error(cli)) { + return(False); + } + + tot_data = this_ldata; + tot_param = this_lparam; + + while (tot_data < ldata || tot_param < lparam) { + this_lparam = MIN(lparam-tot_param,cli->max_xmit - 500); /* hack */ + this_ldata = MIN(ldata-tot_data,cli->max_xmit - (500+this_lparam)); + + set_message(cli->outbuf,trans==SMBtrans?8:9,0,True); + SCVAL(cli->outbuf,smb_com,(trans==SMBtrans ? SMBtranss : SMBtranss2)); + + outparam = smb_buf(cli->outbuf); + outdata = outparam+this_lparam; + + /* secondary request */ + SSVAL(cli->outbuf,smb_tpscnt,lparam); /* tpscnt */ + SSVAL(cli->outbuf,smb_tdscnt,ldata); /* tdscnt */ + SSVAL(cli->outbuf,smb_spscnt,this_lparam); /* pscnt */ + SSVAL(cli->outbuf,smb_spsoff,smb_offset(outparam,cli->outbuf)); /* psoff */ + SSVAL(cli->outbuf,smb_spsdisp,tot_param); /* psdisp */ + SSVAL(cli->outbuf,smb_sdscnt,this_ldata); /* dscnt */ + SSVAL(cli->outbuf,smb_sdsoff,smb_offset(outdata,cli->outbuf)); /* dsoff */ + SSVAL(cli->outbuf,smb_sdsdisp,tot_data); /* dsdisp */ + if (trans==SMBtrans2) + SSVALS(cli->outbuf,smb_sfid,fid); /* fid */ + if (this_lparam) /* param[] */ + memcpy(outparam,param+tot_param,this_lparam); + if (this_ldata) /* data[] */ + memcpy(outdata,data+tot_data,this_ldata); + cli_setup_bcc(cli, outdata+this_ldata); + + show_msg(cli->outbuf); + cli_send_smb(cli); + + tot_data += this_ldata; + tot_param += this_lparam; + } + } + + return(True); +} + + +/**************************************************************************** + receive a SMB trans or trans2 response allocating the necessary memory + ****************************************************************************/ +BOOL cli_receive_trans(struct cli_state *cli,int trans, + char **param, int *param_len, + char **data, int *data_len) +{ + int total_data=0; + int total_param=0; + int this_data,this_param; + NTSTATUS status; + char *tdata; + char *tparam; + + *data_len = *param_len = 0; + + if (!cli_receive_smb(cli)) + return False; + + show_msg(cli->inbuf); + + /* sanity check */ + if (CVAL(cli->inbuf,smb_com) != trans) { + DEBUG(0,("Expected %s response, got command 0x%02x\n", + trans==SMBtrans?"SMBtrans":"SMBtrans2", + CVAL(cli->inbuf,smb_com))); + return(False); + } + + /* + * An NT RPC pipe call can return ERRDOS, ERRmoredata + * to a trans call. This is not an error and should not + * be treated as such. + */ + status = cli_nt_error(cli); + + if (NT_STATUS_IS_ERR(status)) { + return False; + } + + /* parse out the lengths */ + total_data = SVAL(cli->inbuf,smb_tdrcnt); + total_param = SVAL(cli->inbuf,smb_tprcnt); + + /* allocate it */ + if (total_data!=0) { + tdata = Realloc(*data,total_data); + if (!tdata) { + DEBUG(0,("cli_receive_trans: failed to enlarge data buffer\n")); + return False; + } + else + *data = tdata; + } + + if (total_param!=0) { + tparam = Realloc(*param,total_param); + if (!tparam) { + DEBUG(0,("cli_receive_trans: failed to enlarge param buffer\n")); + return False; + } + else + *param = tparam; + } + + while (1) { + this_data = SVAL(cli->inbuf,smb_drcnt); + this_param = SVAL(cli->inbuf,smb_prcnt); + + if (this_data + *data_len > total_data || + this_param + *param_len > total_param) { + DEBUG(1,("Data overflow in cli_receive_trans\n")); + return False; + } + + if (this_data) + memcpy(*data + SVAL(cli->inbuf,smb_drdisp), + smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_droff), + this_data); + if (this_param) + memcpy(*param + SVAL(cli->inbuf,smb_prdisp), + smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_proff), + this_param); + *data_len += this_data; + *param_len += this_param; + + /* parse out the total lengths again - they can shrink! */ + total_data = SVAL(cli->inbuf,smb_tdrcnt); + total_param = SVAL(cli->inbuf,smb_tprcnt); + + if (total_data <= *data_len && total_param <= *param_len) + break; + + if (!cli_receive_smb(cli)) + return False; + + show_msg(cli->inbuf); + + /* sanity check */ + if (CVAL(cli->inbuf,smb_com) != trans) { + DEBUG(0,("Expected %s response, got command 0x%02x\n", + trans==SMBtrans?"SMBtrans":"SMBtrans2", + CVAL(cli->inbuf,smb_com))); + return(False); + } + if (NT_STATUS_IS_ERR(cli_nt_error(cli))) { + return(False); + } + } + + return(True); +} + + + + +/**************************************************************************** + send a SMB nttrans request + ****************************************************************************/ +BOOL cli_send_nt_trans(struct cli_state *cli, + int function, + int flags, + uint16 *setup, int lsetup, int msetup, + char *param, int lparam, int mparam, + char *data, int ldata, int mdata) +{ + int i; + int this_ldata,this_lparam; + int tot_data=0,tot_param=0; + char *outdata,*outparam; + + this_lparam = MIN(lparam,cli->max_xmit - (500+lsetup*2)); /* hack */ + this_ldata = MIN(ldata,cli->max_xmit - (500+lsetup*2+this_lparam)); + + memset(cli->outbuf,'\0',smb_size); + set_message(cli->outbuf,19+lsetup,0,True); + SCVAL(cli->outbuf,smb_com,SMBnttrans); + SSVAL(cli->outbuf,smb_tid, cli->cnum); + cli_setup_packet(cli); + + outparam = smb_buf(cli->outbuf)+3; + outdata = outparam+this_lparam; + + /* primary request */ + SCVAL(cli->outbuf,smb_nt_MaxSetupCount,msetup); + SCVAL(cli->outbuf,smb_nt_Flags,flags); + SIVAL(cli->outbuf,smb_nt_TotalParameterCount, lparam); + SIVAL(cli->outbuf,smb_nt_TotalDataCount, ldata); + SIVAL(cli->outbuf,smb_nt_MaxParameterCount, mparam); + SIVAL(cli->outbuf,smb_nt_MaxDataCount, mdata); + SIVAL(cli->outbuf,smb_nt_ParameterCount, this_lparam); + SIVAL(cli->outbuf,smb_nt_ParameterOffset, smb_offset(outparam,cli->outbuf)); + SIVAL(cli->outbuf,smb_nt_DataCount, this_ldata); + SIVAL(cli->outbuf,smb_nt_DataOffset, smb_offset(outdata,cli->outbuf)); + SIVAL(cli->outbuf,smb_nt_SetupCount, lsetup); + SIVAL(cli->outbuf,smb_nt_Function, function); + for (i=0;i<lsetup;i++) /* setup[] */ + SSVAL(cli->outbuf,smb_nt_SetupStart+i*2,setup[i]); + + if (this_lparam) /* param[] */ + memcpy(outparam,param,this_lparam); + if (this_ldata) /* data[] */ + memcpy(outdata,data,this_ldata); + + cli_setup_bcc(cli, outdata+this_ldata); + + show_msg(cli->outbuf); + cli_send_smb(cli); + + if (this_ldata < ldata || this_lparam < lparam) { + /* receive interim response */ + if (!cli_receive_smb(cli) || + cli_is_error(cli)) { + return(False); + } + + tot_data = this_ldata; + tot_param = this_lparam; + + while (tot_data < ldata || tot_param < lparam) { + this_lparam = MIN(lparam-tot_param,cli->max_xmit - 500); /* hack */ + this_ldata = MIN(ldata-tot_data,cli->max_xmit - (500+this_lparam)); + + set_message(cli->outbuf,18,0,True); + SCVAL(cli->outbuf,smb_com,SMBnttranss); + + /* XXX - these should probably be aligned */ + outparam = smb_buf(cli->outbuf); + outdata = outparam+this_lparam; + + /* secondary request */ + SIVAL(cli->outbuf,smb_nts_TotalParameterCount,lparam); + SIVAL(cli->outbuf,smb_nts_TotalDataCount,ldata); + SIVAL(cli->outbuf,smb_nts_ParameterCount,this_lparam); + SIVAL(cli->outbuf,smb_nts_ParameterOffset,smb_offset(outparam,cli->outbuf)); + SIVAL(cli->outbuf,smb_nts_ParameterDisplacement,tot_param); + SIVAL(cli->outbuf,smb_nts_DataCount,this_ldata); + SIVAL(cli->outbuf,smb_nts_DataOffset,smb_offset(outdata,cli->outbuf)); + SIVAL(cli->outbuf,smb_nts_DataDisplacement,tot_data); + if (this_lparam) /* param[] */ + memcpy(outparam,param+tot_param,this_lparam); + if (this_ldata) /* data[] */ + memcpy(outdata,data+tot_data,this_ldata); + cli_setup_bcc(cli, outdata+this_ldata); + + show_msg(cli->outbuf); + cli_send_smb(cli); + + tot_data += this_ldata; + tot_param += this_lparam; + } + } + + return(True); +} + + + +/**************************************************************************** + receive a SMB nttrans response allocating the necessary memory + ****************************************************************************/ +BOOL cli_receive_nt_trans(struct cli_state *cli, + char **param, int *param_len, + char **data, int *data_len) +{ + int total_data=0; + int total_param=0; + int this_data,this_param; + uint8 eclass; + uint32 ecode; + char *tdata; + char *tparam; + + *data_len = *param_len = 0; + + if (!cli_receive_smb(cli)) + return False; + + show_msg(cli->inbuf); + + /* sanity check */ + if (CVAL(cli->inbuf,smb_com) != SMBnttrans) { + DEBUG(0,("Expected SMBnttrans response, got command 0x%02x\n", + CVAL(cli->inbuf,smb_com))); + return(False); + } + + /* + * An NT RPC pipe call can return ERRDOS, ERRmoredata + * to a trans call. This is not an error and should not + * be treated as such. + */ + if (cli_is_dos_error(cli)) { + cli_dos_error(cli, &eclass, &ecode); + if (cli->nt_pipe_fnum == 0 || !(eclass == ERRDOS && ecode == ERRmoredata)) + return(False); + } + + /* parse out the lengths */ + total_data = SVAL(cli->inbuf,smb_ntr_TotalDataCount); + total_param = SVAL(cli->inbuf,smb_ntr_TotalParameterCount); + + /* allocate it */ + if (total_data) { + tdata = Realloc(*data,total_data); + if (!tdata) { + DEBUG(0,("cli_receive_nt_trans: failed to enlarge data buffer to %d\n",total_data)); + return False; + } else { + *data = tdata; + } + } + + if (total_param) { + tparam = Realloc(*param,total_param); + if (!tparam) { + DEBUG(0,("cli_receive_nt_trans: failed to enlarge param buffer to %d\n", total_param)); + return False; + } else { + *param = tparam; + } + } + + while (1) { + this_data = SVAL(cli->inbuf,smb_ntr_DataCount); + this_param = SVAL(cli->inbuf,smb_ntr_ParameterCount); + + if (this_data + *data_len > total_data || + this_param + *param_len > total_param) { + DEBUG(1,("Data overflow in cli_receive_trans\n")); + return False; + } + + if (this_data) + memcpy(*data + SVAL(cli->inbuf,smb_ntr_DataDisplacement), + smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_ntr_DataOffset), + this_data); + if (this_param) + memcpy(*param + SVAL(cli->inbuf,smb_ntr_ParameterDisplacement), + smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_ntr_ParameterOffset), + this_param); + *data_len += this_data; + *param_len += this_param; + + /* parse out the total lengths again - they can shrink! */ + total_data = SVAL(cli->inbuf,smb_ntr_TotalDataCount); + total_param = SVAL(cli->inbuf,smb_ntr_TotalParameterCount); + + if (total_data <= *data_len && total_param <= *param_len) + break; + + if (!cli_receive_smb(cli)) + return False; + + show_msg(cli->inbuf); + + /* sanity check */ + if (CVAL(cli->inbuf,smb_com) != SMBnttrans) { + DEBUG(0,("Expected SMBnttrans response, got command 0x%02x\n", + CVAL(cli->inbuf,smb_com))); + return(False); + } + if (cli_is_dos_error(cli)) { + cli_dos_error(cli, &eclass, &ecode); + if(cli->nt_pipe_fnum == 0 || + !(eclass == ERRDOS && ecode == ERRmoredata)) + return(False); + } + } + + return(True); +} diff --git a/source3/libsmb/credentials.c b/source3/libsmb/credentials.c new file mode 100644 index 0000000000..0d521bae8a --- /dev/null +++ b/source3/libsmb/credentials.c @@ -0,0 +1,215 @@ +/* + Unix SMB/CIFS implementation. + code to manipulate domain credentials + Copyright (C) Andrew Tridgell 1997-1998 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/**************************************************************************** +represent a credential as a string +****************************************************************************/ +char *credstr(const uchar *cred) +{ + static fstring buf; + slprintf(buf, sizeof(buf) - 1, "%02X%02X%02X%02X%02X%02X%02X%02X", + cred[0], cred[1], cred[2], cred[3], + cred[4], cred[5], cred[6], cred[7]); + return buf; +} + + +/**************************************************************************** + setup the session key. +Input: 8 byte challenge block + 8 byte server challenge block + 16 byte md4 encrypted password +Output: + 8 byte session key +****************************************************************************/ +void cred_session_key(const DOM_CHAL *clnt_chal, const DOM_CHAL *srv_chal, const uchar *pass, + uchar session_key[8]) +{ + uint32 sum[2]; + unsigned char sum2[8]; + + sum[0] = IVAL(clnt_chal->data, 0) + IVAL(srv_chal->data, 0); + sum[1] = IVAL(clnt_chal->data, 4) + IVAL(srv_chal->data, 4); + + SIVAL(sum2,0,sum[0]); + SIVAL(sum2,4,sum[1]); + + cred_hash1(session_key, sum2, pass); + + /* debug output */ + DEBUG(4,("cred_session_key\n")); + + DEBUG(5,(" clnt_chal: %s\n", credstr(clnt_chal->data))); + DEBUG(5,(" srv_chal : %s\n", credstr(srv_chal->data))); + DEBUG(5,(" clnt+srv : %s\n", credstr(sum2))); + DEBUG(5,(" sess_key : %s\n", credstr(session_key))); +} + + +/**************************************************************************** +create a credential + +Input: + 8 byte sesssion key + 8 byte stored credential + 4 byte timestamp + +Output: + 8 byte credential +****************************************************************************/ +void cred_create(uchar session_key[8], DOM_CHAL *stor_cred, UTIME timestamp, + DOM_CHAL *cred) +{ + DOM_CHAL time_cred; + + SIVAL(time_cred.data, 0, IVAL(stor_cred->data, 0) + timestamp.time); + SIVAL(time_cred.data, 4, IVAL(stor_cred->data, 4)); + + cred_hash2(cred->data, time_cred.data, session_key); + + /* debug output*/ + DEBUG(4,("cred_create\n")); + + DEBUG(5,(" sess_key : %s\n", credstr(session_key))); + DEBUG(5,(" stor_cred: %s\n", credstr(stor_cred->data))); + DEBUG(5,(" timestamp: %x\n" , timestamp.time)); + DEBUG(5,(" timecred : %s\n", credstr(time_cred.data))); + DEBUG(5,(" calc_cred: %s\n", credstr(cred->data))); +} + + +/**************************************************************************** + check a supplied credential + +Input: + 8 byte received credential + 8 byte sesssion key + 8 byte stored credential + 4 byte timestamp + +Output: + returns 1 if computed credential matches received credential + returns 0 otherwise +****************************************************************************/ +int cred_assert(DOM_CHAL *cred, uchar session_key[8], DOM_CHAL *stored_cred, + UTIME timestamp) +{ + DOM_CHAL cred2; + + cred_create(session_key, stored_cred, timestamp, &cred2); + + /* debug output*/ + DEBUG(4,("cred_assert\n")); + + DEBUG(5,(" challenge : %s\n", credstr(cred->data))); + DEBUG(5,(" calculated: %s\n", credstr(cred2.data))); + + if (memcmp(cred->data, cred2.data, 8) == 0) + { + DEBUG(5, ("credentials check ok\n")); + return True; + } + else + { + DEBUG(5, ("credentials check wrong\n")); + return False; + } +} + + +/**************************************************************************** + checks credentials; generates next step in the credential chain +****************************************************************************/ +BOOL clnt_deal_with_creds(uchar sess_key[8], + DOM_CRED *sto_clnt_cred, DOM_CRED *rcv_srv_cred) +{ + UTIME new_clnt_time; + uint32 new_cred; + + DEBUG(5,("clnt_deal_with_creds: %d\n", __LINE__)); + + /* increment client time by one second */ + new_clnt_time.time = sto_clnt_cred->timestamp.time + 1; + + /* check that the received server credentials are valid */ + if (!cred_assert(&rcv_srv_cred->challenge, sess_key, + &sto_clnt_cred->challenge, new_clnt_time)) + { + return False; + } + + /* first 4 bytes of the new seed is old client 4 bytes + clnt time + 1 */ + new_cred = IVAL(sto_clnt_cred->challenge.data, 0); + new_cred += new_clnt_time.time; + + /* store new seed in client credentials */ + SIVAL(sto_clnt_cred->challenge.data, 0, new_cred); + + DEBUG(5,(" new clnt cred: %s\n", credstr(sto_clnt_cred->challenge.data))); + return True; +} + + +/**************************************************************************** + checks credentials; generates next step in the credential chain +****************************************************************************/ +BOOL deal_with_creds(uchar sess_key[8], + DOM_CRED *sto_clnt_cred, + DOM_CRED *rcv_clnt_cred, DOM_CRED *rtn_srv_cred) +{ + UTIME new_clnt_time; + uint32 new_cred; + + DEBUG(5,("deal_with_creds: %d\n", __LINE__)); + + /* check that the received client credentials are valid */ + if (!cred_assert(&rcv_clnt_cred->challenge, sess_key, + &sto_clnt_cred->challenge, rcv_clnt_cred->timestamp)) + { + return False; + } + + /* increment client time by one second */ + new_clnt_time.time = rcv_clnt_cred->timestamp.time + 1; + + /* first 4 bytes of the new seed is old client 4 bytes + clnt time + 1 */ + new_cred = IVAL(sto_clnt_cred->challenge.data, 0); + new_cred += new_clnt_time.time; + + DEBUG(5,("deal_with_creds: new_cred[0]=%x\n", new_cred)); + + /* doesn't matter that server time is 0 */ + rtn_srv_cred->timestamp.time = 0; + + DEBUG(5,("deal_with_creds: new_clnt_time=%x\n", new_clnt_time.time)); + + /* create return credentials for inclusion in the reply */ + cred_create(sess_key, &sto_clnt_cred->challenge, new_clnt_time, + &rtn_srv_cred->challenge); + + DEBUG(5,("deal_with_creds: clnt_cred=%s\n", credstr(sto_clnt_cred->challenge.data))); + + /* store new seed in client credentials */ + SIVAL(sto_clnt_cred->challenge.data, 0, new_cred); + + return True; +} diff --git a/source3/libsmb/doserr.c b/source3/libsmb/doserr.c new file mode 100644 index 0000000000..adc001bf29 --- /dev/null +++ b/source3/libsmb/doserr.c @@ -0,0 +1,89 @@ +/* + * Unix SMB/CIFS implementation. + * DOS error routines + * Copyright (C) Tim Potter 2002. + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* DOS error codes. please read doserr.h */ + +#include "includes.h" + +typedef const struct +{ + char *dos_errstr; + WERROR werror; +} werror_code_struct; + +werror_code_struct dos_errs[] = +{ + { "WERR_OK", WERR_OK }, + { "WERR_BADFILE", WERR_BADFILE }, + { "WERR_ACCESS_DENIED", WERR_ACCESS_DENIED }, + { "WERR_BADFID", WERR_BADFID }, + { "WERR_BADFUNC", WERR_BADFUNC }, + { "WERR_INSUFFICIENT_BUFFER", WERR_INSUFFICIENT_BUFFER }, + { "WERR_NO_SUCH_SHARE", WERR_NO_SUCH_SHARE }, + { "WERR_ALREADY_EXISTS", WERR_ALREADY_EXISTS }, + { "WERR_INVALID_PARAM", WERR_INVALID_PARAM }, + { "WERR_NOT_SUPPORTED", WERR_NOT_SUPPORTED }, + { "WERR_BAD_PASSWORD", WERR_BAD_PASSWORD }, + { "WERR_NOMEM", WERR_NOMEM }, + { "WERR_INVALID_NAME", WERR_INVALID_NAME }, + { "WERR_UNKNOWN_LEVEL", WERR_UNKNOWN_LEVEL }, + { "WERR_OBJECT_PATH_INVALID", WERR_OBJECT_PATH_INVALID }, + { "WERR_NO_MORE_ITEMS", WERR_NO_MORE_ITEMS }, + { "WERR_MORE_DATA", WERR_MORE_DATA }, + { "WERR_UNKNOWN_PRINTER_DRIVER", WERR_UNKNOWN_PRINTER_DRIVER }, + { "WERR_INVALID_PRINTER_NAME", WERR_INVALID_PRINTER_NAME }, + { "WERR_PRINTER_ALREADY_EXISTS", WERR_PRINTER_ALREADY_EXISTS }, + { "WERR_INVALID_DATATYPE", WERR_INVALID_DATATYPE }, + { "WERR_INVALID_ENVIRONMENT", WERR_INVALID_ENVIRONMENT }, + { "WERR_INVALID_FORM_NAME", WERR_INVALID_FORM_NAME }, + { "WERR_INVALID_FORM_SIZE", WERR_INVALID_FORM_SIZE }, + { "WERR_BUF_TOO_SMALL", WERR_BUF_TOO_SMALL }, + { "WERR_JOB_NOT_FOUND", WERR_JOB_NOT_FOUND }, + { "WERR_DEST_NOT_FOUND", WERR_DEST_NOT_FOUND }, + { "WERR_NOT_LOCAL_DOMAIN", WERR_NOT_LOCAL_DOMAIN }, + { "WERR_PRINTER_DRIVER_IN_USE", WERR_PRINTER_DRIVER_IN_USE }, + { "WERR_STATUS_MORE_ENTRIES ", WERR_STATUS_MORE_ENTRIES }, + { "WERR_DFS_NO_SUCH_VOL", WERR_DFS_NO_SUCH_VOL }, + { "WERR_DFS_NO_SUCH_SHARE", WERR_DFS_NO_SUCH_SHARE }, + { "WERR_DFS_NO_SUCH_SERVER", WERR_DFS_NO_SUCH_SERVER }, + { "WERR_DFS_INTERNAL_ERROR", WERR_DFS_INTERNAL_ERROR }, + { "WERR_DFS_CANT_CREATE_JUNCT", WERR_DFS_CANT_CREATE_JUNCT }, + { NULL, W_ERROR(0) } +}; + +/***************************************************************************** + returns a DOS error message. not amazingly helpful, but better than a number. + *****************************************************************************/ +char *dos_errstr(WERROR werror) +{ + static pstring msg; + int idx = 0; + + slprintf(msg, sizeof(msg), "DOS code 0x%08x", W_ERROR_V(werror)); + + while (dos_errs[idx].dos_errstr != NULL) { + if (W_ERROR_V(dos_errs[idx].werror) == + W_ERROR_V(werror)) + return dos_errs[idx].dos_errstr; + idx++; + } + + return msg; +} diff --git a/source3/libsmb/errormap.c b/source3/libsmb/errormap.c new file mode 100644 index 0000000000..c30db3ad95 --- /dev/null +++ b/source3/libsmb/errormap.c @@ -0,0 +1,1483 @@ +/* + * Unix SMB/CIFS implementation. + * error mapping functions + * Copyright (C) Andrew Tridgell 2001 + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "includes.h" + +/* This map was extracted by the ERRMAPEXTRACT smbtorture command. + The setup was a Samba HEAD (2002-01-03) PDC and an Win2k member + workstation. The PDC was modified (by using the 'name_to_nt_status' + authentication module) to convert the username (in hex) into the + corresponding NTSTATUS error return. + + By opening two nbt sessions to the Win2k workstation, one negotiating + DOS and one negotiating NT errors it was possible to extract the + error mapping. (Because the server only supplies NT errors, the + NT4 workstation had to use its own error tables to convert these + to dos errors). + + Some errors show up as 'squashed' because the NT error connection + got back a different error to the one it sent, so a mapping could + not be determined (a guess has been made in this case, to map the + error as squashed). This is done mainly to prevent users from getting + NT_STATUS_WRONG_PASSWORD and NT_STATUS_NO_SUCH_USER errors (they get + NT_STATUS_LOGON_FAILURE instead. + + -- abartlet (2002-01-03) +*/ + +/* NT status -> dos error map */ +const static struct { + uint8 dos_class; + uint32 dos_code; + NTSTATUS ntstatus; +} ntstatus_to_dos_map[] = { + {ERRDOS, ERRgeneral, NT_STATUS_UNSUCCESSFUL}, + {ERRDOS, ERRbadfunc, NT_STATUS_NOT_IMPLEMENTED}, + {ERRDOS, 87, NT_STATUS_INVALID_INFO_CLASS}, + {ERRDOS, 24, NT_STATUS_INFO_LENGTH_MISMATCH}, + {ERRHRD, ERRgeneral, NT_STATUS_ACCESS_VIOLATION}, + {ERRHRD, ERRgeneral, NT_STATUS_IN_PAGE_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA}, + {ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_INITIAL_STACK}, + {ERRDOS, 193, NT_STATUS_BAD_INITIAL_PC}, + {ERRDOS, 87, NT_STATUS_INVALID_CID}, + {ERRHRD, ERRgeneral, NT_STATUS_TIMER_NOT_CANCELED}, + {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER}, + {ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_DEVICE}, + {ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_FILE}, + {ERRDOS, ERRbadfunc, NT_STATUS_INVALID_DEVICE_REQUEST}, + {ERRDOS, 38, NT_STATUS_END_OF_FILE}, + {ERRDOS, 34, NT_STATUS_WRONG_VOLUME}, + {ERRDOS, 21, NT_STATUS_NO_MEDIA_IN_DEVICE}, + {ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_MEDIA}, + {ERRDOS, 27, NT_STATUS_NONEXISTENT_SECTOR}, +/** Session setup succeeded. This shouldn't happen...*/ +/** Session setup succeeded. This shouldn't happen...*/ +/** NT error on DOS connection! (NT_STATUS_OK) */ +/* { This NT error code was 'sqashed' + from NT_STATUS_MORE_PROCESSING_REQUIRED to NT_STATUS_OK + during the session setup } +*/ +#if 0 + {SUCCESS, 0, NT_STATUS_OK}, +#endif + {ERRDOS, ERRnomem, NT_STATUS_NO_MEMORY}, + {ERRDOS, 487, NT_STATUS_CONFLICTING_ADDRESSES}, + {ERRDOS, 487, NT_STATUS_NOT_MAPPED_VIEW}, + {ERRDOS, 87, NT_STATUS_UNABLE_TO_FREE_VM}, + {ERRDOS, 87, NT_STATUS_UNABLE_TO_DELETE_SECTION}, + {ERRDOS, 2142, NT_STATUS_INVALID_SYSTEM_SERVICE}, + {ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_INSTRUCTION}, + {ERRDOS, ERRnoaccess, NT_STATUS_INVALID_LOCK_SEQUENCE}, + {ERRDOS, ERRnoaccess, NT_STATUS_INVALID_VIEW_SIZE}, + {ERRDOS, 193, NT_STATUS_INVALID_FILE_FOR_SECTION}, + {ERRDOS, ERRnoaccess, NT_STATUS_ALREADY_COMMITTED}, +/* { This NT error code was 'sqashed' + from NT_STATUS_ACCESS_DENIED to NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE + during the session setup } +*/ + {ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED}, + {ERRDOS, 111, NT_STATUS_BUFFER_TOO_SMALL}, + {ERRDOS, ERRbadfid, NT_STATUS_OBJECT_TYPE_MISMATCH}, + {ERRHRD, ERRgeneral, NT_STATUS_NONCONTINUABLE_EXCEPTION}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_DISPOSITION}, + {ERRHRD, ERRgeneral, NT_STATUS_UNWIND}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_STACK}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_UNWIND_TARGET}, + {ERRDOS, 158, NT_STATUS_NOT_LOCKED}, + {ERRHRD, ERRgeneral, NT_STATUS_PARITY_ERROR}, + {ERRDOS, 487, NT_STATUS_UNABLE_TO_DECOMMIT_VM}, + {ERRDOS, 487, NT_STATUS_NOT_COMMITTED}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_PORT_ATTRIBUTES}, + {ERRHRD, ERRgeneral, NT_STATUS_PORT_MESSAGE_TOO_LONG}, + {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_MIX}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_QUOTA_LOWER}, + {ERRHRD, ERRgeneral, NT_STATUS_DISK_CORRUPT_ERROR}, + {ERRDOS, ERRinvalidname, NT_STATUS_OBJECT_NAME_INVALID}, + {ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_NOT_FOUND}, + {ERRDOS, 183, NT_STATUS_OBJECT_NAME_COLLISION}, + {ERRHRD, ERRgeneral, NT_STATUS_HANDLE_NOT_WAITABLE}, + {ERRDOS, ERRbadfid, NT_STATUS_PORT_DISCONNECTED}, + {ERRHRD, ERRgeneral, NT_STATUS_DEVICE_ALREADY_ATTACHED}, + {ERRDOS, 161, NT_STATUS_OBJECT_PATH_INVALID}, + {ERRDOS, ERRbadpath, NT_STATUS_OBJECT_PATH_NOT_FOUND}, + {ERRDOS, 161, NT_STATUS_OBJECT_PATH_SYNTAX_BAD}, + {ERRHRD, ERRgeneral, NT_STATUS_DATA_OVERRUN}, + {ERRHRD, ERRgeneral, NT_STATUS_DATA_LATE_ERROR}, + {ERRDOS, 23, NT_STATUS_DATA_ERROR}, + {ERRDOS, 23, NT_STATUS_CRC_ERROR}, + {ERRDOS, ERRnomem, NT_STATUS_SECTION_TOO_BIG}, + {ERRDOS, ERRnoaccess, NT_STATUS_PORT_CONNECTION_REFUSED}, + {ERRDOS, ERRbadfid, NT_STATUS_INVALID_PORT_HANDLE}, + {ERRDOS, ERRbadshare, NT_STATUS_SHARING_VIOLATION}, + {ERRHRD, ERRgeneral, NT_STATUS_QUOTA_EXCEEDED}, + {ERRDOS, 87, NT_STATUS_INVALID_PAGE_PROTECTION}, + {ERRDOS, 288, NT_STATUS_MUTANT_NOT_OWNED}, + {ERRDOS, 298, NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED}, + {ERRDOS, 87, NT_STATUS_PORT_ALREADY_SET}, + {ERRDOS, 87, NT_STATUS_SECTION_NOT_IMAGE}, + {ERRDOS, 156, NT_STATUS_SUSPEND_COUNT_EXCEEDED}, + {ERRDOS, ERRnoaccess, NT_STATUS_THREAD_IS_TERMINATING}, + {ERRDOS, 87, NT_STATUS_BAD_WORKING_SET_LIMIT}, + {ERRDOS, 87, NT_STATUS_INCOMPATIBLE_FILE_MAP}, + {ERRDOS, 87, NT_STATUS_SECTION_PROTECTION}, + {ERRDOS, 282, NT_STATUS_EAS_NOT_SUPPORTED}, + {ERRDOS, 255, NT_STATUS_EA_TOO_LARGE}, + {ERRHRD, ERRgeneral, NT_STATUS_NONEXISTENT_EA_ENTRY}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_EAS_ON_FILE}, + {ERRHRD, ERRgeneral, NT_STATUS_EA_CORRUPT_ERROR}, + {ERRDOS, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT}, + {ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED}, + {ERRDOS, ERRnoaccess, NT_STATUS_DELETE_PENDING}, + {ERRDOS, ERRunsup, NT_STATUS_CTL_FILE_NOT_SUPPORTED}, + {ERRHRD, ERRgeneral, NT_STATUS_UNKNOWN_REVISION}, + {ERRHRD, ERRgeneral, NT_STATUS_REVISION_MISMATCH}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_OWNER}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_PRIMARY_GROUP}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_IMPERSONATION_TOKEN}, + {ERRHRD, ERRgeneral, NT_STATUS_CANT_DISABLE_MANDATORY}, + {ERRDOS, 2215, NT_STATUS_NO_LOGON_SERVERS}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_LOGON_SESSION}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PRIVILEGE}, + {ERRDOS, ERRnoaccess, NT_STATUS_PRIVILEGE_NOT_HELD}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACCOUNT_NAME}, + {ERRHRD, ERRgeneral, NT_STATUS_USER_EXISTS}, +/* { This NT error code was 'sqashed' + from NT_STATUS_NO_SUCH_USER to NT_STATUS_LOGON_FAILURE + during the session setup } +*/ + {ERRDOS, ERRnoaccess, NT_STATUS_NO_SUCH_USER}, + {ERRHRD, ERRgeneral, NT_STATUS_GROUP_EXISTS}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_GROUP}, + {ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_GROUP}, + {ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_GROUP}, + {ERRHRD, ERRgeneral, NT_STATUS_LAST_ADMIN}, +/* { This NT error code was 'sqashed' + from NT_STATUS_WRONG_PASSWORD to NT_STATUS_LOGON_FAILURE + during the session setup } +*/ + {ERRSRV, ERRbadpw, NT_STATUS_WRONG_PASSWORD}, + {ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_PASSWORD}, + {ERRHRD, ERRgeneral, NT_STATUS_PASSWORD_RESTRICTION}, + {ERRDOS, ERRnoaccess, NT_STATUS_LOGON_FAILURE}, + {ERRHRD, ERRgeneral, NT_STATUS_ACCOUNT_RESTRICTION}, + {ERRSRV, 2241, NT_STATUS_INVALID_LOGON_HOURS}, + {ERRSRV, 2240, NT_STATUS_INVALID_WORKSTATION}, + {ERRSRV, 2242, NT_STATUS_PASSWORD_EXPIRED}, + {ERRSRV, 2239, NT_STATUS_ACCOUNT_DISABLED}, + {ERRHRD, ERRgeneral, NT_STATUS_NONE_MAPPED}, + {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_LUIDS_REQUESTED}, + {ERRHRD, ERRgeneral, NT_STATUS_LUIDS_EXHAUSTED}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_SUB_AUTHORITY}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACL}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_SID}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_SECURITY_DESCR}, + {ERRDOS, 127, NT_STATUS_PROCEDURE_NOT_FOUND}, + {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_FORMAT}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_TOKEN}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_INHERITANCE_ACL}, + {ERRDOS, 158, NT_STATUS_RANGE_NOT_LOCKED}, + {ERRDOS, 112, NT_STATUS_DISK_FULL}, + {ERRHRD, ERRgeneral, NT_STATUS_SERVER_DISABLED}, + {ERRHRD, ERRgeneral, NT_STATUS_SERVER_NOT_DISABLED}, + {ERRDOS, 68, NT_STATUS_TOO_MANY_GUIDS_REQUESTED}, + {ERRDOS, 259, NT_STATUS_GUIDS_EXHAUSTED}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ID_AUTHORITY}, + {ERRDOS, 259, NT_STATUS_AGENTS_EXHAUSTED}, + {ERRDOS, 154, NT_STATUS_INVALID_VOLUME_LABEL}, + {ERRDOS, ERRres, NT_STATUS_SECTION_NOT_EXTENDED}, + {ERRDOS, 487, NT_STATUS_NOT_MAPPED_DATA}, + {ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_DATA_NOT_FOUND}, + {ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_TYPE_NOT_FOUND}, + {ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_NAME_NOT_FOUND}, + {ERRHRD, ERRgeneral, NT_STATUS_ARRAY_BOUNDS_EXCEEDED}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DENORMAL_OPERAND}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DIVIDE_BY_ZERO}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INEXACT_RESULT}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INVALID_OPERATION}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_OVERFLOW}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_STACK_CHECK}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_UNDERFLOW}, + {ERRHRD, ERRgeneral, NT_STATUS_INTEGER_DIVIDE_BY_ZERO}, + {ERRDOS, 534, NT_STATUS_INTEGER_OVERFLOW}, + {ERRHRD, ERRgeneral, NT_STATUS_PRIVILEGED_INSTRUCTION}, + {ERRDOS, ERRnomem, NT_STATUS_TOO_MANY_PAGING_FILES}, + {ERRHRD, ERRgeneral, NT_STATUS_FILE_INVALID}, + {ERRHRD, ERRgeneral, NT_STATUS_ALLOTTED_SPACE_EXCEEDED}, +/* { This NT error code was 'sqashed' + from NT_STATUS_INSUFFICIENT_RESOURCES to NT_STATUS_INSUFF_SERVER_RESOURCES + during the session setup } +*/ + {ERRDOS, ERRnomem, NT_STATUS_INSUFFICIENT_RESOURCES}, + {ERRDOS, ERRbadpath, NT_STATUS_DFS_EXIT_PATH_FOUND}, + {ERRDOS, 23, NT_STATUS_DEVICE_DATA_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_CONNECTED}, + {ERRDOS, 21, NT_STATUS_DEVICE_POWER_FAILURE}, + {ERRDOS, 487, NT_STATUS_FREE_VM_NOT_AT_BASE}, + {ERRDOS, 487, NT_STATUS_MEMORY_NOT_ALLOCATED}, + {ERRHRD, ERRgeneral, NT_STATUS_WORKING_SET_QUOTA}, + {ERRDOS, 19, NT_STATUS_MEDIA_WRITE_PROTECTED}, + {ERRDOS, 21, NT_STATUS_DEVICE_NOT_READY}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_GROUP_ATTRIBUTES}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_IMPERSONATION_LEVEL}, + {ERRHRD, ERRgeneral, NT_STATUS_CANT_OPEN_ANONYMOUS}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_VALIDATION_CLASS}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_TOKEN_TYPE}, + {ERRDOS, 87, NT_STATUS_BAD_MASTER_BOOT_RECORD}, + {ERRHRD, ERRgeneral, NT_STATUS_INSTRUCTION_MISALIGNMENT}, + {ERRDOS, ERRpipebusy, NT_STATUS_INSTANCE_NOT_AVAILABLE}, + {ERRDOS, ERRpipebusy, NT_STATUS_PIPE_NOT_AVAILABLE}, + {ERRDOS, ERRbadpipe, NT_STATUS_INVALID_PIPE_STATE}, + {ERRDOS, ERRpipebusy, NT_STATUS_PIPE_BUSY}, + {ERRDOS, ERRbadfunc, NT_STATUS_ILLEGAL_FUNCTION}, + {ERRDOS, ERRnotconnected, NT_STATUS_PIPE_DISCONNECTED}, + {ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_CLOSING}, + {ERRHRD, ERRgeneral, NT_STATUS_PIPE_CONNECTED}, + {ERRHRD, ERRgeneral, NT_STATUS_PIPE_LISTENING}, + {ERRDOS, ERRbadpipe, NT_STATUS_INVALID_READ_MODE}, + {ERRDOS, 121, NT_STATUS_IO_TIMEOUT}, + {ERRDOS, 38, NT_STATUS_FILE_FORCED_CLOSED}, + {ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STARTED}, + {ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STOPPED}, + {ERRHRD, ERRgeneral, NT_STATUS_COULD_NOT_INTERPRET}, + {ERRDOS, ERRnoaccess, NT_STATUS_FILE_IS_A_DIRECTORY}, + {ERRDOS, ERRunsup, NT_STATUS_NOT_SUPPORTED}, + {ERRDOS, 51, NT_STATUS_REMOTE_NOT_LISTENING}, + {ERRDOS, 52, NT_STATUS_DUPLICATE_NAME}, + {ERRDOS, 53, NT_STATUS_BAD_NETWORK_PATH}, + {ERRDOS, 54, NT_STATUS_NETWORK_BUSY}, + {ERRDOS, 55, NT_STATUS_DEVICE_DOES_NOT_EXIST}, + {ERRDOS, 56, NT_STATUS_TOO_MANY_COMMANDS}, + {ERRDOS, 57, NT_STATUS_ADAPTER_HARDWARE_ERROR}, + {ERRDOS, 58, NT_STATUS_INVALID_NETWORK_RESPONSE}, + {ERRDOS, 59, NT_STATUS_UNEXPECTED_NETWORK_ERROR}, + {ERRDOS, 60, NT_STATUS_BAD_REMOTE_ADAPTER}, + {ERRDOS, 61, NT_STATUS_PRINT_QUEUE_FULL}, + {ERRDOS, 62, NT_STATUS_NO_SPOOL_SPACE}, + {ERRDOS, 63, NT_STATUS_PRINT_CANCELLED}, + {ERRDOS, 64, NT_STATUS_NETWORK_NAME_DELETED}, + {ERRDOS, 65, NT_STATUS_NETWORK_ACCESS_DENIED}, + {ERRDOS, 66, NT_STATUS_BAD_DEVICE_TYPE}, + {ERRDOS, ERRnosuchshare, NT_STATUS_BAD_NETWORK_NAME}, + {ERRDOS, 68, NT_STATUS_TOO_MANY_NAMES}, + {ERRDOS, 69, NT_STATUS_TOO_MANY_SESSIONS}, + {ERRDOS, 70, NT_STATUS_SHARING_PAUSED}, + {ERRDOS, 71, NT_STATUS_REQUEST_NOT_ACCEPTED}, + {ERRDOS, 72, NT_STATUS_REDIRECTOR_PAUSED}, + {ERRDOS, 88, NT_STATUS_NET_WRITE_FAULT}, + {ERRHRD, ERRgeneral, NT_STATUS_PROFILING_AT_LIMIT}, + {ERRDOS, ERRdiffdevice, NT_STATUS_NOT_SAME_DEVICE}, + {ERRDOS, ERRnoaccess, NT_STATUS_FILE_RENAMED}, + {ERRDOS, 240, NT_STATUS_VIRTUAL_CIRCUIT_CLOSED}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_SECURITY_ON_OBJECT}, + {ERRHRD, ERRgeneral, NT_STATUS_CANT_WAIT}, + {ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_EMPTY}, + {ERRHRD, ERRgeneral, NT_STATUS_CANT_ACCESS_DOMAIN_INFO}, + {ERRHRD, ERRgeneral, NT_STATUS_CANT_TERMINATE_SELF}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_SERVER_STATE}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_STATE}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_ROLE}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_DOMAIN}, + {ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_EXISTS}, + {ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_LIMIT_EXCEEDED}, + {ERRDOS, 300, NT_STATUS_OPLOCK_NOT_GRANTED}, + {ERRDOS, 301, NT_STATUS_INVALID_OPLOCK_PROTOCOL}, + {ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_CORRUPTION}, + {ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_GENERIC_NOT_MAPPED}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_DESCRIPTOR_FORMAT}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_USER_BUFFER}, + {ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_IO_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_CREATE_ERR}, + {ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_MAP_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_EXTEND_ERR}, + {ERRHRD, ERRgeneral, NT_STATUS_NOT_LOGON_PROCESS}, + {ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_EXISTS}, + {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_1}, + {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_2}, + {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_3}, + {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_4}, + {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_5}, + {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_6}, + {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_7}, + {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_8}, + {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_9}, + {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_10}, + {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_11}, + {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_12}, + {ERRDOS, ERRbadpath, NT_STATUS_REDIRECTOR_NOT_STARTED}, + {ERRHRD, ERRgeneral, NT_STATUS_REDIRECTOR_STARTED}, + {ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PACKAGE}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_FUNCTION_TABLE}, + {ERRDOS, 203, NT_STATUS(0xc0000100)}, + {ERRDOS, 145, NT_STATUS_DIRECTORY_NOT_EMPTY}, + {ERRHRD, ERRgeneral, NT_STATUS_FILE_CORRUPT_ERROR}, + {ERRDOS, 267, NT_STATUS_NOT_A_DIRECTORY}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_LOGON_SESSION_STATE}, + {ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_COLLISION}, + {ERRDOS, 206, NT_STATUS_NAME_TOO_LONG}, + {ERRDOS, 2401, NT_STATUS_FILES_OPEN}, + {ERRDOS, 2404, NT_STATUS_CONNECTION_IN_USE}, + {ERRHRD, ERRgeneral, NT_STATUS_MESSAGE_NOT_FOUND}, + {ERRDOS, ERRnoaccess, NT_STATUS_PROCESS_IS_TERMINATING}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_LOGON_TYPE}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_GUID_TRANSLATION}, + {ERRHRD, ERRgeneral, NT_STATUS_CANNOT_IMPERSONATE}, + {ERRHRD, ERRgeneral, NT_STATUS_IMAGE_ALREADY_LOADED}, + {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_PRESENT}, + {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_NOT_EXIST}, + {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_ALREADY_OWNED}, + {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_LID_OWNER}, + {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_COMMAND}, + {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_LID}, + {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE}, + {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_SELECTOR}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_LDT}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_SIZE}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_OFFSET}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_DESCRIPTOR}, + {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NE_FORMAT}, + {ERRHRD, ERRgeneral, NT_STATUS_RXACT_INVALID_STATE}, + {ERRHRD, ERRgeneral, NT_STATUS_RXACT_COMMIT_FAILURE}, + {ERRHRD, ERRgeneral, NT_STATUS_MAPPED_FILE_SIZE_ZERO}, + {ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES}, + {ERRHRD, ERRgeneral, NT_STATUS_CANCELLED}, + {ERRDOS, ERRnoaccess, NT_STATUS_CANNOT_DELETE}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_COMPUTER_NAME}, + {ERRDOS, ERRnoaccess, NT_STATUS_FILE_DELETED}, + {ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_ACCOUNT}, + {ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_GROUP}, + {ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_USER}, + {ERRHRD, ERRgeneral, NT_STATUS_MEMBERS_PRIMARY_GROUP}, + {ERRDOS, ERRbadfid, NT_STATUS_FILE_CLOSED}, + {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_THREADS}, + {ERRHRD, ERRgeneral, NT_STATUS_THREAD_NOT_IN_PROCESS}, + {ERRHRD, ERRgeneral, NT_STATUS_TOKEN_ALREADY_IN_USE}, + {ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA_EXCEEDED}, + {ERRHRD, ERRgeneral, NT_STATUS_COMMITMENT_LIMIT}, + {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_LE_FORMAT}, + {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NOT_MZ}, + {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_PROTECT}, + {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_WIN_16}, + {ERRHRD, ERRgeneral, NT_STATUS_LOGON_SERVER_CONFLICT}, + {ERRHRD, ERRgeneral, NT_STATUS_TIME_DIFFERENCE_AT_DC}, + {ERRHRD, ERRgeneral, NT_STATUS_SYNCHRONIZATION_REQUIRED}, + {ERRDOS, 126, NT_STATUS_DLL_NOT_FOUND}, + {ERRHRD, ERRgeneral, NT_STATUS_OPEN_FAILED}, + {ERRHRD, ERRgeneral, NT_STATUS_IO_PRIVILEGE_FAILED}, + {ERRDOS, 182, NT_STATUS_ORDINAL_NOT_FOUND}, + {ERRDOS, 127, NT_STATUS_ENTRYPOINT_NOT_FOUND}, + {ERRHRD, ERRgeneral, NT_STATUS_CONTROL_C_EXIT}, + {ERRDOS, 64, NT_STATUS_LOCAL_DISCONNECT}, + {ERRDOS, 64, NT_STATUS_REMOTE_DISCONNECT}, + {ERRDOS, 51, NT_STATUS_REMOTE_RESOURCES}, + {ERRDOS, 59, NT_STATUS_LINK_FAILED}, + {ERRDOS, 59, NT_STATUS_LINK_TIMEOUT}, + {ERRDOS, 59, NT_STATUS_INVALID_CONNECTION}, + {ERRDOS, 59, NT_STATUS_INVALID_ADDRESS}, + {ERRHRD, ERRgeneral, NT_STATUS_DLL_INIT_FAILED}, + {ERRHRD, ERRgeneral, NT_STATUS_MISSING_SYSTEMFILE}, + {ERRHRD, ERRgeneral, NT_STATUS_UNHANDLED_EXCEPTION}, + {ERRHRD, ERRgeneral, NT_STATUS_APP_INIT_FAILURE}, + {ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_CREATE_FAILED}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_PAGEFILE}, + {ERRDOS, 124, NT_STATUS_INVALID_LEVEL}, + {ERRDOS, 86, NT_STATUS_WRONG_PASSWORD_CORE}, + {ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_FLOAT_CONTEXT}, + {ERRDOS, 109, NT_STATUS_PIPE_BROKEN}, + {ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_CORRUPT}, + {ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_IO_FAILED}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_EVENT_PAIR}, + {ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_VOLUME}, + {ERRHRD, ERRgeneral, NT_STATUS_SERIAL_NO_DEVICE_INITED}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_ALIAS}, + {ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_ALIAS}, + {ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_ALIAS}, + {ERRHRD, ERRgeneral, NT_STATUS_ALIAS_EXISTS}, + {ERRHRD, ERRgeneral, NT_STATUS_LOGON_NOT_GRANTED}, + {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SECRETS}, + {ERRHRD, ERRgeneral, NT_STATUS_SECRET_TOO_LONG}, + {ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_FULLSCREEN_MODE}, + {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_CONTEXT_IDS}, + {ERRDOS, ERRnoaccess, NT_STATUS_LOGON_TYPE_NOT_GRANTED}, + {ERRHRD, ERRgeneral, NT_STATUS_NOT_REGISTRY_FILE}, + {ERRHRD, ERRgeneral, NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED}, + {ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_FT_MISSING_MEMBER}, + {ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_SERVICE_ENTRY}, + {ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_CHARACTER}, + {ERRHRD, ERRgeneral, NT_STATUS_UNMAPPABLE_CHARACTER}, + {ERRHRD, ERRgeneral, NT_STATUS_UNDEFINED_CHARACTER}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_VOLUME}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_WRONG_CYLINDER}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_UNKNOWN_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_BAD_REGISTERS}, + {ERRHRD, ERRgeneral, NT_STATUS_DISK_RECALIBRATE_FAILED}, + {ERRHRD, ERRgeneral, NT_STATUS_DISK_OPERATION_FAILED}, + {ERRHRD, ERRgeneral, NT_STATUS_DISK_RESET_FAILED}, + {ERRHRD, ERRgeneral, NT_STATUS_SHARED_IRQ_BUSY}, + {ERRHRD, ERRgeneral, NT_STATUS_FT_ORPHANING}, + {ERRHRD, ERRgeneral, NT_STATUS(0xc000016e)}, + {ERRHRD, ERRgeneral, NT_STATUS(0xc000016f)}, + {ERRHRD, ERRgeneral, NT_STATUS(0xc0000170)}, + {ERRHRD, ERRgeneral, NT_STATUS(0xc0000171)}, + {ERRHRD, ERRgeneral, NT_STATUS_PARTITION_FAILURE}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_BLOCK_LENGTH}, + {ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_PARTITIONED}, + {ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_LOCK_MEDIA}, + {ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_UNLOAD_MEDIA}, + {ERRHRD, ERRgeneral, NT_STATUS_EOM_OVERFLOW}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_MEDIA}, + {ERRHRD, ERRgeneral, NT_STATUS(0xc0000179)}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_MEMBER}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_MEMBER}, + {ERRHRD, ERRgeneral, NT_STATUS_KEY_DELETED}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_LOG_SPACE}, + {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SIDS}, + {ERRHRD, ERRgeneral, NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED}, + {ERRHRD, ERRgeneral, NT_STATUS_KEY_HAS_CHILDREN}, + {ERRHRD, ERRgeneral, NT_STATUS_CHILD_MUST_BE_VOLATILE}, + {ERRDOS, 87, NT_STATUS_DEVICE_CONFIGURATION_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_DRIVER_INTERNAL_ERROR}, + {ERRDOS, 22, NT_STATUS_INVALID_DEVICE_STATE}, + {ERRHRD, ERRgeneral, NT_STATUS_IO_DEVICE_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_DEVICE_PROTOCOL_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_BACKUP_CONTROLLER}, + {ERRHRD, ERRgeneral, NT_STATUS_LOG_FILE_FULL}, + {ERRDOS, 19, NT_STATUS_TOO_LATE}, + {ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_LSA_SECRET}, +/* { This NT error code was 'sqashed' + from NT_STATUS_NO_TRUST_SAM_ACCOUNT to NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE + during the session setup } +*/ + {ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_SAM_ACCOUNT}, + {ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_DOMAIN_FAILURE}, + {ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE}, + {ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CORRUPT}, + {ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_CANT_START}, + {ERRDOS, ERRnoaccess, NT_STATUS_TRUST_FAILURE}, + {ERRHRD, ERRgeneral, NT_STATUS_MUTANT_LIMIT_EXCEEDED}, + {ERRDOS, ERRinvgroup, NT_STATUS_NETLOGON_NOT_STARTED}, + {ERRSRV, 2239, NT_STATUS_ACCOUNT_EXPIRED}, + {ERRHRD, ERRgeneral, NT_STATUS_POSSIBLE_DEADLOCK}, + {ERRHRD, ERRgeneral, NT_STATUS_NETWORK_CREDENTIAL_CONFLICT}, + {ERRHRD, ERRgeneral, NT_STATUS_REMOTE_SESSION_LIMIT}, + {ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CHANGED}, + {ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT}, + {ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT}, + {ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT}, +/* { This NT error code was 'sqashed' + from NT_STATUS_DOMAIN_TRUST_INCONSISTENT to NT_STATUS_LOGON_FAILURE + during the session setup } +*/ + {ERRDOS, ERRnoaccess, NT_STATUS_DOMAIN_TRUST_INCONSISTENT}, + {ERRHRD, ERRgeneral, NT_STATUS_FS_DRIVER_REQUIRED}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_USER_SESSION_KEY}, + {ERRDOS, 59, NT_STATUS_USER_SESSION_DELETED}, + {ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_LANG_NOT_FOUND}, + {ERRDOS, ERRnomem, NT_STATUS_INSUFF_SERVER_RESOURCES}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_BUFFER_SIZE}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_COMPONENT}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_WILDCARD}, + {ERRDOS, 68, NT_STATUS_TOO_MANY_ADDRESSES}, + {ERRDOS, 52, NT_STATUS_ADDRESS_ALREADY_EXISTS}, + {ERRDOS, 64, NT_STATUS_ADDRESS_CLOSED}, + {ERRDOS, 64, NT_STATUS_CONNECTION_DISCONNECTED}, + {ERRDOS, 64, NT_STATUS_CONNECTION_RESET}, + {ERRDOS, 68, NT_STATUS_TOO_MANY_NODES}, + {ERRDOS, 59, NT_STATUS_TRANSACTION_ABORTED}, + {ERRDOS, 59, NT_STATUS_TRANSACTION_TIMED_OUT}, + {ERRDOS, 59, NT_STATUS_TRANSACTION_NO_RELEASE}, + {ERRDOS, 59, NT_STATUS_TRANSACTION_NO_MATCH}, + {ERRDOS, 59, NT_STATUS_TRANSACTION_RESPONDED}, + {ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_ID}, + {ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_TYPE}, + {ERRDOS, ERRunsup, NT_STATUS_NOT_SERVER_SESSION}, + {ERRDOS, ERRunsup, NT_STATUS_NOT_CLIENT_SESSION}, + {ERRHRD, ERRgeneral, NT_STATUS_CANNOT_LOAD_REGISTRY_FILE}, + {ERRHRD, ERRgeneral, NT_STATUS_DEBUG_ATTACH_FAILED}, + {ERRHRD, ERRgeneral, NT_STATUS_SYSTEM_PROCESS_TERMINATED}, + {ERRHRD, ERRgeneral, NT_STATUS_DATA_NOT_ACCEPTED}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_BROWSER_SERVERS_FOUND}, + {ERRHRD, ERRgeneral, NT_STATUS_VDM_HARD_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_DRIVER_CANCEL_TIMEOUT}, + {ERRHRD, ERRgeneral, NT_STATUS_REPLY_MESSAGE_MISMATCH}, + {ERRHRD, ERRgeneral, NT_STATUS_MAPPED_ALIGNMENT}, + {ERRDOS, 193, NT_STATUS_IMAGE_CHECKSUM_MISMATCH}, + {ERRHRD, ERRgeneral, NT_STATUS_LOST_WRITEBEHIND_DATA}, + {ERRHRD, ERRgeneral, NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID}, + {ERRSRV, 2242, NT_STATUS_PASSWORD_MUST_CHANGE}, + {ERRHRD, ERRgeneral, NT_STATUS_NOT_FOUND}, + {ERRHRD, ERRgeneral, NT_STATUS_NOT_TINY_STREAM}, + {ERRHRD, ERRgeneral, NT_STATUS_RECOVERY_FAILURE}, + {ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW_READ}, + {ERRHRD, ERRgeneral, NT_STATUS_FAIL_CHECK}, + {ERRHRD, ERRgeneral, NT_STATUS_DUPLICATE_OBJECTID}, + {ERRHRD, ERRgeneral, NT_STATUS_OBJECTID_EXISTS}, + {ERRHRD, ERRgeneral, NT_STATUS_CONVERT_TO_LARGE}, + {ERRHRD, ERRgeneral, NT_STATUS_RETRY}, + {ERRHRD, ERRgeneral, NT_STATUS_FOUND_OUT_OF_SCOPE}, + {ERRHRD, ERRgeneral, NT_STATUS_ALLOCATE_BUCKET}, + {ERRHRD, ERRgeneral, NT_STATUS_PROPSET_NOT_FOUND}, + {ERRHRD, ERRgeneral, NT_STATUS_MARSHALL_OVERFLOW}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_VARIANT}, + {ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND}, + {ERRDOS, ERRnoaccess, NT_STATUS_ACCOUNT_LOCKED_OUT}, + {ERRDOS, ERRbadfid, NT_STATUS_HANDLE_NOT_CLOSABLE}, + {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_REFUSED}, + {ERRHRD, ERRgeneral, NT_STATUS_GRACEFUL_DISCONNECT}, + {ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_ALREADY_ASSOCIATED}, + {ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_NOT_ASSOCIATED}, + {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_INVALID}, + {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ACTIVE}, + {ERRHRD, ERRgeneral, NT_STATUS_NETWORK_UNREACHABLE}, + {ERRHRD, ERRgeneral, NT_STATUS_HOST_UNREACHABLE}, + {ERRHRD, ERRgeneral, NT_STATUS_PROTOCOL_UNREACHABLE}, + {ERRHRD, ERRgeneral, NT_STATUS_PORT_UNREACHABLE}, + {ERRHRD, ERRgeneral, NT_STATUS_REQUEST_ABORTED}, + {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ABORTED}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_COMPRESSION_BUFFER}, + {ERRHRD, ERRgeneral, NT_STATUS_USER_MAPPED_FILE}, + {ERRHRD, ERRgeneral, NT_STATUS_AUDIT_FAILED}, + {ERRHRD, ERRgeneral, NT_STATUS_TIMER_RESOLUTION_NOT_SET}, + {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_COUNT_LIMIT}, + {ERRHRD, ERRgeneral, NT_STATUS_LOGIN_TIME_RESTRICTION}, + {ERRHRD, ERRgeneral, NT_STATUS_LOGIN_WKSTA_RESTRICTION}, + {ERRDOS, 193, NT_STATUS_IMAGE_MP_UP_MISMATCH}, + {ERRHRD, ERRgeneral, NT_STATUS(0xc000024a)}, + {ERRHRD, ERRgeneral, NT_STATUS(0xc000024b)}, + {ERRHRD, ERRgeneral, NT_STATUS(0xc000024c)}, + {ERRHRD, ERRgeneral, NT_STATUS(0xc000024d)}, + {ERRHRD, ERRgeneral, NT_STATUS(0xc000024e)}, + {ERRHRD, ERRgeneral, NT_STATUS(0xc000024f)}, + {ERRHRD, ERRgeneral, NT_STATUS_INSUFFICIENT_LOGON_INFO}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_DLL_ENTRYPOINT}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_SERVICE_ENTRYPOINT}, + {ERRHRD, ERRgeneral, NT_STATUS_LPC_REPLY_LOST}, + {ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT1}, + {ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT2}, + {ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_QUOTA_LIMIT}, + {ERRSRV, ERRbadtype, NT_STATUS_PATH_NOT_COVERED}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_CALLBACK_ACTIVE}, + {ERRHRD, ERRgeneral, NT_STATUS_LICENSE_QUOTA_EXCEEDED}, + {ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_SHORT}, + {ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_RECENT}, + {ERRHRD, ERRgeneral, NT_STATUS_PWD_HISTORY_CONFLICT}, + {ERRHRD, ERRgeneral, NT_STATUS(0xc000025d)}, + {ERRHRD, ERRgeneral, NT_STATUS_PLUGPLAY_NO_DEVICE}, + {ERRHRD, ERRgeneral, NT_STATUS_UNSUPPORTED_COMPRESSION}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_HW_PROFILE}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH}, + {ERRDOS, 182, NT_STATUS_DRIVER_ORDINAL_NOT_FOUND}, + {ERRDOS, 127, NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND}, + {ERRDOS, 288, NT_STATUS_RESOURCE_NOT_OWNED}, + {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_LINKS}, + {ERRHRD, ERRgeneral, NT_STATUS_QUOTA_LIST_INCONSISTENT}, + {ERRHRD, ERRgeneral, NT_STATUS_FILE_IS_OFFLINE}, + {ERRDOS, 21, NT_STATUS(0xc000026e)}, + {ERRDOS, 161, NT_STATUS(0xc0000281)}, + {ERRDOS, ERRnoaccess, NT_STATUS(0xc000028a)}, + {ERRDOS, ERRnoaccess, NT_STATUS(0xc000028b)}, + {ERRHRD, ERRgeneral, NT_STATUS(0xc000028c)}, + {ERRDOS, ERRnoaccess, NT_STATUS(0xc000028d)}, + {ERRDOS, ERRnoaccess, NT_STATUS(0xc000028e)}, + {ERRDOS, ERRnoaccess, NT_STATUS(0xc000028f)}, + {ERRDOS, ERRnoaccess, NT_STATUS(0xc0000290)}, + {ERRDOS, ERRbadfunc, NT_STATUS(0xc000029c)}, +}; + + +/* dos -> nt status error map */ +const static struct { + uint8 dos_class; + uint32 dos_code; + NTSTATUS ntstatus; +} dos_to_ntstatus_map[] = { + {ERRDOS, ERRbadfunc, NT_STATUS_NOT_IMPLEMENTED}, + {ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_FILE}, + {ERRDOS, ERRbadpath, NT_STATUS_OBJECT_PATH_NOT_FOUND}, + {ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES}, + {ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED}, + {ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE}, + {ERRDOS, ERRnomem, NT_STATUS_INSUFFICIENT_RESOURCES}, + {ERRDOS, ERRbadaccess, NT_STATUS_INVALID_LOCK_SEQUENCE}, + {ERRDOS, ERRbaddata, NT_STATUS_DATA_ERROR}, + {ERRDOS, 14, NT_STATUS_SECTION_NOT_EXTENDED}, + {ERRDOS, ERRremcd, NT_STATUS_DIRECTORY_NOT_EMPTY}, + {ERRDOS, ERRdiffdevice, NT_STATUS_NOT_SAME_DEVICE}, + {ERRDOS, ERRnofiles, NT_STATUS(0x80000006)}, + {ERRDOS, 19, NT_STATUS_MEDIA_WRITE_PROTECTED}, + {ERRDOS, 21, NT_STATUS_NO_MEDIA_IN_DEVICE}, + {ERRDOS, 22, NT_STATUS_INVALID_DEVICE_STATE}, + {ERRDOS, 23, NT_STATUS_DATA_ERROR}, + {ERRDOS, 24, NT_STATUS_DATA_ERROR}, + {ERRDOS, 26, NT_STATUS_DISK_CORRUPT_ERROR}, + {ERRDOS, 27, NT_STATUS_NONEXISTENT_SECTOR}, + {ERRDOS, 28, NT_STATUS(0x8000000e)}, + {ERRDOS, 31, NT_STATUS_UNSUCCESSFUL}, + {ERRDOS, ERRbadshare, NT_STATUS_SHARING_VIOLATION}, + {ERRDOS, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT}, + {ERRDOS, 34, NT_STATUS_WRONG_VOLUME}, + {ERRDOS, 38, NT_STATUS_END_OF_FILE}, + {ERRDOS, ERRunsup, NT_STATUS_CTL_FILE_NOT_SUPPORTED}, + {ERRDOS, 51, NT_STATUS_REMOTE_NOT_LISTENING}, + {ERRDOS, 52, NT_STATUS_DUPLICATE_NAME}, + {ERRDOS, 53, NT_STATUS_BAD_NETWORK_PATH}, + {ERRDOS, 54, NT_STATUS_NETWORK_BUSY}, + {ERRDOS, 55, NT_STATUS_DEVICE_DOES_NOT_EXIST}, + {ERRDOS, 56, NT_STATUS_TOO_MANY_COMMANDS}, + {ERRDOS, 57, NT_STATUS_ADAPTER_HARDWARE_ERROR}, + {ERRDOS, 58, NT_STATUS_INVALID_NETWORK_RESPONSE}, + {ERRDOS, 59, NT_STATUS_UNEXPECTED_NETWORK_ERROR}, + {ERRDOS, 60, NT_STATUS_BAD_REMOTE_ADAPTER}, + {ERRDOS, 61, NT_STATUS_PRINT_QUEUE_FULL}, + {ERRDOS, 62, NT_STATUS_NO_SPOOL_SPACE}, + {ERRDOS, 63, NT_STATUS_PRINT_CANCELLED}, + {ERRDOS, 64, NT_STATUS_NETWORK_NAME_DELETED}, + {ERRDOS, 65, NT_STATUS_NETWORK_ACCESS_DENIED}, + {ERRDOS, 66, NT_STATUS_BAD_DEVICE_TYPE}, + {ERRDOS, ERRnosuchshare, NT_STATUS_BAD_NETWORK_NAME}, + {ERRDOS, 68, NT_STATUS_TOO_MANY_GUIDS_REQUESTED}, + {ERRDOS, 69, NT_STATUS_TOO_MANY_SESSIONS}, + {ERRDOS, 70, NT_STATUS_SHARING_PAUSED}, + {ERRDOS, 71, NT_STATUS_REQUEST_NOT_ACCEPTED}, + {ERRDOS, 72, NT_STATUS_REDIRECTOR_PAUSED}, + {ERRDOS, ERRfilexists, NT_STATUS_OBJECT_NAME_COLLISION}, + {ERRDOS, 86, NT_STATUS_WRONG_PASSWORD}, + {ERRDOS, 87, NT_STATUS_INVALID_INFO_CLASS}, + {ERRDOS, 88, NT_STATUS_NET_WRITE_FAULT}, + {ERRDOS, 109, NT_STATUS_PIPE_BROKEN}, + {ERRDOS, 111, STATUS_MORE_ENTRIES}, + {ERRDOS, 112, NT_STATUS_DISK_FULL}, + {ERRDOS, 121, NT_STATUS_IO_TIMEOUT}, + {ERRDOS, 122, NT_STATUS_BUFFER_TOO_SMALL}, + {ERRDOS, ERRinvalidname, NT_STATUS_OBJECT_NAME_INVALID}, + {ERRDOS, 124, NT_STATUS_INVALID_LEVEL}, + {ERRDOS, 126, NT_STATUS_DLL_NOT_FOUND}, + {ERRDOS, 127, NT_STATUS_PROCEDURE_NOT_FOUND}, + {ERRDOS, 145, NT_STATUS_DIRECTORY_NOT_EMPTY}, + {ERRDOS, 154, NT_STATUS_INVALID_VOLUME_LABEL}, + {ERRDOS, 156, NT_STATUS_SUSPEND_COUNT_EXCEEDED}, + {ERRDOS, 158, NT_STATUS_NOT_LOCKED}, + {ERRDOS, 161, NT_STATUS_OBJECT_PATH_INVALID}, + {ERRDOS, 170, NT_STATUS(0x80000011)}, + {ERRDOS, 182, NT_STATUS_ORDINAL_NOT_FOUND}, + {ERRDOS, 183, NT_STATUS_OBJECT_NAME_COLLISION}, + {ERRDOS, 193, NT_STATUS_BAD_INITIAL_PC}, + {ERRDOS, 203, NT_STATUS(0xc0000100)}, + {ERRDOS, 206, NT_STATUS_NAME_TOO_LONG}, + {ERRDOS, ERRbadpipe, NT_STATUS_INVALID_INFO_CLASS}, + {ERRDOS, ERRpipebusy, NT_STATUS_INSTANCE_NOT_AVAILABLE}, + {ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_CLOSING}, + {ERRDOS, ERRnotconnected, NT_STATUS_PIPE_DISCONNECTED}, + {ERRDOS, ERRmoredata, NT_STATUS_MORE_PROCESSING_REQUIRED}, + {ERRDOS, 240, NT_STATUS_VIRTUAL_CIRCUIT_CLOSED}, + {ERRDOS, 254, NT_STATUS(0x80000013)}, + {ERRDOS, 255, NT_STATUS_EA_TOO_LARGE}, + {ERRDOS, 259, NT_STATUS_GUIDS_EXHAUSTED}, + {ERRDOS, 267, NT_STATUS_NOT_A_DIRECTORY}, + {ERRDOS, 275, NT_STATUS_EA_TOO_LARGE}, + {ERRDOS, 276, NT_STATUS_NONEXISTENT_EA_ENTRY}, + {ERRDOS, 277, NT_STATUS_NONEXISTENT_EA_ENTRY}, + {ERRDOS, 278, NT_STATUS_NONEXISTENT_EA_ENTRY}, + {ERRDOS, 282, NT_STATUS_EAS_NOT_SUPPORTED}, + {ERRDOS, 288, NT_STATUS_MUTANT_NOT_OWNED}, + {ERRDOS, 298, NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED}, + {ERRDOS, 299, NT_STATUS(0x8000000d)}, + {ERRDOS, 300, NT_STATUS_OPLOCK_NOT_GRANTED}, + {ERRDOS, 301, NT_STATUS_INVALID_OPLOCK_PROTOCOL}, + {ERRDOS, 487, NT_STATUS_CONFLICTING_ADDRESSES}, + {ERRDOS, 534, NT_STATUS_INTEGER_OVERFLOW}, + {ERRDOS, 535, NT_STATUS_PIPE_CONNECTED}, + {ERRDOS, 536, NT_STATUS_PIPE_LISTENING}, + {ERRDOS, 995, NT_STATUS_CANCELLED}, + {ERRDOS, 997, NT_STATUS(0x00000103)}, + {ERRDOS, 998, NT_STATUS_ACCESS_VIOLATION}, + {ERRDOS, 999, NT_STATUS_IN_PAGE_ERROR}, + {ERRDOS, 1001, NT_STATUS_BAD_INITIAL_STACK}, + {ERRDOS, 1005, NT_STATUS_UNRECOGNIZED_VOLUME}, + {ERRDOS, 1006, NT_STATUS_FILE_INVALID}, + {ERRDOS, 1007, NT_STATUS_FULLSCREEN_MODE}, + {ERRDOS, 1008, NT_STATUS_NO_TOKEN}, + {ERRDOS, 1009, NT_STATUS_REGISTRY_CORRUPT}, + {ERRDOS, 1016, NT_STATUS_REGISTRY_IO_FAILED}, + {ERRDOS, 1017, NT_STATUS_NOT_REGISTRY_FILE}, + {ERRDOS, 1018, NT_STATUS_KEY_DELETED}, + {ERRDOS, 1019, NT_STATUS_NO_LOG_SPACE}, + {ERRDOS, 1020, NT_STATUS_KEY_HAS_CHILDREN}, + {ERRDOS, 1021, NT_STATUS_CHILD_MUST_BE_VOLATILE}, + {ERRDOS, 1022, NT_STATUS(0x0000010c)}, + {ERRSRV, ERRbadpw, NT_STATUS_WRONG_PASSWORD}, + {ERRSRV, ERRbadtype, NT_STATUS_BAD_DEVICE_TYPE}, + {ERRSRV, ERRaccess, NT_STATUS_NETWORK_ACCESS_DENIED}, + {ERRSRV, ERRinvnid, NT_STATUS_NETWORK_NAME_DELETED}, + {ERRSRV, ERRinvnetname, NT_STATUS_BAD_NETWORK_NAME}, + {ERRSRV, ERRinvdevice, NT_STATUS_BAD_DEVICE_TYPE}, + {ERRSRV, ERRqfull, NT_STATUS_PRINT_QUEUE_FULL}, + {ERRSRV, ERRqtoobig, NT_STATUS_NO_SPOOL_SPACE}, + {ERRSRV, ERRinvpfid, NT_STATUS_PRINT_CANCELLED}, + {ERRSRV, ERRsmbcmd, NT_STATUS_NOT_IMPLEMENTED}, + {ERRSRV, ERRbadpermits, NT_STATUS_NETWORK_ACCESS_DENIED}, + {ERRSRV, ERRpaused, NT_STATUS_SHARING_PAUSED}, + {ERRSRV, ERRmsgoff, NT_STATUS_REQUEST_NOT_ACCEPTED}, + {ERRSRV, ERRnoroom, NT_STATUS_DISK_FULL}, + {ERRSRV, ERRnoresource, NT_STATUS_REQUEST_NOT_ACCEPTED}, + {ERRSRV, ERRtoomanyuids, NT_STATUS_TOO_MANY_SESSIONS}, + {ERRSRV, 123, NT_STATUS_OBJECT_NAME_INVALID}, + {ERRSRV, 206, NT_STATUS_OBJECT_NAME_INVALID}, + {ERRHRD, 1, NT_STATUS_NOT_IMPLEMENTED}, + {ERRHRD, 2, NT_STATUS_NO_SUCH_DEVICE}, + {ERRHRD, 3, NT_STATUS_OBJECT_PATH_NOT_FOUND}, + {ERRHRD, 4, NT_STATUS_TOO_MANY_OPENED_FILES}, + {ERRHRD, 5, NT_STATUS_INVALID_LOCK_SEQUENCE}, + {ERRHRD, 6, NT_STATUS_INVALID_HANDLE}, + {ERRHRD, 8, NT_STATUS_INSUFFICIENT_RESOURCES}, + {ERRHRD, 12, NT_STATUS_INVALID_LOCK_SEQUENCE}, + {ERRHRD, 13, NT_STATUS_DATA_ERROR}, + {ERRHRD, 14, NT_STATUS_SECTION_NOT_EXTENDED}, + {ERRHRD, 16, NT_STATUS_DIRECTORY_NOT_EMPTY}, + {ERRHRD, 17, NT_STATUS_NOT_SAME_DEVICE}, + {ERRHRD, 18, NT_STATUS(0x80000006)}, + {ERRHRD, ERRnowrite, NT_STATUS_MEDIA_WRITE_PROTECTED}, + {ERRHRD, ERRnotready, NT_STATUS_NO_MEDIA_IN_DEVICE}, + {ERRHRD, ERRbadcmd, NT_STATUS_INVALID_DEVICE_STATE}, + {ERRHRD, ERRdata, NT_STATUS_DATA_ERROR}, + {ERRHRD, ERRbadreq, NT_STATUS_DATA_ERROR}, + {ERRHRD, ERRbadmedia, NT_STATUS_DISK_CORRUPT_ERROR}, + {ERRHRD, ERRbadsector, NT_STATUS_NONEXISTENT_SECTOR}, + {ERRHRD, ERRnopaper, NT_STATUS(0x8000000e)}, + {ERRHRD, ERRgeneral, NT_STATUS_UNSUCCESSFUL}, + {ERRHRD, ERRbadshare, NT_STATUS_SHARING_VIOLATION}, + {ERRHRD, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT}, + {ERRHRD, ERRwrongdisk, NT_STATUS_WRONG_VOLUME}, + {ERRHRD, 38, NT_STATUS_END_OF_FILE}, + {ERRHRD, ERRdiskfull, NT_STATUS_DISK_FULL}, + {ERRHRD, 50, NT_STATUS_CTL_FILE_NOT_SUPPORTED}, + {ERRHRD, 51, NT_STATUS_REMOTE_NOT_LISTENING}, + {ERRHRD, 52, NT_STATUS_DUPLICATE_NAME}, + {ERRHRD, 53, NT_STATUS_BAD_NETWORK_PATH}, + {ERRHRD, 54, NT_STATUS_NETWORK_BUSY}, + {ERRHRD, 55, NT_STATUS_DEVICE_DOES_NOT_EXIST}, + {ERRHRD, 56, NT_STATUS_TOO_MANY_COMMANDS}, + {ERRHRD, 57, NT_STATUS_ADAPTER_HARDWARE_ERROR}, + {ERRHRD, 58, NT_STATUS_INVALID_NETWORK_RESPONSE}, + {ERRHRD, 59, NT_STATUS_UNEXPECTED_NETWORK_ERROR}, + {ERRHRD, 60, NT_STATUS_BAD_REMOTE_ADAPTER}, + {ERRHRD, 61, NT_STATUS_PRINT_QUEUE_FULL}, + {ERRHRD, 62, NT_STATUS_NO_SPOOL_SPACE}, + {ERRHRD, 63, NT_STATUS_PRINT_CANCELLED}, + {ERRHRD, 64, NT_STATUS_NETWORK_NAME_DELETED}, + {ERRHRD, 65, NT_STATUS_NETWORK_ACCESS_DENIED}, + {ERRHRD, 66, NT_STATUS_BAD_DEVICE_TYPE}, + {ERRHRD, 67, NT_STATUS_BAD_NETWORK_NAME}, + {ERRHRD, 68, NT_STATUS_TOO_MANY_GUIDS_REQUESTED}, + {ERRHRD, 69, NT_STATUS_TOO_MANY_SESSIONS}, + {ERRHRD, 70, NT_STATUS_SHARING_PAUSED}, + {ERRHRD, 71, NT_STATUS_REQUEST_NOT_ACCEPTED}, + {ERRHRD, 72, NT_STATUS_REDIRECTOR_PAUSED}, + {ERRHRD, 80, NT_STATUS_OBJECT_NAME_COLLISION}, + {ERRHRD, 86, NT_STATUS_WRONG_PASSWORD}, + {ERRHRD, 87, NT_STATUS_INVALID_INFO_CLASS}, + {ERRHRD, 88, NT_STATUS_NET_WRITE_FAULT}, + {ERRHRD, 109, NT_STATUS_PIPE_BROKEN}, + {ERRHRD, 111, STATUS_MORE_ENTRIES}, + {ERRHRD, 112, NT_STATUS_DISK_FULL}, + {ERRHRD, 121, NT_STATUS_IO_TIMEOUT}, + {ERRHRD, 122, NT_STATUS_BUFFER_TOO_SMALL}, + {ERRHRD, 123, NT_STATUS_OBJECT_NAME_INVALID}, + {ERRHRD, 124, NT_STATUS_INVALID_LEVEL}, + {ERRHRD, 126, NT_STATUS_DLL_NOT_FOUND}, + {ERRHRD, 127, NT_STATUS_PROCEDURE_NOT_FOUND}, + {ERRHRD, 145, NT_STATUS_DIRECTORY_NOT_EMPTY}, + {ERRHRD, 154, NT_STATUS_INVALID_VOLUME_LABEL}, + {ERRHRD, 156, NT_STATUS_SUSPEND_COUNT_EXCEEDED}, + {ERRHRD, 158, NT_STATUS_NOT_LOCKED}, + {ERRHRD, 161, NT_STATUS_OBJECT_PATH_INVALID}, + {ERRHRD, 170, NT_STATUS(0x80000011)}, + {ERRHRD, 182, NT_STATUS_ORDINAL_NOT_FOUND}, + {ERRHRD, 183, NT_STATUS_OBJECT_NAME_COLLISION}, + {ERRHRD, 193, NT_STATUS_BAD_INITIAL_PC}, + {ERRHRD, 203, NT_STATUS(0xc0000100)}, + {ERRHRD, 206, NT_STATUS_NAME_TOO_LONG}, + {ERRHRD, 230, NT_STATUS_INVALID_INFO_CLASS}, + {ERRHRD, 231, NT_STATUS_INSTANCE_NOT_AVAILABLE}, + {ERRHRD, 232, NT_STATUS_PIPE_CLOSING}, + {ERRHRD, 233, NT_STATUS_PIPE_DISCONNECTED}, + {ERRHRD, 234, STATUS_MORE_ENTRIES}, + {ERRHRD, 240, NT_STATUS_VIRTUAL_CIRCUIT_CLOSED}, + {ERRHRD, 254, NT_STATUS(0x80000013)}, + {ERRHRD, 255, NT_STATUS_EA_TOO_LARGE}, + {ERRHRD, 259, NT_STATUS_GUIDS_EXHAUSTED}, + {ERRHRD, 267, NT_STATUS_NOT_A_DIRECTORY}, + {ERRHRD, 275, NT_STATUS_EA_TOO_LARGE}, + {ERRHRD, 276, NT_STATUS_NONEXISTENT_EA_ENTRY}, + {ERRHRD, 277, NT_STATUS_NONEXISTENT_EA_ENTRY}, + {ERRHRD, 278, NT_STATUS_NONEXISTENT_EA_ENTRY}, + {ERRHRD, 282, NT_STATUS_EAS_NOT_SUPPORTED}, + {ERRHRD, 288, NT_STATUS_MUTANT_NOT_OWNED}, + {ERRHRD, 298, NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED}, + {ERRHRD, 299, NT_STATUS(0x8000000d)}, + {ERRHRD, 300, NT_STATUS_OPLOCK_NOT_GRANTED}, + {ERRHRD, 301, NT_STATUS_INVALID_OPLOCK_PROTOCOL}, + {ERRHRD, 487, NT_STATUS_CONFLICTING_ADDRESSES}, + {ERRHRD, 534, NT_STATUS_INTEGER_OVERFLOW}, + {ERRHRD, 535, NT_STATUS_PIPE_CONNECTED}, + {ERRHRD, 536, NT_STATUS_PIPE_LISTENING}, + {ERRHRD, 995, NT_STATUS_CANCELLED}, + {ERRHRD, 997, NT_STATUS(0x00000103)}, + {ERRHRD, 998, NT_STATUS_ACCESS_VIOLATION}, + {ERRHRD, 999, NT_STATUS_IN_PAGE_ERROR}, + {ERRHRD, 1001, NT_STATUS_BAD_INITIAL_STACK}, + {ERRHRD, 1005, NT_STATUS_UNRECOGNIZED_VOLUME}, + {ERRHRD, 1006, NT_STATUS_FILE_INVALID}, + {ERRHRD, 1007, NT_STATUS_FULLSCREEN_MODE}, + {ERRHRD, 1008, NT_STATUS_NO_TOKEN}, + {ERRHRD, 1009, NT_STATUS_REGISTRY_CORRUPT}, + {ERRHRD, 1016, NT_STATUS_REGISTRY_IO_FAILED}, + {ERRHRD, 1017, NT_STATUS_NOT_REGISTRY_FILE}, + {ERRHRD, 1018, NT_STATUS_KEY_DELETED}, + {ERRHRD, 1019, NT_STATUS_NO_LOG_SPACE}, + {ERRHRD, 1020, NT_STATUS_KEY_HAS_CHILDREN}, + {ERRHRD, 1021, NT_STATUS_CHILD_MUST_BE_VOLATILE}, + {ERRHRD, 1022, NT_STATUS(0x0000010c)}, +}; + +/* errmap NTSTATUS->Win32 */ +const static struct { + NTSTATUS ntstatus; + WERROR werror; +} ntstatus_to_werror_map[] = { + {NT_STATUS(0x103), W_ERROR(0x3e5)}, + {NT_STATUS(0x105), W_ERROR(0xea)}, + {NT_STATUS(0x106), W_ERROR(0x514)}, + {NT_STATUS(0x107), W_ERROR(0x515)}, + {NT_STATUS(0x10c), W_ERROR(0x3fe)}, + {NT_STATUS(0x10d), W_ERROR(0x516)}, + {NT_STATUS(0x121), W_ERROR(0x2009)}, + {NT_STATUS(0xc0000001), W_ERROR(0x1f)}, + {NT_STATUS(0xc0000002), W_ERROR(0x1)}, + {NT_STATUS(0xc0000003), W_ERROR(0x57)}, + {NT_STATUS(0xc0000004), W_ERROR(0x18)}, + {NT_STATUS(0xc0000005), W_ERROR(0x3e6)}, + {NT_STATUS(0xc0000006), W_ERROR(0x3e7)}, + {NT_STATUS(0xc0000007), W_ERROR(0x5ae)}, + {NT_STATUS(0xc0000008), W_ERROR(0x6)}, + {NT_STATUS(0xc0000009), W_ERROR(0x3e9)}, + {NT_STATUS(0xc000000a), W_ERROR(0xc1)}, + {NT_STATUS(0xc000000b), W_ERROR(0x57)}, + {NT_STATUS(0xc000000d), W_ERROR(0x57)}, + {NT_STATUS(0xc000000e), W_ERROR(0x2)}, + {NT_STATUS(0xc000000f), W_ERROR(0x2)}, + {NT_STATUS(0xc0000010), W_ERROR(0x1)}, + {NT_STATUS(0xc0000011), W_ERROR(0x26)}, + {NT_STATUS(0xc0000012), W_ERROR(0x22)}, + {NT_STATUS(0xc0000013), W_ERROR(0x15)}, + {NT_STATUS(0xc0000014), W_ERROR(0x6f9)}, + {NT_STATUS(0xc0000015), W_ERROR(0x1b)}, + {NT_STATUS(0xc0000016), W_ERROR(0xea)}, + {NT_STATUS(0xc0000017), W_ERROR(0x8)}, + {NT_STATUS(0xc0000018), W_ERROR(0x1e7)}, + {NT_STATUS(0xc0000019), W_ERROR(0x1e7)}, + {NT_STATUS(0xc000001a), W_ERROR(0x57)}, + {NT_STATUS(0xc000001b), W_ERROR(0x57)}, + {NT_STATUS(0xc000001c), W_ERROR(0x1)}, + {NT_STATUS(0xc000001d), W_ERROR(0xc000001d)}, + {NT_STATUS(0xc000001e), W_ERROR(0x5)}, + {NT_STATUS(0xc000001f), W_ERROR(0x5)}, + {NT_STATUS(0xc0000020), W_ERROR(0xc1)}, + {NT_STATUS(0xc0000021), W_ERROR(0x5)}, + {NT_STATUS(0xc0000022), W_ERROR(0x5)}, + {NT_STATUS(0xc0000023), W_ERROR(0x7a)}, + {NT_STATUS(0xc0000024), W_ERROR(0x6)}, + {NT_STATUS(0xc0000025), W_ERROR(0xc0000025)}, + {NT_STATUS(0xc0000026), W_ERROR(0xc0000026)}, + {NT_STATUS(0xc000002a), W_ERROR(0x9e)}, + {NT_STATUS(0xc000002b), W_ERROR(0xc000002b)}, + {NT_STATUS(0xc000002c), W_ERROR(0x1e7)}, + {NT_STATUS(0xc000002d), W_ERROR(0x1e7)}, + {NT_STATUS(0xc0000030), W_ERROR(0x57)}, + {NT_STATUS(0xc0000032), W_ERROR(0x571)}, + {NT_STATUS(0xc0000033), W_ERROR(0x7b)}, + {NT_STATUS(0xc0000034), W_ERROR(0x2)}, + {NT_STATUS(0xc0000035), W_ERROR(0xb7)}, + {NT_STATUS(0xc0000037), W_ERROR(0x6)}, + {NT_STATUS(0xc0000039), W_ERROR(0xa1)}, + {NT_STATUS(0xc000003a), W_ERROR(0x3)}, + {NT_STATUS(0xc000003b), W_ERROR(0xa1)}, + {NT_STATUS(0xc000003c), W_ERROR(0x45d)}, + {NT_STATUS(0xc000003d), W_ERROR(0x45d)}, + {NT_STATUS(0xc000003e), W_ERROR(0x17)}, + {NT_STATUS(0xc000003f), W_ERROR(0x17)}, + {NT_STATUS(0xc0000040), W_ERROR(0x8)}, + {NT_STATUS(0xc0000041), W_ERROR(0x5)}, + {NT_STATUS(0xc0000042), W_ERROR(0x6)}, + {NT_STATUS(0xc0000043), W_ERROR(0x20)}, + {NT_STATUS(0xc0000044), W_ERROR(0x718)}, + {NT_STATUS(0xc0000045), W_ERROR(0x57)}, + {NT_STATUS(0xc0000046), W_ERROR(0x120)}, + {NT_STATUS(0xc0000047), W_ERROR(0x12a)}, + {NT_STATUS(0xc0000048), W_ERROR(0x57)}, + {NT_STATUS(0xc0000049), W_ERROR(0x57)}, + {NT_STATUS(0xc000004a), W_ERROR(0x9c)}, + {NT_STATUS(0xc000004b), W_ERROR(0x5)}, + {NT_STATUS(0xc000004c), W_ERROR(0x57)}, + {NT_STATUS(0xc000004d), W_ERROR(0x57)}, + {NT_STATUS(0xc000004e), W_ERROR(0x57)}, + {NT_STATUS(0xc000004f), W_ERROR(0x11a)}, + {NT_STATUS(0xc0000050), W_ERROR(0xff)}, + {NT_STATUS(0xc0000051), W_ERROR(0x570)}, + {NT_STATUS(0xc0000052), W_ERROR(0x570)}, + {NT_STATUS(0xc0000053), W_ERROR(0x570)}, + {NT_STATUS(0xc0000054), W_ERROR(0x21)}, + {NT_STATUS(0xc0000055), W_ERROR(0x21)}, + {NT_STATUS(0xc0000056), W_ERROR(0x5)}, + {NT_STATUS(0xc0000057), W_ERROR(0x32)}, + {NT_STATUS(0xc0000058), W_ERROR(0x519)}, + {NT_STATUS(0xc0000059), W_ERROR(0x51a)}, + {NT_STATUS(0xc000005a), W_ERROR(0x51b)}, + {NT_STATUS(0xc000005b), W_ERROR(0x51c)}, + {NT_STATUS(0xc000005c), W_ERROR(0x51d)}, + {NT_STATUS(0xc000005d), W_ERROR(0x51e)}, + {NT_STATUS(0xc000005e), W_ERROR(0x51f)}, + {NT_STATUS(0xc000005f), W_ERROR(0x520)}, + {NT_STATUS(0xc0000060), W_ERROR(0x521)}, + {NT_STATUS(0xc0000061), W_ERROR(0x522)}, + {NT_STATUS(0xc0000062), W_ERROR(0x523)}, + {NT_STATUS(0xc0000063), W_ERROR(0x524)}, + {NT_STATUS(0xc0000064), W_ERROR(0x525)}, + {NT_STATUS(0xc0000065), W_ERROR(0x526)}, + {NT_STATUS(0xc0000066), W_ERROR(0x527)}, + {NT_STATUS(0xc0000067), W_ERROR(0x528)}, + {NT_STATUS(0xc0000068), W_ERROR(0x529)}, + {NT_STATUS(0xc0000069), W_ERROR(0x52a)}, + {NT_STATUS(0xc000006a), W_ERROR(0x56)}, + {NT_STATUS(0xc000006b), W_ERROR(0x52c)}, + {NT_STATUS(0xc000006c), W_ERROR(0x52d)}, + {NT_STATUS(0xc000006d), W_ERROR(0x52e)}, + {NT_STATUS(0xc000006e), W_ERROR(0x52f)}, + {NT_STATUS(0xc000006f), W_ERROR(0x530)}, + {NT_STATUS(0xc0000070), W_ERROR(0x531)}, + {NT_STATUS(0xc0000071), W_ERROR(0x532)}, + {NT_STATUS(0xc0000072), W_ERROR(0x533)}, + {NT_STATUS(0xc0000073), W_ERROR(0x534)}, + {NT_STATUS(0xc0000074), W_ERROR(0x535)}, + {NT_STATUS(0xc0000075), W_ERROR(0x536)}, + {NT_STATUS(0xc0000076), W_ERROR(0x537)}, + {NT_STATUS(0xc0000077), W_ERROR(0x538)}, + {NT_STATUS(0xc0000078), W_ERROR(0x539)}, + {NT_STATUS(0xc0000079), W_ERROR(0x53a)}, + {NT_STATUS(0xc000007a), W_ERROR(0x7f)}, + {NT_STATUS(0xc000007b), W_ERROR(0xc1)}, + {NT_STATUS(0xc000007c), W_ERROR(0x3f0)}, + {NT_STATUS(0xc000007d), W_ERROR(0x53c)}, + {NT_STATUS(0xc000007e), W_ERROR(0x9e)}, + {NT_STATUS(0xc000007f), W_ERROR(0x70)}, + {NT_STATUS(0xc0000080), W_ERROR(0x53d)}, + {NT_STATUS(0xc0000081), W_ERROR(0x53e)}, + {NT_STATUS(0xc0000082), W_ERROR(0x44)}, + {NT_STATUS(0xc0000083), W_ERROR(0x103)}, + {NT_STATUS(0xc0000084), W_ERROR(0x53f)}, + {NT_STATUS(0xc0000085), W_ERROR(0x103)}, + {NT_STATUS(0xc0000086), W_ERROR(0x9a)}, + {NT_STATUS(0xc0000087), W_ERROR(0xe)}, + {NT_STATUS(0xc0000088), W_ERROR(0x1e7)}, + {NT_STATUS(0xc0000089), W_ERROR(0x714)}, + {NT_STATUS(0xc000008a), W_ERROR(0x715)}, + {NT_STATUS(0xc000008b), W_ERROR(0x716)}, + {NT_STATUS(0xc000008c), W_ERROR(0xc000008c)}, + {NT_STATUS(0xc000008d), W_ERROR(0xc000008d)}, + {NT_STATUS(0xc000008e), W_ERROR(0xc000008e)}, + {NT_STATUS(0xc000008f), W_ERROR(0xc000008f)}, + {NT_STATUS(0xc0000090), W_ERROR(0xc0000090)}, + {NT_STATUS(0xc0000091), W_ERROR(0xc0000091)}, + {NT_STATUS(0xc0000092), W_ERROR(0xc0000092)}, + {NT_STATUS(0xc0000093), W_ERROR(0xc0000093)}, + {NT_STATUS(0xc0000094), W_ERROR(0xc0000094)}, + {NT_STATUS(0xc0000095), W_ERROR(0x216)}, + {NT_STATUS(0xc0000096), W_ERROR(0xc0000096)}, + {NT_STATUS(0xc0000097), W_ERROR(0x8)}, + {NT_STATUS(0xc0000098), W_ERROR(0x3ee)}, + {NT_STATUS(0xc0000099), W_ERROR(0x540)}, + {NT_STATUS(0xc000009a), W_ERROR(0x5aa)}, + {NT_STATUS(0xc000009b), W_ERROR(0x3)}, + {NT_STATUS(0xc000009c), W_ERROR(0x17)}, + {NT_STATUS(0xc000009d), W_ERROR(0x48f)}, + {NT_STATUS(0xc000009e), W_ERROR(0x15)}, + {NT_STATUS(0xc000009f), W_ERROR(0x1e7)}, + {NT_STATUS(0xc00000a0), W_ERROR(0x1e7)}, + {NT_STATUS(0xc00000a1), W_ERROR(0x5ad)}, + {NT_STATUS(0xc00000a2), W_ERROR(0x13)}, + {NT_STATUS(0xc00000a3), W_ERROR(0x15)}, + {NT_STATUS(0xc00000a4), W_ERROR(0x541)}, + {NT_STATUS(0xc00000a5), W_ERROR(0x542)}, + {NT_STATUS(0xc00000a6), W_ERROR(0x543)}, + {NT_STATUS(0xc00000a7), W_ERROR(0x544)}, + {NT_STATUS(0xc00000a8), W_ERROR(0x545)}, + {NT_STATUS(0xc00000a9), W_ERROR(0x57)}, + {NT_STATUS(0xc00000ab), W_ERROR(0xe7)}, + {NT_STATUS(0xc00000ac), W_ERROR(0xe7)}, + {NT_STATUS(0xc00000ad), W_ERROR(0xe6)}, + {NT_STATUS(0xc00000ae), W_ERROR(0xe7)}, + {NT_STATUS(0xc00000af), W_ERROR(0x1)}, + {NT_STATUS(0xc00000b0), W_ERROR(0xe9)}, + {NT_STATUS(0xc00000b1), W_ERROR(0xe8)}, + {NT_STATUS(0xc00000b2), W_ERROR(0x217)}, + {NT_STATUS(0xc00000b3), W_ERROR(0x218)}, + {NT_STATUS(0xc00000b4), W_ERROR(0xe6)}, + {NT_STATUS(0xc00000b5), W_ERROR(0x79)}, + {NT_STATUS(0xc00000b6), W_ERROR(0x26)}, + {NT_STATUS(0xc00000ba), W_ERROR(0x5)}, + {NT_STATUS(0xc00000bb), W_ERROR(0x32)}, + {NT_STATUS(0xc00000bc), W_ERROR(0x33)}, + {NT_STATUS(0xc00000bd), W_ERROR(0x34)}, + {NT_STATUS(0xc00000be), W_ERROR(0x35)}, + {NT_STATUS(0xc00000bf), W_ERROR(0x36)}, + {NT_STATUS(0xc00000c0), W_ERROR(0x37)}, + {NT_STATUS(0xc00000c1), W_ERROR(0x38)}, + {NT_STATUS(0xc00000c2), W_ERROR(0x39)}, + {NT_STATUS(0xc00000c3), W_ERROR(0x3a)}, + {NT_STATUS(0xc00000c4), W_ERROR(0x3b)}, + {NT_STATUS(0xc00000c5), W_ERROR(0x3c)}, + {NT_STATUS(0xc00000c6), W_ERROR(0x3d)}, + {NT_STATUS(0xc00000c7), W_ERROR(0x3e)}, + {NT_STATUS(0xc00000c8), W_ERROR(0x3f)}, + {NT_STATUS(0xc00000c9), W_ERROR(0x40)}, + {NT_STATUS(0xc00000ca), W_ERROR(0x41)}, + {NT_STATUS(0xc00000cb), W_ERROR(0x42)}, + {NT_STATUS(0xc00000cc), W_ERROR(0x43)}, + {NT_STATUS(0xc00000cd), W_ERROR(0x44)}, + {NT_STATUS(0xc00000ce), W_ERROR(0x45)}, + {NT_STATUS(0xc00000cf), W_ERROR(0x46)}, + {NT_STATUS(0xc00000d0), W_ERROR(0x47)}, + {NT_STATUS(0xc00000d1), W_ERROR(0x48)}, + {NT_STATUS(0xc00000d2), W_ERROR(0x58)}, + {NT_STATUS(0xc00000d4), W_ERROR(0x11)}, + {NT_STATUS(0xc00000d5), W_ERROR(0x5)}, + {NT_STATUS(0xc00000d6), W_ERROR(0xf0)}, + {NT_STATUS(0xc00000d7), W_ERROR(0x546)}, + {NT_STATUS(0xc00000d9), W_ERROR(0xe8)}, + {NT_STATUS(0xc00000da), W_ERROR(0x547)}, + {NT_STATUS(0xc00000dc), W_ERROR(0x548)}, + {NT_STATUS(0xc00000dd), W_ERROR(0x549)}, + {NT_STATUS(0xc00000de), W_ERROR(0x54a)}, + {NT_STATUS(0xc00000df), W_ERROR(0x54b)}, + {NT_STATUS(0xc00000e0), W_ERROR(0x54c)}, + {NT_STATUS(0xc00000e1), W_ERROR(0x54d)}, + {NT_STATUS(0xc00000e2), W_ERROR(0x12c)}, + {NT_STATUS(0xc00000e3), W_ERROR(0x12d)}, + {NT_STATUS(0xc00000e4), W_ERROR(0x54e)}, + {NT_STATUS(0xc00000e5), W_ERROR(0x54f)}, + {NT_STATUS(0xc00000e6), W_ERROR(0x550)}, + {NT_STATUS(0xc00000e7), W_ERROR(0x551)}, + {NT_STATUS(0xc00000e8), W_ERROR(0x6f8)}, + {NT_STATUS(0xc00000ed), W_ERROR(0x552)}, + {NT_STATUS(0xc00000ee), W_ERROR(0x553)}, + {NT_STATUS(0xc00000ef), W_ERROR(0x57)}, + {NT_STATUS(0xc00000f0), W_ERROR(0x57)}, + {NT_STATUS(0xc00000f1), W_ERROR(0x57)}, + {NT_STATUS(0xc00000f2), W_ERROR(0x57)}, + {NT_STATUS(0xc00000f3), W_ERROR(0x57)}, + {NT_STATUS(0xc00000f4), W_ERROR(0x57)}, + {NT_STATUS(0xc00000f5), W_ERROR(0x57)}, + {NT_STATUS(0xc00000f6), W_ERROR(0x57)}, + {NT_STATUS(0xc00000f7), W_ERROR(0x57)}, + {NT_STATUS(0xc00000f8), W_ERROR(0x57)}, + {NT_STATUS(0xc00000f9), W_ERROR(0x57)}, + {NT_STATUS(0xc00000fa), W_ERROR(0x57)}, + {NT_STATUS(0xc00000fb), W_ERROR(0x3)}, + {NT_STATUS(0xc00000fd), W_ERROR(0x3e9)}, + {NT_STATUS(0xc00000fe), W_ERROR(0x554)}, + {NT_STATUS(0xc0000100), W_ERROR(0xcb)}, + {NT_STATUS(0xc0000101), W_ERROR(0x91)}, + {NT_STATUS(0xc0000102), W_ERROR(0x570)}, + {NT_STATUS(0xc0000103), W_ERROR(0x10b)}, + {NT_STATUS(0xc0000104), W_ERROR(0x555)}, + {NT_STATUS(0xc0000105), W_ERROR(0x556)}, + {NT_STATUS(0xc0000106), W_ERROR(0xce)}, + {NT_STATUS(0xc0000107), W_ERROR(0x961)}, + {NT_STATUS(0xc0000108), W_ERROR(0x964)}, + {NT_STATUS(0xc000010a), W_ERROR(0x5)}, + {NT_STATUS(0xc000010b), W_ERROR(0x557)}, + {NT_STATUS(0xc000010d), W_ERROR(0x558)}, + {NT_STATUS(0xc000010e), W_ERROR(0x420)}, + {NT_STATUS(0xc0000117), W_ERROR(0x5a4)}, + {NT_STATUS(0xc000011b), W_ERROR(0xc1)}, + {NT_STATUS(0xc000011c), W_ERROR(0x559)}, + {NT_STATUS(0xc000011d), W_ERROR(0x55a)}, + {NT_STATUS(0xc000011e), W_ERROR(0x3ee)}, + {NT_STATUS(0xc000011f), W_ERROR(0x4)}, + {NT_STATUS(0xc0000120), W_ERROR(0x3e3)}, + {NT_STATUS(0xc0000121), W_ERROR(0x5)}, + {NT_STATUS(0xc0000122), W_ERROR(0x4ba)}, + {NT_STATUS(0xc0000123), W_ERROR(0x5)}, + {NT_STATUS(0xc0000124), W_ERROR(0x55b)}, + {NT_STATUS(0xc0000125), W_ERROR(0x55c)}, + {NT_STATUS(0xc0000126), W_ERROR(0x55d)}, + {NT_STATUS(0xc0000127), W_ERROR(0x55e)}, + {NT_STATUS(0xc0000128), W_ERROR(0x6)}, + {NT_STATUS(0xc000012b), W_ERROR(0x55f)}, + {NT_STATUS(0xc000012d), W_ERROR(0x5af)}, + {NT_STATUS(0xc000012e), W_ERROR(0xc1)}, + {NT_STATUS(0xc000012f), W_ERROR(0xc1)}, + {NT_STATUS(0xc0000130), W_ERROR(0xc1)}, + {NT_STATUS(0xc0000131), W_ERROR(0xc1)}, + {NT_STATUS(0xc0000133), W_ERROR(0x576)}, + {NT_STATUS(0xc0000135), W_ERROR(0x7e)}, + {NT_STATUS(0xc0000138), W_ERROR(0xb6)}, + {NT_STATUS(0xc0000139), W_ERROR(0x7f)}, + {NT_STATUS(0xc000013b), W_ERROR(0x40)}, + {NT_STATUS(0xc000013c), W_ERROR(0x40)}, + {NT_STATUS(0xc000013d), W_ERROR(0x33)}, + {NT_STATUS(0xc000013e), W_ERROR(0x3b)}, + {NT_STATUS(0xc000013f), W_ERROR(0x3b)}, + {NT_STATUS(0xc0000140), W_ERROR(0x3b)}, + {NT_STATUS(0xc0000141), W_ERROR(0x3b)}, + {NT_STATUS(0xc0000142), W_ERROR(0x45a)}, + {NT_STATUS(0xc0000148), W_ERROR(0x7c)}, + {NT_STATUS(0xc0000149), W_ERROR(0x56)}, + {NT_STATUS(0xc000014b), W_ERROR(0x6d)}, + {NT_STATUS(0xc000014c), W_ERROR(0x3f1)}, + {NT_STATUS(0xc000014d), W_ERROR(0x3f8)}, + {NT_STATUS(0xc000014f), W_ERROR(0x3ed)}, + {NT_STATUS(0xc0000150), W_ERROR(0x45e)}, + {NT_STATUS(0xc0000151), W_ERROR(0x560)}, + {NT_STATUS(0xc0000152), W_ERROR(0x561)}, + {NT_STATUS(0xc0000153), W_ERROR(0x562)}, + {NT_STATUS(0xc0000154), W_ERROR(0x563)}, + {NT_STATUS(0xc0000155), W_ERROR(0x564)}, + {NT_STATUS(0xc0000156), W_ERROR(0x565)}, + {NT_STATUS(0xc0000157), W_ERROR(0x566)}, + {NT_STATUS(0xc0000158), W_ERROR(0x567)}, + {NT_STATUS(0xc0000159), W_ERROR(0x3ef)}, + {NT_STATUS(0xc000015a), W_ERROR(0x568)}, + {NT_STATUS(0xc000015b), W_ERROR(0x569)}, + {NT_STATUS(0xc000015c), W_ERROR(0x3f9)}, + {NT_STATUS(0xc000015d), W_ERROR(0x56a)}, + {NT_STATUS(0xc000015f), W_ERROR(0x45d)}, + {NT_STATUS(0xc0000162), W_ERROR(0x459)}, + {NT_STATUS(0xc0000165), W_ERROR(0x462)}, + {NT_STATUS(0xc0000166), W_ERROR(0x463)}, + {NT_STATUS(0xc0000167), W_ERROR(0x464)}, + {NT_STATUS(0xc0000168), W_ERROR(0x465)}, + {NT_STATUS(0xc0000169), W_ERROR(0x466)}, + {NT_STATUS(0xc000016a), W_ERROR(0x467)}, + {NT_STATUS(0xc000016b), W_ERROR(0x468)}, + {NT_STATUS(0xc000016c), W_ERROR(0x45f)}, + {NT_STATUS(0xc000016d), W_ERROR(0x45d)}, + {NT_STATUS(0xc0000172), W_ERROR(0x451)}, + {NT_STATUS(0xc0000173), W_ERROR(0x452)}, + {NT_STATUS(0xc0000174), W_ERROR(0x453)}, + {NT_STATUS(0xc0000175), W_ERROR(0x454)}, + {NT_STATUS(0xc0000176), W_ERROR(0x455)}, + {NT_STATUS(0xc0000177), W_ERROR(0x469)}, + {NT_STATUS(0xc0000178), W_ERROR(0x458)}, + {NT_STATUS(0xc000017a), W_ERROR(0x56b)}, + {NT_STATUS(0xc000017b), W_ERROR(0x56c)}, + {NT_STATUS(0xc000017c), W_ERROR(0x3fa)}, + {NT_STATUS(0xc000017d), W_ERROR(0x3fb)}, + {NT_STATUS(0xc000017e), W_ERROR(0x56d)}, + {NT_STATUS(0xc000017f), W_ERROR(0x56e)}, + {NT_STATUS(0xc0000180), W_ERROR(0x3fc)}, + {NT_STATUS(0xc0000181), W_ERROR(0x3fd)}, + {NT_STATUS(0xc0000182), W_ERROR(0x57)}, + {NT_STATUS(0xc0000183), W_ERROR(0x45d)}, + {NT_STATUS(0xc0000184), W_ERROR(0x16)}, + {NT_STATUS(0xc0000185), W_ERROR(0x45d)}, + {NT_STATUS(0xc0000186), W_ERROR(0x45d)}, + {NT_STATUS(0xc0000188), W_ERROR(0x5de)}, + {NT_STATUS(0xc0000189), W_ERROR(0x13)}, + {NT_STATUS(0xc000018a), W_ERROR(0x6fa)}, + {NT_STATUS(0xc000018b), W_ERROR(0x6fb)}, + {NT_STATUS(0xc000018c), W_ERROR(0x6fc)}, + {NT_STATUS(0xc000018d), W_ERROR(0x6fd)}, + {NT_STATUS(0xc000018e), W_ERROR(0x5dc)}, + {NT_STATUS(0xc000018f), W_ERROR(0x5dd)}, + {NT_STATUS(0xc0000190), W_ERROR(0x6fe)}, + {NT_STATUS(0xc0000192), W_ERROR(0x700)}, + {NT_STATUS(0xc0000193), W_ERROR(0x701)}, + {NT_STATUS(0xc0000194), W_ERROR(0x46b)}, + {NT_STATUS(0xc0000195), W_ERROR(0x4c3)}, + {NT_STATUS(0xc0000196), W_ERROR(0x4c4)}, + {NT_STATUS(0xc0000197), W_ERROR(0x5df)}, + {NT_STATUS(0xc0000198), W_ERROR(0x70f)}, + {NT_STATUS(0xc0000199), W_ERROR(0x710)}, + {NT_STATUS(0xc000019a), W_ERROR(0x711)}, + {NT_STATUS(0xc000019b), W_ERROR(0x712)}, + {NT_STATUS(0xc0000202), W_ERROR(0x572)}, + {NT_STATUS(0xc0000203), W_ERROR(0x3b)}, + {NT_STATUS(0xc0000204), W_ERROR(0x717)}, + {NT_STATUS(0xc0000205), W_ERROR(0x46a)}, + {NT_STATUS(0xc0000206), W_ERROR(0x6f8)}, + {NT_STATUS(0xc0000207), W_ERROR(0x4be)}, + {NT_STATUS(0xc0000208), W_ERROR(0x4be)}, + {NT_STATUS(0xc0000209), W_ERROR(0x44)}, + {NT_STATUS(0xc000020a), W_ERROR(0x34)}, + {NT_STATUS(0xc000020b), W_ERROR(0x40)}, + {NT_STATUS(0xc000020c), W_ERROR(0x40)}, + {NT_STATUS(0xc000020d), W_ERROR(0x40)}, + {NT_STATUS(0xc000020e), W_ERROR(0x44)}, + {NT_STATUS(0xc000020f), W_ERROR(0x3b)}, + {NT_STATUS(0xc0000210), W_ERROR(0x3b)}, + {NT_STATUS(0xc0000211), W_ERROR(0x3b)}, + {NT_STATUS(0xc0000212), W_ERROR(0x3b)}, + {NT_STATUS(0xc0000213), W_ERROR(0x3b)}, + {NT_STATUS(0xc0000214), W_ERROR(0x3b)}, + {NT_STATUS(0xc0000215), W_ERROR(0x3b)}, + {NT_STATUS(0xc0000216), W_ERROR(0x32)}, + {NT_STATUS(0xc0000217), W_ERROR(0x32)}, + {NT_STATUS(0xc000021c), W_ERROR(0x17e6)}, + {NT_STATUS(0xc0000220), W_ERROR(0x46c)}, + {NT_STATUS(0xc0000221), W_ERROR(0xc1)}, + {NT_STATUS(0xc0000224), W_ERROR(0x773)}, + {NT_STATUS(0xc0000225), W_ERROR(0x490)}, + {NT_STATUS(0xc000022a), W_ERROR(0xc000022a)}, + {NT_STATUS(0xc000022b), W_ERROR(0xc000022b)}, + {NT_STATUS(0xc000022d), W_ERROR(0x4d5)}, + {NT_STATUS(0xc0000230), W_ERROR(0x492)}, + {NT_STATUS(0xc0000233), W_ERROR(0x774)}, + {NT_STATUS(0xc0000234), W_ERROR(0x775)}, + {NT_STATUS(0xc0000235), W_ERROR(0x6)}, + {NT_STATUS(0xc0000236), W_ERROR(0x4c9)}, + {NT_STATUS(0xc0000237), W_ERROR(0x4ca)}, + {NT_STATUS(0xc0000238), W_ERROR(0x4cb)}, + {NT_STATUS(0xc0000239), W_ERROR(0x4cc)}, + {NT_STATUS(0xc000023a), W_ERROR(0x4cd)}, + {NT_STATUS(0xc000023b), W_ERROR(0x4ce)}, + {NT_STATUS(0xc000023c), W_ERROR(0x4cf)}, + {NT_STATUS(0xc000023d), W_ERROR(0x4d0)}, + {NT_STATUS(0xc000023e), W_ERROR(0x4d1)}, + {NT_STATUS(0xc000023f), W_ERROR(0x4d2)}, + {NT_STATUS(0xc0000240), W_ERROR(0x4d3)}, + {NT_STATUS(0xc0000241), W_ERROR(0x4d4)}, + {NT_STATUS(0xc0000243), W_ERROR(0x4c8)}, + {NT_STATUS(0xc0000246), W_ERROR(0x4d6)}, + {NT_STATUS(0xc0000247), W_ERROR(0x4d7)}, + {NT_STATUS(0xc0000248), W_ERROR(0x4d8)}, + {NT_STATUS(0xc0000249), W_ERROR(0xc1)}, + {NT_STATUS(0xc0000253), W_ERROR(0x54f)}, + {NT_STATUS(0xc0000257), W_ERROR(0x4d0)}, + {NT_STATUS(0xc0000259), W_ERROR(0x573)}, + {NT_STATUS(0xc000025e), W_ERROR(0x422)}, + {NT_STATUS(0xc0000262), W_ERROR(0xb6)}, + {NT_STATUS(0xc0000263), W_ERROR(0x7f)}, + {NT_STATUS(0xc0000264), W_ERROR(0x120)}, + {NT_STATUS(0xc0000265), W_ERROR(0x476)}, + {NT_STATUS(0xc0000267), W_ERROR(0x10fe)}, + {NT_STATUS(0xc000026c), W_ERROR(0x7d1)}, + {NT_STATUS(0xc000026d), W_ERROR(0x4b1)}, + {NT_STATUS(0xc000026e), W_ERROR(0x15)}, + {NT_STATUS(0xc0000272), W_ERROR(0x491)}, + {NT_STATUS(0xc0000275), W_ERROR(0x1126)}, + {NT_STATUS(0xc0000276), W_ERROR(0x1129)}, + {NT_STATUS(0xc0000277), W_ERROR(0x112a)}, + {NT_STATUS(0xc0000278), W_ERROR(0x1128)}, + {NT_STATUS(0xc0000279), W_ERROR(0x780)}, + {NT_STATUS(0xc0000280), W_ERROR(0x781)}, + {NT_STATUS(0xc0000281), W_ERROR(0xa1)}, + {NT_STATUS(0xc0000283), W_ERROR(0x488)}, + {NT_STATUS(0xc0000284), W_ERROR(0x489)}, + {NT_STATUS(0xc0000285), W_ERROR(0x48a)}, + {NT_STATUS(0xc0000286), W_ERROR(0x48b)}, + {NT_STATUS(0xc0000287), W_ERROR(0x48c)}, + {NT_STATUS(0xc000028a), W_ERROR(0x5)}, + {NT_STATUS(0xc000028b), W_ERROR(0x5)}, + {NT_STATUS(0xc000028d), W_ERROR(0x5)}, + {NT_STATUS(0xc000028e), W_ERROR(0x5)}, + {NT_STATUS(0xc000028f), W_ERROR(0x5)}, + {NT_STATUS(0xc0000290), W_ERROR(0x5)}, + {NT_STATUS(0xc0000291), W_ERROR(0x1777)}, + {NT_STATUS(0xc0000292), W_ERROR(0x1778)}, + {NT_STATUS(0xc0000293), W_ERROR(0x1772)}, + {NT_STATUS(0xc0000295), W_ERROR(0x1068)}, + {NT_STATUS(0xc0000296), W_ERROR(0x1069)}, + {NT_STATUS(0xc0000297), W_ERROR(0x106a)}, + {NT_STATUS(0xc0000298), W_ERROR(0x106b)}, + {NT_STATUS(0xc0000299), W_ERROR(0x201a)}, + {NT_STATUS(0xc000029a), W_ERROR(0x201b)}, + {NT_STATUS(0xc000029b), W_ERROR(0x201c)}, + {NT_STATUS(0xc000029c), W_ERROR(0x1)}, + {NT_STATUS(0xc000029d), W_ERROR(0x10ff)}, + {NT_STATUS(0xc000029e), W_ERROR(0x1100)}, + {NT_STATUS(0xc000029f), W_ERROR(0x494)}, + {NT_STATUS(0xc00002a1), W_ERROR(0x200a)}, + {NT_STATUS(0xc00002a2), W_ERROR(0x200b)}, + {NT_STATUS(0xc00002a3), W_ERROR(0x200c)}, + {NT_STATUS(0xc00002a4), W_ERROR(0x200d)}, + {NT_STATUS(0xc00002a5), W_ERROR(0x200e)}, + {NT_STATUS(0xc00002a6), W_ERROR(0x200f)}, + {NT_STATUS(0xc00002a7), W_ERROR(0x2010)}, + {NT_STATUS(0xc00002a8), W_ERROR(0x2011)}, + {NT_STATUS(0xc00002a9), W_ERROR(0x2012)}, + {NT_STATUS(0xc00002aa), W_ERROR(0x2013)}, + {NT_STATUS(0xc00002ab), W_ERROR(0x2014)}, + {NT_STATUS(0xc00002ac), W_ERROR(0x2015)}, + {NT_STATUS(0xc00002ad), W_ERROR(0x2016)}, + {NT_STATUS(0xc00002ae), W_ERROR(0x2017)}, + {NT_STATUS(0xc00002af), W_ERROR(0x2018)}, + {NT_STATUS(0xc00002b0), W_ERROR(0x2019)}, + {NT_STATUS(0xc00002b1), W_ERROR(0x211e)}, + {NT_STATUS(0xc00002b2), W_ERROR(0x1127)}, + {NT_STATUS(0xc00002b6), W_ERROR(0x651)}, + {NT_STATUS(0xc00002b7), W_ERROR(0x49a)}, + {NT_STATUS(0xc00002b8), W_ERROR(0x49b)}, + {NT_STATUS(0xc00002c1), W_ERROR(0x2024)}, + {NT_STATUS(0xc00002c3), W_ERROR(0x575)}, + {NT_STATUS(0xc00002c5), W_ERROR(0x3e6)}, + {NT_STATUS(0xc00002c6), W_ERROR(0x1075)}, + {NT_STATUS(0xc00002c7), W_ERROR(0x1076)}, + {NT_STATUS(0xc00002ca), W_ERROR(0x10e8)}, + {NT_STATUS(0xc00002cb), W_ERROR(0x2138)}, + {NT_STATUS(0xc00002cc), W_ERROR(0x4e3)}, + {NT_STATUS(0xc00002cd), W_ERROR(0x2139)}, + {NT_STATUS(0xc00002cf), W_ERROR(0x49d)}, + {NT_STATUS(0xc00002d0), W_ERROR(0x213a)}, + {NT_STATUS(0xc00002d4), W_ERROR(0x2141)}, + {NT_STATUS(0xc00002d5), W_ERROR(0x2142)}, + {NT_STATUS(0xc00002d6), W_ERROR(0x2143)}, + {NT_STATUS(0xc00002d7), W_ERROR(0x2144)}, + {NT_STATUS(0xc00002d8), W_ERROR(0x2145)}, + {NT_STATUS(0xc00002d9), W_ERROR(0x2146)}, + {NT_STATUS(0xc00002da), W_ERROR(0x2147)}, + {NT_STATUS(0xc00002db), W_ERROR(0x2148)}, + {NT_STATUS(0xc00002dc), W_ERROR(0x2149)}, + {NT_STATUS(0xc00002dd), W_ERROR(0x32)}, + {NT_STATUS(0xc00002df), W_ERROR(0x2151)}, + {NT_STATUS(0xc00002e0), W_ERROR(0x2152)}, + {NT_STATUS(0xc00002e1), W_ERROR(0x2153)}, + {NT_STATUS(0xc00002e2), W_ERROR(0x2154)}, + {NT_STATUS(0xc00002e3), W_ERROR(0x215d)}, + {NT_STATUS(0xc00002e4), W_ERROR(0x2163)}, + {NT_STATUS(0xc00002e5), W_ERROR(0x2164)}, + {NT_STATUS(0xc00002e6), W_ERROR(0x2165)}, + {NT_STATUS(0xc00002e7), W_ERROR(0x216d)}, + {NT_STATUS(0xc00002fe), W_ERROR(0x45b)}, + {NT_STATUS(0xc00002ff), W_ERROR(0x4e7)}, + {NT_STATUS(0xc0000300), W_ERROR(0x4e6)}, + {NT_STATUS(0x80000001), W_ERROR(0x80000001)}, + {NT_STATUS(0x80000002), W_ERROR(0x3e6)}, + {NT_STATUS(0x80000003), W_ERROR(0x80000003)}, + {NT_STATUS(0x80000004), W_ERROR(0x80000004)}, + {NT_STATUS(0x80000005), W_ERROR(0xea)}, + {NT_STATUS(0x80000006), W_ERROR(0x12)}, + {NT_STATUS(0x8000000b), W_ERROR(0x56f)}, + {NT_STATUS(0x8000000d), W_ERROR(0x12b)}, + {NT_STATUS(0x8000000e), W_ERROR(0x1c)}, + {NT_STATUS(0x8000000f), W_ERROR(0x15)}, + {NT_STATUS(0x80000010), W_ERROR(0x15)}, + {NT_STATUS(0x80000011), W_ERROR(0xaa)}, + {NT_STATUS(0x80000012), W_ERROR(0x103)}, + {NT_STATUS(0x80000013), W_ERROR(0xfe)}, + {NT_STATUS(0x80000014), W_ERROR(0xff)}, + {NT_STATUS(0x80000015), W_ERROR(0xff)}, + {NT_STATUS(0x80000016), W_ERROR(0x456)}, + {NT_STATUS(0x8000001a), W_ERROR(0x103)}, + {NT_STATUS(0x8000001b), W_ERROR(0x44d)}, + {NT_STATUS(0x8000001c), W_ERROR(0x456)}, + {NT_STATUS(0x8000001d), W_ERROR(0x457)}, + {NT_STATUS(0x8000001e), W_ERROR(0x44c)}, + {NT_STATUS(0x8000001f), W_ERROR(0x44e)}, + {NT_STATUS(0x80000021), W_ERROR(0x44f)}, + {NT_STATUS(0x80000022), W_ERROR(0x450)}, + {NT_STATUS(0x80000025), W_ERROR(0x962)}, + {NT_STATUS(0x80000288), W_ERROR(0x48d)}, + {NT_STATUS(0x80000289), W_ERROR(0x48e)}, + {NT_STATUS_OK, WERR_OK}}; + + +/***************************************************************************** +convert a dos eclas/ecode to a NT status32 code + *****************************************************************************/ +NTSTATUS dos_to_ntstatus(int eclass, int ecode) +{ + int i; + if (eclass == 0 && ecode == 0) return NT_STATUS_OK; + for (i=0; NT_STATUS_V(dos_to_ntstatus_map[i].ntstatus); i++) { + if (eclass == dos_to_ntstatus_map[i].dos_class && + ecode == dos_to_ntstatus_map[i].dos_code) { + return dos_to_ntstatus_map[i].ntstatus; + } + } + return NT_STATUS_UNSUCCESSFUL; +} + + +/***************************************************************************** +convert a NT status code to a dos class/code + *****************************************************************************/ +void ntstatus_to_dos(NTSTATUS ntstatus, uint8 *eclass, uint32 *ecode) +{ + int i; + if (NT_STATUS_IS_OK(ntstatus)) { + *eclass = 0; + *ecode = 0; + return; + } + for (i=0; NT_STATUS_V(ntstatus_to_dos_map[i].ntstatus); i++) { + if (NT_STATUS_V(ntstatus) == + NT_STATUS_V(ntstatus_to_dos_map[i].ntstatus)) { + *eclass = ntstatus_to_dos_map[i].dos_class; + *ecode = ntstatus_to_dos_map[i].dos_code; + return; + } + } + *eclass = ERRHRD; + *ecode = ERRgeneral; +} + + +/***************************************************************************** +convert a WERROR to a NT status32 code + *****************************************************************************/ +NTSTATUS werror_to_ntstatus(WERROR error) +{ + int i; + if (W_ERROR_IS_OK(error)) return NT_STATUS_OK; + for (i=0; NT_STATUS_V(ntstatus_to_werror_map[i].ntstatus); i++) { + if (W_ERROR_V(error) == + W_ERROR_V(ntstatus_to_werror_map[i].werror)) { + return ntstatus_to_werror_map[i].ntstatus; + } + } + + /* just guess ... */ + return NT_STATUS(W_ERROR_V(error) | 0xc0000000); +} + +/***************************************************************************** +convert a NTSTATUS to a WERROR + *****************************************************************************/ +WERROR ntstatus_to_werror(NTSTATUS error) +{ + int i; + if (NT_STATUS_IS_OK(error)) return WERR_OK; + for (i=0; NT_STATUS_V(ntstatus_to_werror_map[i].ntstatus); i++) { + if (NT_STATUS_V(error) == + NT_STATUS_V(ntstatus_to_werror_map[i].ntstatus)) { + return ntstatus_to_werror_map[i].werror; + } + } + + /* a lame guess */ + return W_ERROR(NT_STATUS_V(error) & 0xffff); +} diff --git a/source3/libsmb/libsmbclient.c b/source3/libsmb/libsmbclient.c new file mode 100644 index 0000000000..237701b968 --- /dev/null +++ b/source3/libsmb/libsmbclient.c @@ -0,0 +1,2581 @@ +/* + Unix SMB/CIFS implementation. + SMB client library implementation + Copyright (C) Andrew Tridgell 1998 + Copyright (C) Richard Sharpe 2000 + Copyright (C) John Terpstra 2000 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "libsmbclient.h" + +/* Structure for servers ... Held here so we don't need an include ... + * May be better to put in an include file + */ + +struct smbc_server { + struct smbc_server *next, *prev; + struct cli_state cli; + dev_t dev; + char *server_name; + char *share_name; + char *workgroup; + char *username; + BOOL no_pathinfo2; +}; + +/* Keep directory entries in a list */ +struct smbc_dir_list { + struct smbc_dir_list *next; + struct smbc_dirent *dirent; +}; + +struct smbc_file { + int cli_fd; + int smbc_fd; + char *fname; + off_t offset; + struct smbc_server *srv; + BOOL file; + struct smbc_dir_list *dir_list, *dir_end, *dir_next; + int dir_type, dir_error; +}; + +int smbc_fstatdir(int fd, struct stat *st); /* Forward decl */ +BOOL smbc_getatr(struct smbc_server *srv, char *path, + uint16 *mode, size_t *size, + time_t *c_time, time_t *a_time, time_t *m_time, + SMB_INO_T *ino); + +extern BOOL in_client; +extern pstring global_myname; +static int smbc_initialized = 0; +static smbc_get_auth_data_fn smbc_auth_fn = NULL; +/*static int smbc_debug;*/ +static int smbc_start_fd; +static struct smbc_file **smbc_file_table; +static struct smbc_server *smbc_srvs; +static pstring my_netbios_name; +static pstring smbc_user; + +/* + * Function to parse a path and turn it into components + * + * We accept smb://[[[domain;]user[:password@]]server[/share[/path[/file]]]] + * + * smb:// means show all the workgroups + * smb://name/ means, if name<1D> exists, list servers in workgroup, + * else, if name<20> exists, list all shares for server ... + */ + +static const char *smbc_prefix = "smb:"; + +static int +smbc_parse_path(const char *fname, char *server, char *share, char *path, + char *user, char *password) /* FIXME, lengths of strings */ +{ + static pstring s; + pstring userinfo; + char *p; + char *q, *r; + int len; + + server[0] = share[0] = path[0] = user[0] = password[0] = (char)0; + pstrcpy(s, fname); + + /* clean_fname(s); causing problems ... */ + + /* see if it has the right prefix */ + len = strlen(smbc_prefix); + if (strncmp(s,smbc_prefix,len) || + (s[len] != '/' && s[len] != 0)) return -1; /* What about no smb: ? */ + + p = s + len; + + /* Watch the test below, we are testing to see if we should exit */ + + if (strncmp(p, "//", 2) && strncmp(p, "\\\\", 2)) { + + return -1; + + } + + p += 2; /* Skip the // or \\ */ + + if (*p == (char)0) + return 0; + + if (*p == '/') { + + strncpy(server, (char *)lp_workgroup(), 16); /* FIXME: Danger here */ + return 0; + + } + + /* + * ok, its for us. Now parse out the server, share etc. + * + * However, we want to parse out [[domain;]user[:password]@] if it + * exists ... + */ + + /* check that '@' occurs before '/', if '/' exists at all */ + q = strchr_m(p, '@'); + r = strchr_m(p, '/'); + if (q && (!r || q < r)) { + pstring username, passwd, domain; + char *u = userinfo; + + next_token(&p, userinfo, "@", sizeof(fstring)); + + username[0] = passwd[0] = domain[0] = 0; + + if (strchr_m(u, ';')) { + + next_token(&u, domain, ";", sizeof(fstring)); + + } + + if (strchr_m(u, ':')) { + + next_token(&u, username, ":", sizeof(fstring)); + + pstrcpy(passwd, u); + + } + else { + + pstrcpy(username, u); + + } + + if (username[0]) + strncpy(user, username, sizeof(fstring)); /* FIXME, size and domain */ + + if (passwd[0]) + strncpy(password, passwd, sizeof(fstring)); /* FIXME, size */ + + } + + if (!next_token(&p, server, "/", sizeof(fstring))) { + + return -1; + + } + + if (*p == (char)0) return 0; /* That's it ... */ + + if (!next_token(&p, share, "/", sizeof(fstring))) { + + return -1; + + } + + pstrcpy(path, p); + + all_string_sub(path, "/", "\\", 0); + + return 0; +} + +/* + * Convert an SMB error into a UNIX error ... + */ + +int smbc_errno(struct cli_state *c) +{ + int ret; + + if (cli_is_dos_error(c)) { + uint8 eclass; + uint32 ecode; + + cli_dos_error(c, &eclass, &ecode); + ret = cli_errno_from_dos(eclass, ecode); + + DEBUG(3,("smbc_error %d %d (0x%x) -> %d\n", + (int)eclass, (int)ecode, (int)ecode, ret)); + } else { + NTSTATUS status; + + status = cli_nt_error(c); + ret = cli_errno_from_nt(status); + + DEBUG(3,("smbc errno %s -> %d\n", + nt_errstr(status), ret)); + } + + return ret; +} + +/* + * Connect to a server, possibly on an existing connection + * + * Here, what we want to do is: If the server and username + * match an existing connection, reuse that, otherwise, establish a + * new connection. + * + * If we have to create a new connection, call the auth_fn to get the + * info we need, unless the username and password were passed in. + */ + +struct smbc_server *smbc_server(char *server, char *share, + char *workgroup, char *username, + char *password) +{ + struct smbc_server *srv=NULL; + struct cli_state c; + struct nmb_name called, calling; + char *p, *server_n = server; + fstring group; + pstring ipenv; + struct in_addr ip; + + zero_ip(&ip); + ZERO_STRUCT(c); + + /* try to use an existing connection */ + for (srv=smbc_srvs;srv;srv=srv->next) { + if (strcmp(server,srv->server_name)==0 && + strcmp(share,srv->share_name)==0 && + strcmp(workgroup,srv->workgroup)==0 && + strcmp(username, srv->username) == 0) + return srv; + } + + if (server[0] == 0) { + errno = EPERM; + return NULL; + } + + /* + * Pick up the auth info here, once we know we need to connect + * But only if we do not have a username and password ... + */ + + if (!username[0] || !password[0]) + smbc_auth_fn(server, share, workgroup, sizeof(fstring), + username, sizeof(fstring), password, sizeof(fstring)); + + /* + * However, smbc_auth_fn may have picked up info relating to an + * existing connection, so try for an existing connection again ... + */ + + for (srv=smbc_srvs;srv;srv=srv->next) { + if (strcmp(server,srv->server_name)==0 && + strcmp(share,srv->share_name)==0 && + strcmp(workgroup,srv->workgroup)==0 && + strcmp(username, srv->username) == 0) + return srv; + } + + make_nmb_name(&calling, my_netbios_name, 0x0); + make_nmb_name(&called , server, 0x20); + + DEBUG(4,("smbc_server: server_n=[%s] server=[%s]\n", server_n, server)); + + if ((p=strchr_m(server_n,'#')) && + (strcmp(p+1,"1D")==0 || strcmp(p+1,"01")==0)) { + + fstrcpy(group, server_n); + p = strchr_m(group,'#'); + *p = 0; + + } + + DEBUG(4,(" -> server_n=[%s] server=[%s]\n", server_n, server)); + + again: + slprintf(ipenv,sizeof(ipenv)-1,"HOST_%s", server_n); + + zero_ip(&ip); + + /* have to open a new connection */ + if (!cli_initialise(&c) || !cli_connect(&c, server_n, &ip)) { + if (c.initialised) cli_shutdown(&c); + errno = ENOENT; + return NULL; + } + + if (!cli_session_request(&c, &calling, &called)) { + cli_shutdown(&c); + if (strcmp(called.name, "*SMBSERVER")) { + make_nmb_name(&called , "*SMBSERVER", 0x20); + goto again; + } + errno = ENOENT; + return NULL; + } + + DEBUG(4,(" session request ok\n")); + + if (!cli_negprot(&c)) { + cli_shutdown(&c); + errno = ENOENT; + return NULL; + } + + if (!cli_session_setup(&c, username, + password, strlen(password), + password, strlen(password), + workgroup) && + /* try an anonymous login if it failed */ + !cli_session_setup(&c, "", "", 1,"", 0, workgroup)) { + cli_shutdown(&c); + errno = EPERM; + return NULL; + } + + DEBUG(4,(" session setup ok\n")); + + if (!cli_send_tconX(&c, share, "?????", + password, strlen(password)+1)) { + errno = smbc_errno(&c); + cli_shutdown(&c); + return NULL; + } + + DEBUG(4,(" tconx ok\n")); + + srv = (struct smbc_server *)malloc(sizeof(*srv)); + if (!srv) { + errno = ENOMEM; + goto failed; + } + + ZERO_STRUCTP(srv); + + srv->cli = c; + + srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share)); + + srv->server_name = strdup(server); + if (!srv->server_name) { + errno = ENOMEM; + goto failed; + } + + srv->share_name = strdup(share); + if (!srv->share_name) { + errno = ENOMEM; + goto failed; + } + + srv->workgroup = strdup(workgroup); + if (!srv->workgroup) { + errno = ENOMEM; + goto failed; + } + + srv->username = strdup(username); + if (!srv->username) { + errno = ENOMEM; + goto failed; + } + + DLIST_ADD(smbc_srvs, srv); + + return srv; + + failed: + cli_shutdown(&c); + if (!srv) return NULL; + + SAFE_FREE(srv->server_name); + SAFE_FREE(srv->share_name); + SAFE_FREE(srv->workgroup); + SAFE_FREE(srv->username); + SAFE_FREE(srv); + return NULL; +} + +/* + *Remove a server from the list smbc_srvs if it's unused -- Tom (tom@ninja.nl) + * + * We accept a *srv + */ +BOOL smbc_remove_unused_server(struct smbc_server * s) +{ + int p; + + /* are we being fooled ? */ + if (!s) return False; + + /* close all open files/directories on this server */ + for (p = 0; p < SMBC_MAX_FD; p++) { + if (smbc_file_table[p] && + smbc_file_table[p]->srv == s) { + /* Still used .. DARN */ + DEBUG(3, ("smbc_remove_usused_server: %x still used by %s (%d).\n", (int) s, + smbc_file_table[p]->fname, smbc_file_table[p]->smbc_fd)); + return False; + } + } + + cli_shutdown(&s->cli); + + SAFE_FREE(s->username); + SAFE_FREE(s->workgroup); + SAFE_FREE(s->server_name); + SAFE_FREE(s->share_name); + DLIST_REMOVE(smbc_srvs, s); + DEBUG(3, ("smbc_remove_usused_server: %x removed.\n", (int) s)); + SAFE_FREE(s); + return True; +} + +/* + *Initialise the library etc + * + * We accept valid values for debug from 0 to 100, + * and insist that fn must be non-null. + */ + +int smbc_init(smbc_get_auth_data_fn fn, int debug) +{ + pstring conf; + int p, pid; + char *user = NULL, *home = NULL, *pname="libsmbclient"; + + if (!fn || debug < 0 || debug > 100) { + + errno = EINVAL; + return -1; + + } + + if (smbc_initialized) { /* Don't go through this if we have already done it */ + + return 0; + + } + + smbc_initialized = 1; + smbc_auth_fn = fn; + /* smbc_debug = debug; */ + + DEBUGLEVEL = -1; + + setup_logging(pname, False); + + /* Here we would open the smb.conf file if needed ... */ + + home = getenv("HOME"); + + slprintf(conf, sizeof(conf), "%s/.smb/smb.conf", home); + + load_interfaces(); /* Load the list of interfaces ... */ + + in_client = True; /* FIXME, make a param */ + + if (!lp_load(conf, True, False, False)) { + + /* + * Hmmm, what the hell do we do here ... we could not parse the + * config file ... We must return an error ... and keep info around + * about why we failed + */ + + errno = ENOENT; /* FIXME: Figure out the correct error response */ + return -1; + + } + + reopen_logs(); /* Get logging working ... */ + + /* + * FIXME: Is this the best way to get the user info? + */ + + user = getenv("USER"); + /* walk around as "guest" if no username can be found */ + if (!user) user = strdup("guest"); + pstrcpy(smbc_user, user); /* Save for use elsewhere */ + + /* + * We try to get our netbios name from the config. If that fails we fall + * back on constructing our netbios name from our hostname etc + */ + if (global_myname) { + pstrcpy(my_netbios_name, global_myname); + } + else { + /* + * Hmmm, I want to get hostname as well, but I am too lazy for the moment + */ + pid = sys_getpid(); + slprintf(my_netbios_name, 16, "smbc%s%d", user, pid); + } + DEBUG(0,("Using netbios name %s.\n", my_netbios_name)); + + name_register_wins(my_netbios_name, 0); + + /* + * Now initialize the file descriptor array and figure out what the + * max open files is, so we can return FD's that are above the max + * open file, and separated by a guard band + */ + +#if (defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE)) + do { + struct rlimit rlp; + + if (getrlimit(RLIMIT_NOFILE, &rlp)) { + + DEBUG(0, ("smbc_init: getrlimit(1) for RLIMIT_NOFILE failed with error %s\n", strerror(errno))); + + smbc_start_fd = 1000000; + + } + else { + + smbc_start_fd = rlp.rlim_max + 10000; /* Leave a guard space of 10,000 */ + + } + } while ( 0 ); +#else /* !defined(HAVE_GETRLIMIT) || !defined(RLIMIT_NOFILE) */ + + smbc_start_fd = 1000000; + +#endif + + smbc_file_table = malloc(SMBC_MAX_FD * sizeof(struct smbc_file *)); + + for (p = 0; p < SMBC_MAX_FD; p++) + smbc_file_table[p] = NULL; + + if (!smbc_file_table) + return ENOMEM; + + return 0; /* Success */ + +} + +/* + * Routine to open() a file ... + */ + +int smbc_open(const char *fname, int flags, mode_t mode) +{ + fstring server, share, user, password, workgroup; + pstring path; + struct smbc_server *srv = NULL; + int fd; + + if (!smbc_initialized) { + + errno = EINVAL; /* Best I can think of ... */ + return -1; + + } + + if (!fname) { + + errno = EINVAL; + return -1; + + } + + smbc_parse_path(fname, server, share, path, user, password); /* FIXME, check errors */ + + if (user[0] == (char)0) pstrcpy(user, smbc_user); + + pstrcpy(workgroup, lp_workgroup()); + + srv = smbc_server(server, share, workgroup, user, password); + + if (!srv) { + + if (errno == EPERM) errno = EACCES; + return -1; /* smbc_server sets errno */ + + } + + /* Hmmm, the test for a directory is suspect here ... FIXME */ + + if (strlen(path) > 0 && path[strlen(path) - 1] == '\\') { + + fd = -1; + + } + else { + + int slot = 0; + + /* Find a free slot first */ + + while (smbc_file_table[slot]) + slot++; + + if (slot > SMBC_MAX_FD) { + + errno = ENOMEM; /* FIXME, is this best? */ + return -1; + + } + + smbc_file_table[slot] = malloc(sizeof(struct smbc_file)); + + if (!smbc_file_table[slot]) { + + errno = ENOMEM; + return -1; + + } + + if ((fd = cli_open(&srv->cli, path, flags, DENY_NONE)) < 0) { + + /* Handle the error ... */ + + SAFE_FREE(smbc_file_table[slot]); + errno = smbc_errno(&srv->cli); + return -1; + + } + + /* Fill in file struct */ + + smbc_file_table[slot]->cli_fd = fd; + smbc_file_table[slot]->smbc_fd = slot + smbc_start_fd; + smbc_file_table[slot]->fname = strdup(fname); + smbc_file_table[slot]->srv = srv; + smbc_file_table[slot]->offset = 0; + smbc_file_table[slot]->file = True; + + return smbc_file_table[slot]->smbc_fd; + + } + + /* Check if opendir needed ... */ + + if (fd == -1) { + int eno = 0; + + eno = smbc_errno(&srv->cli); + fd = smbc_opendir(fname); + if (fd < 0) errno = eno; + return fd; + + } + + return 1; /* Success, with fd ... */ + +} + +/* + * Routine to create a file + */ + +static int creat_bits = O_WRONLY | O_CREAT | O_TRUNC; /* FIXME: Do we need this */ + +int smbc_creat(const char *path, mode_t mode) +{ + + if (!smbc_initialized) { + + errno = EINVAL; + return -1; + + } + + return smbc_open(path, creat_bits, mode); +} + +/* + * Routine to read() a file ... + */ + +ssize_t smbc_read(int fd, void *buf, size_t count) +{ + struct smbc_file *fe; + int ret; + + if (!smbc_initialized) { + + errno = EINVAL; + return -1; + + } + + DEBUG(4, ("smbc_read(%d, %d)\n", fd, (int)count)); + + if (fd < smbc_start_fd || fd >= (smbc_start_fd + SMBC_MAX_FD)) { + + errno = EBADF; + return -1; + + } + + /* Check that the buffer exists ... */ + + if (buf == NULL) { + + errno = EINVAL; + return -1; + + } + + fe = smbc_file_table[fd - smbc_start_fd]; + + if (!fe || !fe->file) { + + errno = EBADF; + return -1; + + } + + ret = cli_read(&fe->srv->cli, fe->cli_fd, buf, fe->offset, count); + + if (ret < 0) { + + errno = smbc_errno(&fe->srv->cli); + return -1; + + } + + fe->offset += ret; + + DEBUG(4, (" --> %d\n", ret)); + + return ret; /* Success, ret bytes of data ... */ + +} + +/* + * Routine to write() a file ... + */ + +ssize_t smbc_write(int fd, void *buf, size_t count) +{ + int ret; + struct smbc_file *fe; + + if (!smbc_initialized) { + + errno = EINVAL; + return -1; + + } + + if (fd < smbc_start_fd || fd >= (smbc_start_fd + SMBC_MAX_FD)) { + + errno = EBADF; + return -1; + + } + + /* Check that the buffer exists ... */ + + if (buf == NULL) { + + errno = EINVAL; + return -1; + + } + + fe = smbc_file_table[fd - smbc_start_fd]; + + if (!fe || !fe->file) { + + errno = EBADF; + return -1; + + } + + ret = cli_write(&fe->srv->cli, fe->cli_fd, 0, buf, fe->offset, count); + + if (ret <= 0) { + + errno = smbc_errno(&fe->srv->cli); + return -1; + + } + + fe->offset += ret; + + return ret; /* Success, 0 bytes of data ... */ +} + +/* + * Routine to close() a file ... + */ + +int smbc_close(int fd) +{ + struct smbc_file *fe; + struct smbc_server *srv; + + if (!smbc_initialized) { + + errno = EINVAL; + return -1; + + } + + if (fd < smbc_start_fd || fd >= (smbc_start_fd + SMBC_MAX_FD)) { + + errno = EBADF; + return -1; + + } + + fe = smbc_file_table[fd - smbc_start_fd]; + + if (!fe) { + + errno = EBADF; + return -1; + + } + + if (!fe->file) { + + return smbc_closedir(fd); + + } + + if (!cli_close(&fe->srv->cli, fe->cli_fd)) { + + DEBUG(3, ("cli_close failed on %s (%d). purging server.\n", + fe->fname, fe->smbc_fd)); + /* Deallocate slot and remove the server + * from the server cache if unused */ + errno = smbc_errno(&fe->srv->cli); + srv = fe->srv; + SAFE_FREE(fe->fname); + SAFE_FREE(fe); + smbc_file_table[fd - smbc_start_fd] = NULL; + smbc_remove_unused_server(srv); + + return -1; + + } + + SAFE_FREE(fe->fname); + SAFE_FREE(fe); + smbc_file_table[fd - smbc_start_fd] = NULL; + + return 0; +} + +/* + * Routine to unlink() a file + */ + +int smbc_unlink(const char *fname) +{ + fstring server, share, user, password, workgroup; + pstring path; + struct smbc_server *srv = NULL; + + if (!smbc_initialized) { + + errno = EINVAL; /* Best I can think of ... */ + return -1; + + } + + if (!fname) { + + errno = EINVAL; + return -1; + + } + + smbc_parse_path(fname, server, share, path, user, password); /* FIXME, check errors */ + + if (user[0] == (char)0) pstrcpy(user, smbc_user); + + pstrcpy(workgroup, lp_workgroup()); + + srv = smbc_server(server, share, workgroup, user, password); + + if (!srv) { + + return -1; /* smbc_server sets errno */ + + } + + /* if (strncmp(srv->cli.dev, "LPT", 3) == 0) { + + int job = smbc_stat_printjob(srv, path, NULL, NULL); + if (job == -1) { + + return -1; + + } + if ((err = cli_printjob_del(&srv->cli, job)) != 0) { + + + return -1; + + } + } else */ + + if (!cli_unlink(&srv->cli, path)) { + + errno = smbc_errno(&srv->cli); + + if (errno == EACCES) { /* Check if the file is a directory */ + + int saverr = errno; + size_t size = 0; + uint16 mode = 0; + time_t m_time = 0, a_time = 0, c_time = 0; + SMB_INO_T ino = 0; + + if (!smbc_getatr(srv, path, &mode, &size, + &c_time, &a_time, &m_time, &ino)) { + + /* Hmmm, bad error ... What? */ + + errno = smbc_errno(&srv->cli); + return -1; + + } + else { + + if (IS_DOS_DIR(mode)) + errno = EISDIR; + else + errno = saverr; /* Restore this */ + + } + } + + return -1; + + } + + return 0; /* Success ... */ + +} + +/* + * Routine to rename() a file + */ + +int smbc_rename(const char *oname, const char *nname) +{ + fstring server1, share1, server2, share2, user1, user2, password1, password2, workgroup; + pstring path1, path2; + struct smbc_server *srv = NULL; + + if (!smbc_initialized) { + + errno = EINVAL; /* Best I can think of ... */ + return -1; + + } + + if (!oname || !nname) { + + errno = EINVAL; + return -1; + + } + + DEBUG(4, ("smbc_rename(%s,%s)\n", oname, nname)); + + smbc_parse_path(oname, server1, share1, path1, user1, password1); + + if (user1[0] == (char)0) pstrcpy(user1, smbc_user); + + smbc_parse_path(nname, server2, share2, path2, user2, password2); + + if (user2[0] == (char)0) pstrcpy(user2, smbc_user); + + if (strcmp(server1, server2) || strcmp(share1, share2) || + strcmp(user1, user2)) { + + /* Can't rename across file systems, or users?? */ + + errno = EXDEV; + return -1; + + } + + pstrcpy(workgroup, lp_workgroup()); + + srv = smbc_server(server1, share1, workgroup, user1, password1); + if (!srv) { + + return -1; + + } + + if (!cli_rename(&srv->cli, path1, path2)) { + int eno = smbc_errno(&srv->cli); + + if (eno != EEXIST || + !cli_unlink(&srv->cli, path2) || + !cli_rename(&srv->cli, path1, path2)) { + + errno = eno; + return -1; + + } + } + + return 0; /* Success */ + +} + +/* + * A routine to lseek() a file + */ + +off_t smbc_lseek(int fd, off_t offset, int whence) +{ + struct smbc_file *fe; + size_t size; + + if (!smbc_initialized) { + + errno = EINVAL; + return -1; + + } + + if (fd < smbc_start_fd || fd >= (smbc_start_fd + SMBC_MAX_FD)) { + + errno = EBADF; + return -1; + + } + + fe = smbc_file_table[fd - smbc_start_fd]; + + if (!fe) { + + errno = EBADF; + return -1; + + } + + if (!fe->file) { + + errno = EINVAL; + return -1; /* Can't lseek a dir ... */ + + } + + switch (whence) { + case SEEK_SET: + fe->offset = offset; + break; + + case SEEK_CUR: + fe->offset += offset; + break; + + case SEEK_END: + if (!cli_qfileinfo(&fe->srv->cli, fe->cli_fd, NULL, &size, NULL, NULL, + NULL, NULL, NULL) && + !cli_getattrE(&fe->srv->cli, fe->cli_fd, NULL, &size, NULL, NULL, + NULL)) { + + errno = EINVAL; + return -1; + } + fe->offset = size + offset; + break; + + default: + errno = EINVAL; + break; + + } + + return fe->offset; + +} + +/* + * Generate an inode number from file name for those things that need it + */ + +static +ino_t smbc_inode(const char *name) +{ + + if (!*name) return 2; /* FIXME, why 2 ??? */ + return (ino_t)str_checksum(name); + +} + +/* + * Routine to put basic stat info into a stat structure ... Used by stat and + * fstat below. + */ + +static +int smbc_setup_stat(struct stat *st, char *fname, size_t size, int mode) +{ + + st->st_mode = 0; + + if (IS_DOS_DIR(mode)) { + st->st_mode = SMBC_DIR_MODE; + } else { + st->st_mode = SMBC_FILE_MODE; + } + + if (IS_DOS_ARCHIVE(mode)) st->st_mode |= S_IXUSR; + if (IS_DOS_SYSTEM(mode)) st->st_mode |= S_IXGRP; + if (IS_DOS_HIDDEN(mode)) st->st_mode |= S_IXOTH; + if (!IS_DOS_READONLY(mode)) st->st_mode |= S_IWUSR; + + st->st_size = size; + st->st_blksize = 512; + st->st_blocks = (size+511)/512; + st->st_uid = getuid(); + st->st_gid = getgid(); + + if (IS_DOS_DIR(mode)) { + st->st_nlink = 2; + } else { + st->st_nlink = 1; + } + + if (st->st_ino == 0) { + st->st_ino = smbc_inode(fname); + } + + return True; /* FIXME: Is this needed ? */ + +} + +/* + * Get info from an SMB server on a file. Use a qpathinfo call first + * and if that fails, use getatr, as Win95 sometimes refuses qpathinfo + */ + +BOOL smbc_getatr(struct smbc_server *srv, char *path, + uint16 *mode, size_t *size, + time_t *c_time, time_t *a_time, time_t *m_time, + SMB_INO_T *ino) +{ + + if (!smbc_initialized) { + + errno = EINVAL; + return -1; + + } + + DEBUG(4,("smbc_getatr: sending qpathinfo\n")); + + if (!srv->no_pathinfo2 && + cli_qpathinfo2(&srv->cli, path, c_time, a_time, m_time, NULL, + size, mode, ino)) return True; + + /* if this is NT then don't bother with the getatr */ + if (srv->cli.capabilities & CAP_NT_SMBS) return False; + + if (cli_getatr(&srv->cli, path, mode, size, m_time)) { + a_time = c_time = m_time; + srv->no_pathinfo2 = True; + return True; + } + return False; +} + +/* + * Routine to stat a file given a name + */ + +int smbc_stat(const char *fname, struct stat *st) +{ + struct smbc_server *srv; + fstring server, share, user, password, workgroup; + pstring path; + time_t m_time = 0, a_time = 0, c_time = 0; + size_t size = 0; + uint16 mode = 0; + SMB_INO_T ino = 0; + + if (!smbc_initialized) { + + errno = EINVAL; /* Best I can think of ... */ + return -1; + + } + + if (!fname) { + + errno = EINVAL; + return -1; + + } + + DEBUG(4, ("smbc_stat(%s)\n", fname)); + + smbc_parse_path(fname, server, share, path, user, password); /*FIXME, errors*/ + + if (user[0] == (char)0) pstrcpy(user, smbc_user); + + pstrcpy(workgroup, lp_workgroup()); + + srv = smbc_server(server, share, workgroup, user, password); + + if (!srv) { + + return -1; /* errno set by smbc_server */ + + } + + /* if (strncmp(srv->cli.dev, "IPC", 3) == 0) { + + mode = aDIR | aRONLY; + + } + else if (strncmp(srv->cli.dev, "LPT", 3) == 0) { + + if (strcmp(path, "\\") == 0) { + + mode = aDIR | aRONLY; + + } + else { + + mode = aRONLY; + smbc_stat_printjob(srv, path, &size, &m_time); + c_time = a_time = m_time; + + } + else { */ + + if (!smbc_getatr(srv, path, &mode, &size, + &c_time, &a_time, &m_time, &ino)) { + + errno = smbc_errno(&srv->cli); + return -1; + + } + + /* } */ + + st->st_ino = ino; + + smbc_setup_stat(st, path, size, mode); + + st->st_atime = a_time; + st->st_ctime = c_time; + st->st_mtime = m_time; + st->st_dev = srv->dev; + + return 0; + +} + +/* + * Routine to stat a file given an fd + */ + +int smbc_fstat(int fd, struct stat *st) +{ + struct smbc_file *fe; + time_t c_time, a_time, m_time; + size_t size; + uint16 mode; + SMB_INO_T ino = 0; + + if (!smbc_initialized) { + + errno = EINVAL; + return -1; + + } + + if (fd < smbc_start_fd || fd >= (smbc_start_fd + SMBC_MAX_FD)) { + + errno = EBADF; + return -1; + + } + + fe = smbc_file_table[fd - smbc_start_fd]; + + if (!fe) { + + errno = EBADF; + return -1; + + } + + if (!fe->file) { + + return smbc_fstatdir(fd, st); + + } + + if (!cli_qfileinfo(&fe->srv->cli, fe->cli_fd, + &mode, &size, &c_time, &a_time, &m_time, NULL, &ino) && + !cli_getattrE(&fe->srv->cli, fe->cli_fd, + &mode, &size, &c_time, &a_time, &m_time)) { + + errno = EINVAL; + return -1; + + } + + st->st_ino = ino; + + smbc_setup_stat(st, fe->fname, size, mode); + + st->st_atime = a_time; + st->st_ctime = c_time; + st->st_mtime = m_time; + st->st_dev = fe->srv->dev; + + return 0; + +} + +/* + * Routine to open a directory + * + * We want to allow: + * + * smb: which should list all the workgroups available + * smb:workgroup + * smb:workgroup//server + * smb://server + * smb://server/share + * smb://<IP-addr> which should list shares on server + * smb://<IP-addr>/share which should list files on share + */ + +static void smbc_remove_dir(struct smbc_file *dir) +{ + struct smbc_dir_list *d,*f; + + d = dir->dir_list; + while (d) { + + f = d; d = d->next; + + SAFE_FREE(f->dirent); + SAFE_FREE(f); + + } + + dir->dir_list = dir->dir_end = dir->dir_next = NULL; + +} + +static int add_dirent(struct smbc_file *dir, const char *name, const char *comment, uint32 type) +{ + struct smbc_dirent *dirent; + int size; + + /* + * Allocate space for the dirent, which must be increased by the + * size of the name and the comment and 1 for the null on the comment. + * The null on the name is already accounted for. + */ + + size = sizeof(struct smbc_dirent) + (name?strlen(name):0) + + (comment?strlen(comment):0) + 1; + + dirent = malloc(size); + + if (!dirent) { + + dir->dir_error = ENOMEM; + return -1; + + } + + if (dir->dir_list == NULL) { + + dir->dir_list = malloc(sizeof(struct smbc_dir_list)); + if (!dir->dir_list) { + + SAFE_FREE(dirent); + dir->dir_error = ENOMEM; + return -1; + + } + + dir->dir_end = dir->dir_next = dir->dir_list; + + } + else { + + dir->dir_end->next = malloc(sizeof(struct smbc_dir_list)); + + if (!dir->dir_end) { + + SAFE_FREE(dirent); + dir->dir_error = ENOMEM; + return -1; + + } + + dir->dir_end = dir->dir_end->next; + + } + + dir->dir_end->next = NULL; + dir->dir_end->dirent = dirent; + + dirent->smbc_type = type; + dirent->namelen = (name?strlen(name):0); + dirent->commentlen = (comment?strlen(comment):0); + dirent->dirlen = size; + + strncpy(dirent->name, (name?name:""), dirent->namelen + 1); + + dirent->comment = (char *)(&dirent->name + dirent->namelen + 1); + strncpy(dirent->comment, (comment?comment:""), dirent->commentlen + 1); + + return 0; + +} + +static void +list_fn(const char *name, uint32 type, const char *comment, void *state) +{ + struct smbc_file *dir = (struct smbc_file *)state; + int dirent_type; + + /* We need to process the type a little ... */ + + if (dir->dir_type == SMBC_FILE_SHARE) { + + switch (type) { + case 0: /* Directory tree */ + dirent_type = SMBC_FILE_SHARE; + break; + + case 1: + dirent_type = SMBC_PRINTER_SHARE; + break; + + case 2: + dirent_type = SMBC_COMMS_SHARE; + break; + + case 3: + dirent_type = SMBC_IPC_SHARE; + break; + + default: + dirent_type = SMBC_FILE_SHARE; /* FIXME, error? */ + break; + } + + } + else dirent_type = dir->dir_type; + + if (add_dirent(dir, name, comment, dirent_type) < 0) { + + /* An error occurred, what do we do? */ + /* FIXME: Add some code here */ + + } + +} + +static void +dir_list_fn(file_info *finfo, const char *mask, void *state) +{ + + if (add_dirent((struct smbc_file *)state, finfo->name, "", + (finfo->mode&aDIR?SMBC_DIR:SMBC_FILE)) < 0) { + + /* Handle an error ... */ + /* FIXME: Add some code ... */ + + } + +} + +int smbc_opendir(const char *fname) +{ + fstring server, share, user, password, workgroup; + pstring path; + struct smbc_server *srv = NULL; + struct in_addr rem_ip; + int slot = 0; + + if (!smbc_initialized) { + + errno = EINVAL; + return -1; + + } + + if (!fname) { + + errno = EINVAL; + return -1; + + } + + if (smbc_parse_path(fname, server, share, path, user, password)) { + + errno = EINVAL; + return -1; + + } + + if (user[0] == (char)0) pstrcpy(user, smbc_user); + + pstrcpy(workgroup, lp_workgroup()); + + /* Get a file entry ... */ + + slot = 0; + + while (smbc_file_table[slot]) + slot++; + + if (slot > SMBC_MAX_FD) { + + errno = ENOMEM; + return -1; /* FIXME, ... move into a func */ + + } + + smbc_file_table[slot] = malloc(sizeof(struct smbc_file)); + + if (!smbc_file_table[slot]) { + + errno = ENOMEM; + return -1; + + } + + smbc_file_table[slot]->cli_fd = 0; + smbc_file_table[slot]->smbc_fd = slot + smbc_start_fd; + smbc_file_table[slot]->fname = strdup(fname); + smbc_file_table[slot]->srv = NULL; + smbc_file_table[slot]->offset = 0; + smbc_file_table[slot]->file = False; + smbc_file_table[slot]->dir_list = + smbc_file_table[slot]->dir_next = + smbc_file_table[slot]->dir_end = NULL; + + if (server[0] == (char)0) { + + if (share[0] != (char)0 || path[0] != (char)0) { + + errno = EINVAL; + if (smbc_file_table[slot]) { + SAFE_FREE(smbc_file_table[slot]->fname); + SAFE_FREE(smbc_file_table[slot]); + } + return -1; + + } + + /* We have server and share and path empty ... so list the workgroups */ + + if (!resolve_name(lp_workgroup(), &rem_ip, 0x1d)) { + + errno = EINVAL; /* Something wrong with smb.conf? */ + return -1; + + } + + smbc_file_table[slot]->dir_type = SMBC_WORKGROUP; + + /* find the name of the server ... */ + + if (!name_status_find("*", 0, 0, rem_ip, server)) { + + DEBUG(0, ("Could not get the name of local master browser for server %s\n", server)); + errno = EINVAL; + return -1; + + } + + /* + * Get a connection to IPC$ on the server if we do not already have one + */ + + srv = smbc_server(server, "IPC$", workgroup, user, password); + + if (!srv) { + + if (smbc_file_table[slot]) { + SAFE_FREE(smbc_file_table[slot]->fname); + SAFE_FREE(smbc_file_table[slot]); + } + return -1; + + } + + smbc_file_table[slot]->srv = srv; + + /* Now, list the stuff ... */ + + if (!cli_NetServerEnum(&srv->cli, workgroup, 0x80000000, list_fn, + (void *)smbc_file_table[slot])) { + + if (smbc_file_table[slot]) { + SAFE_FREE(smbc_file_table[slot]->fname); + SAFE_FREE(smbc_file_table[slot]); + } + errno = cli_errno(&srv->cli); + return -1; + + } + } + else { /* Server not an empty string ... Check the rest and see what gives */ + + if (share[0] == (char)0) { + + if (path[0] != (char)0) { /* Should not have empty share with path */ + + errno = EINVAL; + if (smbc_file_table[slot]) { + SAFE_FREE(smbc_file_table[slot]->fname); + SAFE_FREE(smbc_file_table[slot]); + } + return -1; + + } + + /* Check to see if <server><1D> translates, or <server><20> translates */ + /* However, we check to see if <server> is an IP address first */ + + if (!is_ipaddress(server) && /* Not an IP addr so check next */ + resolve_name(server, &rem_ip, 0x1d)) { /* Found LMB */ + pstring buserver; + + smbc_file_table[slot]->dir_type = SMBC_SERVER; + + /* + * Get the backup list ... + */ + + + if (!name_status_find("*", 0, 0, rem_ip, buserver)) { + + DEBUG(0, ("Could not get name of local master browser %s\n", server)); + errno = EPERM; /* FIXME, is this correct */ + return -1; + + } + + /* + * Get a connection to IPC$ on the server if we do not already have one + */ + + srv = smbc_server(buserver, "IPC$", workgroup, user, password); + + if (!srv) { + + if (smbc_file_table[slot]) { + SAFE_FREE(smbc_file_table[slot]->fname); + SAFE_FREE(smbc_file_table[slot]); + } + return -1; + + } + + smbc_file_table[slot]->srv = srv; + + /* Now, list the servers ... */ + + if (!cli_NetServerEnum(&srv->cli, server, 0x0000FFFE, list_fn, + (void *)smbc_file_table[slot])) { + + if (smbc_file_table[slot]) { + SAFE_FREE(smbc_file_table[slot]->fname); + SAFE_FREE(smbc_file_table[slot]); + } + errno = cli_errno(&srv->cli); + return -1; + + } + + } + else { + + if (resolve_name(server, &rem_ip, 0x20)) { + + /* Now, list the shares ... */ + + smbc_file_table[slot]->dir_type = SMBC_FILE_SHARE; + + srv = smbc_server(server, "IPC$", workgroup, user, password); + + if (!srv) { + + if (smbc_file_table[slot]) { + SAFE_FREE(smbc_file_table[slot]->fname); + SAFE_FREE(smbc_file_table[slot]); + } + return -1; + + } + + smbc_file_table[slot]->srv = srv; + + /* Now, list the servers ... */ + + if (cli_RNetShareEnum(&srv->cli, list_fn, + (void *)smbc_file_table[slot]) < 0) { + + errno = cli_errno(&srv->cli); + if (smbc_file_table[slot]) { + SAFE_FREE(smbc_file_table[slot]->fname); + SAFE_FREE(smbc_file_table[slot]); + } + return -1; + + } + + } + else { + + errno = ENODEV; /* Neither the workgroup nor server exists */ + if (smbc_file_table[slot]) { + SAFE_FREE(smbc_file_table[slot]->fname); + SAFE_FREE(smbc_file_table[slot]); + } + return -1; + + } + + } + + } + else { /* The server and share are specified ... work from there ... */ + + /* Well, we connect to the server and list the directory */ + + smbc_file_table[slot]->dir_type = SMBC_FILE_SHARE; + + srv = smbc_server(server, share, workgroup, user, password); + + if (!srv) { + + if (smbc_file_table[slot]) { + SAFE_FREE(smbc_file_table[slot]->fname); + SAFE_FREE(smbc_file_table[slot]); + } + return -1; + + } + + smbc_file_table[slot]->srv = srv; + + /* Now, list the files ... */ + + pstrcat(path, "\\*"); + + if (cli_list(&srv->cli, path, aDIR | aSYSTEM | aHIDDEN, dir_list_fn, + (void *)smbc_file_table[slot]) < 0) { + + if (smbc_file_table[slot]) { + SAFE_FREE(smbc_file_table[slot]->fname); + SAFE_FREE(smbc_file_table[slot]); + } + errno = smbc_errno(&srv->cli); + return -1; + + } + } + + } + + return smbc_file_table[slot]->smbc_fd; + +} + +/* + * Routine to close a directory + */ + +int smbc_closedir(int fd) +{ + struct smbc_file *fe; + + if (!smbc_initialized) { + + errno = EINVAL; + return -1; + + } + + if (fd < smbc_start_fd || fd >= (smbc_start_fd + SMBC_MAX_FD)) { + + errno = EBADF; + return -1; + + } + + fe = smbc_file_table[fd - smbc_start_fd]; + + if (!fe) { + + errno = EBADF; + return -1; + + } + + smbc_remove_dir(fe); /* Clean it up */ + + if (fe) { + + SAFE_FREE(fe->fname); + SAFE_FREE(fe); /* Free the space too */ + + } + + smbc_file_table[fd - smbc_start_fd] = NULL; + + return 0; + +} + +/* + * Routine to get a directory entry + */ + +static char smbc_local_dirent[512]; /* Make big enough */ + +struct smbc_dirent *smbc_readdir(unsigned int fd) +{ + struct smbc_file *fe; + struct smbc_dirent *dirp, *dirent; + + /* Check that all is ok first ... */ + + if (!smbc_initialized) { + + errno = EINVAL; + return NULL; + + } + + if (fd < smbc_start_fd || fd >= (smbc_start_fd + SMBC_MAX_FD)) { + + errno = EBADF; + return NULL; + + } + + fe = smbc_file_table[fd - smbc_start_fd]; + + if (!fe) { + + errno = EBADF; + return NULL; + + } + + if (fe->file != False) { /* FIXME, should be dir, perhaps */ + + errno = ENOTDIR; + return NULL; + + } + + if (!fe->dir_next) + return NULL; + else { + + dirent = fe->dir_next->dirent; + + if (!dirent) { + + errno = ENOENT; + return NULL; + + } + + /* Hmmm, do I even need to copy it? */ + + memcpy(smbc_local_dirent, dirent, dirent->dirlen); /* Copy the dirent */ + + dirp = (struct smbc_dirent *)smbc_local_dirent; + + dirp->comment = (char *)(&dirp->name + dirent->namelen + 1); + + fe->dir_next = fe->dir_next->next; + + return (struct smbc_dirent *)smbc_local_dirent; + } + +} + +/* + * Routine to get directory entries + */ + +int smbc_getdents(unsigned int fd, struct smbc_dirent *dirp, int count) +{ + struct smbc_file *fe; + struct smbc_dir_list *dir; + int rem = count, reqd; + char *ndir = (char *)dirp; + + /* Check that all is ok first ... */ + + if (!smbc_initialized) { + + errno = EINVAL; + return -1; + + } + + if (fd < smbc_start_fd || fd >= (smbc_start_fd + SMBC_MAX_FD)) { + + errno = EBADF; + return -1; + + } + + fe = smbc_file_table[fd - smbc_start_fd]; + + if (!fe) { + + errno = EBADF; + return -1; + + } + + if (fe->file != False) { /* FIXME, should be dir, perhaps */ + + errno = ENOTDIR; + return -1; + + } + + /* + * Now, retrieve the number of entries that will fit in what was passed + * We have to figure out if the info is in the list, or we need to + * send a request to the server to get the info. + */ + + while ((dir = fe->dir_next)) { + struct smbc_dirent *dirent; + + if (!dir->dirent) { + + errno = ENOENT; /* Bad error */ + return -1; + + } + + if (rem < (reqd = (sizeof(struct smbc_dirent) + dir->dirent->namelen + + dir->dirent->commentlen + 1))) { + + if (rem < count) { /* We managed to copy something */ + + errno = 0; + return count - rem; + + } + else { /* Nothing copied ... */ + + errno = EINVAL; /* Not enough space ... */ + return -1; + + } + + } + + dirent = dir->dirent; + + memcpy(ndir, dirent, reqd); /* Copy the data in ... */ + + ((struct smbc_dirent *)ndir)->comment = + (char *)(&((struct smbc_dirent *)ndir)->name + dirent->namelen + 1); + + ndir += reqd; + + rem -= reqd; + + fe->dir_next = dir = dir -> next; + } + + if (rem == count) + return 0; + else + return count - rem; + +} + +/* + * Routine to create a directory ... + */ + +int smbc_mkdir(const char *fname, mode_t mode) +{ + struct smbc_server *srv; + fstring server, share, user, password, workgroup; + pstring path; + + if (!smbc_initialized) { + + errno = EINVAL; + return -1; + + } + + if (!fname) { + + errno = EINVAL; + return -1; + + } + + DEBUG(4, ("smbc_mkdir(%s)\n", fname)); + + smbc_parse_path(fname, server, share, path, user, password); /*FIXME, errors*/ + + if (user[0] == (char)0) pstrcpy(user, smbc_user); + + pstrcpy(workgroup, lp_workgroup()); + + srv = smbc_server(server, share, workgroup, user, password); + + if (!srv) { + + return -1; /* errno set by smbc_server */ + + } + + /* if (strncmp(srv->cli.dev, "IPC", 3) == 0) { + + mode = aDIR | aRONLY; + + } + else if (strncmp(srv->cli.dev, "LPT", 3) == 0) { + + if (strcmp(path, "\\") == 0) { + + mode = aDIR | aRONLY; + + } + else { + + mode = aRONLY; + smbc_stat_printjob(srv, path, &size, &m_time); + c_time = a_time = m_time; + + } + else { */ + + if (!cli_mkdir(&srv->cli, path)) { + + errno = smbc_errno(&srv->cli); + return -1; + + } + + return 0; + +} + +/* + * Our list function simply checks to see if a directory is not empty + */ + +static int smbc_rmdir_dirempty = True; + +static void rmdir_list_fn(file_info *finfo, const char *mask, void *state) +{ + + if (strncmp(finfo->name, ".", 1) != 0 && strncmp(finfo->name, "..", 2) != 0) + smbc_rmdir_dirempty = False; + +} + +/* + * Routine to remove a directory + */ + +int smbc_rmdir(const char *fname) +{ + struct smbc_server *srv; + fstring server, share, user, password, workgroup; + pstring path; + + if (!smbc_initialized) { + + errno = EINVAL; + return -1; + + } + + if (!fname) { + + errno = EINVAL; + return -1; + + } + + DEBUG(4, ("smbc_rmdir(%s)\n", fname)); + + smbc_parse_path(fname, server, share, path, user, password); /*FIXME, errors*/ + + if (user[0] == (char)0) pstrcpy(user, smbc_user); + + pstrcpy(workgroup, lp_workgroup()); + + srv = smbc_server(server, share, workgroup, user, password); + + if (!srv) { + + return -1; /* errno set by smbc_server */ + + } + + /* if (strncmp(srv->cli.dev, "IPC", 3) == 0) { + + mode = aDIR | aRONLY; + + } + else if (strncmp(srv->cli.dev, "LPT", 3) == 0) { + + if (strcmp(path, "\\") == 0) { + + mode = aDIR | aRONLY; + + } + else { + + mode = aRONLY; + smbc_stat_printjob(srv, path, &size, &m_time); + c_time = a_time = m_time; + + } + else { */ + + if (!cli_rmdir(&srv->cli, path)) { + + errno = smbc_errno(&srv->cli); + + if (errno == EACCES) { /* Check if the dir empty or not */ + + pstring lpath; /* Local storage to avoid buffer overflows */ + + smbc_rmdir_dirempty = True; /* Make this so ... */ + + pstrcpy(lpath, path); + pstrcat(lpath, "\\*"); + + if (cli_list(&srv->cli, lpath, aDIR | aSYSTEM | aHIDDEN, rmdir_list_fn, + NULL) < 0) { + + /* Fix errno to ignore latest error ... */ + + DEBUG(5, ("smbc_rmdir: cli_list returned an error: %d\n", + smbc_errno(&srv->cli))); + errno = EACCES; + + } + + if (smbc_rmdir_dirempty) + errno = EACCES; + else + errno = ENOTEMPTY; + + } + + return -1; + + } + + return 0; + +} + +/* + * Routine to return the current directory position + */ + +off_t smbc_telldir(int fd) +{ + struct smbc_file *fe; + + if (!smbc_initialized) { + + errno = EINVAL; + return -1; + + } + + if (fd < smbc_start_fd || fd >= (smbc_start_fd + SMBC_MAX_FD)) { + + errno = EBADF; + return -1; + + } + + fe = smbc_file_table[fd - smbc_start_fd]; + + if (!fe) { + + errno = EBADF; + return -1; + + } + + if (fe->file != False) { /* FIXME, should be dir, perhaps */ + + errno = ENOTDIR; + return -1; + + } + + return (off_t) fe->dir_next; + +} + +/* + * A routine to run down the list and see if the entry is OK + */ + +struct smbc_dir_list *smbc_check_dir_ent(struct smbc_dir_list *list, + struct smbc_dirent *dirent) +{ + + /* Run down the list looking for what we want */ + + if (dirent) { + + struct smbc_dir_list *tmp = list; + + while (tmp) { + + if (tmp->dirent == dirent) + return tmp; + + tmp = tmp->next; + + } + + } + + return NULL; /* Not found, or an error */ + +} + + +/* + * Routine to seek on a directory + */ + +int smbc_lseekdir(int fd, off_t offset) +{ + struct smbc_file *fe; + struct smbc_dirent *dirent = (struct smbc_dirent *)offset; + struct smbc_dir_list *list_ent = NULL; + + if (!smbc_initialized) { + + errno = EINVAL; + return -1; + + } + + if (fd < smbc_start_fd || fd >= (smbc_start_fd + SMBC_MAX_FD)) { + + errno = EBADF; + return -1; + + } + + fe = smbc_file_table[fd - smbc_start_fd]; + + if (!fe) { + + errno = EBADF; + return -1; + + } + + if (fe->file != False) { /* FIXME, should be dir, perhaps */ + + errno = ENOTDIR; + return -1; + + } + + /* Now, check what we were passed and see if it is OK ... */ + + if (dirent == NULL) { /* Seek to the begining of the list */ + + fe->dir_next = fe->dir_list; + return 0; + + } + + /* Now, run down the list and make sure that the entry is OK */ + /* This may need to be changed if we change the format of the list */ + + if ((list_ent = smbc_check_dir_ent(fe->dir_list, dirent)) == NULL) { + + errno = EINVAL; /* Bad entry */ + return -1; + + } + + fe->dir_next = list_ent; + + return 0; + +} + +/* + * Routine to fstat a dir + */ + +int smbc_fstatdir(int fd, struct stat *st) +{ + + if (!smbc_initialized) { + + errno = EINVAL; + return -1; + + } + + /* No code yet ... */ + + return 0; + +} + +/* + * Routine to print a file on a remote server ... + * + * We open the file, which we assume to be on a remote server, and then + * copy it to a print file on the share specified by printq. + */ + +int smbc_print_file(const char *fname, const char *printq) +{ + int fid1, fid2, bytes, saverr, tot_bytes = 0; + char buf[4096]; + + if (!smbc_initialized) { + + errno = EINVAL; + return -1; + + } + + if (!fname && !printq) { + + errno = EINVAL; + return -1; + + } + + /* Try to open the file for reading ... */ + + if ((fid1 = smbc_open(fname, O_RDONLY, 0666)) < 0) { + + DEBUG(3, ("Error, fname=%s, errno=%i\n", fname, errno)); + return -1; /* smbc_open sets errno */ + + } + + /* Now, try to open the printer file for writing */ + + if ((fid2 = smbc_open_print_job(printq)) < 0) { + + saverr = errno; /* Save errno */ + smbc_close(fid1); + errno = saverr; + return -1; + + } + + while ((bytes = smbc_read(fid1, buf, sizeof(buf))) > 0) { + + tot_bytes += bytes; + + if ((smbc_write(fid2, buf, bytes)) < 0) { + + saverr = errno; + smbc_close(fid1); + smbc_close(fid2); + errno = saverr; + + } + + } + + saverr = errno; + + smbc_close(fid1); /* We have to close these anyway */ + smbc_close(fid2); + + if (bytes < 0) { + + errno = saverr; + return -1; + + } + + return tot_bytes; + +} + +/* + * Open a print file to be written to by other calls + */ + +int smbc_open_print_job(const char *fname) +{ + fstring server, share, user, password; + pstring path; + + if (!smbc_initialized) { + + errno = EINVAL; + return -1; + + } + + if (!fname) { + + errno = EINVAL; + return -1; + + } + + DEBUG(4, ("smbc_open_print_job(%s)\n", fname)); + + smbc_parse_path(fname, server, share, path, user, password); /*FIXME, errors*/ + + /* What if the path is empty, or the file exists? */ + + return smbc_open(fname, O_WRONLY, 666); + +} + +/* + * Routine to list print jobs on a printer share ... + */ + +int smbc_list_print_jobs(const char *fname, void (*fn)(struct print_job_info *)) +{ + struct smbc_server *srv; + fstring server, share, user, password, workgroup; + pstring path; + + if (!smbc_initialized) { + + errno = EINVAL; + return -1; + + } + + if (!fname) { + + errno = EINVAL; + return -1; + + } + + DEBUG(4, ("smbc_list_print_jobs(%s)\n", fname)); + + smbc_parse_path(fname, server, share, path, user, password); /*FIXME, errors*/ + + if (user[0] == (char)0) pstrcpy(user, smbc_user); + + pstrcpy(workgroup, lp_workgroup()); + + srv = smbc_server(server, share, workgroup, user, password); + + if (!srv) { + + return -1; /* errno set by smbc_server */ + + } + + if (cli_print_queue(&srv->cli, fn) < 0) { + + errno = smbc_errno(&srv->cli); + return -1; + + } + + return 0; + +} + +/* + * Delete a print job from a remote printer share + */ + +int smbc_unlink_print_job(const char *fname, int id) +{ + struct smbc_server *srv; + fstring server, share, user, password, workgroup; + pstring path; + int err; + + if (!smbc_initialized) { + + errno = EINVAL; + return -1; + + } + + if (!fname) { + + errno = EINVAL; + return -1; + + } + + DEBUG(4, ("smbc_unlink_print_job(%s)\n", fname)); + + smbc_parse_path(fname, server, share, path, user, password); /*FIXME, errors*/ + + if (user[0] == (char)0) pstrcpy(user, smbc_user); + + pstrcpy(workgroup, lp_workgroup()); + + srv = smbc_server(server, share, workgroup, user, password); + + if (!srv) { + + return -1; /* errno set by smbc_server */ + + } + + if ((err = cli_printjob_del(&srv->cli, id)) != 0) { + + if (err < 0) + errno = smbc_errno(&srv->cli); + else if (err == ERRnosuchprintjob) + errno = EINVAL; + return -1; + + + } + + return 0; + +} + diff --git a/source3/libsmb/namequery.c b/source3/libsmb/namequery.c new file mode 100644 index 0000000000..7928d44652 --- /dev/null +++ b/source3/libsmb/namequery.c @@ -0,0 +1,1330 @@ +/* + Unix SMB/CIFS implementation. + name query routines + Copyright (C) Andrew Tridgell 1994-1998 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +/* nmbd.c sets this to True. */ +BOOL global_in_nmbd = False; + +/**************************************************************************** +generate a random trn_id +****************************************************************************/ +static int generate_trn_id(void) +{ + static int trn_id; + + if (trn_id == 0) { + sys_srandom(sys_getpid()); + } + + trn_id = sys_random(); + + return trn_id % (unsigned)0x7FFF; +} + + +/**************************************************************************** + parse a node status response into an array of structures +****************************************************************************/ +static struct node_status *parse_node_status(char *p, int *num_names) +{ + struct node_status *ret; + int i; + + *num_names = CVAL(p,0); + + if (*num_names == 0) return NULL; + + ret = (struct node_status *)malloc(sizeof(struct node_status)* (*num_names)); + if (!ret) return NULL; + + p++; + for (i=0;i< *num_names;i++) { + StrnCpy(ret[i].name,p,15); + trim_string(ret[i].name,NULL," "); + ret[i].type = CVAL(p,15); + ret[i].flags = p[16]; + p += 18; + DEBUG(10, ("%s#%02x: flags = 0x%02x\n", ret[i].name, + ret[i].type, ret[i].flags)); + } + return ret; +} + + +/**************************************************************************** +do a NBT node status query on an open socket and return an array of +structures holding the returned names or NULL if the query failed +**************************************************************************/ +struct node_status *node_status_query(int fd,struct nmb_name *name, + struct in_addr to_ip, int *num_names) +{ + BOOL found=False; + int retries = 2; + int retry_time = 2000; + struct timeval tval; + struct packet_struct p; + struct packet_struct *p2; + struct nmb_packet *nmb = &p.packet.nmb; + struct node_status *ret; + + ZERO_STRUCT(p); + + nmb->header.name_trn_id = generate_trn_id(); + nmb->header.opcode = 0; + nmb->header.response = False; + nmb->header.nm_flags.bcast = False; + nmb->header.nm_flags.recursion_available = False; + nmb->header.nm_flags.recursion_desired = False; + nmb->header.nm_flags.trunc = False; + nmb->header.nm_flags.authoritative = False; + nmb->header.rcode = 0; + nmb->header.qdcount = 1; + nmb->header.ancount = 0; + nmb->header.nscount = 0; + nmb->header.arcount = 0; + nmb->question.question_name = *name; + nmb->question.question_type = 0x21; + nmb->question.question_class = 0x1; + + p.ip = to_ip; + p.port = NMB_PORT; + p.fd = fd; + p.timestamp = time(NULL); + p.packet_type = NMB_PACKET; + + GetTimeOfDay(&tval); + + if (!send_packet(&p)) + return NULL; + + retries--; + + while (1) { + struct timeval tval2; + GetTimeOfDay(&tval2); + if (TvalDiff(&tval,&tval2) > retry_time) { + if (!retries) + break; + if (!found && !send_packet(&p)) + return NULL; + GetTimeOfDay(&tval); + retries--; + } + + if ((p2=receive_nmb_packet(fd,90,nmb->header.name_trn_id))) { + struct nmb_packet *nmb2 = &p2->packet.nmb; + debug_nmb_packet(p2); + + if (nmb2->header.opcode != 0 || + nmb2->header.nm_flags.bcast || + nmb2->header.rcode || + !nmb2->header.ancount || + nmb2->answers->rr_type != 0x21) { + /* XXXX what do we do with this? could be a + redirect, but we'll discard it for the + moment */ + free_packet(p2); + continue; + } + + ret = parse_node_status(&nmb2->answers->rdata[0], num_names); + free_packet(p2); + return ret; + } + } + + return NULL; +} + + +/**************************************************************************** +find the first type XX name in a node status reply - used for finding +a servers name given its IP +return the matched name in *name +**************************************************************************/ + +BOOL name_status_find(const char *q_name, int q_type, int type, struct in_addr to_ip, char *name) +{ + struct node_status *status = NULL; + struct nmb_name nname; + int count, i; + int sock; + BOOL result = False; + + DEBUG(10, ("name_status_find: looking up %s#%02x at %s\n", q_name, + q_type, inet_ntoa(to_ip))); + + sock = open_socket_in(SOCK_DGRAM, 0, 3, interpret_addr(lp_socket_address()), True); + if (sock == -1) + goto done; + + /* W2K PDC's seem not to respond to '*'#0. JRA */ + make_nmb_name(&nname, q_name, q_type); + status = node_status_query(sock, &nname, to_ip, &count); + close(sock); + if (!status) + goto done; + + for (i=0;i<count;i++) { + if (status[i].type == type) + break; + } + if (i == count) + goto done; + + pull_ascii(name, status[i].name, 15, 0, STR_TERMINATE); + result = True; + + done: + SAFE_FREE(status); + + DEBUG(10, ("name_status_find: name %sfound", result ? "" : "not ")); + + if (result) + DEBUGADD(10, (", ip address is %s", inet_ntoa(to_ip))); + + DEBUG(10, ("\n")); + + return result; +} + +/**************************************************************************** + Do a NetBIOS name registation to try to claim a name ... +***************************************************************************/ +BOOL name_register(int fd, const char *name, int name_type, + struct in_addr name_ip, int opcode, + BOOL bcast, + struct in_addr to_ip, int *count) +{ + int retries = 3; + struct timeval tval; + struct packet_struct p; + struct packet_struct *p2; + struct nmb_packet *nmb = &p.packet.nmb; + struct in_addr register_ip; + + DEBUG(4, ("name_register: %s as %s on %s\n", name, inet_ntoa(name_ip), inet_ntoa(to_ip))); + + register_ip.s_addr = name_ip.s_addr; /* Fix this ... */ + + memset((char *)&p, '\0', sizeof(p)); + + *count = 0; + + nmb->header.name_trn_id = generate_trn_id(); + nmb->header.opcode = opcode; + nmb->header.response = False; + nmb->header.nm_flags.bcast = False; + nmb->header.nm_flags.recursion_available = False; + nmb->header.nm_flags.recursion_desired = True; /* ? */ + nmb->header.nm_flags.trunc = False; + nmb->header.nm_flags.authoritative = True; + + nmb->header.qdcount = 1; + nmb->header.ancount = 0; + nmb->header.nscount = 0; + nmb->header.arcount = 1; + + make_nmb_name(&nmb->question.question_name, name, name_type); + + nmb->question.question_type = 0x20; + nmb->question.question_class = 0x1; + + /* Now, create the additional stuff for a registration request */ + + if ((nmb->additional = (struct res_rec *)malloc(sizeof(struct res_rec))) == NULL) { + + DEBUG(0, ("name_register: malloc fail for additional record.\n")); + return False; + + } + + memset((char *)nmb->additional, '\0', sizeof(struct res_rec)); + + nmb->additional->rr_name = nmb->question.question_name; + nmb->additional->rr_type = RR_TYPE_NB; + nmb->additional->rr_class = RR_CLASS_IN; + + /* See RFC 1002, sections 5.1.1.1, 5.1.1.2 and 5.1.1.3 */ + if (nmb->header.nm_flags.bcast) + nmb->additional->ttl = PERMANENT_TTL; + else + nmb->additional->ttl = lp_max_ttl(); + + nmb->additional->rdlength = 6; + + nmb->additional->rdata[0] = NB_MFLAG & 0xFF; + + /* Set the address for the name we are registering. */ + putip(&nmb->additional->rdata[2], ®ister_ip); + + p.ip = to_ip; + p.port = NMB_PORT; + p.fd = fd; + p.timestamp = time(NULL); + p.packet_type = NMB_PACKET; + + GetTimeOfDay(&tval); + + if (!send_packet(&p)) + return False; + + retries--; + + if ((p2 = receive_nmb_packet(fd, 10, nmb->header.name_trn_id))) { + debug_nmb_packet(p2); + SAFE_FREE(p2); /* No memory leaks ... */ + } + + return True; +} + +/**************************************************************************** + Do a netbios name query to find someones IP. + Returns an array of IP addresses or NULL if none. + *count will be set to the number of addresses returned. +****************************************************************************/ +struct in_addr *name_query(int fd,const char *name,int name_type, + BOOL bcast,BOOL recurse, + struct in_addr to_ip, int *count) +{ + BOOL found=False; + int i, retries = 3; + int retry_time = bcast?250:2000; + struct timeval tval; + struct packet_struct p; + struct packet_struct *p2; + struct nmb_packet *nmb = &p.packet.nmb; + struct in_addr *ip_list = NULL; + + memset((char *)&p,'\0',sizeof(p)); + (*count) = 0; + + nmb->header.name_trn_id = generate_trn_id(); + nmb->header.opcode = 0; + nmb->header.response = False; + nmb->header.nm_flags.bcast = bcast; + nmb->header.nm_flags.recursion_available = False; + nmb->header.nm_flags.recursion_desired = recurse; + nmb->header.nm_flags.trunc = False; + nmb->header.nm_flags.authoritative = False; + nmb->header.rcode = 0; + nmb->header.qdcount = 1; + nmb->header.ancount = 0; + nmb->header.nscount = 0; + nmb->header.arcount = 0; + + make_nmb_name(&nmb->question.question_name,name,name_type); + + nmb->question.question_type = 0x20; + nmb->question.question_class = 0x1; + + p.ip = to_ip; + p.port = NMB_PORT; + p.fd = fd; + p.timestamp = time(NULL); + p.packet_type = NMB_PACKET; + + GetTimeOfDay(&tval); + + if (!send_packet(&p)) + return NULL; + + retries--; + + while (1) { + struct timeval tval2; + struct in_addr *tmp_ip_list; + + GetTimeOfDay(&tval2); + if (TvalDiff(&tval,&tval2) > retry_time) { + if (!retries) + break; + if (!found && !send_packet(&p)) + return NULL; + GetTimeOfDay(&tval); + retries--; + } + + if ((p2=receive_nmb_packet(fd,90,nmb->header.name_trn_id))) { + struct nmb_packet *nmb2 = &p2->packet.nmb; + debug_nmb_packet(p2); + + /* If we get a Negative Name Query Response from a WINS + * server, we should report it and give up. + */ + if( 0 == nmb2->header.opcode /* A query response */ + && !(bcast) /* from a WINS server */ + && nmb2->header.rcode /* Error returned */ + ) { + + if( DEBUGLVL( 3 ) ) { + /* Only executed if DEBUGLEVEL >= 3 */ + dbgtext( "Negative name query response, rcode 0x%02x: ", nmb2->header.rcode ); + switch( nmb2->header.rcode ) { + case 0x01: + dbgtext( "Request was invalidly formatted.\n" ); + break; + case 0x02: + dbgtext( "Problem with NBNS, cannot process name.\n"); + break; + case 0x03: + dbgtext( "The name requested does not exist.\n" ); + break; + case 0x04: + dbgtext( "Unsupported request error.\n" ); + break; + case 0x05: + dbgtext( "Query refused error.\n" ); + break; + default: + dbgtext( "Unrecognized error code.\n" ); + break; + } + } + free_packet(p2); + return( NULL ); + } + + if (nmb2->header.opcode != 0 || + nmb2->header.nm_flags.bcast || + nmb2->header.rcode || + !nmb2->header.ancount) { + /* + * XXXX what do we do with this? Could be a + * redirect, but we'll discard it for the + * moment. + */ + free_packet(p2); + continue; + } + + tmp_ip_list = (struct in_addr *)Realloc( ip_list, sizeof( ip_list[0] ) + * ( (*count) + nmb2->answers->rdlength/6 ) ); + + if (!tmp_ip_list) { + DEBUG(0,("name_query: Realloc failed.\n")); + SAFE_FREE(ip_list); + } + + ip_list = tmp_ip_list; + + if (ip_list) { + DEBUG(2,("Got a positive name query response from %s ( ", inet_ntoa(p2->ip))); + for (i=0;i<nmb2->answers->rdlength/6;i++) { + putip((char *)&ip_list[(*count)],&nmb2->answers->rdata[2+i*6]); + DEBUGADD(2,("%s ",inet_ntoa(ip_list[(*count)]))); + (*count)++; + } + DEBUGADD(2,(")\n")); + } + + found=True; + retries=0; + free_packet(p2); + /* + * If we're doing a unicast lookup we only + * expect one reply. Don't wait the full 2 + * seconds if we got one. JRA. + */ + if(!bcast && found) + break; + } + } + + /* Reach here if we've timed out waiting for replies.. */ + if( !bcast && !found ) { + /* Timed out wating for WINS server to respond. Mark it dead. */ + wins_srv_died( to_ip ); + } + + return ip_list; +} + +/******************************************************** + Start parsing the lmhosts file. +*********************************************************/ + +XFILE *startlmhosts(char *fname) +{ + XFILE *fp = x_fopen(fname,O_RDONLY, 0); + if (!fp) { + DEBUG(4,("startlmhosts: Can't open lmhosts file %s. Error was %s\n", + fname, strerror(errno))); + return NULL; + } + return fp; +} + +/******************************************************** + Parse the next line in the lmhosts file. +*********************************************************/ + +BOOL getlmhostsent( XFILE *fp, pstring name, int *name_type, struct in_addr *ipaddr) +{ + pstring line; + + while(!x_feof(fp) && !x_ferror(fp)) { + pstring ip,flags,extra; + char *ptr; + int count = 0; + + *name_type = -1; + + if (!fgets_slash(line,sizeof(pstring),fp)) + continue; + + if (*line == '#') + continue; + + pstrcpy(ip,""); + pstrcpy(name,""); + pstrcpy(flags,""); + + ptr = line; + + if (next_token(&ptr,ip ,NULL,sizeof(ip))) + ++count; + if (next_token(&ptr,name ,NULL, sizeof(pstring))) + ++count; + if (next_token(&ptr,flags,NULL, sizeof(flags))) + ++count; + if (next_token(&ptr,extra,NULL, sizeof(extra))) + ++count; + + if (count <= 0) + continue; + + if (count > 0 && count < 2) + { + DEBUG(0,("getlmhostsent: Ill formed hosts line [%s]\n",line)); + continue; + } + + if (count >= 4) + { + DEBUG(0,("getlmhostsent: too many columns in lmhosts file (obsolete syntax)\n")); + continue; + } + + DEBUG(4, ("getlmhostsent: lmhost entry: %s %s %s\n", ip, name, flags)); + + if (strchr_m(flags,'G') || strchr_m(flags,'S')) + { + DEBUG(0,("getlmhostsent: group flag in lmhosts ignored (obsolete)\n")); + continue; + } + + *ipaddr = *interpret_addr2(ip); + + /* Extra feature. If the name ends in '#XX', where XX is a hex number, + then only add that name type. */ + if((ptr = strchr_m(name, '#')) != NULL) + { + char *endptr; + + ptr++; + *name_type = (int)strtol(ptr, &endptr, 16); + + if(!*ptr || (endptr == ptr)) + { + DEBUG(0,("getlmhostsent: invalid name %s containing '#'.\n", name)); + continue; + } + + *(--ptr) = '\0'; /* Truncate at the '#' */ + } + + return True; + } + + return False; +} + +/******************************************************** + Finish parsing the lmhosts file. +*********************************************************/ + +void endlmhosts(XFILE *fp) +{ + x_fclose(fp); +} + +BOOL name_register_wins(const char *name, int name_type) +{ + int sock, i, return_count; + int num_interfaces = iface_count(); + struct in_addr sendto_ip; + + /* + * Check if we have any interfaces, prevents a segfault later + */ + + if (num_interfaces <= 0) + return False; /* Should return some indication of the problem */ + + /* + * Do a broadcast register ... + */ + + if (0 == wins_srv_count()) + return False; + + if( DEBUGLVL( 4 ) ) + { + dbgtext( "name_register_wins: Registering my name %s ", name ); + dbgtext( "with WINS server %s.\n", wins_srv_name() ); + } + + sock = open_socket_in( SOCK_DGRAM, 0, 3, + interpret_addr("0.0.0.0"), True ); + + if (sock == -1) return False; + + set_socket_options(sock, "SO_BROADCAST"); /* ????! crh */ + + sendto_ip = wins_srv_ip(); + + if (num_interfaces > 1) { + + for (i = 0; i < num_interfaces; i++) { + + if (!name_register(sock, name, name_type, *iface_n_ip(i), + NMB_NAME_MULTIHOMED_REG_OPCODE, + True, sendto_ip, &return_count)) { + + close(sock); + return False; + + } + + } + + } + else { + + if (!name_register(sock, name, name_type, *iface_n_ip(0), + NMB_NAME_REG_OPCODE, + True, sendto_ip, &return_count)) { + + close(sock); + return False; + + } + + } + + close(sock); + + return True; + +} + +/******************************************************** + Resolve via "bcast" method. +*********************************************************/ + +BOOL name_resolve_bcast(const char *name, int name_type, + struct in_addr **return_ip_list, int *return_count) +{ + int sock, i; + int num_interfaces = iface_count(); + + *return_ip_list = NULL; + *return_count = 0; + + /* + * "bcast" means do a broadcast lookup on all the local interfaces. + */ + + DEBUG(3,("name_resolve_bcast: Attempting broadcast lookup for name %s<0x%x>\n", name, name_type)); + + sock = open_socket_in( SOCK_DGRAM, 0, 3, + interpret_addr(lp_socket_address()), True ); + + if (sock == -1) return False; + + set_socket_options(sock,"SO_BROADCAST"); + /* + * Lookup the name on all the interfaces, return on + * the first successful match. + */ + for( i = num_interfaces-1; i >= 0; i--) { + struct in_addr sendto_ip; + /* Done this way to fix compiler error on IRIX 5.x */ + sendto_ip = *iface_n_bcast(i); + *return_ip_list = name_query(sock, name, name_type, True, + True, sendto_ip, return_count); + if(*return_ip_list != NULL) { + close(sock); + return True; + } + } + + close(sock); + return False; +} + +/******************************************************** + Resolve via "wins" method. +*********************************************************/ + +static BOOL resolve_wins(const char *name, int name_type, + struct in_addr **return_iplist, int *return_count) +{ + int sock; + struct in_addr wins_ip; + BOOL wins_ismyip; + + *return_iplist = NULL; + *return_count = 0; + + /* + * "wins" means do a unicast lookup to the WINS server. + * Ignore if there is no WINS server specified or if the + * WINS server is one of our interfaces (if we're being + * called from within nmbd - we can't do this call as we + * would then block). + */ + + DEBUG(3,("resolve_wins: Attempting wins lookup for name %s<0x%x>\n", name, name_type)); + + if (lp_wins_support()) { + /* + * We're providing WINS support. Call ourselves so + * long as we're not nmbd. + */ + extern struct in_addr loopback_ip; + wins_ip = loopback_ip; + wins_ismyip = True; + } else if( wins_srv_count() < 1 ) { + DEBUG(3,("resolve_wins: WINS server resolution selected and no WINS servers listed.\n")); + return False; + } else { + wins_ip = wins_srv_ip(); + wins_ismyip = ismyip(wins_ip); + } + + DEBUG(3, ("resolve_wins: WINS server == <%s>\n", inet_ntoa(wins_ip)) ); + if((wins_ismyip && !global_in_nmbd) || !wins_ismyip) { + sock = open_socket_in( SOCK_DGRAM, 0, 3, + interpret_addr(lp_socket_address()), + True ); + if (sock != -1) { + *return_iplist = name_query( sock, name, + name_type, False, + True, wins_ip, + return_count); + if(*return_iplist != NULL) { + close(sock); + return True; + } + close(sock); + } + } + + return False; +} + +/******************************************************** + Resolve via "lmhosts" method. +*********************************************************/ + +static BOOL resolve_lmhosts(const char *name, int name_type, + struct in_addr **return_iplist, int *return_count) +{ + /* + * "lmhosts" means parse the local lmhosts file. + */ + + XFILE *fp; + pstring lmhost_name; + int name_type2; + struct in_addr return_ip; + + *return_iplist = NULL; + *return_count = 0; + + DEBUG(3,("resolve_lmhosts: Attempting lmhosts lookup for name %s<0x%x>\n", name, name_type)); + + fp = startlmhosts(dyn_LMHOSTSFILE); + if(fp) { + while (getlmhostsent(fp, lmhost_name, &name_type2, &return_ip)) { + if (strequal(name, lmhost_name) && + ((name_type2 == -1) || (name_type == name_type2)) + ) { + endlmhosts(fp); + *return_iplist = (struct in_addr *)malloc(sizeof(struct in_addr)); + if(*return_iplist == NULL) { + DEBUG(3,("resolve_lmhosts: malloc fail !\n")); + return False; + } + **return_iplist = return_ip; + *return_count = 1; + return True; + } + } + endlmhosts(fp); + } + return False; +} + + +/******************************************************** + Resolve via "hosts" method. +*********************************************************/ + +static BOOL resolve_hosts(const char *name, + struct in_addr **return_iplist, int *return_count) +{ + /* + * "host" means do a localhost, or dns lookup. + */ + struct hostent *hp; + + *return_iplist = NULL; + *return_count = 0; + + DEBUG(3,("resolve_hosts: Attempting host lookup for name %s<0x20>\n", name)); + + if (((hp = sys_gethostbyname(name)) != NULL) && (hp->h_addr != NULL)) { + struct in_addr return_ip; + putip((char *)&return_ip,(char *)hp->h_addr); + *return_iplist = (struct in_addr *)malloc(sizeof(struct in_addr)); + if(*return_iplist == NULL) { + DEBUG(3,("resolve_hosts: malloc fail !\n")); + return False; + } + **return_iplist = return_ip; + *return_count = 1; + return True; + } + return False; +} + +/******************************************************** + Internal interface to resolve a name into an IP address. + Use this function if the string is either an IP address, DNS + or host name or NetBIOS name. This uses the name switch in the + smb.conf to determine the order of name resolution. +*********************************************************/ + +static BOOL internal_resolve_name(const char *name, int name_type, + struct in_addr **return_iplist, int *return_count) +{ + pstring name_resolve_list; + fstring tok; + char *ptr; + BOOL allones = (strcmp(name,"255.255.255.255") == 0); + BOOL allzeros = (strcmp(name,"0.0.0.0") == 0); + BOOL is_address = is_ipaddress(name); + BOOL result = False; + struct in_addr *nodupes_iplist; + int i; + + *return_iplist = NULL; + *return_count = 0; + + DEBUG(10, ("internal_resolve_name: looking up %s#%x\n", name, name_type)); + + if (allzeros || allones || is_address) { + *return_iplist = (struct in_addr *)malloc(sizeof(struct in_addr)); + if(*return_iplist == NULL) { + DEBUG(3,("internal_resolve_name: malloc fail !\n")); + return False; + } + if(is_address) { + /* if it's in the form of an IP address then get the lib to interpret it */ + (*return_iplist)->s_addr = inet_addr(name); + } else { + (*return_iplist)->s_addr = allones ? 0xFFFFFFFF : 0; + *return_count = 1; + } + return True; + } + + pstrcpy(name_resolve_list, lp_name_resolve_order()); + ptr = name_resolve_list; + if (!ptr || !*ptr) + ptr = "host"; + + while (next_token(&ptr, tok, LIST_SEP, sizeof(tok))) { + if((strequal(tok, "host") || strequal(tok, "hosts"))) { + if (name_type == 0x20 && resolve_hosts(name, return_iplist, return_count)) { + result = True; + goto done; + } + } else if(strequal( tok, "lmhosts")) { + if (resolve_lmhosts(name, name_type, return_iplist, return_count)) { + result = True; + goto done; + } + } else if(strequal( tok, "wins")) { + /* don't resolve 1D via WINS */ + if (name_type != 0x1D && + resolve_wins(name, name_type, return_iplist, return_count)) { + result = True; + goto done; + } + } else if(strequal( tok, "bcast")) { + if (name_resolve_bcast(name, name_type, return_iplist, return_count)) { + result = True; + goto done; + } + } else { + DEBUG(0,("resolve_name: unknown name switch type %s\n", tok)); + } + } + + /* All of the resolve_* functions above have returned false. */ + + SAFE_FREE(*return_iplist); + *return_count = 0; + + return False; + + done: + + /* Remove duplicate entries. Some queries, notably #1c (domain + controllers) return the PDC in iplist[0] and then all domain + controllers including the PDC in iplist[1..n]. Iterating over + the iplist when the PDC is down will cause two sets of timeouts. */ + + if (*return_count && (nodupes_iplist = (struct in_addr *) + malloc(sizeof(struct in_addr) * (*return_count)))) { + int nodupes_count = 0; + + /* Iterate over return_iplist looking for duplicates */ + + for (i = 0; i < *return_count; i++) { + BOOL is_dupe = False; + int j; + + for (j = i + 1; j < *return_count; j++) { + if (ip_equal((*return_iplist)[i], + (*return_iplist)[j])) { + is_dupe = True; + break; + } + } + + if (!is_dupe) { + + /* This one not a duplicate */ + + nodupes_iplist[nodupes_count] = (*return_iplist)[i]; + nodupes_count++; + } + } + + /* Switcheroo with original list */ + + free(*return_iplist); + + *return_iplist = nodupes_iplist; + *return_count = nodupes_count; + } + + /* Display some debugging info */ + + DEBUG(10, ("internal_resolve_name: returning %d addresses: ", + *return_count)); + + for (i = 0; i < *return_count; i++) + DEBUGADD(10, ("%s ", inet_ntoa((*return_iplist)[i]))); + + DEBUG(10, ("\n")); + + return result; +} + +/******************************************************** + Internal interface to resolve a name into one IP address. + Use this function if the string is either an IP address, DNS + or host name or NetBIOS name. This uses the name switch in the + smb.conf to determine the order of name resolution. +*********************************************************/ + +BOOL resolve_name(const char *name, struct in_addr *return_ip, int name_type) +{ + struct in_addr *ip_list = NULL; + int count = 0; + + if (is_ipaddress(name)) { + *return_ip = *interpret_addr2(name); + return True; + } + + if (internal_resolve_name(name, name_type, &ip_list, &count)) { + int i; + /* only return valid addresses for TCP connections */ + for (i=0; i<count; i++) { + char *ip_str = inet_ntoa(ip_list[i]); + if (ip_str && + strcmp(ip_str, "255.255.255.255") != 0 && + strcmp(ip_str, "0.0.0.0") != 0) { + *return_ip = ip_list[i]; + SAFE_FREE(ip_list); + return True; + } + } + } + SAFE_FREE(ip_list); + return False; +} + + +/******************************************************** + resolve a name of format \\server_name or \\ipaddress + into a name. also, cut the \\ from the front for us. +*********************************************************/ + +BOOL resolve_srv_name(const char* srv_name, fstring dest_host, + struct in_addr *ip) +{ + BOOL ret; + const char *sv_name = srv_name; + + DEBUG(10,("resolve_srv_name: %s\n", srv_name)); + + if (srv_name == NULL || strequal("\\\\.", srv_name)) + { + extern pstring global_myname; + fstrcpy(dest_host, global_myname); + ip = interpret_addr2("127.0.0.1"); + return True; + } + + if (strnequal("\\\\", srv_name, 2)) + { + sv_name = &srv_name[2]; + } + + fstrcpy(dest_host, sv_name); + /* treat the '*' name specially - it is a magic name for the PDC */ + if (strcmp(dest_host,"*") == 0) { + extern pstring global_myname; + ret = resolve_name(lp_workgroup(), ip, 0x1B); + lookup_dc_name(global_myname, lp_workgroup(), ip, dest_host); + } else { + ret = resolve_name(dest_host, ip, 0x20); + } + + if (is_ipaddress(dest_host)) + { + fstrcpy(dest_host, "*SMBSERVER"); + } + + return ret; +} + + +/******************************************************** + Find the IP address of the master browser or DMB for a workgroup. +*********************************************************/ + +BOOL find_master_ip(char *group, struct in_addr *master_ip) +{ + struct in_addr *ip_list = NULL; + int count = 0; + + if (internal_resolve_name(group, 0x1D, &ip_list, &count)) { + *master_ip = ip_list[0]; + SAFE_FREE(ip_list); + return True; + } + if(internal_resolve_name(group, 0x1B, &ip_list, &count)) { + *master_ip = ip_list[0]; + SAFE_FREE(ip_list); + return True; + } + + SAFE_FREE(ip_list); + return False; +} + +/******************************************************** + Lookup a DC name given a Domain name and IP address. +*********************************************************/ + +BOOL lookup_dc_name(const char *srcname, const char *domain, + struct in_addr *dc_ip, char *ret_name) +{ +#if !defined(I_HATE_WINDOWS_REPLY_CODE) + + fstring dc_name; + BOOL ret; + + /* + * Due to the fact win WinNT *sucks* we must do a node status + * query here... JRA. + */ + + *dc_name = '\0'; + + ret = name_status_find(domain, 0x1c, 0x20, *dc_ip, dc_name); + + if(ret && *dc_name) { + fstrcpy(ret_name, dc_name); + return True; + } + + return False; + +#else /* defined(I_HATE_WINDOWS_REPLY_CODE) */ + +JRA - This code is broken with BDC rollover - we need to do a full +NT GETDC call, UNICODE, NT domain SID and uncle tom cobbley and all... + + int retries = 3; + int retry_time = 2000; + struct timeval tval; + struct packet_struct p; + struct dgram_packet *dgram = &p.packet.dgram; + char *ptr,*p2; + char tmp[4]; + int len; + struct sockaddr_in sock_name; + int sock_len = sizeof(sock_name); + const char *mailslot = NET_LOGON_MAILSLOT; + char *mailslot_name; + char buffer[1024]; + char *bufp; + int dgm_id = generate_trn_id(); + int sock = open_socket_in(SOCK_DGRAM, 0, 3, interpret_addr(lp_socket_address()), True ); + + if(sock == -1) + return False; + + /* Find out the transient UDP port we have been allocated. */ + if(getsockname(sock, (struct sockaddr *)&sock_name, &sock_len)<0) { + DEBUG(0,("lookup_pdc_name: Failed to get local UDP port. Error was %s\n", + strerror(errno))); + close(sock); + return False; + } + + /* + * Create the request data. + */ + + memset(buffer,'\0',sizeof(buffer)); + bufp = buffer; + SSVAL(bufp,0,QUERYFORPDC); + bufp += 2; + fstrcpy(bufp,srcname); + bufp += (strlen(bufp) + 1); + slprintf(bufp, sizeof(fstring)-1, "\\MAILSLOT\\NET\\GETDC%d", dgm_id); + mailslot_name = bufp; + bufp += (strlen(bufp) + 1); + bufp = ALIGN2(bufp, buffer); + bufp += push_ucs2(NULL, bufp, srcname, sizeof(buffer) - (bufp - buffer), STR_TERMINATE); + + SIVAL(bufp,0,1); + SSVAL(bufp,4,0xFFFF); + SSVAL(bufp,6,0xFFFF); + bufp += 8; + len = PTR_DIFF(bufp,buffer); + + memset((char *)&p,'\0',sizeof(p)); + + /* DIRECT GROUP or UNIQUE datagram. */ + dgram->header.msg_type = 0x10; + dgram->header.flags.node_type = M_NODE; + dgram->header.flags.first = True; + dgram->header.flags.more = False; + dgram->header.dgm_id = dgm_id; + dgram->header.source_ip = *iface_ip(*pdc_ip); + dgram->header.source_port = ntohs(sock_name.sin_port); + dgram->header.dgm_length = 0; /* Let build_dgram() handle this. */ + dgram->header.packet_offset = 0; + + make_nmb_name(&dgram->source_name,srcname,0); + make_nmb_name(&dgram->dest_name,domain,0x1C); + + ptr = &dgram->data[0]; + + /* Setup the smb part. */ + ptr -= 4; /* XXX Ugliness because of handling of tcp SMB length. */ + memcpy(tmp,ptr,4); + set_message(ptr,17,17 + len,True); + memcpy(ptr,tmp,4); + + CVAL(ptr,smb_com) = SMBtrans; + SSVAL(ptr,smb_vwv1,len); + SSVAL(ptr,smb_vwv11,len); + SSVAL(ptr,smb_vwv12,70 + strlen(mailslot)); + SSVAL(ptr,smb_vwv13,3); + SSVAL(ptr,smb_vwv14,1); + SSVAL(ptr,smb_vwv15,1); + SSVAL(ptr,smb_vwv16,2); + p2 = smb_buf(ptr); + pstrcpy(p2,mailslot); + p2 = skip_string(p2,1); + + memcpy(p2,buffer,len); + p2 += len; + + dgram->datasize = PTR_DIFF(p2,ptr+4); /* +4 for tcp length. */ + + p.ip = *pdc_ip; + p.port = DGRAM_PORT; + p.fd = sock; + p.timestamp = time(NULL); + p.packet_type = DGRAM_PACKET; + + GetTimeOfDay(&tval); + + if (!send_packet(&p)) { + DEBUG(0,("lookup_pdc_name: send_packet failed.\n")); + close(sock); + return False; + } + + retries--; + + while (1) { + struct timeval tval2; + struct packet_struct *p_ret; + + GetTimeOfDay(&tval2); + if (TvalDiff(&tval,&tval2) > retry_time) { + if (!retries) + break; + if (!send_packet(&p)) { + DEBUG(0,("lookup_pdc_name: send_packet failed.\n")); + close(sock); + return False; + } + GetTimeOfDay(&tval); + retries--; + } + + if ((p_ret = receive_dgram_packet(sock,90,mailslot_name))) { + struct dgram_packet *dgram2 = &p_ret->packet.dgram; + char *buf; + char *buf2; + + buf = &dgram2->data[0]; + buf -= 4; + + if (CVAL(buf,smb_com) != SMBtrans) { + DEBUG(0,("lookup_pdc_name: datagram type %u != SMBtrans(%u)\n", (unsigned int) + CVAL(buf,smb_com), (unsigned int)SMBtrans )); + free_packet(p_ret); + continue; + } + + len = SVAL(buf,smb_vwv11); + buf2 = smb_base(buf) + SVAL(buf,smb_vwv12); + + if (len <= 0) { + DEBUG(0,("lookup_pdc_name: datagram len < 0 (%d)\n", len )); + free_packet(p_ret); + continue; + } + + DEBUG(4,("lookup_pdc_name: datagram reply from %s to %s IP %s for %s of type %d len=%d\n", + nmb_namestr(&dgram2->source_name),nmb_namestr(&dgram2->dest_name), + inet_ntoa(p_ret->ip), smb_buf(buf),SVAL(buf2,0),len)); + + if(SVAL(buf2,0) != QUERYFORPDC_R) { + DEBUG(0,("lookup_pdc_name: datagram type (%u) != QUERYFORPDC_R(%u)\n", + (unsigned int)SVAL(buf,0), (unsigned int)QUERYFORPDC_R )); + free_packet(p_ret); + continue; + } + + buf2 += 2; + /* Note this is safe as it is a bounded strcpy. */ + fstrcpy(ret_name, buf2); + ret_name[sizeof(fstring)-1] = '\0'; + close(sock); + free_packet(p_ret); + return True; + } + } + + close(sock); + return False; +#endif /* defined(I_HATE_WINDOWS_REPLY_CODE) */ +} + +/******************************************************** + Get the IP address list of the Local Master Browsers + ********************************************************/ + +BOOL get_lmb_list(struct in_addr **ip_list, int *count) +{ + return internal_resolve_name( MSBROWSE, 0x1, ip_list, count); +} + +/******************************************************** + Get the IP address list of the PDC/BDC's of a Domain. +*********************************************************/ + +BOOL get_dc_list(BOOL pdc_only, const char *group, struct in_addr **ip_list, int *count) +{ + int name_type = pdc_only ? 0x1B : 0x1C; + + /* + * If it's our domain then + * use the 'password server' parameter. + */ + + if (strequal(group, lp_workgroup())) { + char *p; + char *pserver = lp_passwordserver(); + fstring name; + int num_adresses = 0; + struct in_addr *return_iplist = NULL; + + if (! *pserver) + return internal_resolve_name(group, name_type, ip_list, count); + + p = pserver; + while (next_token(&p,name,LIST_SEP,sizeof(name))) { + if (strequal(name, "*")) + return internal_resolve_name(group, name_type, ip_list, count); + num_adresses++; + } + if (num_adresses == 0) + return internal_resolve_name(group, name_type, ip_list, count); + + return_iplist = (struct in_addr *)malloc(num_adresses * sizeof(struct in_addr)); + if(return_iplist == NULL) { + DEBUG(3,("get_dc_list: malloc fail !\n")); + return False; + } + p = pserver; + *count = 0; + while (next_token(&p,name,LIST_SEP,sizeof(name))) { + struct in_addr name_ip; + if (resolve_name( name, &name_ip, 0x20) == False) + continue; + return_iplist[(*count)++] = name_ip; + } + *ip_list = return_iplist; + return (*count != 0); + } else + return internal_resolve_name(group, name_type, ip_list, count); +} diff --git a/source3/libsmb/netlogon_unigrp.c b/source3/libsmb/netlogon_unigrp.c new file mode 100644 index 0000000000..979ff52bd3 --- /dev/null +++ b/source3/libsmb/netlogon_unigrp.c @@ -0,0 +1,157 @@ +/* + Unix SMB/CIFS implementation. + Universal groups helpers + Copyright (C) Alexander Bokovoy 2002. + Copyright (C) Andrew Bartlett 2002. + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + This work was sponsored by Optifacio Software Services, Inc. +*/ + +#include "includes.h" + +/* + Handle for netlogon_unigrp.tdb database. It is used internally + in cli_store_uni_groups_*() and cli_fetch_uni_groups() + and is initialized on first call to cli_store_uni_groups_*() +*/ +static TDB_CONTEXT *netlogon_unigrp_tdb = NULL; + +/* + Store universal groups info into netlogon_unigrp.tdb for + later usage. We use 'domain_SID/user_rid' as key and + array of uint32 where array[0] is number of elements + and elements are array[1] ... array[array[0]] +*/ + +BOOL uni_group_cache_init(void) +{ + if (!netlogon_unigrp_tdb) { + netlogon_unigrp_tdb = tdb_open_log(lock_path("netlogon_unigrp.tdb"), 0, + TDB_DEFAULT, O_RDWR | O_CREAT, 0644); + } + + return (netlogon_unigrp_tdb != NULL); +} + +void uni_group_cache_store_netlogon(TALLOC_CTX *mem_ctx, NET_USER_INFO_3 *user) +{ + TDB_DATA key,data; + fstring keystr; + int i; + + if (!uni_group_cache_init()) { + DEBUG(0,("uni_group_cache_store_netlogon: cannot open netlogon_unigrp.tdb for write!\n")); + return; + } + + /* Prepare key as DOMAIN-SID/USER-RID string */ + slprintf(keystr, sizeof(keystr), "%s/%d", + sid_string_static(&user->dom_sid.sid), user->user_rid); + key.dptr = keystr; + key.dsize = strlen(keystr) + 1; + + /* Prepare data */ + data.dsize = (user->num_groups2+1)*sizeof(uint32); + data.dptr = talloc(mem_ctx, data.dsize); + if(!data.dptr) { + DEBUG(0,("uni_group_cache_store_netlogon: cannot allocate memory!\n")); + talloc_destroy(mem_ctx); + return; + } + + /* Store data in byteorder-independent format */ + SIVAL(&((uint32*)data.dptr)[0],0,user->num_groups2); + for(i=1; i<=user->num_groups2; i++) { + SIVAL(&((uint32*)data.dptr)[i],0,user->gids[i-1].g_rid); + } + tdb_store(netlogon_unigrp_tdb, key, data, TDB_REPLACE); +} + +/* + Fetch universal groups info from netlogon_unigrp.tdb for given + domain sid and user rid and allocate it using given mem_ctx. + Universal groups are returned as array of uint32 elements + and elements are array[0] ... array[num_elements-1] + +*/ +uint32* uni_group_cache_fetch(DOM_SID *domain, uint32 user_rid, + TALLOC_CTX *mem_ctx, uint32 *num_groups) +{ + TDB_DATA key,data; + fstring keystr; + uint32 *groups; + uint32 i; + uint32 group_count; + + if (!domain) { + DEBUG(1,("uni_group_cache_fetch: expected non-null domain sid\n")); + return NULL; + } + if (!mem_ctx) { + DEBUG(1,("uni_group_cache_fetch: expected non-null memory context\n")); + return NULL; + } + if (!num_groups) { + DEBUG(1,("uni_group_cache_fetch: expected non-null num_groups\n")); + return NULL; + } + if (!netlogon_unigrp_tdb) { + netlogon_unigrp_tdb = tdb_open_log(lock_path("netlogon_unigrp.tdb"), 0, + TDB_DEFAULT, O_RDWR, 0644); + } + if (!netlogon_unigrp_tdb) { + DEBUG(5,("uni_group_cache_fetch: cannot open netlogon_unigrp.tdb for read - normal if not created yet\n")); + return NULL; + } + + *num_groups = 0; + + /* Fetch universal groups */ + slprintf(keystr, sizeof(keystr), "%s/%d", + sid_string_static(domain), user_rid); + key.dptr = keystr; + key.dsize = strlen(keystr) + 1; + data = tdb_fetch(netlogon_unigrp_tdb, key); + + /* There is no cached universal groups in netlogon_unigrp.tdb */ + /* for this user. */ + if (!data.dptr) return NULL; + + /* Transfer data to receiver's memory context */ + group_count = IVAL(&((uint32*)data.dptr)[0],0); + groups = talloc(mem_ctx, (group_count)*sizeof(uint32)); + if (groups) { + for(i=0; i<group_count; i++) { + groups[i] = IVAL(&((uint32*)data.dptr)[i+1],0); + } + + } else { + DEBUG(1,("uni_group_cache_fetch: cannot allocate uni groups in receiver's memory context\n")); + } + SAFE_FREE(data.dptr); + *num_groups = group_count; + return groups; +} + +/* Shutdown netlogon_unigrp database */ +void uni_group_cache_shutdown(void) +{ + if(netlogon_unigrp_tdb) { + tdb_close(netlogon_unigrp_tdb); + } +} + diff --git a/source3/libsmb/nmblib.c b/source3/libsmb/nmblib.c index 6743227173..c78946fa09 100644 --- a/source3/libsmb/nmblib.c +++ b/source3/libsmb/nmblib.c @@ -1,8 +1,7 @@ /* - Unix SMB/Netbios implementation. - Version 1.9. + Unix SMB/CIFS implementation. NBT netbios library routines - Copyright (C) Andrew Tridgell 1994-1995 + Copyright (C) Andrew Tridgell 1994-1998 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 @@ -21,24 +20,141 @@ */ #include "includes.h" -#include "nameserv.h" -extern int DEBUGLEVEL; +int num_good_sends = 0; +int num_good_receives = 0; + +static const struct opcode_names { + char *nmb_opcode_name; + int opcode; +} nmb_header_opcode_names[] = { + {"Query", 0 }, + {"Registration", 5 }, + {"Release", 6 }, + {"WACK", 7 }, + {"Refresh", 8 }, + {"Refresh(altcode)", 9 }, + {"Multi-homed Registration", 15 }, + {0, -1 } +}; -int num_good_sends=0; -int num_good_receives=0; -static uint16 name_trn_id = 0; -BOOL CanRecurse = True; -extern pstring scope; +/**************************************************************************** + * Lookup a nmb opcode name. + ****************************************************************************/ +static const char *lookup_opcode_name( int opcode ) +{ + const struct opcode_names *op_namep; + int i; + + for(i = 0; nmb_header_opcode_names[i].nmb_opcode_name != 0; i++) { + op_namep = &nmb_header_opcode_names[i]; + if(opcode == op_namep->opcode) + return op_namep->nmb_opcode_name; + } + return "<unknown opcode>"; +} + +/**************************************************************************** + print out a res_rec structure + ****************************************************************************/ +static void debug_nmb_res_rec(struct res_rec *res, char *hdr) +{ + int i, j; + + DEBUGADD( 4, ( " %s: nmb_name=%s rr_type=%d rr_class=%d ttl=%d\n", + hdr, + nmb_namestr(&res->rr_name), + res->rr_type, + res->rr_class, + res->ttl ) ); + + if( res->rdlength == 0 || res->rdata == NULL ) + return; + + for (i = 0; i < res->rdlength; i+= 16) + { + DEBUGADD(4, (" %s %3x char ", hdr, i)); + + for (j = 0; j < 16; j++) + { + uchar x = res->rdata[i+j]; + if (x < 32 || x > 127) x = '.'; + + if (i+j >= res->rdlength) break; + DEBUGADD(4, ("%c", x)); + } + + DEBUGADD(4, (" hex ")); + + for (j = 0; j < 16; j++) + { + if (i+j >= res->rdlength) break; + DEBUGADD(4, ("%02X", (uchar)res->rdata[i+j])); + } + + DEBUGADD(4, ("\n")); + } +} + +/**************************************************************************** + process a nmb packet + ****************************************************************************/ +void debug_nmb_packet(struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + + if( DEBUGLVL( 4 ) ) + { + dbgtext( "nmb packet from %s(%d) header: id=%d opcode=%s(%d) response=%s\n", + inet_ntoa(p->ip), p->port, + nmb->header.name_trn_id, + lookup_opcode_name(nmb->header.opcode), + nmb->header.opcode, + BOOLSTR(nmb->header.response) ); + dbgtext( " header: flags: bcast=%s rec_avail=%s rec_des=%s trunc=%s auth=%s\n", + BOOLSTR(nmb->header.nm_flags.bcast), + BOOLSTR(nmb->header.nm_flags.recursion_available), + BOOLSTR(nmb->header.nm_flags.recursion_desired), + BOOLSTR(nmb->header.nm_flags.trunc), + BOOLSTR(nmb->header.nm_flags.authoritative) ); + dbgtext( " header: rcode=%d qdcount=%d ancount=%d nscount=%d arcount=%d\n", + nmb->header.rcode, + nmb->header.qdcount, + nmb->header.ancount, + nmb->header.nscount, + nmb->header.arcount ); + } + + if (nmb->header.qdcount) + { + DEBUGADD( 4, ( " question: q_name=%s q_type=%d q_class=%d\n", + nmb_namestr(&nmb->question.question_name), + nmb->question.question_type, + nmb->question.question_class) ); + } + + if (nmb->answers && nmb->header.ancount) + { + debug_nmb_res_rec(nmb->answers,"answers"); + } + if (nmb->nsrecs && nmb->header.nscount) + { + debug_nmb_res_rec(nmb->nsrecs,"nsrecs"); + } + if (nmb->additional && nmb->header.arcount) + { + debug_nmb_res_rec(nmb->additional,"additional"); + } +} /******************************************************************* handle "compressed" name pointers ******************************************************************/ -static BOOL handle_name_ptrs(unsigned char *ubuf,int *offset,int length, +static BOOL handle_name_ptrs(uchar *ubuf,int *offset,int length, BOOL *got_pointer,int *ret) { int loop_count=0; - + while ((ubuf[*offset] & 0xC0) == 0xC0) { if (!*got_pointer) (*ret) += 2; (*got_pointer)=True; @@ -54,34 +170,41 @@ static BOOL handle_name_ptrs(unsigned char *ubuf,int *offset,int length, parse a nmb name from "compressed" format to something readable return the space taken by the name, or 0 if the name is invalid ******************************************************************/ -static int parse_nmb_name(char *inbuf,int offset,int length, - struct nmb_name *name) +static int parse_nmb_name(char *inbuf,int ofs,int length, struct nmb_name *name) { int m,n=0; - unsigned char *ubuf = (unsigned char *)inbuf; + uchar *ubuf = (uchar *)inbuf; int ret = 0; BOOL got_pointer=False; + int loop_count=0; + int offset = ofs; - if (length - offset < 2) return(0); + if (length - offset < 2) + return(0); /* handle initial name pointers */ - if (!handle_name_ptrs(ubuf,&offset,length,&got_pointer,&ret)) return(0); + if (!handle_name_ptrs(ubuf,&offset,length,&got_pointer,&ret)) + return(0); m = ubuf[offset]; - if (!m) return(0); - if ((m & 0xC0) || offset+m+2 > length) return(0); + if (!m) + return(0); + if ((m & 0xC0) || offset+m+2 > length) + return(0); - bzero((char *)name,sizeof(*name)); + memset((char *)name,'\0',sizeof(*name)); /* the "compressed" part */ - if (!got_pointer) ret += m + 2; + if (!got_pointer) + ret += m + 2; offset++; - while (m) { - unsigned char c1,c2; + while (m > 0) { + uchar c1,c2; c1 = ubuf[offset++]-'A'; c2 = ubuf[offset++]-'A'; - if ((c1 & 0xF0) || (c2 & 0xF0)) return(0); + if ((c1 & 0xF0) || (c2 & 0xF0) || (n > sizeof(name->name)-1)) + return(0); name->name[n++] = (c1<<4) | c2; m -= 2; } @@ -90,25 +213,43 @@ static int parse_nmb_name(char *inbuf,int offset,int length, if (n==16) { /* parse out the name type, its always in the 16th byte of the name */ - name->name_type = name->name[15]; + name->name_type = ((uchar)name->name[15]) & 0xff; /* remove trailing spaces */ name->name[15] = 0; n = 14; - while (n && name->name[n]==' ') name->name[n--] = 0; + while (n && name->name[n]==' ') + name->name[n--] = 0; } /* now the domain parts (if any) */ n = 0; - while ((m=ubuf[offset])) { + while (ubuf[offset]) { /* we can have pointers within the domain part as well */ - if (!handle_name_ptrs(ubuf,&offset,length,&got_pointer,&ret)) return(0); - - if (!got_pointer) ret += m+1; - if (n) name->scope[n++] = '.'; - if (m+2+offset>length || n+m+1>sizeof(name->scope)) return(0); + if (!handle_name_ptrs(ubuf,&offset,length,&got_pointer,&ret)) + return(0); + + m = ubuf[offset]; + /* + * Don't allow null domain parts. + */ + if (!m) + return(0); + if (!got_pointer) + ret += m+1; + if (n) + name->scope[n++] = '.'; + if (m+2+offset>length || n+m+1>sizeof(name->scope)) + return(0); offset++; - while (m--) name->scope[n++] = (char)ubuf[offset++]; + while (m--) + name->scope[n++] = (char)ubuf[offset++]; + + /* + * Watch for malicious loops. + */ + if (loop_count++ == 10) + return 0; } name->scope[n++] = 0; @@ -130,12 +271,13 @@ static int put_nmb_name(char *buf,int offset,struct nmb_name *name) fstring buf1; char *p; - if (name->name[0] == '*') { + if (strcmp(name->name,"*") == 0) { /* special case for wildcard name */ - bzero(buf1,20); + memset(buf1,'\0',20); buf1[0] = '*'; + buf1[15] = name->name_type; } else { - sprintf(buf1,"%-15.15s%c",name->name,name->name_type); + slprintf(buf1, sizeof(buf1) - 1,"%-15.15s%c",name->name,name->name_type); } buf[offset] = 0x20; @@ -153,12 +295,12 @@ static int put_nmb_name(char *buf,int offset,struct nmb_name *name) if (name->scope[0]) { /* XXXX this scope handling needs testing */ ret += strlen(name->scope) + 1; - strcpy(&buf[offset+1],name->scope); + pstrcpy(&buf[offset+1],name->scope); p = &buf[offset+1]; - while ((p = strchr(p,'.'))) { - buf[offset] = PTR_DIFF(p,&buf[offset]); - offset += buf[offset]; + while ((p = strchr_m(p,'.'))) { + buf[offset] = PTR_DIFF(p,&buf[offset+1]); + offset += (buf[offset] + 1); p = &buf[offset+1]; } buf[offset] = strlen(&buf[offset+1]); @@ -170,39 +312,38 @@ static int put_nmb_name(char *buf,int offset,struct nmb_name *name) /******************************************************************* useful for debugging messages ******************************************************************/ -char *namestr(struct nmb_name *n) +char *nmb_namestr(struct nmb_name *n) { static int i=0; static fstring ret[4]; char *p = ret[i]; if (!n->scope[0]) - sprintf(p,"%s(%x)",n->name,n->name_type); + slprintf(p,sizeof(fstring)-1, "%s<%02x>",n->name,n->name_type); else - sprintf(p,"%s(%x).%s",n->name,n->name_type,n->scope); + slprintf(p,sizeof(fstring)-1, "%s<%02x>.%s",n->name,n->name_type,n->scope); i = (i+1)%4; return(p); } /******************************************************************* - allocate are parse some resource records + allocate and parse some resource records ******************************************************************/ static BOOL parse_alloc_res_rec(char *inbuf,int *offset,int length, - struct res_rec **recs, - int count) + struct res_rec **recs, int count) { int i; *recs = (struct res_rec *)malloc(sizeof(**recs)*count); if (!*recs) return(False); - bzero(*recs,sizeof(**recs)*count); + memset((char *)*recs,'\0',sizeof(**recs)*count); for (i=0;i<count;i++) { int l = parse_nmb_name(inbuf,*offset,length,&(*recs)[i].rr_name); (*offset) += l; if (!l || (*offset)+10 > length) { - free(*recs); + SAFE_FREE(*recs); return(False); } (*recs)[i].rr_type = RSVAL(inbuf,(*offset)); @@ -212,7 +353,7 @@ static BOOL parse_alloc_res_rec(char *inbuf,int *offset,int length, (*offset) += 10; if ((*recs)[i].rdlength>sizeof((*recs)[i].rdata) || (*offset)+(*recs)[i].rdlength > length) { - free(*recs); + SAFE_FREE(*recs); return(False); } memcpy((*recs)[i].rdata,inbuf+(*offset),(*recs)[i].rdlength); @@ -246,6 +387,27 @@ static int put_res_rec(char *buf,int offset,struct res_rec *recs,int count) } /******************************************************************* + put a compressed name pointer record into a packet + ******************************************************************/ +static int put_compressed_name_ptr(uchar *buf,int offset,struct res_rec *rec,int ptr_offset) +{ + int ret=0; + buf[offset] = (0xC0 | ((ptr_offset >> 8) & 0xFF)); + buf[offset+1] = (ptr_offset & 0xFF); + offset += 2; + ret += 2; + RSSVAL(buf,offset,rec->rr_type); + RSSVAL(buf,offset+2,rec->rr_class); + RSIVAL(buf,offset+4,rec->ttl); + RSSVAL(buf,offset+8,rec->rdlength); + memcpy(buf+offset+10,rec->rdata,rec->rdlength); + offset += 10+rec->rdlength; + ret += 10+rec->rdlength; + + return(ret); +} + +/******************************************************************* parse a dgram packet. Return False if the packet can't be parsed or is invalid for some reason, True otherwise @@ -256,7 +418,7 @@ static BOOL parse_dgram(char *inbuf,int length,struct dgram_packet *dgram) int offset; int flags; - bzero((char *)dgram,sizeof(*dgram)); + memset((char *)dgram,'\0',sizeof(*dgram)); if (length < 14) return(False); @@ -298,12 +460,15 @@ static BOOL parse_nmb(char *inbuf,int length,struct nmb_packet *nmb) { int nm_flags,offset; - bzero((char *)nmb,sizeof(*nmb)); + memset((char *)nmb,'\0',sizeof(*nmb)); if (length < 12) return(False); /* parse the header */ nmb->header.name_trn_id = RSVAL(inbuf,0); + + DEBUG(10,("parse_nmb: packet id = %d\n", nmb->header.name_trn_id)); + nmb->header.opcode = (CVAL(inbuf,2) >> 3) & 0xF; nmb->header.response = ((CVAL(inbuf,2)>>7)&1)?True:False; nm_flags = ((CVAL(inbuf,2) & 0x7) << 4) + (CVAL(inbuf,3)>>4); @@ -311,7 +476,7 @@ static BOOL parse_nmb(char *inbuf,int length,struct nmb_packet *nmb) nmb->header.nm_flags.recursion_available = (nm_flags&8)?True:False; nmb->header.nm_flags.recursion_desired = (nm_flags&0x10)?True:False; nmb->header.nm_flags.trunc = (nm_flags&0x20)?True:False; - nmb->header.nm_flags.authoritative = (nm_flags&0x40)?True:False; + nmb->header.nm_flags.authoritative = (nm_flags&0x40)?True:False; nmb->header.rcode = CVAL(inbuf,3) & 0xF; nmb->header.qdcount = RSVAL(inbuf,4); nmb->header.ancount = RSVAL(inbuf,6); @@ -351,13 +516,128 @@ static BOOL parse_nmb(char *inbuf,int length,struct nmb_packet *nmb) } /******************************************************************* + 'Copy constructor' for an nmb packet + ******************************************************************/ +static struct packet_struct *copy_nmb_packet(struct packet_struct *packet) +{ + struct nmb_packet *nmb; + struct nmb_packet *copy_nmb; + struct packet_struct *pkt_copy; + + if(( pkt_copy = (struct packet_struct *)malloc(sizeof(*packet))) == NULL) + { + DEBUG(0,("copy_nmb_packet: malloc fail.\n")); + return NULL; + } + + /* Structure copy of entire thing. */ + + *pkt_copy = *packet; + + /* Ensure this copy is not locked. */ + pkt_copy->locked = False; + + /* Ensure this copy has no resource records. */ + nmb = &packet->packet.nmb; + copy_nmb = &pkt_copy->packet.nmb; + + copy_nmb->answers = NULL; + copy_nmb->nsrecs = NULL; + copy_nmb->additional = NULL; + + /* Now copy any resource records. */ + + if (nmb->answers) + { + if((copy_nmb->answers = (struct res_rec *) + malloc(nmb->header.ancount * sizeof(struct res_rec))) == NULL) + goto free_and_exit; + memcpy((char *)copy_nmb->answers, (char *)nmb->answers, + nmb->header.ancount * sizeof(struct res_rec)); + } + if (nmb->nsrecs) + { + if((copy_nmb->nsrecs = (struct res_rec *) + malloc(nmb->header.nscount * sizeof(struct res_rec))) == NULL) + goto free_and_exit; + memcpy((char *)copy_nmb->nsrecs, (char *)nmb->nsrecs, + nmb->header.nscount * sizeof(struct res_rec)); + } + if (nmb->additional) + { + if((copy_nmb->additional = (struct res_rec *) + malloc(nmb->header.arcount * sizeof(struct res_rec))) == NULL) + goto free_and_exit; + memcpy((char *)copy_nmb->additional, (char *)nmb->additional, + nmb->header.arcount * sizeof(struct res_rec)); + } + + return pkt_copy; + +free_and_exit: + + SAFE_FREE(copy_nmb->answers); + SAFE_FREE(copy_nmb->nsrecs); + SAFE_FREE(copy_nmb->additional); + SAFE_FREE(pkt_copy); + + DEBUG(0,("copy_nmb_packet: malloc fail in resource records.\n")); + return NULL; +} + +/******************************************************************* + 'Copy constructor' for a dgram packet + ******************************************************************/ +static struct packet_struct *copy_dgram_packet(struct packet_struct *packet) +{ + struct packet_struct *pkt_copy; + + if(( pkt_copy = (struct packet_struct *)malloc(sizeof(*packet))) == NULL) + { + DEBUG(0,("copy_dgram_packet: malloc fail.\n")); + return NULL; + } + + /* Structure copy of entire thing. */ + + *pkt_copy = *packet; + + /* Ensure this copy is not locked. */ + pkt_copy->locked = False; + + /* There are no additional pointers in a dgram packet, + we are finished. */ + return pkt_copy; +} + +/******************************************************************* + 'Copy constructor' for a generic packet + ******************************************************************/ +struct packet_struct *copy_packet(struct packet_struct *packet) +{ + if(packet->packet_type == NMB_PACKET) + return copy_nmb_packet(packet); + else if (packet->packet_type == DGRAM_PACKET) + return copy_dgram_packet(packet); + return NULL; +} + +/******************************************************************* free up any resources associated with an nmb packet ******************************************************************/ -void free_nmb_packet(struct nmb_packet *nmb) +static void free_nmb_packet(struct nmb_packet *nmb) { - if (nmb->answers) free(nmb->answers); - if (nmb->nsrecs) free(nmb->nsrecs); - if (nmb->additional) free(nmb->additional); + SAFE_FREE(nmb->answers); + SAFE_FREE(nmb->nsrecs); + SAFE_FREE(nmb->additional); +} + +/******************************************************************* + free up any resources associated with a dgram packet + ******************************************************************/ +static void free_dgram_packet(struct dgram_packet *nmb) +{ + /* We have nothing to do for a dgram packet. */ } /******************************************************************* @@ -365,58 +645,80 @@ void free_nmb_packet(struct nmb_packet *nmb) ******************************************************************/ void free_packet(struct packet_struct *packet) { + if (packet->locked) + return; if (packet->packet_type == NMB_PACKET) free_nmb_packet(&packet->packet.nmb); - free(packet); + else if (packet->packet_type == DGRAM_PACKET) + free_dgram_packet(&packet->packet.dgram); + ZERO_STRUCTPN(packet); + SAFE_FREE(packet); } /******************************************************************* - read a packet from a socket and parse it, returning a packet ready - to be used or put on the queue. This assumes a UDP socket +parse a packet buffer into a packet structure ******************************************************************/ -struct packet_struct *read_packet(int fd,enum packet_type packet_type) +struct packet_struct *parse_packet(char *buf,int length, + enum packet_type packet_type) { - extern struct in_addr lastip; - extern int lastport; - struct packet_struct *packet; - char buf[MAX_DGRAM_SIZE]; - int length; - BOOL ok=False; - - length = read_udp_socket(fd,buf,sizeof(buf)); - if (length < MIN_DGRAM_SIZE) return(NULL); - - packet = (struct packet_struct *)malloc(sizeof(*packet)); - if (!packet) return(NULL); - - packet->next = NULL; - packet->prev = NULL; - packet->ip = lastip; - packet->port = lastport; - packet->fd = fd; - packet->timestamp = time(NULL); - packet->packet_type = packet_type; - switch (packet_type) - { - case NMB_PACKET: - ok = parse_nmb(buf,length,&packet->packet.nmb); - break; + extern struct in_addr lastip; + extern int lastport; + struct packet_struct *p; + BOOL ok=False; + + p = (struct packet_struct *)malloc(sizeof(*p)); + if (!p) return(NULL); + + p->next = NULL; + p->prev = NULL; + p->ip = lastip; + p->port = lastport; + p->locked = False; + p->timestamp = time(NULL); + p->packet_type = packet_type; + + switch (packet_type) { + case NMB_PACKET: + ok = parse_nmb(buf,length,&p->packet.nmb); + break; + + case DGRAM_PACKET: + ok = parse_dgram(buf,length,&p->packet.dgram); + break; + } - case DGRAM_PACKET: - ok = parse_dgram(buf,length,&packet->packet.dgram); - break; - } - if (!ok) { - free(packet); - return(NULL); - } + if (!ok) { + free_packet(p); + return NULL; + } - num_good_receives++; + return p; +} - DEBUG(4,("%s received a packet of len %d from (%s) port %d\n", - timestring(),length,inet_ntoa(packet->ip),packet->port)); +/******************************************************************* + read a packet from a socket and parse it, returning a packet ready + to be used or put on the queue. This assumes a UDP socket + ******************************************************************/ +struct packet_struct *read_packet(int fd,enum packet_type packet_type) +{ + struct packet_struct *packet; + char buf[MAX_DGRAM_SIZE]; + int length; + + length = read_udp_socket(fd,buf,sizeof(buf)); + if (length < MIN_DGRAM_SIZE) return(NULL); + + packet = parse_packet(buf, length, packet_type); + if (!packet) return NULL; - return(packet); + packet->fd = fd; + + num_good_receives++; + + DEBUG(5,("Received a packet of len %d from (%s) port %d\n", + length, inet_ntoa(packet->ip), packet->port ) ); + + return(packet); } @@ -425,20 +727,28 @@ struct packet_struct *read_packet(int fd,enum packet_type packet_type) ******************************************************************/ static BOOL send_udp(int fd,char *buf,int len,struct in_addr ip,int port) { - BOOL ret; + BOOL ret = False; + int i; struct sockaddr_in sock_out; /* set the address and port */ - bzero((char *)&sock_out,sizeof(sock_out)); + memset((char *)&sock_out,'\0',sizeof(sock_out)); putip((char *)&sock_out.sin_addr,(char *)&ip); sock_out.sin_port = htons( port ); sock_out.sin_family = AF_INET; - DEBUG(4,("%s sending a packet of len %d to (%s) on port %d\n", - timestring(),len,inet_ntoa(ip),port)); + DEBUG( 5, ( "Sending a packet of len %d to (%s) on port %d\n", + len, inet_ntoa(ip), port ) ); + + /* + * Patch to fix asynch error notifications from Linux kernel. + */ - ret = (sendto(fd,buf,len,0,(struct sockaddr *)&sock_out, - sizeof(sock_out)) >= 0); + for (i = 0; i < 5; i++) { + ret = (sendto(fd,buf,len,0,(struct sockaddr *)&sock_out, sizeof(sock_out)) >= 0); + if (ret || errno != ECONNREFUSED) + break; + } if (!ret) DEBUG(0,("Packet send failed to %s(%d) ERRNO=%s\n", @@ -460,7 +770,7 @@ static BOOL send_udp(int fd,char *buf,int len,struct in_addr ip,int port) static int build_dgram(char *buf,struct packet_struct *p) { struct dgram_packet *dgram = &p->packet.dgram; - unsigned char *ubuf = (unsigned char *)buf; + uchar *ubuf = (uchar *)buf; int offset=0; /* put in the header */ @@ -494,15 +804,27 @@ static int build_dgram(char *buf,struct packet_struct *p) /******************************************************************* build a nmb name - ******************************************************************/ -void make_nmb_name(struct nmb_name *n,char *name,int type,char *this_scope) + *******************************************************************/ +void make_nmb_name( struct nmb_name *n, const char *name, int type) { - strcpy(n->name,name); - strupper(n->name); - n->name_type = type; - strcpy(n->scope,this_scope); + extern pstring global_scope; + memset( (char *)n, '\0', sizeof(struct nmb_name) ); + push_ascii(n->name, name, 16, STR_TERMINATE|STR_UPPER); + n->name_type = (unsigned int)type & 0xFF; + StrnCpy( n->scope, global_scope, 63 ); + strupper( n->scope ); } +/******************************************************************* + Compare two nmb names + ******************************************************************/ + +BOOL nmb_name_equal(struct nmb_name *n1, struct nmb_name *n2) +{ + return ((n1->name_type == n2->name_type) && + strequal(n1->name ,n2->name ) && + strequal(n1->scope,n2->scope)); +} /******************************************************************* build a nmb packet ready for sending @@ -515,19 +837,22 @@ void make_nmb_name(struct nmb_name *n,char *name,int type,char *this_scope) static int build_nmb(char *buf,struct packet_struct *p) { struct nmb_packet *nmb = &p->packet.nmb; - unsigned char *ubuf = (unsigned char *)buf; + uchar *ubuf = (uchar *)buf; int offset=0; /* put in the header */ RSSVAL(ubuf,offset,nmb->header.name_trn_id); ubuf[offset+2] = (nmb->header.opcode & 0xF) << 3; if (nmb->header.response) ubuf[offset+2] |= (1<<7); - if (nmb->header.nm_flags.authoritative) ubuf[offset+2] |= 0x4; + if (nmb->header.nm_flags.authoritative && + nmb->header.response) ubuf[offset+2] |= 0x4; if (nmb->header.nm_flags.trunc) ubuf[offset+2] |= 0x2; if (nmb->header.nm_flags.recursion_desired) ubuf[offset+2] |= 0x1; - if (nmb->header.nm_flags.recursion_available) ubuf[offset+3] |= 0x80; + if (nmb->header.nm_flags.recursion_available && + nmb->header.response) ubuf[offset+3] |= 0x80; if (nmb->header.nm_flags.bcast) ubuf[offset+3] |= 0x10; ubuf[offset+3] |= (nmb->header.rcode & 0xF); + RSSVAL(ubuf,offset+4,nmb->header.qdcount); RSSVAL(ubuf,offset+6,nmb->header.ancount); RSSVAL(ubuf,offset+8,nmb->header.nscount); @@ -550,15 +875,52 @@ static int build_nmb(char *buf,struct packet_struct *p) offset += put_res_rec((char *)ubuf,offset,nmb->nsrecs, nmb->header.nscount); - if (nmb->header.arcount) + /* + * The spec says we must put compressed name pointers + * in the following outgoing packets : + * NAME_REGISTRATION_REQUEST, NAME_REFRESH_REQUEST, + * NAME_RELEASE_REQUEST. + */ + + if((nmb->header.response == False) && + ((nmb->header.opcode == NMB_NAME_REG_OPCODE) || + (nmb->header.opcode == NMB_NAME_RELEASE_OPCODE) || + (nmb->header.opcode == NMB_NAME_REFRESH_OPCODE_8) || + (nmb->header.opcode == NMB_NAME_REFRESH_OPCODE_9) || + (nmb->header.opcode == NMB_NAME_MULTIHOMED_REG_OPCODE)) && + (nmb->header.arcount == 1)) { + + offset += put_compressed_name_ptr(ubuf,offset,nmb->additional,12); + + } else if (nmb->header.arcount) { offset += put_res_rec((char *)ubuf,offset,nmb->additional, nmb->header.arcount); - + } return(offset); } /******************************************************************* +linearise a packet + ******************************************************************/ +int build_packet(char *buf, struct packet_struct *p) +{ + int len = 0; + + switch (p->packet_type) { + case NMB_PACKET: + len = build_nmb(buf,p); + break; + + case DGRAM_PACKET: + len = build_dgram(buf,p); + break; + } + + return len; +} + +/******************************************************************* send a packet_struct ******************************************************************/ BOOL send_packet(struct packet_struct *p) @@ -566,18 +928,9 @@ BOOL send_packet(struct packet_struct *p) char buf[1024]; int len=0; - bzero(buf,sizeof(buf)); - - switch (p->packet_type) - { - case NMB_PACKET: - len = build_nmb(buf,p); - break; + memset(buf,'\0',sizeof(buf)); - case DGRAM_PACKET: - len = build_dgram(buf,p); - break; - } + len = build_packet(buf, p); if (!len) return(False); @@ -590,347 +943,327 @@ BOOL send_packet(struct packet_struct *p) ***************************************************************************/ struct packet_struct *receive_packet(int fd,enum packet_type type,int t) { - fd_set fds; - struct timeval timeout; + fd_set fds; + struct timeval timeout; + int ret; + + FD_ZERO(&fds); + FD_SET(fd,&fds); + timeout.tv_sec = t/1000; + timeout.tv_usec = 1000*(t%1000); + + if ((ret = sys_select_intr(fd+1,&fds,NULL,NULL,&timeout)) == -1) { + /* errno should be EBADF or EINVAL. */ + DEBUG(0,("select returned -1, errno = %s (%d)\n", strerror(errno), errno)); + return NULL; + } - FD_ZERO(&fds); - FD_SET(fd,&fds); - timeout.tv_sec = t/1000; - timeout.tv_usec = 1000*(t%1000); + if (ret == 0) /* timeout */ + return NULL; - sys_select(&fds,&timeout); + if (FD_ISSET(fd,&fds)) + return(read_packet(fd,type)); + + return(NULL); +} - if (FD_ISSET(fd,&fds)) - return(read_packet(fd,type)); - return(NULL); -} +/**************************************************************************** + receive a UDP/137 packet either via UDP or from the unexpected packet + queue. The packet must be a reply packet and have the specified trn_id + The timeout is in milliseconds + ***************************************************************************/ +struct packet_struct *receive_nmb_packet(int fd, int t, int trn_id) +{ + struct packet_struct *p; + p = receive_packet(fd, NMB_PACKET, t); + + if (p && p->packet.nmb.header.response && + p->packet.nmb.header.name_trn_id == trn_id) { + return p; + } + if (p) free_packet(p); + + /* try the unexpected packet queue */ + return receive_unexpected(NMB_PACKET, trn_id, NULL); +} /**************************************************************************** -interpret a node status response -****************************************************************************/ -static void interpret_node_status(char *p, char *master,char *rname) + receive a UDP/138 packet either via UDP or from the unexpected packet + queue. The packet must be a reply packet and have the specified mailslot name + The timeout is in milliseconds + ***************************************************************************/ +struct packet_struct *receive_dgram_packet(int fd, int t, char *mailslot_name) { - int level = (master||rname)?4:0; - int numnames = CVAL(p,0); - DEBUG(level,("received %d names\n",numnames)); + struct packet_struct *p; - if (rname) *rname = 0; - if (master) *master = 0; + p = receive_packet(fd, DGRAM_PACKET, t); - p += 1; - while (numnames--) - { - char qname[17]; - int type; - fstring flags; - *flags = 0; - StrnCpy(qname,p,15); - type = CVAL(p,15); - p += 16; - - if (p[0] & 0x80) strcat(flags,"<GROUP> "); - if ((p[0] & 0x60) == 0) strcat(flags,"B "); - if ((p[0] & 0x60) == 1) strcat(flags,"P "); - if ((p[0] & 0x60) == 2) strcat(flags,"M "); - if ((p[0] & 0x60) == 3) strcat(flags,"_ "); - if (p[0] & 0x10) strcat(flags,"<DEREGISTERING> "); - if (p[0] & 0x08) strcat(flags,"<CONFLICT> "); - if (p[0] & 0x04) strcat(flags,"<ACTIVE> "); - if (p[0] & 0x02) strcat(flags,"<PERMANENT> "); - - if (master && !*master && type == 0x1d) { - StrnCpy(master,qname,15); - trim_string(master,NULL," "); - } + if (p && match_mailslot_name(p, mailslot_name)) { + return p; + } + if (p) free_packet(p); - if (rname && !*rname && type == 0x20 && !(p[0]&0x80)) { - StrnCpy(rname,qname,15); - trim_string(rname,NULL," "); - } - - DEBUG(level,("\t%s (type=0x%x)\t%s\n",qname,type,flags)); - p+=2; - } - DEBUG(level,("num_good_sends=%d num_good_receives=%d\n", - IVAL(p,20),IVAL(p,24))); + /* try the unexpected packet queue */ + return receive_unexpected(DGRAM_PACKET, 0, mailslot_name); } /**************************************************************************** - do a netbios name status query on a host - - the "master" parameter is a hack used for finding workgroups. - **************************************************************************/ -BOOL name_status(int fd,char *name,int name_type,BOOL recurse, - struct in_addr to_ip,char *master,char *rname, - void (*fn)()) -{ - BOOL found=False; - int retries = 2; - int retry_time = 5000; - struct timeval tval; - struct packet_struct p; - struct packet_struct *p2; - struct nmb_packet *nmb = &p.packet.nmb; - - bzero((char *)&p,sizeof(p)); - - if (!name_trn_id) name_trn_id = (time(NULL)%(unsigned)0x7FFF) + - (getpid()%(unsigned)100); - name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF; - - nmb->header.name_trn_id = name_trn_id; - nmb->header.opcode = 0; - nmb->header.response = False; - nmb->header.nm_flags.bcast = False; - nmb->header.nm_flags.recursion_available = CanRecurse; - nmb->header.nm_flags.recursion_desired = recurse; - nmb->header.nm_flags.trunc = False; - nmb->header.nm_flags.authoritative = False; - nmb->header.rcode = 0; - nmb->header.qdcount = 1; - nmb->header.ancount = 0; - nmb->header.nscount = 0; - nmb->header.arcount = 0; - - make_nmb_name(&nmb->question.question_name,name,name_type,scope); - - nmb->question.question_type = 0x21; - nmb->question.question_class = 0x1; - - p.ip = to_ip; - p.port = NMB_PORT; - p.fd = fd; - p.timestamp = time(NULL); - p.packet_type = NMB_PACKET; - - GetTimeOfDay(&tval); - - if (!send_packet(&p)) - return(False); + see if a datagram has the right mailslot name +***************************************************************************/ +BOOL match_mailslot_name(struct packet_struct *p, char *mailslot_name) +{ + struct dgram_packet *dgram = &p->packet.dgram; + char *buf; - retries--; + buf = &dgram->data[0]; + buf -= 4; - while (1) - { - struct timeval tval2; - GetTimeOfDay(&tval2); - if (TvalDiff(&tval,&tval2) > retry_time) { - if (!retries) break; - if (!found && !send_packet(&p)) - return False; - GetTimeOfDay(&tval); - retries--; - } + buf = smb_buf(buf); - if ((p2=receive_packet(fd,NMB_PACKET,90))) - { - struct nmb_packet *nmb2 = &p2->packet.nmb; - if (nmb->header.name_trn_id != nmb2->header.name_trn_id || - !nmb2->header.response) { - /* its not for us - maybe deal with it later */ - if (fn) - fn(p2); - else - free_packet(p2); - continue; - } - - if (nmb2->header.opcode != 0 || - nmb2->header.nm_flags.bcast || - nmb2->header.rcode || - !nmb2->header.ancount || - nmb2->answers->rr_type != 0x21) { - /* XXXX what do we do with this? could be a redirect, but - we'll discard it for the moment */ - free_packet(p2); - continue; - } - - interpret_node_status(&nmb2->answers->rdata[0], master,rname); - free_packet(p2); - return(True); + if (memcmp(buf, mailslot_name, strlen(mailslot_name)+1) == 0) { + return True; } - } - - DEBUG(0,("No status response (this is not unusual)\n")); + return False; +} + + +/**************************************************************************** +return the number of bits that match between two 4 character buffers + ***************************************************************************/ +static int matching_bits(uchar *p1, uchar *p2) +{ + int i, j, ret = 0; + for (i=0; i<4; i++) { + if (p1[i] != p2[i]) break; + ret += 8; + } + + if (i==4) return ret; + + for (j=0; j<8; j++) { + if ((p1[i] & (1<<(7-j))) != (p2[i] & (1<<(7-j)))) break; + ret++; + } + + return ret; +} + + +static uchar sort_ip[4]; + +/**************************************************************************** +compare two query reply records + ***************************************************************************/ +static int name_query_comp(uchar *p1, uchar *p2) +{ + return matching_bits(p2+2, sort_ip) - matching_bits(p1+2, sort_ip); +} + +/**************************************************************************** +sort a set of 6 byte name query response records so that the IPs that +have the most leading bits in common with the specified address come first + ***************************************************************************/ +void sort_query_replies(char *data, int n, struct in_addr ip) +{ + if (n <= 1) return; + + putip(sort_ip, (char *)&ip); - return(False); + qsort(data, n, 6, QSORT_CAST name_query_comp); +} + + +#define TRUNCATE_NETBIOS_NAME 1 + +/******************************************************************* + convert, possibly using a stupid microsoft-ism which has destroyed + the transport independence of netbios (for CIFS vendors that usually + use the Win95-type methods, not for NT to NT communication, which uses + DCE/RPC and therefore full-length unicode strings...) a dns name into + a netbios name. + + the netbios name (NOT necessarily null-terminated) is truncated to 15 + characters. + + ******************************************************************/ +char *dns_to_netbios_name(char *dns_name) +{ + static char netbios_name[16]; + int i; + StrnCpy(netbios_name, dns_name, 15); + netbios_name[15] = 0; + +#ifdef TRUNCATE_NETBIOS_NAME + /* ok. this is because of a stupid microsoft-ism. if the called host + name contains a '.', microsoft clients expect you to truncate the + netbios name up to and including the '.' this even applies, by + mistake, to workgroup (domain) names, which is _really_ daft. + */ + for (i = 15; i >= 0; i--) + { + if (netbios_name[i] == '.') + { + netbios_name[i] = 0; + break; + } + } +#endif /* TRUNCATE_NETBIOS_NAME */ + + return netbios_name; } /**************************************************************************** - do a netbios name query to find someones IP - ****************************************************************************/ -BOOL name_query(int fd,char *name,int name_type, - BOOL bcast,BOOL recurse, - struct in_addr to_ip, struct in_addr *ip,void (*fn)()) -{ - BOOL found=False; - int retries = 3; - int retry_time = bcast?250:2000; - struct timeval tval; - struct packet_struct p; - struct packet_struct *p2; - struct nmb_packet *nmb = &p.packet.nmb; - - bzero((char *)&p,sizeof(p)); - - if (!name_trn_id) name_trn_id = (time(NULL)%(unsigned)0x7FFF) + - (getpid()%(unsigned)100); - name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF; - - nmb->header.name_trn_id = name_trn_id; - nmb->header.opcode = 0; - nmb->header.response = False; - nmb->header.nm_flags.bcast = bcast; - nmb->header.nm_flags.recursion_available = CanRecurse; - nmb->header.nm_flags.recursion_desired = recurse; - nmb->header.nm_flags.trunc = False; - nmb->header.nm_flags.authoritative = False; - nmb->header.rcode = 0; - nmb->header.qdcount = 1; - nmb->header.ancount = 0; - nmb->header.nscount = 0; - nmb->header.arcount = 0; - - make_nmb_name(&nmb->question.question_name,name,name_type,scope); - - nmb->question.question_type = 0x20; - nmb->question.question_class = 0x1; - - p.ip = to_ip; - p.port = NMB_PORT; - p.fd = fd; - p.timestamp = time(NULL); - p.packet_type = NMB_PACKET; - - GetTimeOfDay(&tval); - - if (!send_packet(&p)) - return(False); +interpret the weird netbios "name". Return the name type +****************************************************************************/ +static int name_interpret(char *in,char *out) +{ + int ret; + int len = (*in++) / 2; - retries--; + *out=0; - while (1) + if (len > 30 || len<1) return(0); + + while (len--) { - struct timeval tval2; - GetTimeOfDay(&tval2); - if (TvalDiff(&tval,&tval2) > retry_time) { - if (!retries) break; - if (!found && !send_packet(&p)) - return False; - GetTimeOfDay(&tval); - retries--; + if (in[0] < 'A' || in[0] > 'P' || in[1] < 'A' || in[1] > 'P') { + *out = 0; + return(0); } - - if ((p2=receive_packet(fd,NMB_PACKET,90))) - { - struct nmb_packet *nmb2 = &p2->packet.nmb; - if (nmb->header.name_trn_id != nmb2->header.name_trn_id || - !nmb2->header.response) { - /* its not for us - maybe deal with it later - (put it on the queue?) */ - if (fn) - fn(p2); - else - free_packet(p2); - continue; - } - - if (nmb2->header.opcode != 0 || - nmb2->header.nm_flags.bcast || - nmb2->header.rcode || - !nmb2->header.ancount) { - /* XXXX what do we do with this? could be a redirect, but - we'll discard it for the moment */ - free_packet(p2); - continue; - } - - if (ip) { - putip((char *)ip,&nmb2->answers->rdata[2]); - DEBUG(fn?3:2,("Got a positive name query response from %s", - inet_ntoa(p2->ip))); - DEBUG(fn?3:2,(" (%s)\n",inet_ntoa(*ip))); - } - found=True; retries=0; - free_packet(p2); - if (fn) break; - } + *out = ((in[0]-'A')<<4) + (in[1]-'A'); + in += 2; + out++; } + *out = 0; + ret = out[-1]; - return(found); +#ifdef NETBIOS_SCOPE + /* Handle any scope names */ + while(*in) + { + *out++ = '.'; /* Scope names are separated by periods */ + len = *(uchar *)in++; + StrnCpy(out, in, len); + out += len; + *out=0; + in += len; + } +#endif + return(ret); } - /**************************************************************************** - construct and send a netbios DGRAM - - Note that this currently sends all answers to port 138. thats the - wrong things to do! I should send to the requestors port. XXX - **************************************************************************/ -BOOL send_mailslot_reply(char *mailslot,int fd,char *buf,int len, - char *srcname,char *dstname, - int src_type,int dest_type, - struct in_addr dest_ip, - struct in_addr src_ip) -{ - struct packet_struct p; - struct dgram_packet *dgram = &p.packet.dgram; - char *ptr,*p2; - char tmp[4]; - - bzero((char *)&p,sizeof(p)); - - dgram->header.msg_type = 0x11; /* DIRECT GROUP DATAGRAM */ - dgram->header.flags.node_type = M_NODE; - dgram->header.flags.first = True; - dgram->header.flags.more = False; - dgram->header.dgm_id = name_trn_id++; - dgram->header.source_ip = src_ip; - dgram->header.source_port = DGRAM_PORT; - dgram->header.dgm_length = 0; /* let build_dgram() handle this */ - dgram->header.packet_offset = 0; - - make_nmb_name(&dgram->source_name,srcname,src_type,scope); - make_nmb_name(&dgram->dest_name,dstname,dest_type,scope); +mangle a name into netbios format - ptr = &dgram->data[0]; + Note: <Out> must be (33 + strlen(scope) + 2) bytes long, at minimum. +****************************************************************************/ +int name_mangle( char *In, char *Out, char name_type ) + { + int i; + int c; + int len; + char buf[20]; + char *p = Out; + extern pstring global_scope; + + /* Safely copy the input string, In, into buf[]. */ + (void)memset( buf, 0, 20 ); + if (strcmp(In,"*") == 0) + buf[0] = '*'; + else + (void)slprintf( buf, sizeof(buf) - 1, "%-15.15s%c", In, name_type ); - /* now setup the smb part */ - ptr -= 4; /* XXX ugliness because of handling of tcp SMB length */ - memcpy(tmp,ptr,4); - set_message(ptr,17,17 + len,True); - memcpy(ptr,tmp,4); + /* Place the length of the first field into the output buffer. */ + p[0] = 32; + p++; - CVAL(ptr,smb_com) = SMBtrans; - SSVAL(ptr,smb_vwv1,len); - SSVAL(ptr,smb_vwv11,len); - SSVAL(ptr,smb_vwv12,70 + strlen(mailslot)); - SSVAL(ptr,smb_vwv13,3); - SSVAL(ptr,smb_vwv14,1); - SSVAL(ptr,smb_vwv15,1); - SSVAL(ptr,smb_vwv16,2); - p2 = smb_buf(ptr); - strcpy(p2,mailslot); - p2 = skip_string(p2,1); + /* Now convert the name to the rfc1001/1002 format. */ + for( i = 0; i < 16; i++ ) + { + c = toupper( buf[i] ); + p[i*2] = ( (c >> 4) & 0x000F ) + 'A'; + p[(i*2)+1] = (c & 0x000F) + 'A'; + } + p += 32; + p[0] = '\0'; - memcpy(p2,buf,len); - p2 += len; + /* Add the scope string. */ + for( i = 0, len = 0; NULL != global_scope; i++, len++ ) + { + switch( global_scope[i] ) + { + case '\0': + p[0] = len; + if( len > 0 ) + p[len+1] = 0; + return( name_len(Out) ); + case '.': + p[0] = len; + p += (len + 1); + len = -1; + break; + default: + p[len+1] = global_scope[i]; + break; + } + } - dgram->datasize = PTR_DIFF(p2,ptr+4); /* +4 for tcp length */ + return( name_len(Out) ); + } /* name_mangle */ - p.ip = dest_ip; - p.port = DGRAM_PORT; - p.fd = fd; - p.timestamp = time(NULL); - p.packet_type = DGRAM_PACKET; - return(send_packet(&p)); -} +/**************************************************************************** +find a pointer to a netbios name +****************************************************************************/ +static char *name_ptr(char *buf,int ofs) +{ + uchar c = *(uchar *)(buf+ofs); + + if ((c & 0xC0) == 0xC0) + { + uint16 l = RSVAL(buf, ofs) & 0x3FFF; + DEBUG(5,("name ptr to pos %d from %d is %s\n",l,ofs,buf+l)); + return(buf + l); + } + else + return(buf+ofs); +} +/**************************************************************************** +extract a netbios name from a buf +****************************************************************************/ +int name_extract(char *buf,int ofs,char *name) +{ + char *p = name_ptr(buf,ofs); + int d = PTR_DIFF(p,buf+ofs); + pstrcpy(name,""); + if (d < -50 || d > 50) return(0); + return(name_interpret(p,name)); +} + +/**************************************************************************** +return the total storage length of a mangled name +****************************************************************************/ +int name_len(char *s1) +{ + /* NOTE: this argument _must_ be unsigned */ + uchar *s = (uchar *)s1; + int len; + + /* If the two high bits of the byte are set, return 2. */ + if (0xC0 == (*s & 0xC0)) + return(2); + + /* Add up the length bytes. */ + for (len = 1; (*s); s += (*s) + 1) { + len += *s + 1; + SMB_ASSERT(len < 80); + } + return(len); +} /* name_len */ diff --git a/source3/libsmb/nterr.c b/source3/libsmb/nterr.c new file mode 100644 index 0000000000..b74dde9b14 --- /dev/null +++ b/source3/libsmb/nterr.c @@ -0,0 +1,596 @@ +/* + * Unix SMB/CIFS implementation. + * RPC Pipe client / server routines + * Copyright (C) Luke Kenneth Casson Leighton 1997-2001. + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* NT error codes. please read nterr.h */ + +#include "includes.h" + +typedef const struct +{ + char *nt_errstr; + NTSTATUS nt_errcode; +} nt_err_code_struct; + +nt_err_code_struct nt_errs[] = +{ + { "NT_STATUS_OK", NT_STATUS_OK }, + { "NT_STATUS_UNSUCCESSFUL", NT_STATUS_UNSUCCESSFUL }, + { "NT_STATUS_NOT_IMPLEMENTED", NT_STATUS_NOT_IMPLEMENTED }, + { "NT_STATUS_INVALID_INFO_CLASS", NT_STATUS_INVALID_INFO_CLASS }, + { "NT_STATUS_INFO_LENGTH_MISMATCH", NT_STATUS_INFO_LENGTH_MISMATCH }, + { "NT_STATUS_ACCESS_VIOLATION", NT_STATUS_ACCESS_VIOLATION }, + { "STATUS_BUFFER_OVERFLOW", STATUS_BUFFER_OVERFLOW }, + { "NT_STATUS_IN_PAGE_ERROR", NT_STATUS_IN_PAGE_ERROR }, + { "NT_STATUS_PAGEFILE_QUOTA", NT_STATUS_PAGEFILE_QUOTA }, + { "NT_STATUS_INVALID_HANDLE", NT_STATUS_INVALID_HANDLE }, + { "NT_STATUS_BAD_INITIAL_STACK", NT_STATUS_BAD_INITIAL_STACK }, + { "NT_STATUS_BAD_INITIAL_PC", NT_STATUS_BAD_INITIAL_PC }, + { "NT_STATUS_INVALID_CID", NT_STATUS_INVALID_CID }, + { "NT_STATUS_TIMER_NOT_CANCELED", NT_STATUS_TIMER_NOT_CANCELED }, + { "NT_STATUS_INVALID_PARAMETER", NT_STATUS_INVALID_PARAMETER }, + { "NT_STATUS_NO_SUCH_DEVICE", NT_STATUS_NO_SUCH_DEVICE }, + { "NT_STATUS_NO_SUCH_FILE", NT_STATUS_NO_SUCH_FILE }, + { "NT_STATUS_INVALID_DEVICE_REQUEST", NT_STATUS_INVALID_DEVICE_REQUEST }, + { "NT_STATUS_END_OF_FILE", NT_STATUS_END_OF_FILE }, + { "NT_STATUS_WRONG_VOLUME", NT_STATUS_WRONG_VOLUME }, + { "NT_STATUS_NO_MEDIA_IN_DEVICE", NT_STATUS_NO_MEDIA_IN_DEVICE }, + { "NT_STATUS_UNRECOGNIZED_MEDIA", NT_STATUS_UNRECOGNIZED_MEDIA }, + { "NT_STATUS_NONEXISTENT_SECTOR", NT_STATUS_NONEXISTENT_SECTOR }, + { "NT_STATUS_MORE_PROCESSING_REQUIRED", NT_STATUS_MORE_PROCESSING_REQUIRED }, + { "NT_STATUS_NO_MEMORY", NT_STATUS_NO_MEMORY }, + { "NT_STATUS_CONFLICTING_ADDRESSES", NT_STATUS_CONFLICTING_ADDRESSES }, + { "NT_STATUS_NOT_MAPPED_VIEW", NT_STATUS_NOT_MAPPED_VIEW }, + { "NT_STATUS_UNABLE_TO_FREE_VM", NT_STATUS_UNABLE_TO_FREE_VM }, + { "NT_STATUS_UNABLE_TO_DELETE_SECTION", NT_STATUS_UNABLE_TO_DELETE_SECTION }, + { "NT_STATUS_INVALID_SYSTEM_SERVICE", NT_STATUS_INVALID_SYSTEM_SERVICE }, + { "NT_STATUS_ILLEGAL_INSTRUCTION", NT_STATUS_ILLEGAL_INSTRUCTION }, + { "NT_STATUS_INVALID_LOCK_SEQUENCE", NT_STATUS_INVALID_LOCK_SEQUENCE }, + { "NT_STATUS_INVALID_VIEW_SIZE", NT_STATUS_INVALID_VIEW_SIZE }, + { "NT_STATUS_INVALID_FILE_FOR_SECTION", NT_STATUS_INVALID_FILE_FOR_SECTION }, + { "NT_STATUS_ALREADY_COMMITTED", NT_STATUS_ALREADY_COMMITTED }, + { "NT_STATUS_ACCESS_DENIED", NT_STATUS_ACCESS_DENIED }, + { "NT_STATUS_BUFFER_TOO_SMALL", NT_STATUS_BUFFER_TOO_SMALL }, + { "NT_STATUS_OBJECT_TYPE_MISMATCH", NT_STATUS_OBJECT_TYPE_MISMATCH }, + { "NT_STATUS_NONCONTINUABLE_EXCEPTION", NT_STATUS_NONCONTINUABLE_EXCEPTION }, + { "NT_STATUS_INVALID_DISPOSITION", NT_STATUS_INVALID_DISPOSITION }, + { "NT_STATUS_UNWIND", NT_STATUS_UNWIND }, + { "NT_STATUS_BAD_STACK", NT_STATUS_BAD_STACK }, + { "NT_STATUS_INVALID_UNWIND_TARGET", NT_STATUS_INVALID_UNWIND_TARGET }, + { "NT_STATUS_NOT_LOCKED", NT_STATUS_NOT_LOCKED }, + { "NT_STATUS_PARITY_ERROR", NT_STATUS_PARITY_ERROR }, + { "NT_STATUS_UNABLE_TO_DECOMMIT_VM", NT_STATUS_UNABLE_TO_DECOMMIT_VM }, + { "NT_STATUS_NOT_COMMITTED", NT_STATUS_NOT_COMMITTED }, + { "NT_STATUS_INVALID_PORT_ATTRIBUTES", NT_STATUS_INVALID_PORT_ATTRIBUTES }, + { "NT_STATUS_PORT_MESSAGE_TOO_LONG", NT_STATUS_PORT_MESSAGE_TOO_LONG }, + { "NT_STATUS_INVALID_PARAMETER_MIX", NT_STATUS_INVALID_PARAMETER_MIX }, + { "NT_STATUS_INVALID_QUOTA_LOWER", NT_STATUS_INVALID_QUOTA_LOWER }, + { "NT_STATUS_DISK_CORRUPT_ERROR", NT_STATUS_DISK_CORRUPT_ERROR }, + { "NT_STATUS_OBJECT_NAME_INVALID", NT_STATUS_OBJECT_NAME_INVALID }, + { "NT_STATUS_OBJECT_NAME_NOT_FOUND", NT_STATUS_OBJECT_NAME_NOT_FOUND }, + { "NT_STATUS_OBJECT_NAME_COLLISION", NT_STATUS_OBJECT_NAME_COLLISION }, + { "NT_STATUS_HANDLE_NOT_WAITABLE", NT_STATUS_HANDLE_NOT_WAITABLE }, + { "NT_STATUS_PORT_DISCONNECTED", NT_STATUS_PORT_DISCONNECTED }, + { "NT_STATUS_DEVICE_ALREADY_ATTACHED", NT_STATUS_DEVICE_ALREADY_ATTACHED }, + { "NT_STATUS_OBJECT_PATH_INVALID", NT_STATUS_OBJECT_PATH_INVALID }, + { "NT_STATUS_OBJECT_PATH_NOT_FOUND", NT_STATUS_OBJECT_PATH_NOT_FOUND }, + { "NT_STATUS_OBJECT_PATH_SYNTAX_BAD", NT_STATUS_OBJECT_PATH_SYNTAX_BAD }, + { "NT_STATUS_DATA_OVERRUN", NT_STATUS_DATA_OVERRUN }, + { "NT_STATUS_DATA_LATE_ERROR", NT_STATUS_DATA_LATE_ERROR }, + { "NT_STATUS_DATA_ERROR", NT_STATUS_DATA_ERROR }, + { "NT_STATUS_CRC_ERROR", NT_STATUS_CRC_ERROR }, + { "NT_STATUS_SECTION_TOO_BIG", NT_STATUS_SECTION_TOO_BIG }, + { "NT_STATUS_PORT_CONNECTION_REFUSED", NT_STATUS_PORT_CONNECTION_REFUSED }, + { "NT_STATUS_INVALID_PORT_HANDLE", NT_STATUS_INVALID_PORT_HANDLE }, + { "NT_STATUS_SHARING_VIOLATION", NT_STATUS_SHARING_VIOLATION }, + { "NT_STATUS_QUOTA_EXCEEDED", NT_STATUS_QUOTA_EXCEEDED }, + { "NT_STATUS_INVALID_PAGE_PROTECTION", NT_STATUS_INVALID_PAGE_PROTECTION }, + { "NT_STATUS_MUTANT_NOT_OWNED", NT_STATUS_MUTANT_NOT_OWNED }, + { "NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED", NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED }, + { "NT_STATUS_PORT_ALREADY_SET", NT_STATUS_PORT_ALREADY_SET }, + { "NT_STATUS_SECTION_NOT_IMAGE", NT_STATUS_SECTION_NOT_IMAGE }, + { "NT_STATUS_SUSPEND_COUNT_EXCEEDED", NT_STATUS_SUSPEND_COUNT_EXCEEDED }, + { "NT_STATUS_THREAD_IS_TERMINATING", NT_STATUS_THREAD_IS_TERMINATING }, + { "NT_STATUS_BAD_WORKING_SET_LIMIT", NT_STATUS_BAD_WORKING_SET_LIMIT }, + { "NT_STATUS_INCOMPATIBLE_FILE_MAP", NT_STATUS_INCOMPATIBLE_FILE_MAP }, + { "NT_STATUS_SECTION_PROTECTION", NT_STATUS_SECTION_PROTECTION }, + { "NT_STATUS_EAS_NOT_SUPPORTED", NT_STATUS_EAS_NOT_SUPPORTED }, + { "NT_STATUS_EA_TOO_LARGE", NT_STATUS_EA_TOO_LARGE }, + { "NT_STATUS_NONEXISTENT_EA_ENTRY", NT_STATUS_NONEXISTENT_EA_ENTRY }, + { "NT_STATUS_NO_EAS_ON_FILE", NT_STATUS_NO_EAS_ON_FILE }, + { "NT_STATUS_EA_CORRUPT_ERROR", NT_STATUS_EA_CORRUPT_ERROR }, + { "NT_STATUS_FILE_LOCK_CONFLICT", NT_STATUS_FILE_LOCK_CONFLICT }, + { "NT_STATUS_LOCK_NOT_GRANTED", NT_STATUS_LOCK_NOT_GRANTED }, + { "NT_STATUS_DELETE_PENDING", NT_STATUS_DELETE_PENDING }, + { "NT_STATUS_CTL_FILE_NOT_SUPPORTED", NT_STATUS_CTL_FILE_NOT_SUPPORTED }, + { "NT_STATUS_UNKNOWN_REVISION", NT_STATUS_UNKNOWN_REVISION }, + { "NT_STATUS_REVISION_MISMATCH", NT_STATUS_REVISION_MISMATCH }, + { "NT_STATUS_INVALID_OWNER", NT_STATUS_INVALID_OWNER }, + { "NT_STATUS_INVALID_PRIMARY_GROUP", NT_STATUS_INVALID_PRIMARY_GROUP }, + { "NT_STATUS_NO_IMPERSONATION_TOKEN", NT_STATUS_NO_IMPERSONATION_TOKEN }, + { "NT_STATUS_CANT_DISABLE_MANDATORY", NT_STATUS_CANT_DISABLE_MANDATORY }, + { "NT_STATUS_NO_LOGON_SERVERS", NT_STATUS_NO_LOGON_SERVERS }, + { "NT_STATUS_NO_SUCH_LOGON_SESSION", NT_STATUS_NO_SUCH_LOGON_SESSION }, + { "NT_STATUS_NO_SUCH_PRIVILEGE", NT_STATUS_NO_SUCH_PRIVILEGE }, + { "NT_STATUS_PRIVILEGE_NOT_HELD", NT_STATUS_PRIVILEGE_NOT_HELD }, + { "NT_STATUS_INVALID_ACCOUNT_NAME", NT_STATUS_INVALID_ACCOUNT_NAME }, + { "NT_STATUS_USER_EXISTS", NT_STATUS_USER_EXISTS }, + { "NT_STATUS_NO_SUCH_USER", NT_STATUS_NO_SUCH_USER }, + { "NT_STATUS_GROUP_EXISTS", NT_STATUS_GROUP_EXISTS }, + { "NT_STATUS_NO_SUCH_GROUP", NT_STATUS_NO_SUCH_GROUP }, + { "NT_STATUS_MEMBER_IN_GROUP", NT_STATUS_MEMBER_IN_GROUP }, + { "NT_STATUS_MEMBER_NOT_IN_GROUP", NT_STATUS_MEMBER_NOT_IN_GROUP }, + { "NT_STATUS_LAST_ADMIN", NT_STATUS_LAST_ADMIN }, + { "NT_STATUS_WRONG_PASSWORD", NT_STATUS_WRONG_PASSWORD }, + { "NT_STATUS_ILL_FORMED_PASSWORD", NT_STATUS_ILL_FORMED_PASSWORD }, + { "NT_STATUS_PASSWORD_RESTRICTION", NT_STATUS_PASSWORD_RESTRICTION }, + { "NT_STATUS_LOGON_FAILURE", NT_STATUS_LOGON_FAILURE }, + { "NT_STATUS_ACCOUNT_RESTRICTION", NT_STATUS_ACCOUNT_RESTRICTION }, + { "NT_STATUS_INVALID_LOGON_HOURS", NT_STATUS_INVALID_LOGON_HOURS }, + { "NT_STATUS_INVALID_WORKSTATION", NT_STATUS_INVALID_WORKSTATION }, + { "NT_STATUS_PASSWORD_EXPIRED", NT_STATUS_PASSWORD_EXPIRED }, + { "NT_STATUS_ACCOUNT_DISABLED", NT_STATUS_ACCOUNT_DISABLED }, + { "NT_STATUS_NONE_MAPPED", NT_STATUS_NONE_MAPPED }, + { "NT_STATUS_TOO_MANY_LUIDS_REQUESTED", NT_STATUS_TOO_MANY_LUIDS_REQUESTED }, + { "NT_STATUS_LUIDS_EXHAUSTED", NT_STATUS_LUIDS_EXHAUSTED }, + { "NT_STATUS_INVALID_SUB_AUTHORITY", NT_STATUS_INVALID_SUB_AUTHORITY }, + { "NT_STATUS_INVALID_ACL", NT_STATUS_INVALID_ACL }, + { "NT_STATUS_INVALID_SID", NT_STATUS_INVALID_SID }, + { "NT_STATUS_INVALID_SECURITY_DESCR", NT_STATUS_INVALID_SECURITY_DESCR }, + { "NT_STATUS_PROCEDURE_NOT_FOUND", NT_STATUS_PROCEDURE_NOT_FOUND }, + { "NT_STATUS_INVALID_IMAGE_FORMAT", NT_STATUS_INVALID_IMAGE_FORMAT }, + { "NT_STATUS_NO_TOKEN", NT_STATUS_NO_TOKEN }, + { "NT_STATUS_BAD_INHERITANCE_ACL", NT_STATUS_BAD_INHERITANCE_ACL }, + { "NT_STATUS_RANGE_NOT_LOCKED", NT_STATUS_RANGE_NOT_LOCKED }, + { "NT_STATUS_DISK_FULL", NT_STATUS_DISK_FULL }, + { "NT_STATUS_SERVER_DISABLED", NT_STATUS_SERVER_DISABLED }, + { "NT_STATUS_SERVER_NOT_DISABLED", NT_STATUS_SERVER_NOT_DISABLED }, + { "NT_STATUS_TOO_MANY_GUIDS_REQUESTED", NT_STATUS_TOO_MANY_GUIDS_REQUESTED }, + { "NT_STATUS_GUIDS_EXHAUSTED", NT_STATUS_GUIDS_EXHAUSTED }, + { "NT_STATUS_INVALID_ID_AUTHORITY", NT_STATUS_INVALID_ID_AUTHORITY }, + { "NT_STATUS_AGENTS_EXHAUSTED", NT_STATUS_AGENTS_EXHAUSTED }, + { "NT_STATUS_INVALID_VOLUME_LABEL", NT_STATUS_INVALID_VOLUME_LABEL }, + { "NT_STATUS_SECTION_NOT_EXTENDED", NT_STATUS_SECTION_NOT_EXTENDED }, + { "NT_STATUS_NOT_MAPPED_DATA", NT_STATUS_NOT_MAPPED_DATA }, + { "NT_STATUS_RESOURCE_DATA_NOT_FOUND", NT_STATUS_RESOURCE_DATA_NOT_FOUND }, + { "NT_STATUS_RESOURCE_TYPE_NOT_FOUND", NT_STATUS_RESOURCE_TYPE_NOT_FOUND }, + { "NT_STATUS_RESOURCE_NAME_NOT_FOUND", NT_STATUS_RESOURCE_NAME_NOT_FOUND }, + { "NT_STATUS_ARRAY_BOUNDS_EXCEEDED", NT_STATUS_ARRAY_BOUNDS_EXCEEDED }, + { "NT_STATUS_FLOAT_DENORMAL_OPERAND", NT_STATUS_FLOAT_DENORMAL_OPERAND }, + { "NT_STATUS_FLOAT_DIVIDE_BY_ZERO", NT_STATUS_FLOAT_DIVIDE_BY_ZERO }, + { "NT_STATUS_FLOAT_INEXACT_RESULT", NT_STATUS_FLOAT_INEXACT_RESULT }, + { "NT_STATUS_FLOAT_INVALID_OPERATION", NT_STATUS_FLOAT_INVALID_OPERATION }, + { "NT_STATUS_FLOAT_OVERFLOW", NT_STATUS_FLOAT_OVERFLOW }, + { "NT_STATUS_FLOAT_STACK_CHECK", NT_STATUS_FLOAT_STACK_CHECK }, + { "NT_STATUS_FLOAT_UNDERFLOW", NT_STATUS_FLOAT_UNDERFLOW }, + { "NT_STATUS_INTEGER_DIVIDE_BY_ZERO", NT_STATUS_INTEGER_DIVIDE_BY_ZERO }, + { "NT_STATUS_INTEGER_OVERFLOW", NT_STATUS_INTEGER_OVERFLOW }, + { "NT_STATUS_PRIVILEGED_INSTRUCTION", NT_STATUS_PRIVILEGED_INSTRUCTION }, + { "NT_STATUS_TOO_MANY_PAGING_FILES", NT_STATUS_TOO_MANY_PAGING_FILES }, + { "NT_STATUS_FILE_INVALID", NT_STATUS_FILE_INVALID }, + { "NT_STATUS_ALLOTTED_SPACE_EXCEEDED", NT_STATUS_ALLOTTED_SPACE_EXCEEDED }, + { "NT_STATUS_INSUFFICIENT_RESOURCES", NT_STATUS_INSUFFICIENT_RESOURCES }, + { "NT_STATUS_DFS_EXIT_PATH_FOUND", NT_STATUS_DFS_EXIT_PATH_FOUND }, + { "NT_STATUS_DEVICE_DATA_ERROR", NT_STATUS_DEVICE_DATA_ERROR }, + { "NT_STATUS_DEVICE_NOT_CONNECTED", NT_STATUS_DEVICE_NOT_CONNECTED }, + { "NT_STATUS_DEVICE_POWER_FAILURE", NT_STATUS_DEVICE_POWER_FAILURE }, + { "NT_STATUS_FREE_VM_NOT_AT_BASE", NT_STATUS_FREE_VM_NOT_AT_BASE }, + { "NT_STATUS_MEMORY_NOT_ALLOCATED", NT_STATUS_MEMORY_NOT_ALLOCATED }, + { "NT_STATUS_WORKING_SET_QUOTA", NT_STATUS_WORKING_SET_QUOTA }, + { "NT_STATUS_MEDIA_WRITE_PROTECTED", NT_STATUS_MEDIA_WRITE_PROTECTED }, + { "NT_STATUS_DEVICE_NOT_READY", NT_STATUS_DEVICE_NOT_READY }, + { "NT_STATUS_INVALID_GROUP_ATTRIBUTES", NT_STATUS_INVALID_GROUP_ATTRIBUTES }, + { "NT_STATUS_BAD_IMPERSONATION_LEVEL", NT_STATUS_BAD_IMPERSONATION_LEVEL }, + { "NT_STATUS_CANT_OPEN_ANONYMOUS", NT_STATUS_CANT_OPEN_ANONYMOUS }, + { "NT_STATUS_BAD_VALIDATION_CLASS", NT_STATUS_BAD_VALIDATION_CLASS }, + { "NT_STATUS_BAD_TOKEN_TYPE", NT_STATUS_BAD_TOKEN_TYPE }, + { "NT_STATUS_BAD_MASTER_BOOT_RECORD", NT_STATUS_BAD_MASTER_BOOT_RECORD }, + { "NT_STATUS_INSTRUCTION_MISALIGNMENT", NT_STATUS_INSTRUCTION_MISALIGNMENT }, + { "NT_STATUS_INSTANCE_NOT_AVAILABLE", NT_STATUS_INSTANCE_NOT_AVAILABLE }, + { "NT_STATUS_PIPE_NOT_AVAILABLE", NT_STATUS_PIPE_NOT_AVAILABLE }, + { "NT_STATUS_INVALID_PIPE_STATE", NT_STATUS_INVALID_PIPE_STATE }, + { "NT_STATUS_PIPE_BUSY", NT_STATUS_PIPE_BUSY }, + { "NT_STATUS_ILLEGAL_FUNCTION", NT_STATUS_ILLEGAL_FUNCTION }, + { "NT_STATUS_PIPE_DISCONNECTED", NT_STATUS_PIPE_DISCONNECTED }, + { "NT_STATUS_PIPE_CLOSING", NT_STATUS_PIPE_CLOSING }, + { "NT_STATUS_PIPE_CONNECTED", NT_STATUS_PIPE_CONNECTED }, + { "NT_STATUS_PIPE_LISTENING", NT_STATUS_PIPE_LISTENING }, + { "NT_STATUS_INVALID_READ_MODE", NT_STATUS_INVALID_READ_MODE }, + { "NT_STATUS_IO_TIMEOUT", NT_STATUS_IO_TIMEOUT }, + { "NT_STATUS_FILE_FORCED_CLOSED", NT_STATUS_FILE_FORCED_CLOSED }, + { "NT_STATUS_PROFILING_NOT_STARTED", NT_STATUS_PROFILING_NOT_STARTED }, + { "NT_STATUS_PROFILING_NOT_STOPPED", NT_STATUS_PROFILING_NOT_STOPPED }, + { "NT_STATUS_COULD_NOT_INTERPRET", NT_STATUS_COULD_NOT_INTERPRET }, + { "NT_STATUS_FILE_IS_A_DIRECTORY", NT_STATUS_FILE_IS_A_DIRECTORY }, + { "NT_STATUS_NOT_SUPPORTED", NT_STATUS_NOT_SUPPORTED }, + { "NT_STATUS_REMOTE_NOT_LISTENING", NT_STATUS_REMOTE_NOT_LISTENING }, + { "NT_STATUS_DUPLICATE_NAME", NT_STATUS_DUPLICATE_NAME }, + { "NT_STATUS_BAD_NETWORK_PATH", NT_STATUS_BAD_NETWORK_PATH }, + { "NT_STATUS_NETWORK_BUSY", NT_STATUS_NETWORK_BUSY }, + { "NT_STATUS_DEVICE_DOES_NOT_EXIST", NT_STATUS_DEVICE_DOES_NOT_EXIST }, + { "NT_STATUS_TOO_MANY_COMMANDS", NT_STATUS_TOO_MANY_COMMANDS }, + { "NT_STATUS_ADAPTER_HARDWARE_ERROR", NT_STATUS_ADAPTER_HARDWARE_ERROR }, + { "NT_STATUS_INVALID_NETWORK_RESPONSE", NT_STATUS_INVALID_NETWORK_RESPONSE }, + { "NT_STATUS_UNEXPECTED_NETWORK_ERROR", NT_STATUS_UNEXPECTED_NETWORK_ERROR }, + { "NT_STATUS_BAD_REMOTE_ADAPTER", NT_STATUS_BAD_REMOTE_ADAPTER }, + { "NT_STATUS_PRINT_QUEUE_FULL", NT_STATUS_PRINT_QUEUE_FULL }, + { "NT_STATUS_NO_SPOOL_SPACE", NT_STATUS_NO_SPOOL_SPACE }, + { "NT_STATUS_PRINT_CANCELLED", NT_STATUS_PRINT_CANCELLED }, + { "NT_STATUS_NETWORK_NAME_DELETED", NT_STATUS_NETWORK_NAME_DELETED }, + { "NT_STATUS_NETWORK_ACCESS_DENIED", NT_STATUS_NETWORK_ACCESS_DENIED }, + { "NT_STATUS_BAD_DEVICE_TYPE", NT_STATUS_BAD_DEVICE_TYPE }, + { "NT_STATUS_BAD_NETWORK_NAME", NT_STATUS_BAD_NETWORK_NAME }, + { "NT_STATUS_TOO_MANY_NAMES", NT_STATUS_TOO_MANY_NAMES }, + { "NT_STATUS_TOO_MANY_SESSIONS", NT_STATUS_TOO_MANY_SESSIONS }, + { "NT_STATUS_SHARING_PAUSED", NT_STATUS_SHARING_PAUSED }, + { "NT_STATUS_REQUEST_NOT_ACCEPTED", NT_STATUS_REQUEST_NOT_ACCEPTED }, + { "NT_STATUS_REDIRECTOR_PAUSED", NT_STATUS_REDIRECTOR_PAUSED }, + { "NT_STATUS_NET_WRITE_FAULT", NT_STATUS_NET_WRITE_FAULT }, + { "NT_STATUS_PROFILING_AT_LIMIT", NT_STATUS_PROFILING_AT_LIMIT }, + { "NT_STATUS_NOT_SAME_DEVICE", NT_STATUS_NOT_SAME_DEVICE }, + { "NT_STATUS_FILE_RENAMED", NT_STATUS_FILE_RENAMED }, + { "NT_STATUS_VIRTUAL_CIRCUIT_CLOSED", NT_STATUS_VIRTUAL_CIRCUIT_CLOSED }, + { "NT_STATUS_NO_SECURITY_ON_OBJECT", NT_STATUS_NO_SECURITY_ON_OBJECT }, + { "NT_STATUS_CANT_WAIT", NT_STATUS_CANT_WAIT }, + { "NT_STATUS_PIPE_EMPTY", NT_STATUS_PIPE_EMPTY }, + { "NT_STATUS_CANT_ACCESS_DOMAIN_INFO", NT_STATUS_CANT_ACCESS_DOMAIN_INFO }, + { "NT_STATUS_CANT_TERMINATE_SELF", NT_STATUS_CANT_TERMINATE_SELF }, + { "NT_STATUS_INVALID_SERVER_STATE", NT_STATUS_INVALID_SERVER_STATE }, + { "NT_STATUS_INVALID_DOMAIN_STATE", NT_STATUS_INVALID_DOMAIN_STATE }, + { "NT_STATUS_INVALID_DOMAIN_ROLE", NT_STATUS_INVALID_DOMAIN_ROLE }, + { "NT_STATUS_NO_SUCH_DOMAIN", NT_STATUS_NO_SUCH_DOMAIN }, + { "NT_STATUS_DOMAIN_EXISTS", NT_STATUS_DOMAIN_EXISTS }, + { "NT_STATUS_DOMAIN_LIMIT_EXCEEDED", NT_STATUS_DOMAIN_LIMIT_EXCEEDED }, + { "NT_STATUS_OPLOCK_NOT_GRANTED", NT_STATUS_OPLOCK_NOT_GRANTED }, + { "NT_STATUS_INVALID_OPLOCK_PROTOCOL", NT_STATUS_INVALID_OPLOCK_PROTOCOL }, + { "NT_STATUS_INTERNAL_DB_CORRUPTION", NT_STATUS_INTERNAL_DB_CORRUPTION }, + { "NT_STATUS_INTERNAL_ERROR", NT_STATUS_INTERNAL_ERROR }, + { "NT_STATUS_GENERIC_NOT_MAPPED", NT_STATUS_GENERIC_NOT_MAPPED }, + { "NT_STATUS_BAD_DESCRIPTOR_FORMAT", NT_STATUS_BAD_DESCRIPTOR_FORMAT }, + { "NT_STATUS_INVALID_USER_BUFFER", NT_STATUS_INVALID_USER_BUFFER }, + { "NT_STATUS_UNEXPECTED_IO_ERROR", NT_STATUS_UNEXPECTED_IO_ERROR }, + { "NT_STATUS_UNEXPECTED_MM_CREATE_ERR", NT_STATUS_UNEXPECTED_MM_CREATE_ERR }, + { "NT_STATUS_UNEXPECTED_MM_MAP_ERROR", NT_STATUS_UNEXPECTED_MM_MAP_ERROR }, + { "NT_STATUS_UNEXPECTED_MM_EXTEND_ERR", NT_STATUS_UNEXPECTED_MM_EXTEND_ERR }, + { "NT_STATUS_NOT_LOGON_PROCESS", NT_STATUS_NOT_LOGON_PROCESS }, + { "NT_STATUS_LOGON_SESSION_EXISTS", NT_STATUS_LOGON_SESSION_EXISTS }, + { "NT_STATUS_INVALID_PARAMETER_1", NT_STATUS_INVALID_PARAMETER_1 }, + { "NT_STATUS_INVALID_PARAMETER_2", NT_STATUS_INVALID_PARAMETER_2 }, + { "NT_STATUS_INVALID_PARAMETER_3", NT_STATUS_INVALID_PARAMETER_3 }, + { "NT_STATUS_INVALID_PARAMETER_4", NT_STATUS_INVALID_PARAMETER_4 }, + { "NT_STATUS_INVALID_PARAMETER_5", NT_STATUS_INVALID_PARAMETER_5 }, + { "NT_STATUS_INVALID_PARAMETER_6", NT_STATUS_INVALID_PARAMETER_6 }, + { "NT_STATUS_INVALID_PARAMETER_7", NT_STATUS_INVALID_PARAMETER_7 }, + { "NT_STATUS_INVALID_PARAMETER_8", NT_STATUS_INVALID_PARAMETER_8 }, + { "NT_STATUS_INVALID_PARAMETER_9", NT_STATUS_INVALID_PARAMETER_9 }, + { "NT_STATUS_INVALID_PARAMETER_10", NT_STATUS_INVALID_PARAMETER_10 }, + { "NT_STATUS_INVALID_PARAMETER_11", NT_STATUS_INVALID_PARAMETER_11 }, + { "NT_STATUS_INVALID_PARAMETER_12", NT_STATUS_INVALID_PARAMETER_12 }, + { "NT_STATUS_REDIRECTOR_NOT_STARTED", NT_STATUS_REDIRECTOR_NOT_STARTED }, + { "NT_STATUS_REDIRECTOR_STARTED", NT_STATUS_REDIRECTOR_STARTED }, + { "NT_STATUS_STACK_OVERFLOW", NT_STATUS_STACK_OVERFLOW }, + { "NT_STATUS_NO_SUCH_PACKAGE", NT_STATUS_NO_SUCH_PACKAGE }, + { "NT_STATUS_BAD_FUNCTION_TABLE", NT_STATUS_BAD_FUNCTION_TABLE }, + { "NT_STATUS_DIRECTORY_NOT_EMPTY", NT_STATUS_DIRECTORY_NOT_EMPTY }, + { "NT_STATUS_FILE_CORRUPT_ERROR", NT_STATUS_FILE_CORRUPT_ERROR }, + { "NT_STATUS_NOT_A_DIRECTORY", NT_STATUS_NOT_A_DIRECTORY }, + { "NT_STATUS_BAD_LOGON_SESSION_STATE", NT_STATUS_BAD_LOGON_SESSION_STATE }, + { "NT_STATUS_LOGON_SESSION_COLLISION", NT_STATUS_LOGON_SESSION_COLLISION }, + { "NT_STATUS_NAME_TOO_LONG", NT_STATUS_NAME_TOO_LONG }, + { "NT_STATUS_FILES_OPEN", NT_STATUS_FILES_OPEN }, + { "NT_STATUS_CONNECTION_IN_USE", NT_STATUS_CONNECTION_IN_USE }, + { "NT_STATUS_MESSAGE_NOT_FOUND", NT_STATUS_MESSAGE_NOT_FOUND }, + { "NT_STATUS_PROCESS_IS_TERMINATING", NT_STATUS_PROCESS_IS_TERMINATING }, + { "NT_STATUS_INVALID_LOGON_TYPE", NT_STATUS_INVALID_LOGON_TYPE }, + { "NT_STATUS_NO_GUID_TRANSLATION", NT_STATUS_NO_GUID_TRANSLATION }, + { "NT_STATUS_CANNOT_IMPERSONATE", NT_STATUS_CANNOT_IMPERSONATE }, + { "NT_STATUS_IMAGE_ALREADY_LOADED", NT_STATUS_IMAGE_ALREADY_LOADED }, + { "NT_STATUS_ABIOS_NOT_PRESENT", NT_STATUS_ABIOS_NOT_PRESENT }, + { "NT_STATUS_ABIOS_LID_NOT_EXIST", NT_STATUS_ABIOS_LID_NOT_EXIST }, + { "NT_STATUS_ABIOS_LID_ALREADY_OWNED", NT_STATUS_ABIOS_LID_ALREADY_OWNED }, + { "NT_STATUS_ABIOS_NOT_LID_OWNER", NT_STATUS_ABIOS_NOT_LID_OWNER }, + { "NT_STATUS_ABIOS_INVALID_COMMAND", NT_STATUS_ABIOS_INVALID_COMMAND }, + { "NT_STATUS_ABIOS_INVALID_LID", NT_STATUS_ABIOS_INVALID_LID }, + { "NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE", NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE }, + { "NT_STATUS_ABIOS_INVALID_SELECTOR", NT_STATUS_ABIOS_INVALID_SELECTOR }, + { "NT_STATUS_NO_LDT", NT_STATUS_NO_LDT }, + { "NT_STATUS_INVALID_LDT_SIZE", NT_STATUS_INVALID_LDT_SIZE }, + { "NT_STATUS_INVALID_LDT_OFFSET", NT_STATUS_INVALID_LDT_OFFSET }, + { "NT_STATUS_INVALID_LDT_DESCRIPTOR", NT_STATUS_INVALID_LDT_DESCRIPTOR }, + { "NT_STATUS_INVALID_IMAGE_NE_FORMAT", NT_STATUS_INVALID_IMAGE_NE_FORMAT }, + { "NT_STATUS_RXACT_INVALID_STATE", NT_STATUS_RXACT_INVALID_STATE }, + { "NT_STATUS_RXACT_COMMIT_FAILURE", NT_STATUS_RXACT_COMMIT_FAILURE }, + { "NT_STATUS_MAPPED_FILE_SIZE_ZERO", NT_STATUS_MAPPED_FILE_SIZE_ZERO }, + { "NT_STATUS_TOO_MANY_OPENED_FILES", NT_STATUS_TOO_MANY_OPENED_FILES }, + { "NT_STATUS_CANCELLED", NT_STATUS_CANCELLED }, + { "NT_STATUS_CANNOT_DELETE", NT_STATUS_CANNOT_DELETE }, + { "NT_STATUS_INVALID_COMPUTER_NAME", NT_STATUS_INVALID_COMPUTER_NAME }, + { "NT_STATUS_FILE_DELETED", NT_STATUS_FILE_DELETED }, + { "NT_STATUS_SPECIAL_ACCOUNT", NT_STATUS_SPECIAL_ACCOUNT }, + { "NT_STATUS_SPECIAL_GROUP", NT_STATUS_SPECIAL_GROUP }, + { "NT_STATUS_SPECIAL_USER", NT_STATUS_SPECIAL_USER }, + { "NT_STATUS_MEMBERS_PRIMARY_GROUP", NT_STATUS_MEMBERS_PRIMARY_GROUP }, + { "NT_STATUS_FILE_CLOSED", NT_STATUS_FILE_CLOSED }, + { "NT_STATUS_TOO_MANY_THREADS", NT_STATUS_TOO_MANY_THREADS }, + { "NT_STATUS_THREAD_NOT_IN_PROCESS", NT_STATUS_THREAD_NOT_IN_PROCESS }, + { "NT_STATUS_TOKEN_ALREADY_IN_USE", NT_STATUS_TOKEN_ALREADY_IN_USE }, + { "NT_STATUS_PAGEFILE_QUOTA_EXCEEDED", NT_STATUS_PAGEFILE_QUOTA_EXCEEDED }, + { "NT_STATUS_COMMITMENT_LIMIT", NT_STATUS_COMMITMENT_LIMIT }, + { "NT_STATUS_INVALID_IMAGE_LE_FORMAT", NT_STATUS_INVALID_IMAGE_LE_FORMAT }, + { "NT_STATUS_INVALID_IMAGE_NOT_MZ", NT_STATUS_INVALID_IMAGE_NOT_MZ }, + { "NT_STATUS_INVALID_IMAGE_PROTECT", NT_STATUS_INVALID_IMAGE_PROTECT }, + { "NT_STATUS_INVALID_IMAGE_WIN_16", NT_STATUS_INVALID_IMAGE_WIN_16 }, + { "NT_STATUS_LOGON_SERVER_CONFLICT", NT_STATUS_LOGON_SERVER_CONFLICT }, + { "NT_STATUS_TIME_DIFFERENCE_AT_DC", NT_STATUS_TIME_DIFFERENCE_AT_DC }, + { "NT_STATUS_SYNCHRONIZATION_REQUIRED", NT_STATUS_SYNCHRONIZATION_REQUIRED }, + { "NT_STATUS_DLL_NOT_FOUND", NT_STATUS_DLL_NOT_FOUND }, + { "NT_STATUS_OPEN_FAILED", NT_STATUS_OPEN_FAILED }, + { "NT_STATUS_IO_PRIVILEGE_FAILED", NT_STATUS_IO_PRIVILEGE_FAILED }, + { "NT_STATUS_ORDINAL_NOT_FOUND", NT_STATUS_ORDINAL_NOT_FOUND }, + { "NT_STATUS_ENTRYPOINT_NOT_FOUND", NT_STATUS_ENTRYPOINT_NOT_FOUND }, + { "NT_STATUS_CONTROL_C_EXIT", NT_STATUS_CONTROL_C_EXIT }, + { "NT_STATUS_LOCAL_DISCONNECT", NT_STATUS_LOCAL_DISCONNECT }, + { "NT_STATUS_REMOTE_DISCONNECT", NT_STATUS_REMOTE_DISCONNECT }, + { "NT_STATUS_REMOTE_RESOURCES", NT_STATUS_REMOTE_RESOURCES }, + { "NT_STATUS_LINK_FAILED", NT_STATUS_LINK_FAILED }, + { "NT_STATUS_LINK_TIMEOUT", NT_STATUS_LINK_TIMEOUT }, + { "NT_STATUS_INVALID_CONNECTION", NT_STATUS_INVALID_CONNECTION }, + { "NT_STATUS_INVALID_ADDRESS", NT_STATUS_INVALID_ADDRESS }, + { "NT_STATUS_DLL_INIT_FAILED", NT_STATUS_DLL_INIT_FAILED }, + { "NT_STATUS_MISSING_SYSTEMFILE", NT_STATUS_MISSING_SYSTEMFILE }, + { "NT_STATUS_UNHANDLED_EXCEPTION", NT_STATUS_UNHANDLED_EXCEPTION }, + { "NT_STATUS_APP_INIT_FAILURE", NT_STATUS_APP_INIT_FAILURE }, + { "NT_STATUS_PAGEFILE_CREATE_FAILED", NT_STATUS_PAGEFILE_CREATE_FAILED }, + { "NT_STATUS_NO_PAGEFILE", NT_STATUS_NO_PAGEFILE }, + { "NT_STATUS_INVALID_LEVEL", NT_STATUS_INVALID_LEVEL }, + { "NT_STATUS_WRONG_PASSWORD_CORE", NT_STATUS_WRONG_PASSWORD_CORE }, + { "NT_STATUS_ILLEGAL_FLOAT_CONTEXT", NT_STATUS_ILLEGAL_FLOAT_CONTEXT }, + { "NT_STATUS_PIPE_BROKEN", NT_STATUS_PIPE_BROKEN }, + { "NT_STATUS_REGISTRY_CORRUPT", NT_STATUS_REGISTRY_CORRUPT }, + { "NT_STATUS_REGISTRY_IO_FAILED", NT_STATUS_REGISTRY_IO_FAILED }, + { "NT_STATUS_NO_EVENT_PAIR", NT_STATUS_NO_EVENT_PAIR }, + { "NT_STATUS_UNRECOGNIZED_VOLUME", NT_STATUS_UNRECOGNIZED_VOLUME }, + { "NT_STATUS_SERIAL_NO_DEVICE_INITED", NT_STATUS_SERIAL_NO_DEVICE_INITED }, + { "NT_STATUS_NO_SUCH_ALIAS", NT_STATUS_NO_SUCH_ALIAS }, + { "NT_STATUS_MEMBER_NOT_IN_ALIAS", NT_STATUS_MEMBER_NOT_IN_ALIAS }, + { "NT_STATUS_MEMBER_IN_ALIAS", NT_STATUS_MEMBER_IN_ALIAS }, + { "NT_STATUS_ALIAS_EXISTS", NT_STATUS_ALIAS_EXISTS }, + { "NT_STATUS_LOGON_NOT_GRANTED", NT_STATUS_LOGON_NOT_GRANTED }, + { "NT_STATUS_TOO_MANY_SECRETS", NT_STATUS_TOO_MANY_SECRETS }, + { "NT_STATUS_SECRET_TOO_LONG", NT_STATUS_SECRET_TOO_LONG }, + { "NT_STATUS_INTERNAL_DB_ERROR", NT_STATUS_INTERNAL_DB_ERROR }, + { "NT_STATUS_FULLSCREEN_MODE", NT_STATUS_FULLSCREEN_MODE }, + { "NT_STATUS_TOO_MANY_CONTEXT_IDS", NT_STATUS_TOO_MANY_CONTEXT_IDS }, + { "NT_STATUS_LOGON_TYPE_NOT_GRANTED", NT_STATUS_LOGON_TYPE_NOT_GRANTED }, + { "NT_STATUS_NOT_REGISTRY_FILE", NT_STATUS_NOT_REGISTRY_FILE }, + { "NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED", NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED }, + { "NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR", NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR }, + { "NT_STATUS_FT_MISSING_MEMBER", NT_STATUS_FT_MISSING_MEMBER }, + { "NT_STATUS_ILL_FORMED_SERVICE_ENTRY", NT_STATUS_ILL_FORMED_SERVICE_ENTRY }, + { "NT_STATUS_ILLEGAL_CHARACTER", NT_STATUS_ILLEGAL_CHARACTER }, + { "NT_STATUS_UNMAPPABLE_CHARACTER", NT_STATUS_UNMAPPABLE_CHARACTER }, + { "NT_STATUS_UNDEFINED_CHARACTER", NT_STATUS_UNDEFINED_CHARACTER }, + { "NT_STATUS_FLOPPY_VOLUME", NT_STATUS_FLOPPY_VOLUME }, + { "NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND", NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND }, + { "NT_STATUS_FLOPPY_WRONG_CYLINDER", NT_STATUS_FLOPPY_WRONG_CYLINDER }, + { "NT_STATUS_FLOPPY_UNKNOWN_ERROR", NT_STATUS_FLOPPY_UNKNOWN_ERROR }, + { "NT_STATUS_FLOPPY_BAD_REGISTERS", NT_STATUS_FLOPPY_BAD_REGISTERS }, + { "NT_STATUS_DISK_RECALIBRATE_FAILED", NT_STATUS_DISK_RECALIBRATE_FAILED }, + { "NT_STATUS_DISK_OPERATION_FAILED", NT_STATUS_DISK_OPERATION_FAILED }, + { "NT_STATUS_DISK_RESET_FAILED", NT_STATUS_DISK_RESET_FAILED }, + { "NT_STATUS_SHARED_IRQ_BUSY", NT_STATUS_SHARED_IRQ_BUSY }, + { "NT_STATUS_FT_ORPHANING", NT_STATUS_FT_ORPHANING }, + { "NT_STATUS_PARTITION_FAILURE", NT_STATUS_PARTITION_FAILURE }, + { "NT_STATUS_INVALID_BLOCK_LENGTH", NT_STATUS_INVALID_BLOCK_LENGTH }, + { "NT_STATUS_DEVICE_NOT_PARTITIONED", NT_STATUS_DEVICE_NOT_PARTITIONED }, + { "NT_STATUS_UNABLE_TO_LOCK_MEDIA", NT_STATUS_UNABLE_TO_LOCK_MEDIA }, + { "NT_STATUS_UNABLE_TO_UNLOAD_MEDIA", NT_STATUS_UNABLE_TO_UNLOAD_MEDIA }, + { "NT_STATUS_EOM_OVERFLOW", NT_STATUS_EOM_OVERFLOW }, + { "NT_STATUS_NO_MEDIA", NT_STATUS_NO_MEDIA }, + { "NT_STATUS_NO_SUCH_MEMBER", NT_STATUS_NO_SUCH_MEMBER }, + { "NT_STATUS_INVALID_MEMBER", NT_STATUS_INVALID_MEMBER }, + { "NT_STATUS_KEY_DELETED", NT_STATUS_KEY_DELETED }, + { "NT_STATUS_NO_LOG_SPACE", NT_STATUS_NO_LOG_SPACE }, + { "NT_STATUS_TOO_MANY_SIDS", NT_STATUS_TOO_MANY_SIDS }, + { "NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED", NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED }, + { "NT_STATUS_KEY_HAS_CHILDREN", NT_STATUS_KEY_HAS_CHILDREN }, + { "NT_STATUS_CHILD_MUST_BE_VOLATILE", NT_STATUS_CHILD_MUST_BE_VOLATILE }, + { "NT_STATUS_DEVICE_CONFIGURATION_ERROR", NT_STATUS_DEVICE_CONFIGURATION_ERROR }, + { "NT_STATUS_DRIVER_INTERNAL_ERROR", NT_STATUS_DRIVER_INTERNAL_ERROR }, + { "NT_STATUS_INVALID_DEVICE_STATE", NT_STATUS_INVALID_DEVICE_STATE }, + { "NT_STATUS_IO_DEVICE_ERROR", NT_STATUS_IO_DEVICE_ERROR }, + { "NT_STATUS_DEVICE_PROTOCOL_ERROR", NT_STATUS_DEVICE_PROTOCOL_ERROR }, + { "NT_STATUS_BACKUP_CONTROLLER", NT_STATUS_BACKUP_CONTROLLER }, + { "NT_STATUS_LOG_FILE_FULL", NT_STATUS_LOG_FILE_FULL }, + { "NT_STATUS_TOO_LATE", NT_STATUS_TOO_LATE }, + { "NT_STATUS_NO_TRUST_LSA_SECRET", NT_STATUS_NO_TRUST_LSA_SECRET }, + { "NT_STATUS_NO_TRUST_SAM_ACCOUNT", NT_STATUS_NO_TRUST_SAM_ACCOUNT }, + { "NT_STATUS_TRUSTED_DOMAIN_FAILURE", NT_STATUS_TRUSTED_DOMAIN_FAILURE }, + { "NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE", NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE }, + { "NT_STATUS_EVENTLOG_FILE_CORRUPT", NT_STATUS_EVENTLOG_FILE_CORRUPT }, + { "NT_STATUS_EVENTLOG_CANT_START", NT_STATUS_EVENTLOG_CANT_START }, + { "NT_STATUS_TRUST_FAILURE", NT_STATUS_TRUST_FAILURE }, + { "NT_STATUS_MUTANT_LIMIT_EXCEEDED", NT_STATUS_MUTANT_LIMIT_EXCEEDED }, + { "NT_STATUS_NETLOGON_NOT_STARTED", NT_STATUS_NETLOGON_NOT_STARTED }, + { "NT_STATUS_ACCOUNT_EXPIRED", NT_STATUS_ACCOUNT_EXPIRED }, + { "NT_STATUS_POSSIBLE_DEADLOCK", NT_STATUS_POSSIBLE_DEADLOCK }, + { "NT_STATUS_NETWORK_CREDENTIAL_CONFLICT", NT_STATUS_NETWORK_CREDENTIAL_CONFLICT }, + { "NT_STATUS_REMOTE_SESSION_LIMIT", NT_STATUS_REMOTE_SESSION_LIMIT }, + { "NT_STATUS_EVENTLOG_FILE_CHANGED", NT_STATUS_EVENTLOG_FILE_CHANGED }, + { "NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT", NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT }, + { "NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT", NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT }, + { "NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT", NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT }, + { "NT_STATUS_DOMAIN_TRUST_INCONSISTENT", NT_STATUS_DOMAIN_TRUST_INCONSISTENT }, + { "NT_STATUS_FS_DRIVER_REQUIRED", NT_STATUS_FS_DRIVER_REQUIRED }, + { "NT_STATUS_NO_USER_SESSION_KEY", NT_STATUS_NO_USER_SESSION_KEY }, + { "NT_STATUS_USER_SESSION_DELETED", NT_STATUS_USER_SESSION_DELETED }, + { "NT_STATUS_RESOURCE_LANG_NOT_FOUND", NT_STATUS_RESOURCE_LANG_NOT_FOUND }, + { "NT_STATUS_INSUFF_SERVER_RESOURCES", NT_STATUS_INSUFF_SERVER_RESOURCES }, + { "NT_STATUS_INVALID_BUFFER_SIZE", NT_STATUS_INVALID_BUFFER_SIZE }, + { "NT_STATUS_INVALID_ADDRESS_COMPONENT", NT_STATUS_INVALID_ADDRESS_COMPONENT }, + { "NT_STATUS_INVALID_ADDRESS_WILDCARD", NT_STATUS_INVALID_ADDRESS_WILDCARD }, + { "NT_STATUS_TOO_MANY_ADDRESSES", NT_STATUS_TOO_MANY_ADDRESSES }, + { "NT_STATUS_ADDRESS_ALREADY_EXISTS", NT_STATUS_ADDRESS_ALREADY_EXISTS }, + { "NT_STATUS_ADDRESS_CLOSED", NT_STATUS_ADDRESS_CLOSED }, + { "NT_STATUS_CONNECTION_DISCONNECTED", NT_STATUS_CONNECTION_DISCONNECTED }, + { "NT_STATUS_CONNECTION_RESET", NT_STATUS_CONNECTION_RESET }, + { "NT_STATUS_TOO_MANY_NODES", NT_STATUS_TOO_MANY_NODES }, + { "NT_STATUS_TRANSACTION_ABORTED", NT_STATUS_TRANSACTION_ABORTED }, + { "NT_STATUS_TRANSACTION_TIMED_OUT", NT_STATUS_TRANSACTION_TIMED_OUT }, + { "NT_STATUS_TRANSACTION_NO_RELEASE", NT_STATUS_TRANSACTION_NO_RELEASE }, + { "NT_STATUS_TRANSACTION_NO_MATCH", NT_STATUS_TRANSACTION_NO_MATCH }, + { "NT_STATUS_TRANSACTION_RESPONDED", NT_STATUS_TRANSACTION_RESPONDED }, + { "NT_STATUS_TRANSACTION_INVALID_ID", NT_STATUS_TRANSACTION_INVALID_ID }, + { "NT_STATUS_TRANSACTION_INVALID_TYPE", NT_STATUS_TRANSACTION_INVALID_TYPE }, + { "NT_STATUS_NOT_SERVER_SESSION", NT_STATUS_NOT_SERVER_SESSION }, + { "NT_STATUS_NOT_CLIENT_SESSION", NT_STATUS_NOT_CLIENT_SESSION }, + { "NT_STATUS_CANNOT_LOAD_REGISTRY_FILE", NT_STATUS_CANNOT_LOAD_REGISTRY_FILE }, + { "NT_STATUS_DEBUG_ATTACH_FAILED", NT_STATUS_DEBUG_ATTACH_FAILED }, + { "NT_STATUS_SYSTEM_PROCESS_TERMINATED", NT_STATUS_SYSTEM_PROCESS_TERMINATED }, + { "NT_STATUS_DATA_NOT_ACCEPTED", NT_STATUS_DATA_NOT_ACCEPTED }, + { "NT_STATUS_NO_BROWSER_SERVERS_FOUND", NT_STATUS_NO_BROWSER_SERVERS_FOUND }, + { "NT_STATUS_VDM_HARD_ERROR", NT_STATUS_VDM_HARD_ERROR }, + { "NT_STATUS_DRIVER_CANCEL_TIMEOUT", NT_STATUS_DRIVER_CANCEL_TIMEOUT }, + { "NT_STATUS_REPLY_MESSAGE_MISMATCH", NT_STATUS_REPLY_MESSAGE_MISMATCH }, + { "NT_STATUS_MAPPED_ALIGNMENT", NT_STATUS_MAPPED_ALIGNMENT }, + { "NT_STATUS_IMAGE_CHECKSUM_MISMATCH", NT_STATUS_IMAGE_CHECKSUM_MISMATCH }, + { "NT_STATUS_LOST_WRITEBEHIND_DATA", NT_STATUS_LOST_WRITEBEHIND_DATA }, + { "NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID", NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID }, + { "NT_STATUS_PASSWORD_MUST_CHANGE", NT_STATUS_PASSWORD_MUST_CHANGE }, + { "NT_STATUS_NOT_FOUND", NT_STATUS_NOT_FOUND }, + { "NT_STATUS_NOT_TINY_STREAM", NT_STATUS_NOT_TINY_STREAM }, + { "NT_STATUS_RECOVERY_FAILURE", NT_STATUS_RECOVERY_FAILURE }, + { "NT_STATUS_STACK_OVERFLOW_READ", NT_STATUS_STACK_OVERFLOW_READ }, + { "NT_STATUS_FAIL_CHECK", NT_STATUS_FAIL_CHECK }, + { "NT_STATUS_DUPLICATE_OBJECTID", NT_STATUS_DUPLICATE_OBJECTID }, + { "NT_STATUS_OBJECTID_EXISTS", NT_STATUS_OBJECTID_EXISTS }, + { "NT_STATUS_CONVERT_TO_LARGE", NT_STATUS_CONVERT_TO_LARGE }, + { "NT_STATUS_RETRY", NT_STATUS_RETRY }, + { "NT_STATUS_FOUND_OUT_OF_SCOPE", NT_STATUS_FOUND_OUT_OF_SCOPE }, + { "NT_STATUS_ALLOCATE_BUCKET", NT_STATUS_ALLOCATE_BUCKET }, + { "NT_STATUS_PROPSET_NOT_FOUND", NT_STATUS_PROPSET_NOT_FOUND }, + { "NT_STATUS_MARSHALL_OVERFLOW", NT_STATUS_MARSHALL_OVERFLOW }, + { "NT_STATUS_INVALID_VARIANT", NT_STATUS_INVALID_VARIANT }, + { "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND", NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND }, + { "NT_STATUS_ACCOUNT_LOCKED_OUT", NT_STATUS_ACCOUNT_LOCKED_OUT }, + { "NT_STATUS_HANDLE_NOT_CLOSABLE", NT_STATUS_HANDLE_NOT_CLOSABLE }, + { "NT_STATUS_CONNECTION_REFUSED", NT_STATUS_CONNECTION_REFUSED }, + { "NT_STATUS_GRACEFUL_DISCONNECT", NT_STATUS_GRACEFUL_DISCONNECT }, + { "NT_STATUS_ADDRESS_ALREADY_ASSOCIATED", NT_STATUS_ADDRESS_ALREADY_ASSOCIATED }, + { "NT_STATUS_ADDRESS_NOT_ASSOCIATED", NT_STATUS_ADDRESS_NOT_ASSOCIATED }, + { "NT_STATUS_CONNECTION_INVALID", NT_STATUS_CONNECTION_INVALID }, + { "NT_STATUS_CONNECTION_ACTIVE", NT_STATUS_CONNECTION_ACTIVE }, + { "NT_STATUS_NETWORK_UNREACHABLE", NT_STATUS_NETWORK_UNREACHABLE }, + { "NT_STATUS_HOST_UNREACHABLE", NT_STATUS_HOST_UNREACHABLE }, + { "NT_STATUS_PROTOCOL_UNREACHABLE", NT_STATUS_PROTOCOL_UNREACHABLE }, + { "NT_STATUS_PORT_UNREACHABLE", NT_STATUS_PORT_UNREACHABLE }, + { "NT_STATUS_REQUEST_ABORTED", NT_STATUS_REQUEST_ABORTED }, + { "NT_STATUS_CONNECTION_ABORTED", NT_STATUS_CONNECTION_ABORTED }, + { "NT_STATUS_BAD_COMPRESSION_BUFFER", NT_STATUS_BAD_COMPRESSION_BUFFER }, + { "NT_STATUS_USER_MAPPED_FILE", NT_STATUS_USER_MAPPED_FILE }, + { "NT_STATUS_AUDIT_FAILED", NT_STATUS_AUDIT_FAILED }, + { "NT_STATUS_TIMER_RESOLUTION_NOT_SET", NT_STATUS_TIMER_RESOLUTION_NOT_SET }, + { "NT_STATUS_CONNECTION_COUNT_LIMIT", NT_STATUS_CONNECTION_COUNT_LIMIT }, + { "NT_STATUS_LOGIN_TIME_RESTRICTION", NT_STATUS_LOGIN_TIME_RESTRICTION }, + { "NT_STATUS_LOGIN_WKSTA_RESTRICTION", NT_STATUS_LOGIN_WKSTA_RESTRICTION }, + { "NT_STATUS_IMAGE_MP_UP_MISMATCH", NT_STATUS_IMAGE_MP_UP_MISMATCH }, + { "NT_STATUS_INSUFFICIENT_LOGON_INFO", NT_STATUS_INSUFFICIENT_LOGON_INFO }, + { "NT_STATUS_BAD_DLL_ENTRYPOINT", NT_STATUS_BAD_DLL_ENTRYPOINT }, + { "NT_STATUS_BAD_SERVICE_ENTRYPOINT", NT_STATUS_BAD_SERVICE_ENTRYPOINT }, + { "NT_STATUS_LPC_REPLY_LOST", NT_STATUS_LPC_REPLY_LOST }, + { "NT_STATUS_IP_ADDRESS_CONFLICT1", NT_STATUS_IP_ADDRESS_CONFLICT1 }, + { "NT_STATUS_IP_ADDRESS_CONFLICT2", NT_STATUS_IP_ADDRESS_CONFLICT2 }, + { "NT_STATUS_REGISTRY_QUOTA_LIMIT", NT_STATUS_REGISTRY_QUOTA_LIMIT }, + { "NT_STATUS_PATH_NOT_COVERED", NT_STATUS_PATH_NOT_COVERED }, + { "NT_STATUS_NO_CALLBACK_ACTIVE", NT_STATUS_NO_CALLBACK_ACTIVE }, + { "NT_STATUS_LICENSE_QUOTA_EXCEEDED", NT_STATUS_LICENSE_QUOTA_EXCEEDED }, + { "NT_STATUS_PWD_TOO_SHORT", NT_STATUS_PWD_TOO_SHORT }, + { "NT_STATUS_PWD_TOO_RECENT", NT_STATUS_PWD_TOO_RECENT }, + { "NT_STATUS_PWD_HISTORY_CONFLICT", NT_STATUS_PWD_HISTORY_CONFLICT }, + { "NT_STATUS_PLUGPLAY_NO_DEVICE", NT_STATUS_PLUGPLAY_NO_DEVICE }, + { "NT_STATUS_UNSUPPORTED_COMPRESSION", NT_STATUS_UNSUPPORTED_COMPRESSION }, + { "NT_STATUS_INVALID_HW_PROFILE", NT_STATUS_INVALID_HW_PROFILE }, + { "NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH", NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH }, + { "NT_STATUS_DRIVER_ORDINAL_NOT_FOUND", NT_STATUS_DRIVER_ORDINAL_NOT_FOUND }, + { "NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND", NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND }, + { "NT_STATUS_RESOURCE_NOT_OWNED", NT_STATUS_RESOURCE_NOT_OWNED }, + { "NT_STATUS_TOO_MANY_LINKS", NT_STATUS_TOO_MANY_LINKS }, + { "NT_STATUS_QUOTA_LIST_INCONSISTENT", NT_STATUS_QUOTA_LIST_INCONSISTENT }, + { "NT_STATUS_FILE_IS_OFFLINE", NT_STATUS_FILE_IS_OFFLINE }, + { "NT_STATUS_NO_MORE_ENTRIES", NT_STATUS_NO_MORE_ENTRIES }, + { NULL, NT_STATUS(0) } +}; + +/***************************************************************************** + returns an NT error message. not amazingly helpful, but better than a number. + *****************************************************************************/ +char *nt_errstr(NTSTATUS nt_code) +{ + static pstring msg; + int idx = 0; + + slprintf(msg, sizeof(msg), "NT code 0x%08x", NT_STATUS_V(nt_code)); + + while (nt_errs[idx].nt_errstr != NULL) { + if (NT_STATUS_V(nt_errs[idx].nt_errcode) == + NT_STATUS_V(nt_code)) { + return nt_errs[idx].nt_errstr; + } + idx++; + } + + return msg; +} + +/***************************************************************************** + returns an NT_STATUS constant as a string for inclusion in autogen C code + *****************************************************************************/ +char *get_nt_error_c_code(NTSTATUS nt_code) +{ + static pstring out; + int idx = 0; + + while (nt_errs[idx].nt_errstr != NULL) { + if (NT_STATUS_V(nt_errs[idx].nt_errcode) == + NT_STATUS_V(nt_code)) { + return nt_errs[idx].nt_errstr; + } + idx++; + } + + slprintf(out, sizeof(out), "NT_STATUS(0x%08x)", NT_STATUS_V(nt_code)); + + return out; +} + +/***************************************************************************** + returns the NT_STATUS constant matching the string supplied (as an NTSTATUS) + *****************************************************************************/ +NTSTATUS nt_status_string_to_code(char *nt_status_str) +{ + int idx = 0; + + while (nt_errs[idx].nt_errstr != NULL) { + if (strcmp(nt_errs[idx].nt_errstr, nt_status_str) == 0) { + return nt_errs[idx].nt_errcode; + } + idx++; + } + return NT_STATUS_UNSUCCESSFUL; +} diff --git a/source3/libsmb/passchange.c b/source3/libsmb/passchange.c new file mode 100644 index 0000000000..b96bdc95a1 --- /dev/null +++ b/source3/libsmb/passchange.c @@ -0,0 +1,101 @@ +/* + Unix SMB/CIFS implementation. + SMB client password change routine + Copyright (C) Andrew Tridgell 1994-1998 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + + +extern pstring global_myname; + +/************************************************************* +change a password on a remote machine using IPC calls +*************************************************************/ +BOOL remote_password_change(const char *remote_machine, const char *user_name, + const char *old_passwd, const char *new_passwd, + char *err_str, size_t err_str_len) +{ + struct nmb_name calling, called; + struct cli_state cli; + struct in_addr ip; + + *err_str = '\0'; + + if(!resolve_name( remote_machine, &ip, 0x20)) { + slprintf(err_str, err_str_len-1, "unable to find an IP address for machine %s.\n", + remote_machine ); + return False; + } + + ZERO_STRUCT(cli); + + if (!cli_initialise(&cli) || !cli_connect(&cli, remote_machine, &ip)) { + slprintf(err_str, err_str_len-1, "unable to connect to SMB server on machine %s. Error was : %s.\n", + remote_machine, cli_errstr(&cli) ); + return False; + } + + make_nmb_name(&calling, global_myname , 0x0); + make_nmb_name(&called , remote_machine, 0x20); + + if (!cli_session_request(&cli, &calling, &called)) { + slprintf(err_str, err_str_len-1, "machine %s rejected the session setup. Error was : %s.\n", + remote_machine, cli_errstr(&cli) ); + cli_shutdown(&cli); + return False; + } + + cli.protocol = PROTOCOL_NT1; + + if (!cli_negprot(&cli)) { + slprintf(err_str, err_str_len-1, "machine %s rejected the negotiate protocol. Error was : %s.\n", + remote_machine, cli_errstr(&cli) ); + cli_shutdown(&cli); + return False; + } + + /* + * We should connect as the anonymous user here, in case + * the server has "must change password" checked... + * Thanks to <Nicholas.S.Jenkins@cdc.com> for this fix. + */ + + if (!cli_session_setup(&cli, "", "", 0, "", 0, "")) { + slprintf(err_str, err_str_len-1, "machine %s rejected the session setup. Error was : %s.\n", + remote_machine, cli_errstr(&cli) ); + cli_shutdown(&cli); + return False; + } + + if (!cli_send_tconX(&cli, "IPC$", "IPC", "", 1)) { + slprintf(err_str, err_str_len-1, "machine %s rejected the tconX on the IPC$ share. Error was : %s.\n", + remote_machine, cli_errstr(&cli) ); + cli_shutdown(&cli); + return False; + } + + if(!cli_oem_change_password(&cli, user_name, new_passwd, old_passwd)) { + slprintf(err_str, err_str_len-1, "machine %s rejected the password change: Error was : %s.\n", + remote_machine, cli_errstr(&cli) ); + cli_shutdown(&cli); + return False; + } + + cli_shutdown(&cli); + return True; +} diff --git a/source3/libsmb/pwd_cache.c b/source3/libsmb/pwd_cache.c new file mode 100644 index 0000000000..7d1185d9a7 --- /dev/null +++ b/source3/libsmb/pwd_cache.c @@ -0,0 +1,249 @@ +/* + Unix SMB/CIFS implementation. + Password cacheing. obfuscation is planned + Copyright (C) Luke Kenneth Casson Leighton 1996-1998 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/**************************************************************************** + Initialises a password structure. +****************************************************************************/ + +void pwd_init(struct pwd_info *pwd) +{ + memset((char *)pwd->password , '\0', sizeof(pwd->password )); + memset((char *)pwd->smb_lm_pwd, '\0', sizeof(pwd->smb_lm_pwd)); + memset((char *)pwd->smb_nt_pwd, '\0', sizeof(pwd->smb_nt_pwd)); + memset((char *)pwd->smb_lm_owf, '\0', sizeof(pwd->smb_lm_owf)); + memset((char *)pwd->smb_nt_owf, '\0', sizeof(pwd->smb_nt_owf)); + + pwd->null_pwd = True; /* safest option... */ + pwd->cleartext = False; + pwd->crypted = False; +} + +/**************************************************************************** + Returns NULL password flag. +****************************************************************************/ + +BOOL pwd_is_nullpwd(const struct pwd_info *pwd) +{ + return pwd->null_pwd; +} + +/**************************************************************************** + Compares two passwords. hmm, not as trivial as expected. hmm. +****************************************************************************/ + +BOOL pwd_compare(const struct pwd_info *pwd1, const struct pwd_info *pwd2) +{ + if (pwd1->cleartext && pwd2->cleartext) { + if (strequal(pwd1->password, pwd2->password)) + return True; + } + if (pwd1->null_pwd && pwd2->null_pwd) + return True; + + if (!pwd1->null_pwd && !pwd2->null_pwd && + !pwd1->cleartext && !pwd2->cleartext) { +#ifdef DEBUG_PASSWORD + DEBUG(100,("pwd compare: nt#\n")); + dump_data(100, pwd1->smb_nt_pwd, 16); + dump_data(100, pwd2->smb_nt_pwd, 16); +#endif + if (memcmp(pwd1->smb_nt_pwd, pwd2->smb_nt_pwd, 16) == 0) + return True; +#ifdef DEBUG_PASSWORD + DEBUG(100,("pwd compare: lm#\n")); + dump_data(100, pwd1->smb_lm_pwd, 16); + dump_data(100, pwd2->smb_lm_pwd, 16); +#endif + if (memcmp(pwd1->smb_lm_pwd, pwd2->smb_lm_pwd, 16) == 0) + return True; + } + return False; +} + +/**************************************************************************** + Reads a password. +****************************************************************************/ + +void pwd_read(struct pwd_info *pwd, char *passwd_report, BOOL do_encrypt) +{ + /* grab a password */ + char *user_pass; + + pwd_init(pwd); + + user_pass = (char*)getpass(passwd_report); + + /* + * Do not assume that an empty string is a NULL password. + * If you do this will break the session key generation for + * and account with an emtpy password. If you wish to use + * a NULL password, use the -N option to smbclient and rpcclient + * --jerry + */ +#if 0 + if (user_pass == NULL || user_pass[0] == 0) + pwd_set_nullpwd(pwd); + else if (do_encrypt) +#endif + if (do_encrypt) + pwd_make_lm_nt_16(pwd, user_pass); + else + pwd_set_cleartext(pwd, user_pass); +} + +/**************************************************************************** + Stores a cleartext password. +****************************************************************************/ + +void pwd_set_nullpwd(struct pwd_info *pwd) +{ + pwd_init(pwd); + + pwd->cleartext = False; + pwd->null_pwd = True; + pwd->crypted = False; +} + +/**************************************************************************** + Stores a cleartext password. +****************************************************************************/ + +void pwd_set_cleartext(struct pwd_info *pwd, char *clr) +{ + pwd_init(pwd); + push_ascii_fstring(pwd->password, clr); + pwd->cleartext = True; + pwd->null_pwd = False; + pwd->crypted = False; + pwd_make_lm_nt_16(pwd, clr); +} + +/**************************************************************************** + Gets a cleartext password. +****************************************************************************/ + +void pwd_get_cleartext(struct pwd_info *pwd, char *clr) +{ + if (pwd->cleartext) + fstrcpy(clr, pwd->password); + else + clr[0] = 0; + +} + +/**************************************************************************** + Stores lm and nt hashed passwords. +****************************************************************************/ + +void pwd_set_lm_nt_16(struct pwd_info *pwd, uchar lm_pwd[16], uchar nt_pwd[16]) +{ + pwd_init(pwd); + + if (lm_pwd) + memcpy(pwd->smb_lm_pwd, lm_pwd, 16); + else + memset((char *)pwd->smb_lm_pwd, '\0', 16); + + if (nt_pwd) + memcpy(pwd->smb_nt_pwd, nt_pwd, 16); + else + memset((char *)pwd->smb_nt_pwd, '\0', 16); + + pwd->null_pwd = False; + pwd->cleartext = False; + pwd->crypted = False; +} + +/**************************************************************************** + Gets lm and nt hashed passwords. +****************************************************************************/ + +void pwd_get_lm_nt_16(struct pwd_info *pwd, uchar lm_pwd[16], uchar nt_pwd[16]) +{ + if (lm_pwd != NULL) + memcpy(lm_pwd, pwd->smb_lm_pwd, 16); + if (nt_pwd != NULL) + memcpy(nt_pwd, pwd->smb_nt_pwd, 16); +} + +/**************************************************************************** + Makes lm and nt hashed passwords. +****************************************************************************/ + +void pwd_make_lm_nt_16(struct pwd_info *pwd, char *clr) +{ + pstring dos_passwd; + + pwd_init(pwd); + + push_ascii_pstring(dos_passwd, clr); + + nt_lm_owf_gen(dos_passwd, pwd->smb_nt_pwd, pwd->smb_lm_pwd); + pwd->null_pwd = False; + pwd->cleartext = False; + pwd->crypted = False; +} + +/**************************************************************************** + Makes lm and nt OWF crypts. +****************************************************************************/ + +void pwd_make_lm_nt_owf(struct pwd_info *pwd, uchar cryptkey[8]) +{ + +#ifdef DEBUG_PASSWORD + DEBUG(100,("client cryptkey: ")); + dump_data(100, (char *)cryptkey, 8); +#endif + + SMBOWFencrypt(pwd->smb_nt_pwd, cryptkey, pwd->smb_nt_owf); + +#ifdef DEBUG_PASSWORD + DEBUG(100,("nt_owf_passwd: ")); + dump_data(100, (char *)pwd->smb_nt_owf, sizeof(pwd->smb_nt_owf)); + DEBUG(100,("nt_sess_pwd: ")); + dump_data(100, (char *)pwd->smb_nt_pwd, sizeof(pwd->smb_nt_pwd)); +#endif + + SMBOWFencrypt(pwd->smb_lm_pwd, cryptkey, pwd->smb_lm_owf); + +#ifdef DEBUG_PASSWORD + DEBUG(100,("lm_owf_passwd: ")); + dump_data(100, (char *)pwd->smb_lm_owf, sizeof(pwd->smb_lm_owf)); + DEBUG(100,("lm_sess_pwd: ")); + dump_data(100, (char *)pwd->smb_lm_pwd, sizeof(pwd->smb_lm_pwd)); +#endif + + pwd->crypted = True; +} + +/**************************************************************************** + Gets lm and nt crypts. +****************************************************************************/ + +void pwd_get_lm_nt_owf(struct pwd_info *pwd, uchar lm_owf[24], uchar nt_owf[24]) +{ + if (lm_owf != NULL) + memcpy(lm_owf, pwd->smb_lm_owf, 24); + if (nt_owf != NULL) + memcpy(nt_owf, pwd->smb_nt_owf, 24); +} diff --git a/source3/libsmb/smbdes.c b/source3/libsmb/smbdes.c new file mode 100644 index 0000000000..cde77f94a3 --- /dev/null +++ b/source3/libsmb/smbdes.c @@ -0,0 +1,415 @@ +/* + Unix SMB/CIFS implementation. + + a partial implementation of DES designed for use in the + SMB authentication protocol + + Copyright (C) Andrew Tridgell 1998 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +/* NOTES: + + This code makes no attempt to be fast! In fact, it is a very + slow implementation + + This code is NOT a complete DES implementation. It implements only + the minimum necessary for SMB authentication, as used by all SMB + products (including every copy of Microsoft Windows95 ever sold) + + In particular, it can only do a unchained forward DES pass. This + means it is not possible to use this code for encryption/decryption + of data, instead it is only useful as a "hash" algorithm. + + There is no entry point into this code that allows normal DES operation. + + I believe this means that this code does not come under ITAR + regulations but this is NOT a legal opinion. If you are concerned + about the applicability of ITAR regulations to this code then you + should confirm it for yourself (and maybe let me know if you come + up with a different answer to the one above) +*/ + + +#define uchar unsigned char + +static const uchar perm1[56] = {57, 49, 41, 33, 25, 17, 9, + 1, 58, 50, 42, 34, 26, 18, + 10, 2, 59, 51, 43, 35, 27, + 19, 11, 3, 60, 52, 44, 36, + 63, 55, 47, 39, 31, 23, 15, + 7, 62, 54, 46, 38, 30, 22, + 14, 6, 61, 53, 45, 37, 29, + 21, 13, 5, 28, 20, 12, 4}; + +static const uchar perm2[48] = {14, 17, 11, 24, 1, 5, + 3, 28, 15, 6, 21, 10, + 23, 19, 12, 4, 26, 8, + 16, 7, 27, 20, 13, 2, + 41, 52, 31, 37, 47, 55, + 30, 40, 51, 45, 33, 48, + 44, 49, 39, 56, 34, 53, + 46, 42, 50, 36, 29, 32}; + +static const uchar perm3[64] = {58, 50, 42, 34, 26, 18, 10, 2, + 60, 52, 44, 36, 28, 20, 12, 4, + 62, 54, 46, 38, 30, 22, 14, 6, + 64, 56, 48, 40, 32, 24, 16, 8, + 57, 49, 41, 33, 25, 17, 9, 1, + 59, 51, 43, 35, 27, 19, 11, 3, + 61, 53, 45, 37, 29, 21, 13, 5, + 63, 55, 47, 39, 31, 23, 15, 7}; + +static const uchar perm4[48] = { 32, 1, 2, 3, 4, 5, + 4, 5, 6, 7, 8, 9, + 8, 9, 10, 11, 12, 13, + 12, 13, 14, 15, 16, 17, + 16, 17, 18, 19, 20, 21, + 20, 21, 22, 23, 24, 25, + 24, 25, 26, 27, 28, 29, + 28, 29, 30, 31, 32, 1}; + +static const uchar perm5[32] = { 16, 7, 20, 21, + 29, 12, 28, 17, + 1, 15, 23, 26, + 5, 18, 31, 10, + 2, 8, 24, 14, + 32, 27, 3, 9, + 19, 13, 30, 6, + 22, 11, 4, 25}; + + +static const uchar perm6[64] ={ 40, 8, 48, 16, 56, 24, 64, 32, + 39, 7, 47, 15, 55, 23, 63, 31, + 38, 6, 46, 14, 54, 22, 62, 30, + 37, 5, 45, 13, 53, 21, 61, 29, + 36, 4, 44, 12, 52, 20, 60, 28, + 35, 3, 43, 11, 51, 19, 59, 27, + 34, 2, 42, 10, 50, 18, 58, 26, + 33, 1, 41, 9, 49, 17, 57, 25}; + + +static const uchar sc[16] = {1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1}; + +static const uchar sbox[8][4][16] = { + {{14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7}, + {0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8}, + {4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0}, + {15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13}}, + + {{15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10}, + {3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5}, + {0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15}, + {13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9}}, + + {{10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8}, + {13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1}, + {13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7}, + {1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12}}, + + {{7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15}, + {13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9}, + {10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4}, + {3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14}}, + + {{2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9}, + {14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6}, + {4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14}, + {11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3}}, + + {{12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11}, + {10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8}, + {9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6}, + {4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13}}, + + {{4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1}, + {13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6}, + {1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2}, + {6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12}}, + + {{13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7}, + {1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2}, + {7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8}, + {2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11}}}; + +static void permute(char *out, const char *in, const uchar *p, int n) +{ + int i; + for (i=0;i<n;i++) + out[i] = in[p[i]-1]; +} + +static void lshift(char *d, int count, int n) +{ + char out[64]; + int i; + for (i=0;i<n;i++) + out[i] = d[(i+count)%n]; + for (i=0;i<n;i++) + d[i] = out[i]; +} + +static void concat(char *out, char *in1, char *in2, int l1, int l2) +{ + while (l1--) + *out++ = *in1++; + while (l2--) + *out++ = *in2++; +} + +static void xor(char *out, char *in1, char *in2, int n) +{ + int i; + for (i=0;i<n;i++) + out[i] = in1[i] ^ in2[i]; +} + +static void dohash(char *out, char *in, char *key, int forw) +{ + int i, j, k; + char pk1[56]; + char c[28]; + char d[28]; + char cd[56]; + char ki[16][48]; + char pd1[64]; + char l[32], r[32]; + char rl[64]; + + permute(pk1, key, perm1, 56); + + for (i=0;i<28;i++) + c[i] = pk1[i]; + for (i=0;i<28;i++) + d[i] = pk1[i+28]; + + for (i=0;i<16;i++) { + lshift(c, sc[i], 28); + lshift(d, sc[i], 28); + + concat(cd, c, d, 28, 28); + permute(ki[i], cd, perm2, 48); + } + + permute(pd1, in, perm3, 64); + + for (j=0;j<32;j++) { + l[j] = pd1[j]; + r[j] = pd1[j+32]; + } + + for (i=0;i<16;i++) { + char er[48]; + char erk[48]; + char b[8][6]; + char cb[32]; + char pcb[32]; + char r2[32]; + + permute(er, r, perm4, 48); + + xor(erk, er, ki[forw ? i : 15 - i], 48); + + for (j=0;j<8;j++) + for (k=0;k<6;k++) + b[j][k] = erk[j*6 + k]; + + for (j=0;j<8;j++) { + int m, n; + m = (b[j][0]<<1) | b[j][5]; + + n = (b[j][1]<<3) | (b[j][2]<<2) | (b[j][3]<<1) | b[j][4]; + + for (k=0;k<4;k++) + b[j][k] = (sbox[j][m][n] & (1<<(3-k)))?1:0; + } + + for (j=0;j<8;j++) + for (k=0;k<4;k++) + cb[j*4+k] = b[j][k]; + permute(pcb, cb, perm5, 32); + + xor(r2, l, pcb, 32); + + for (j=0;j<32;j++) + l[j] = r[j]; + + for (j=0;j<32;j++) + r[j] = r2[j]; + } + + concat(rl, r, l, 32, 32); + + permute(out, rl, perm6, 64); +} + +static void str_to_key(const unsigned char *str,unsigned char *key) +{ + int i; + + key[0] = str[0]>>1; + key[1] = ((str[0]&0x01)<<6) | (str[1]>>2); + key[2] = ((str[1]&0x03)<<5) | (str[2]>>3); + key[3] = ((str[2]&0x07)<<4) | (str[3]>>4); + key[4] = ((str[3]&0x0F)<<3) | (str[4]>>5); + key[5] = ((str[4]&0x1F)<<2) | (str[5]>>6); + key[6] = ((str[5]&0x3F)<<1) | (str[6]>>7); + key[7] = str[6]&0x7F; + for (i=0;i<8;i++) { + key[i] = (key[i]<<1); + } +} + + +static void smbhash(unsigned char *out, const unsigned char *in, const unsigned char *key, int forw) +{ + int i; + char outb[64]; + char inb[64]; + char keyb[64]; + unsigned char key2[8]; + + str_to_key(key, key2); + + for (i=0;i<64;i++) { + inb[i] = (in[i/8] & (1<<(7-(i%8)))) ? 1 : 0; + keyb[i] = (key2[i/8] & (1<<(7-(i%8)))) ? 1 : 0; + outb[i] = 0; + } + + dohash(outb, inb, keyb, forw); + + for (i=0;i<8;i++) { + out[i] = 0; + } + + for (i=0;i<64;i++) { + if (outb[i]) + out[i/8] |= (1<<(7-(i%8))); + } +} + +void E_P16(const unsigned char *p14,unsigned char *p16) +{ + unsigned char sp8[8] = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25}; + smbhash(p16, sp8, p14, 1); + smbhash(p16+8, sp8, p14+7, 1); +} + +void E_P24(const unsigned char *p21, const unsigned char *c8, unsigned char *p24) +{ + smbhash(p24, c8, p21, 1); + smbhash(p24+8, c8, p21+7, 1); + smbhash(p24+16, c8, p21+14, 1); +} + +void D_P16(const unsigned char *p14, const unsigned char *in, unsigned char *out) +{ + smbhash(out, in, p14, 0); + smbhash(out+8, in+8, p14+7, 0); +} + +void E_old_pw_hash( unsigned char *p14, const unsigned char *in, unsigned char *out) +{ + smbhash(out, in, p14, 1); + smbhash(out+8, in+8, p14+7, 1); +} + +void cred_hash1(unsigned char *out, const unsigned char *in, const unsigned char *key) +{ + unsigned char buf[8]; + + smbhash(buf, in, key, 1); + smbhash(out, buf, key+9, 1); +} + +void cred_hash2(unsigned char *out, const unsigned char *in, const unsigned char *key) +{ + unsigned char buf[8]; + static unsigned char key2[8]; + + smbhash(buf, in, key, 1); + key2[0] = key[7]; + smbhash(out, buf, key2, 1); +} + +void cred_hash3(unsigned char *out, unsigned char *in, const unsigned char *key, int forw) +{ + static unsigned char key2[8]; + + smbhash(out, in, key, forw); + key2[0] = key[7]; + smbhash(out + 8, in + 8, key2, forw); +} + +void SamOEMhash( unsigned char *data, const unsigned char *key, int val) +{ + unsigned char s_box[256]; + unsigned char index_i = 0; + unsigned char index_j = 0; + unsigned char j = 0; + int ind; + + for (ind = 0; ind < 256; ind++) + { + s_box[ind] = (unsigned char)ind; + } + + for( ind = 0; ind < 256; ind++) + { + unsigned char tc; + + j += (s_box[ind] + key[ind%16]); + + tc = s_box[ind]; + s_box[ind] = s_box[j]; + s_box[j] = tc; + } + for( ind = 0; ind < val; ind++) + { + unsigned char tc; + unsigned char t; + + index_i++; + index_j += s_box[index_i]; + + tc = s_box[index_i]; + s_box[index_i] = s_box[index_j]; + s_box[index_j] = tc; + + t = s_box[index_i] + s_box[index_j]; + data[ind] = data[ind] ^ s_box[t]; + } +} + +/* Decode a sam password hash into a password. The password hash is the + same method used to store passwords in the NT registry. The DES key + used is based on the RID of the user. */ + +void sam_pwd_hash(unsigned int rid, const uchar *in, uchar *out, int forw) +{ + uchar s[14]; + + s[0] = s[4] = s[8] = s[12] = (uchar)(rid & 0xFF); + s[1] = s[5] = s[9] = s[13] = (uchar)((rid >> 8) & 0xFF); + s[2] = s[6] = s[10] = (uchar)((rid >> 16) & 0xFF); + s[3] = s[7] = s[11] = (uchar)((rid >> 24) & 0xFF); + + smbhash(out, in, s, forw); + smbhash(out+8, in+8, s+7, forw); +} diff --git a/source3/libsmb/smbencrypt.c b/source3/libsmb/smbencrypt.c index a0683b5d28..6fa8de418a 100644 --- a/source3/libsmb/smbencrypt.c +++ b/source3/libsmb/smbencrypt.c @@ -1,10 +1,10 @@ -#ifdef SMB_PASSWD /* - Unix SMB/Netbios implementation. - Version 1.9. + Unix SMB/CIFS implementation. SMB parameters and setup - Copyright (C) Andrew Tridgell 1992-1995 + Copyright (C) Andrew Tridgell 1992-1998 Modified by Jeremy Allison 1995. + Copyright (C) Jeremy Allison 1995-2000. + Copyright (C) Luke Kennethc Casson Leighton 1996-2000. 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 @@ -22,181 +22,322 @@ */ #include "includes.h" -#include "loadparm.h" -#include "des.h" -#include "md4.h" - -extern int DEBUGLEVEL; - -#ifndef uchar -#define uchar unsigned char -#endif -#ifndef int16 -#define int16 unsigned short -#endif -#ifndef uint16 -#define uint16 unsigned short -#endif -#ifndef uint32 -#define uint32 unsigned int -#endif - #include "byteorder.h" -void str_to_key(uchar *str,uchar *key) +/* + This implements the X/Open SMB password encryption + It takes a password, a 8 byte "crypt key" and puts 24 bytes of + encrypted password into p24 */ +void SMBencrypt(const uchar *passwd, const uchar *c8, uchar *p24) { - void des_set_odd_parity(des_cblock *); - int i; - - key[0] = str[0]>>1; - key[1] = ((str[0]&0x01)<<6) | (str[1]>>2); - key[2] = ((str[1]&0x03)<<5) | (str[2]>>3); - key[3] = ((str[2]&0x07)<<4) | (str[3]>>4); - key[4] = ((str[3]&0x0F)<<3) | (str[4]>>5); - key[5] = ((str[4]&0x1F)<<2) | (str[5]>>6); - key[6] = ((str[5]&0x3F)<<1) | (str[6]>>7); - key[7] = str[6]&0x7F; - for (i=0;i<8;i++) { - key[i] = (key[i]<<1); - } - des_set_odd_parity((des_cblock *)key); -} + uchar p14[15], p21[21]; -void D1(uchar *k, uchar *d, uchar *out) -{ - des_key_schedule ks; - des_cblock deskey; + memset(p21,'\0',21); + memset(p14,'\0',14); + StrnCpy((char *)p14,(const char *)passwd,14); - str_to_key(k,(uchar *)deskey); - des_set_key(deskey,ks); - des_ecb_encrypt(d, out, ks, DES_DECRYPT); -} + strupper((char *)p14); + E_P16(p14, p21); -void E1(uchar *k, uchar *d, uchar *out) -{ - des_key_schedule ks; - des_cblock deskey; + SMBOWFencrypt(p21, c8, p24); - str_to_key(k,(uchar *)deskey); - des_set_key(deskey,ks); - des_ecb_encrypt(d, out, ks, DES_ENCRYPT); +#ifdef DEBUG_PASSWORD + DEBUG(100,("SMBencrypt: lm#, challenge, response\n")); + dump_data(100, (char *)p21, 16); + dump_data(100, (const char *)c8, 8); + dump_data(100, (char *)p24, 24); +#endif } + +/* + * Creates the MD4 Hash of the users password in NT UNICODE. + */ -void E_P16(uchar *p14,uchar *p16) +void E_md4hash(const uchar *passwd, uchar *p16) { - uchar sp7[7]; - /* the following constant makes us compatible with other - implementations. Note that publishing this constant does not reduce the - security of the encryption mechanism */ - uchar sp8[] = {0xAA,0xD3,0xB4,0x35,0xB5,0x14,0x4,0xEE}; - uchar x[8]; - - memset(sp7,'\0',7); - - D1(sp7, sp8, x); - E1(p14, x, p16); - E1(p14+7, x, p16+8); + int len; + smb_ucs2_t wpwd[129]; + + /* Password cannot be longer than 128 characters */ + len = strlen((const char *)passwd); + if(len > 128) + len = 128; + /* Password must be converted to NT unicode - null terminated. */ + push_ucs2(NULL, wpwd, (const char *)passwd, 256, STR_UNICODE|STR_NOALIGN|STR_TERMINATE); + /* Calculate length in bytes */ + len = strlen_w(wpwd) * sizeof(int16); + + mdfour(p16, (unsigned char *)wpwd, len); } -void E_P24(uchar *p21, uchar *c8, uchar *p24) +/* Does both the NT and LM owfs of a user's password */ +void nt_lm_owf_gen(const char *pwd, uchar nt_p16[16], uchar p16[16]) { - E1(p21, c8, p24); - E1(p21+7, c8, p24+8); - E1(p21+14, c8, p24+16); -} + char passwd[514]; + memset(passwd,'\0',514); + safe_strcpy( passwd, pwd, sizeof(passwd)-1); -/* - This implements the X/Open SMB password encryption - It takes a password, a 8 byte "crypt key" and puts 24 bytes of - encrypted password into p24 */ -void SMBencrypt(uchar *passwd, uchar *c8, uchar *p24) -{ - uchar p14[15], p21[21]; + /* Calculate the MD4 hash (NT compatible) of the password */ + memset(nt_p16, '\0', 16); + E_md4hash((uchar *)passwd, nt_p16); + +#ifdef DEBUG_PASSWORD + DEBUG(100,("nt_lm_owf_gen: pwd, nt#\n")); + dump_data(120, passwd, strlen(passwd)); + dump_data(100, (char *)nt_p16, 16); +#endif - memset(p21,'\0',21); - memset(p14,'\0',14); - StrnCpy((char *)p14,(char *)passwd,14); + /* Mangle the passwords into Lanman format */ + passwd[14] = '\0'; + strupper(passwd); - strupper((char *)p14); - E_P16(p14, p21); - E_P24(p21, c8, p24); + /* Calculate the SMB (lanman) hash functions of the password */ + + memset(p16, '\0', 16); + E_P16((uchar *) passwd, (uchar *)p16); + +#ifdef DEBUG_PASSWORD + DEBUG(100,("nt_lm_owf_gen: pwd, lm#\n")); + dump_data(120, passwd, strlen(passwd)); + dump_data(100, (char *)p16, 16); +#endif + /* clear out local copy of user's password (just being paranoid). */ + memset(passwd, '\0', sizeof(passwd)); } -/* Routines for Windows NT MD4 Hash functions. */ -static int _my_wcslen(int16 *str) +/* Does both the NTLMv2 owfs of a user's password */ +void ntv2_owf_gen(const uchar owf[16], + const char *user_n, const char *domain_n, uchar kr_buf[16]) { - int len = 0; - while(*str++ != 0) - len++; - return len; + pstring user_u; + pstring dom_u; + HMACMD5Context ctx; + + int user_l = strlen(user_n); + int domain_l = strlen(domain_n); + + push_ucs2(NULL, user_u, user_n, (user_l+1)*2, STR_UNICODE|STR_NOALIGN|STR_TERMINATE|STR_UPPER); + push_ucs2(NULL, dom_u, domain_n, (domain_l+1)*2, STR_UNICODE|STR_NOALIGN|STR_TERMINATE|STR_UPPER); + + hmac_md5_init_limK_to_64(owf, 16, &ctx); + hmac_md5_update((const unsigned char *)user_u, user_l * 2, &ctx); + hmac_md5_update((const unsigned char *)dom_u, domain_l * 2, &ctx); + hmac_md5_final(kr_buf, &ctx); + +#ifdef DEBUG_PASSWORD + DEBUG(100, ("ntv2_owf_gen: user, domain, owfkey, kr\n")); + dump_data(100, user_u, user_l * 2); + dump_data(100, dom_u, domain_l * 2); + dump_data(100, owf, 16); + dump_data(100, kr_buf, 16); +#endif } -/* - * Convert a string into an NT UNICODE string. - * Note that regardless of processor type - * this must be in intel (little-endian) - * format. - */ - -static int _my_mbstowcs(int16 *dst, uchar *src, int len) +/* Does the des encryption from the NT or LM MD4 hash. */ +void SMBOWFencrypt(const uchar passwd[16], const uchar *c8, uchar p24[24]) { - int i; - int16 val; + uchar p21[21]; - for(i = 0; i < len; i++) { - val = *src; - SSVAL(dst,0,val); - dst++; - src++; - if(val == 0) - break; - } - return i; + memset(p21,'\0',21); + + memcpy(p21, passwd, 16); + E_P24(p21, c8, p24); } -/* - * Creates the MD4 Hash of the users password in NT UNICODE. - */ - -void E_md4hash(uchar *passwd, uchar *p16) +/* Does the des encryption from the FIRST 8 BYTES of the NT or LM MD4 hash. */ +void NTLMSSPOWFencrypt(const uchar passwd[8], const uchar *ntlmchalresp, uchar p24[24]) { - int i, len; - int16 wpwd[129]; - MDstruct MD; - - /* Password cannot be longer than 128 characters */ - len = strlen(passwd); - if(len > 128) - len = 128; - /* Password must be converted to NT unicode */ - _my_mbstowcs( wpwd, passwd, len); - wpwd[len] = 0; /* Ensure string is null terminated */ - /* Calculate length in bytes */ - len = _my_wcslen(wpwd) * sizeof(int16); + uchar p21[21]; - MDbegin(&MD); - for(i = 0; i + 64 <= len; i += 64) - MDupdate(&MD,wpwd + (i/2), 512); - MDupdate(&MD,wpwd + (i/2),(len-i)*8); - SIVAL(p16,0,MD.buffer[0]); - SIVAL(p16,4,MD.buffer[1]); - SIVAL(p16,8,MD.buffer[2]); - SIVAL(p16,12,MD.buffer[3]); + memset(p21,'\0',21); + memcpy(p21, passwd, 8); + memset(p21 + 8, 0xbd, 8); + + E_P24(p21, ntlmchalresp, p24); +#ifdef DEBUG_PASSWORD + DEBUG(100,("NTLMSSPOWFencrypt: p21, c8, p24\n")); + dump_data(100, (char *)p21, 21); + dump_data(100, (const char *)ntlmchalresp, 8); + dump_data(100, (char *)p24, 24); +#endif } + /* Does the NT MD4 hash then des encryption. */ -void SMBNTencrypt(uchar *passwd, uchar *c8, uchar *p24) +void SMBNTencrypt(const uchar *passwd, uchar *c8, uchar *p24) { uchar p21[21]; memset(p21,'\0',21); E_md4hash(passwd, p21); - E_P24(p21, c8, p24); + SMBOWFencrypt(p21, c8, p24); + +#ifdef DEBUG_PASSWORD + DEBUG(100,("SMBNTencrypt: nt#, challenge, response\n")); + dump_data(100, (char *)p21, 16); + dump_data(100, (char *)c8, 8); + dump_data(100, (char *)p24, 24); +#endif +} + +BOOL make_oem_passwd_hash(char data[516], const char *passwd, uchar old_pw_hash[16], BOOL unicode) +{ + int new_pw_len = strlen(passwd) * (unicode ? 2 : 1); + + if (new_pw_len > 512) + { + DEBUG(0,("make_oem_passwd_hash: new password is too long.\n")); + return False; + } + + /* + * Now setup the data area. + * We need to generate a random fill + * for this area to make it harder to + * decrypt. JRA. + */ + generate_random_buffer((unsigned char *)data, 516, False); + push_string(NULL, &data[512 - new_pw_len], passwd, new_pw_len, + STR_NOALIGN | (unicode?STR_UNICODE:STR_ASCII)); + SIVAL(data, 512, new_pw_len); + +#ifdef DEBUG_PASSWORD + DEBUG(100,("make_oem_passwd_hash\n")); + dump_data(100, data, 516); +#endif + SamOEMhash( (unsigned char *)data, (unsigned char *)old_pw_hash, 516); + + return True; +} + +/* Does the md5 encryption from the NT hash for NTLMv2. */ +void SMBOWFencrypt_ntv2(const uchar kr[16], + const DATA_BLOB srv_chal, + const DATA_BLOB cli_chal, + char resp_buf[16]) +{ + HMACMD5Context ctx; + + hmac_md5_init_limK_to_64(kr, 16, &ctx); + hmac_md5_update(srv_chal.data, srv_chal.length, &ctx); + hmac_md5_update(cli_chal.data, cli_chal.length, &ctx); + hmac_md5_final((unsigned char *)resp_buf, &ctx); + +#ifdef DEBUG_PASSWORD + DEBUG(100, ("SMBOWFencrypt_ntv2: srv_chal, cli_chal, resp_buf\n")); + dump_data(100, srv_chal.data, srv_chal.length); + dump_data(100, cli_chal.data, cli_chal.length); + dump_data(100, resp_buf, 16); +#endif +} + +void SMBsesskeygen_ntv2(const uchar kr[16], + const uchar * nt_resp, uint8 sess_key[16]) +{ + HMACMD5Context ctx; + + hmac_md5_init_limK_to_64(kr, 16, &ctx); + hmac_md5_update(nt_resp, 16, &ctx); + hmac_md5_final((unsigned char *)sess_key, &ctx); + +#ifdef DEBUG_PASSWORD + DEBUG(100, ("SMBsesskeygen_ntv2:\n")); + dump_data(100, sess_key, 16); +#endif +} + +void SMBsesskeygen_ntv1(const uchar kr[16], + const uchar * nt_resp, uint8 sess_key[16]) +{ + mdfour((unsigned char *)sess_key, kr, 16); + +#ifdef DEBUG_PASSWORD + DEBUG(100, ("SMBsesskeygen_ntv1:\n")); + dump_data(100, sess_key, 16); +#endif } -#else -void smbencrypt_dummy(void){} +/*********************************************************** + encode a password buffer. The caller gets to figure out + what to put in it. +************************************************************/ +BOOL encode_pw_buffer(char buffer[516], char *new_pw, int new_pw_length) +{ + generate_random_buffer((unsigned char *)buffer, 516, True); + + memcpy(&buffer[512 - new_pw_length], new_pw, new_pw_length); + + /* + * The length of the new password is in the last 4 bytes of + * the data buffer. + */ + SIVAL(buffer, 512, new_pw_length); + + return True; +} + +/*********************************************************** + decode a password buffer + *new_pw_len is the length in bytes of the possibly mulitbyte + returned password including termination. +************************************************************/ +BOOL decode_pw_buffer(char in_buffer[516], char *new_pwrd, + int new_pwrd_size, uint32 *new_pw_len) +{ + int byte_len=0; + + /* + Warning !!! : This function is called from some rpc call. + The password IN the buffer is a UNICODE string. + The password IN new_pwrd is an ASCII string + If you reuse that code somewhere else check first. + */ + + /* The length of the new password is in the last 4 bytes of the data buffer. */ + + byte_len = IVAL(in_buffer, 512); + +#ifdef DEBUG_PASSWORD + dump_data(100, in_buffer, 516); #endif + + /* Password cannot be longer than 128 characters */ + if ( (byte_len < 0) || (byte_len > new_pwrd_size - 1)) { + DEBUG(0, ("decode_pw_buffer: incorrect password length (%d).\n", byte_len)); + DEBUG(0, ("decode_pw_buffer: check that 'encrypt passwords = yes'\n")); + return False; + } + + /* decode into the return buffer. Buffer must be a pstring */ + *new_pw_len = pull_string(NULL, new_pwrd, &in_buffer[512 - byte_len], new_pwrd_size, byte_len, STR_UNICODE); + +#ifdef DEBUG_PASSWORD + DEBUG(100,("decode_pw_buffer: new_pwrd: ")); + dump_data(100, (char *)new_pwrd, *new_pw_len); + DEBUG(100,("multibyte len:%d\n", *new_pw_len)); + DEBUG(100,("original char len:%d\n", byte_len/2)); +#endif + + return True; + +} + +/* Calculate the NT owfs of a user's password */ +void nt_owf_genW(const UNISTR2 *pwd, uchar nt_p16[16]) +{ + char buf[512]; + int i; + + for (i = 0; i < MIN(pwd->uni_str_len, sizeof(buf) / 2); i++) + { + SIVAL(buf, i * 2, pwd->buffer[i]); + } + /* Calculate the MD4 hash (NT compatible) of the password */ + mdfour(nt_p16, (const unsigned char *)buf, pwd->uni_str_len * 2); + + /* clear out local copy of user's password (just being paranoid). */ + ZERO_STRUCT(buf); +} diff --git a/source3/libsmb/smberr.c b/source3/libsmb/smberr.c new file mode 100644 index 0000000000..84b3f507e6 --- /dev/null +++ b/source3/libsmb/smberr.c @@ -0,0 +1,255 @@ +/* + Unix SMB/CIFS implementation. + Copyright (C) Andrew Tridgell 1998 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#define NO_SYSLOG + +#include "includes.h" + +/* error code stuff - put together by Merik Karman + merik@blackadder.dsh.oz.au */ + + +/* There is a big list of error codes and their meanings at: + + http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/errlist_7oz7.asp + + and if you don't like MSDN try: + + http://www.siris.gr/computers/library/error.htm + +*/ + +typedef const struct +{ + char *name; + int code; + char *message; +} err_code_struct; + +/* Dos Error Messages */ +err_code_struct dos_msgs[] = { + {"ERRbadfunc",ERRbadfunc,"Invalid function."}, + {"ERRbadfile",ERRbadfile,"File not found."}, + {"ERRbadpath",ERRbadpath,"Directory invalid."}, + {"ERRnofids",ERRnofids,"No file descriptors available"}, + {"ERRnoaccess",ERRnoaccess,"Access denied."}, + {"ERRbadfid",ERRbadfid,"Invalid file handle."}, + {"ERRbadmcb",ERRbadmcb,"Memory control blocks destroyed."}, + {"ERRnomem",ERRnomem,"Insufficient server memory to perform the requested function."}, + {"ERRbadmem",ERRbadmem,"Invalid memory block address."}, + {"ERRbadenv",ERRbadenv,"Invalid environment."}, + {"ERRbadformat",11,"Invalid format."}, + {"ERRbadaccess",ERRbadaccess,"Invalid open mode."}, + {"ERRbaddata",ERRbaddata,"Invalid data."}, + {"ERRres",ERRres,"reserved."}, + {"ERRbaddrive",ERRbaddrive,"Invalid drive specified."}, + {"ERRremcd",ERRremcd,"A Delete Directory request attempted to remove the server's current directory."}, + {"ERRdiffdevice",ERRdiffdevice,"Not same device."}, + {"ERRnofiles",ERRnofiles,"A File Search command can find no more files matching the specified criteria."}, + {"ERRbadshare",ERRbadshare,"The sharing mode specified for an Open conflicts with existing FIDs on the file."}, + {"ERRlock",ERRlock,"A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process."}, + {"ERRunsup", ERRunsup, "The operation is unsupported"}, + {"ERRnosuchshare", ERRnosuchshare, "You specified an invalid share name"}, + {"ERRfilexists",ERRfilexists,"The file named in a Create Directory, Make New File or Link request already exists."}, + {"ERRinvalidname",ERRinvalidname, "Invalid name"}, + {"ERRbadpipe",ERRbadpipe,"Pipe invalid."}, + {"ERRpipebusy",ERRpipebusy,"All instances of the requested pipe are busy."}, + {"ERRpipeclosing",ERRpipeclosing,"Pipe close in progress."}, + {"ERRnotconnected",ERRnotconnected,"No process on other end of pipe."}, + {"ERRmoredata",ERRmoredata,"There is more data to be returned."}, + {"ERRinvgroup",ERRinvgroup,"Invalid workgroup (try the -W option)"}, + {"ERRlogonfailure",ERRlogonfailure,"Logon failure"}, + {"ERRdiskfull",ERRdiskfull,"Disk full"}, + {"ERRgeneral",ERRgeneral, "General failure"}, + {NULL,-1,NULL}}; + +/* Server Error Messages */ +err_code_struct server_msgs[] = { + {"ERRerror",1,"Non-specific error code."}, + {"ERRbadpw",2,"Bad password - name/password pair in a Tree Connect or Session Setup are invalid."}, + {"ERRbadtype",3,"reserved."}, + {"ERRaccess",4,"The requester does not have the necessary access rights within the specified context for the requested function. The context is defined by the TID or the UID."}, + {"ERRinvnid",5,"The tree ID (TID) specified in a command was invalid."}, + {"ERRinvnetname",6,"Invalid network name in tree connect."}, + {"ERRinvdevice",7,"Invalid device - printer request made to non-printer connection or non-printer request made to printer connection."}, + {"ERRqfull",49,"Print queue full (files) -- returned by open print file."}, + {"ERRqtoobig",50,"Print queue full -- no space."}, + {"ERRqeof",51,"EOF on print queue dump."}, + {"ERRinvpfid",52,"Invalid print file FID."}, + {"ERRsmbcmd",64,"The server did not recognize the command received."}, + {"ERRsrverror",65,"The server encountered an internal error, e.g., system file unavailable."}, + {"ERRfilespecs",67,"The file handle (FID) and pathname parameters contained an invalid combination of values."}, + {"ERRreserved",68,"reserved."}, + {"ERRbadpermits",69,"The access permissions specified for a file or directory are not a valid combination. The server cannot set the requested attribute."}, + {"ERRreserved",70,"reserved."}, + {"ERRsetattrmode",71,"The attribute mode in the Set File Attribute request is invalid."}, + {"ERRpaused",81,"Server is paused."}, + {"ERRmsgoff",82,"Not receiving messages."}, + {"ERRnoroom",83,"No room to buffer message."}, + {"ERRrmuns",87,"Too many remote user names."}, + {"ERRtimeout",88,"Operation timed out."}, + {"ERRnoresource",89,"No resources currently available for request."}, + {"ERRtoomanyuids",90,"Too many UIDs active on this session."}, + {"ERRbaduid",91,"The UID is not known as a valid ID on this session."}, + {"ERRusempx",250,"Temp unable to support Raw, use MPX mode."}, + {"ERRusestd",251,"Temp unable to support Raw, use standard read/write."}, + {"ERRcontmpx",252,"Continue in MPX mode."}, + {"ERRreserved",253,"reserved."}, + {"ERRreserved",254,"reserved."}, + {"ERRnosupport",0xFFFF,"Function not supported."}, + {NULL,-1,NULL}}; + +/* Hard Error Messages */ +err_code_struct hard_msgs[] = { + {"ERRnowrite",19,"Attempt to write on write-protected diskette."}, + {"ERRbadunit",20,"Unknown unit."}, + {"ERRnotready",21,"Drive not ready."}, + {"ERRbadcmd",22,"Unknown command."}, + {"ERRdata",23,"Data error (CRC)."}, + {"ERRbadreq",24,"Bad request structure length."}, + {"ERRseek",25 ,"Seek error."}, + {"ERRbadmedia",26,"Unknown media type."}, + {"ERRbadsector",27,"Sector not found."}, + {"ERRnopaper",28,"Printer out of paper."}, + {"ERRwrite",29,"Write fault."}, + {"ERRread",30,"Read fault."}, + {"ERRgeneral",31,"General failure."}, + {"ERRbadshare",32,"An open conflicts with an existing open."}, + {"ERRlock",33,"A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process."}, + {"ERRwrongdisk",34,"The wrong disk was found in a drive."}, + {"ERRFCBUnavail",35,"No FCBs are available to process request."}, + {"ERRsharebufexc",36,"A sharing buffer has been exceeded."}, + {NULL,-1,NULL}}; + + +const struct +{ + int code; + char *class; + err_code_struct *err_msgs; +} err_classes[] = { + {0,"SUCCESS",NULL}, + {0x01,"ERRDOS",dos_msgs}, + {0x02,"ERRSRV",server_msgs}, + {0x03,"ERRHRD",hard_msgs}, + {0x04,"ERRXOS",NULL}, + {0xE1,"ERRRMX1",NULL}, + {0xE2,"ERRRMX2",NULL}, + {0xE3,"ERRRMX3",NULL}, + {0xFF,"ERRCMD",NULL}, + {-1,NULL,NULL}}; + + +/**************************************************************************** +return a SMB error name from a class and code +****************************************************************************/ +char *smb_dos_err_name(uint8 class, uint16 num) +{ + static pstring ret; + int i,j; + + for (i=0;err_classes[i].class;i++) + if (err_classes[i].code == class) { + if (err_classes[i].err_msgs) { + err_code_struct *err = err_classes[i].err_msgs; + for (j=0;err[j].name;j++) + if (num == err[j].code) { + return err[j].name; + } + } + slprintf(ret, sizeof(ret) - 1, "%d",num); + return ret; + } + + slprintf(ret, sizeof(ret) - 1, "Error: Unknown error class (%d,%d)",class,num); + return(ret); +} + +/* Return a string for a DOS error */ + +char *get_dos_error_msg(WERROR result) +{ + uint16 errnum; + + errnum = W_ERROR_V(result); + + return smb_dos_err_name(ERRDOS, errnum); +} + +/**************************************************************************** +return a SMB error class name as a string. +****************************************************************************/ +char *smb_dos_err_class(uint8 class) +{ + static pstring ret; + int i; + + for (i=0;err_classes[i].class;i++) { + if (err_classes[i].code == class) { + return err_classes[i].class; + } + } + + slprintf(ret, sizeof(ret) - 1, "Error: Unknown class (%d)",class); + return(ret); +} + +/**************************************************************************** +return a SMB string from an SMB buffer +****************************************************************************/ +char *smb_dos_errstr(char *inbuf) +{ + static pstring ret; + int class = CVAL(inbuf,smb_rcls); + int num = SVAL(inbuf,smb_err); + int i,j; + + for (i=0;err_classes[i].class;i++) + if (err_classes[i].code == class) { + if (err_classes[i].err_msgs) { + err_code_struct *err = err_classes[i].err_msgs; + for (j=0;err[j].name;j++) + if (num == err[j].code) { + if (DEBUGLEVEL > 0) + slprintf(ret, sizeof(ret) - 1, "%s - %s (%s)", + err_classes[i].class, + err[j].name,err[j].message); + else + slprintf(ret, sizeof(ret) - 1, "%s - %s", + err_classes[i].class,err[j].name); + return ret; + } + } + + slprintf(ret, sizeof(ret) - 1, "%s - %d",err_classes[i].class,num); + return ret; + } + + slprintf(ret, sizeof(ret) - 1, "Error: Unknown error (%d,%d)",class,num); + return(ret); +} + +/***************************************************************************** +map a unix errno to a win32 error + *****************************************************************************/ +WERROR map_werror_from_unix(int error) +{ + NTSTATUS status = map_nt_error_from_unix(error); + return ntstatus_to_werror(status); +} diff --git a/source3/libsmb/trust_passwd.c b/source3/libsmb/trust_passwd.c new file mode 100644 index 0000000000..51ffa1dd95 --- /dev/null +++ b/source3/libsmb/trust_passwd.c @@ -0,0 +1,115 @@ +/* + * Unix SMB/CIFS implementation. + * Routines to change trust account passwords. + * Copyright (C) Andrew Bartlett 2001. + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "includes.h" + +extern pstring global_myname; + +/********************************************************* + Change the domain password on the PDC. + + Just changes the password betwen the two values specified. + + Caller must have the cli connected to the netlogon pipe + already. +**********************************************************/ +static NTSTATUS just_change_the_password(struct cli_state *cli, TALLOC_CTX *mem_ctx, + unsigned char orig_trust_passwd_hash[16], + unsigned char new_trust_passwd_hash[16]) +{ + NTSTATUS result; + result = new_cli_nt_setup_creds(cli, (lp_server_role() == ROLE_DOMAIN_MEMBER) ? + SEC_CHAN_WKSTA : SEC_CHAN_BDC, orig_trust_passwd_hash); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(0,("just_change_the_password: unable to setup creds (%s)!\n", + nt_errstr(result))); + return result; + } + + result = cli_net_srv_pwset(cli, mem_ctx, global_myname, new_trust_passwd_hash); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(0,("just_change_the_password: unable to change password (%s)!\n", + nt_errstr(result))); + } + return result; +} + +/********************************************************* + Change the domain password on the PDC. + Store the password ourselves, but use the supplied password + Caller must have already setup the connection to the NETLOGON pipe +**********************************************************/ + +NTSTATUS trust_pw_change_and_store_it(struct cli_state *cli, TALLOC_CTX *mem_ctx, + unsigned char orig_trust_passwd_hash[16]) +{ + unsigned char new_trust_passwd_hash[16]; + char *new_trust_passwd; + char *str; + NTSTATUS nt_status; + + /* Create a random machine account password */ + str = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH); + new_trust_passwd = talloc_strdup(mem_ctx, str); + + E_md4hash((uchar *)new_trust_passwd, new_trust_passwd_hash); + + nt_status = just_change_the_password(cli, mem_ctx, orig_trust_passwd_hash, + new_trust_passwd_hash); + + if (NT_STATUS_IS_OK(nt_status)) { + DEBUG(3,("%s : change_trust_account_password: Changed password.\n", timestring(False))); + /* + * Return the result of trying to write the new password + * back into the trust account file. + */ + if (!secrets_store_machine_password(new_trust_passwd)) { + nt_status = NT_STATUS_UNSUCCESSFUL; + } + } + + return nt_status; +} + +/********************************************************* + Change the domain password on the PDC. + Do most of the legwork ourselfs. Caller must have + already setup the connection to the NETLOGON pipe +**********************************************************/ + +NTSTATUS trust_pw_find_change_and_store_it(struct cli_state *cli, TALLOC_CTX *mem_ctx, char *domain) +{ + unsigned char old_trust_passwd_hash[16]; + char *up_domain; + + up_domain = talloc_strdup(mem_ctx, domain); + + if (!secrets_fetch_trust_account_password(domain, + old_trust_passwd_hash, + NULL)) { + DEBUG(0, ("could not fetch domain secrets for domain %s!\n", domain)); + return NT_STATUS_UNSUCCESSFUL; + } + + return trust_pw_change_and_store_it(cli, mem_ctx, old_trust_passwd_hash); + +} diff --git a/source3/libsmb/unexpected.c b/source3/libsmb/unexpected.c new file mode 100644 index 0000000000..f74a05f75f --- /dev/null +++ b/source3/libsmb/unexpected.c @@ -0,0 +1,164 @@ +/* + Unix SMB/CIFS implementation. + handle unexpected packets + Copyright (C) Andrew Tridgell 2000 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +static TDB_CONTEXT *tdbd = NULL; + +/* the key type used in the unexpeceted packet database */ +struct unexpected_key { + enum packet_type packet_type; + time_t timestamp; + int count; +}; + + + +/**************************************************************************** + all unexpected packets are passed in here, to be stored in a unexpected + packet database. This allows nmblookup and other tools to receive packets + erroneoously sent to the wrong port by broken MS systems + **************************************************************************/ +void unexpected_packet(struct packet_struct *p) +{ + static int count; + TDB_DATA kbuf, dbuf; + struct unexpected_key key; + char buf[1024]; + int len=0; + + if (!tdbd) { + tdbd = tdb_open_log(lock_path("unexpected.tdb"), 1, + TDB_CLEAR_IF_FIRST|TDB_DEFAULT, + O_RDWR | O_CREAT, 0644); + if (!tdbd) { + DEBUG(0,("Failed to open unexpected.tdb\n")); + return; + } + } + + memset(buf,'\0',sizeof(buf)); + + len = build_packet(buf, p); + + key.packet_type = p->packet_type; + key.timestamp = p->timestamp; + key.count = count++; + + kbuf.dptr = (char *)&key; + kbuf.dsize = sizeof(key); + dbuf.dptr = buf; + dbuf.dsize = len; + + tdb_store(tdbd, kbuf, dbuf, TDB_REPLACE); +} + + +static time_t lastt; + +/**************************************************************************** +delete the record if it is too old + **************************************************************************/ +static int traverse_fn(TDB_CONTEXT *ttdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state) +{ + struct unexpected_key key; + + memcpy(&key, kbuf.dptr, sizeof(key)); + + if (lastt - key.timestamp > NMBD_UNEXPECTED_TIMEOUT) { + tdb_delete(ttdb, kbuf); + } + + return 0; +} + + +/**************************************************************************** +delete all old unexpected packets + **************************************************************************/ +void clear_unexpected(time_t t) +{ + if (!tdbd) return; + + if ((lastt != 0) && (t < lastt + NMBD_UNEXPECTED_TIMEOUT)) + return; + + lastt = t; + + tdb_traverse(tdbd, traverse_fn, NULL); +} + + +static struct packet_struct *matched_packet; +static int match_id; +static enum packet_type match_type; +static char *match_name; + +/**************************************************************************** +tdb traversal fn to find a matching 137 packet + **************************************************************************/ +static int traverse_match(TDB_CONTEXT *ttdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state) +{ + struct unexpected_key key; + struct packet_struct *p; + + memcpy(&key, kbuf.dptr, sizeof(key)); + + if (key.packet_type != match_type) return 0; + + p = parse_packet(dbuf.dptr, dbuf.dsize, match_type); + + if ((match_type == NMB_PACKET && + p->packet.nmb.header.name_trn_id == match_id) || + (match_type == DGRAM_PACKET && + match_mailslot_name(p, match_name))) { + matched_packet = p; + return -1; + } + + free_packet(p); + + return 0; +} + + +/**************************************************************************** +check for a particular packet in the unexpected packet queue + **************************************************************************/ +struct packet_struct *receive_unexpected(enum packet_type packet_type, int id, + char *mailslot_name) +{ + TDB_CONTEXT *tdb2; + + tdb2 = tdb_open_log(lock_path("unexpected.tdb"), 0, 0, O_RDONLY, 0); + if (!tdb2) return NULL; + + matched_packet = NULL; + match_id = id; + match_type = packet_type; + match_name = mailslot_name; + + tdb_traverse(tdb2, traverse_match, NULL); + + tdb_close(tdb2); + + return matched_packet; +} |