summaryrefslogtreecommitdiff
path: root/source3/libsmb
diff options
context:
space:
mode:
Diffstat (limited to 'source3/libsmb')
-rw-r--r--source3/libsmb/asn1.c603
-rw-r--r--source3/libsmb/async_smb.c999
-rw-r--r--source3/libsmb/cliconnect.c2011
-rw-r--r--source3/libsmb/clidfs.c1125
-rw-r--r--source3/libsmb/clidgram.c357
-rw-r--r--source3/libsmb/clientgen.c789
-rw-r--r--source3/libsmb/clierror.c497
-rw-r--r--source3/libsmb/clifile.c2281
-rw-r--r--source3/libsmb/clifsinfo.c664
-rw-r--r--source3/libsmb/clikrb5.c1923
-rw-r--r--source3/libsmb/clilist.c683
-rw-r--r--source3/libsmb/climessage.c162
-rw-r--r--source3/libsmb/clioplock.c66
-rw-r--r--source3/libsmb/cliprint.c260
-rw-r--r--source3/libsmb/cliquota.c642
-rw-r--r--source3/libsmb/clirap.c1264
-rw-r--r--source3/libsmb/clirap2.c2675
-rw-r--r--source3/libsmb/clireadwrite.c717
-rw-r--r--source3/libsmb/clisecdesc.c130
-rw-r--r--source3/libsmb/clispnego.c600
-rw-r--r--source3/libsmb/clistr.c94
-rw-r--r--source3/libsmb/clitrans.c1409
-rw-r--r--source3/libsmb/conncache.c256
-rw-r--r--source3/libsmb/credentials.c361
-rw-r--r--source3/libsmb/dcerpc_err.c55
-rw-r--r--source3/libsmb/doserr.c202
-rw-r--r--source3/libsmb/dsgetdcname.c1449
-rw-r--r--source3/libsmb/errormap.c1609
-rw-r--r--source3/libsmb/libsmb_cache.c244
-rw-r--r--source3/libsmb/libsmb_compat.c542
-rw-r--r--source3/libsmb/libsmb_context.c645
-rw-r--r--source3/libsmb/libsmb_dir.c1949
-rw-r--r--source3/libsmb/libsmb_file.c864
-rw-r--r--source3/libsmb/libsmb_misc.c73
-rw-r--r--source3/libsmb/libsmb_path.c400
-rw-r--r--source3/libsmb/libsmb_printjob.c336
-rw-r--r--source3/libsmb/libsmb_server.c699
-rw-r--r--source3/libsmb/libsmb_setget.c905
-rw-r--r--source3/libsmb/libsmb_stat.c302
-rw-r--r--source3/libsmb/libsmb_xattr.c2323
-rw-r--r--source3/libsmb/namecache.c394
-rw-r--r--source3/libsmb/namequery.c2145
-rw-r--r--source3/libsmb/namequery_dc.c247
-rw-r--r--source3/libsmb/nmblib.c1400
-rw-r--r--source3/libsmb/nterr.c759
-rw-r--r--source3/libsmb/ntlm_check.c463
-rw-r--r--source3/libsmb/ntlmssp.c1251
-rw-r--r--source3/libsmb/ntlmssp_parse.c384
-rw-r--r--source3/libsmb/ntlmssp_sign.c468
-rw-r--r--source3/libsmb/passchange.c257
-rw-r--r--source3/libsmb/pwd_cache.c61
-rw-r--r--source3/libsmb/samlogon_cache.c258
-rw-r--r--source3/libsmb/smb_seal.c497
-rw-r--r--source3/libsmb/smb_share_modes.c520
-rw-r--r--source3/libsmb/smb_signing.c1004
-rw-r--r--source3/libsmb/smbdes.c420
-rw-r--r--source3/libsmb/smbencrypt.c898
-rw-r--r--source3/libsmb/smberr.c266
-rw-r--r--source3/libsmb/spnego.c346
-rw-r--r--source3/libsmb/trustdom_cache.c338
-rw-r--r--source3/libsmb/trusts_util.c283
-rw-r--r--source3/libsmb/unexpected.c202
62 files changed, 46026 insertions, 0 deletions
diff --git a/source3/libsmb/asn1.c b/source3/libsmb/asn1.c
new file mode 100644
index 0000000000..aca1c48841
--- /dev/null
+++ b/source3/libsmb/asn1.c
@@ -0,0 +1,603 @@
+/*
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/* free an asn1 structure */
+void asn1_free(ASN1_DATA *data)
+{
+ struct nesting *nesting = data->nesting;
+
+ while (nesting) {
+ struct nesting *nnext = nesting->next;
+ free(nesting);
+ nesting = nnext;
+ };
+ data->nesting = NULL;
+ 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) {
+ data->data = SMB_REALLOC_ARRAY(data->data, unsigned char,
+ data->ofs+len);
+ if (!data->data) {
+ data->has_error = true;
+ return false;
+ }
+ 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 = SMB_MALLOC_P(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;
+
+ if (data->has_error) {
+ return false;
+ }
+
+ 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 > 0xFFFF) {
+ data->data[nesting->start] = 0x83;
+ if (!asn1_write_uint8(data, 0)) return false;
+ if (!asn1_write_uint8(data, 0)) return false;
+ if (!asn1_write_uint8(data, 0)) return false;
+ memmove(data->data+nesting->start+4, data->data+nesting->start+1, len);
+ data->data[nesting->start+1] = (len>>16) & 0xFF;
+ data->data[nesting->start+2] = (len>>8) & 0xFF;
+ data->data[nesting->start+3] = len&0xff;
+ } else 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;
+}
+
+/* write a BOOLEAN - hmm, I suspect this one is the correct one, and the
+ above boolean is bogus. Need to check */
+bool asn1_write_BOOLEAN2(ASN1_DATA *data, bool v)
+{
+ asn1_push_tag(data, ASN1_BOOLEAN);
+ asn1_write_uint8(data, v);
+ asn1_pop_tag(data);
+ 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 = (unsigned char *)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->has_error)
+ return false;
+
+ if (len < 0 || data->ofs + len < data->ofs || data->ofs + len < len) {
+ data->has_error = true;
+ return false;
+ }
+
+ 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);
+}
+
+/*
+ * Check thta the value of the ASN1 buffer at the current offset equals tag.
+ */
+bool asn1_check_tag(ASN1_DATA *data, uint8 tag)
+{
+ if (data->has_error || data->ofs >= data->length || data->ofs < 0) {
+ data->has_error = true;
+ return false;
+ }
+
+ return (tag == data->data[data->ofs]);
+}
+
+/* start reading a nested asn1 structure */
+bool asn1_start_tag(ASN1_DATA *data, uint8 tag)
+{
+ uint8 b;
+ struct nesting *nesting;
+
+ if (!asn1_read_uint8(data, &b))
+ return false;
+
+ if (b != tag) {
+ data->has_error = true;
+ return false;
+ }
+ nesting = SMB_MALLOC_P(struct nesting);
+ if (!nesting) {
+ data->has_error = true;
+ return false;
+ }
+
+ if (!asn1_read_uint8(data, &b)) {
+ SAFE_FREE(nesting);
+ return false;
+ }
+
+ if (b & 0x80) {
+ int n = b & 0x7f;
+ if (!asn1_read_uint8(data, &b)) {
+ SAFE_FREE(nesting);
+ return false;
+ }
+ nesting->taglen = b;
+ while (n > 1) {
+ if (!asn1_read_uint8(data, &b)) {
+ SAFE_FREE(nesting);
+ return false;
+ }
+ nesting->taglen = (nesting->taglen << 8) | b;
+ n--;
+ }
+ } 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->has_error)
+ return 0;
+
+ 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 = 0;
+ char *oid_str = NULL;
+
+ *OID = NULL;
+
+ if (!asn1_start_tag(data, ASN1_OID)) {
+ return false;
+ }
+ asn1_read_uint8(data, &b);
+
+ oid_str = talloc_asprintf(NULL,
+ "%u",
+ b/40);
+ if (!oid_str) {
+ data->has_error = true;
+ goto out;
+ }
+ oid_str = talloc_asprintf_append(oid_str,
+ " %u",
+ b%40);
+ if (!oid_str) {
+ data->has_error = true;
+ goto out;
+ }
+
+ 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);
+ oid_str = talloc_asprintf_append(oid_str,
+ " %u",
+ v);
+ if (!oid_str) {
+ data->has_error = true;
+ goto out;
+ }
+ }
+
+ out:
+
+ asn1_end_tag(data);
+
+ if (!data->has_error) {
+ *OID = SMB_STRDUP(oid_str);
+ }
+
+ TALLOC_FREE(oid_str);
+
+ return !data->has_error;
+}
+
+/* check that the next object ID is correct */
+bool asn1_check_OID(ASN1_DATA *data, const char *OID)
+{
+ char *id;
+
+ if (!asn1_read_OID(data, &id)) {
+ return false;
+ }
+
+ if (strcmp(id, OID) != 0) {
+ data->has_error = true;
+ free(id);
+ return false;
+ }
+ free(id);
+ return true;
+}
+
+/* read a GeneralString from a ASN1 buffer */
+bool asn1_read_GeneralString(ASN1_DATA *data, char **s)
+{
+ int len;
+ char *str;
+
+ *s = NULL;
+
+ if (!asn1_start_tag(data, ASN1_GENERAL_STRING)) {
+ return false;
+ }
+ len = asn1_tag_remaining(data);
+ if (len < 0) {
+ data->has_error = true;
+ return false;
+ }
+ str = SMB_MALLOC_ARRAY(char, len+1);
+ if (!str) {
+ data->has_error = true;
+ return false;
+ }
+ asn1_read(data, str, len);
+ str[len] = 0;
+ asn1_end_tag(data);
+
+ if (!data->has_error) {
+ *s = str;
+ }
+ return !data->has_error;
+}
+
+/* read a octet string blob */
+bool asn1_read_OctetString(ASN1_DATA *data, DATA_BLOB *blob)
+{
+ int len;
+ ZERO_STRUCTP(blob);
+ if (!asn1_start_tag(data, ASN1_OCTET_STRING)) return false;
+ len = asn1_tag_remaining(data);
+ if (len < 0) {
+ data->has_error = true;
+ return false;
+ }
+ *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) {
+ asn1_read_uint8(data, &b);
+ *i = (*i << 8) + 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);
+
+ if (v != b)
+ data->has_error = false;
+
+ return !data->has_error;
+}
+
+/* write an enumarted value to the stream */
+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;
+}
+
+bool ber_write_OID_String(DATA_BLOB *blob, const char *OID)
+{
+ uint_t v, v2;
+ const char *p = (const char *)OID;
+ char *newp;
+ int i;
+
+ v = strtoul(p, &newp, 10);
+ if (newp[0] != '.') return false;
+ p = newp + 1;
+
+ v2 = strtoul(p, &newp, 10);
+ if (newp[0] != '.') return false;
+ p = newp + 1;
+
+ /*the ber representation can't use more space then the string one */
+ *blob = data_blob(NULL, strlen(OID));
+ if (!blob->data) return false;
+
+ blob->data[0] = 40*v + v2;
+
+ i = 1;
+ while (*p) {
+ v = strtoul(p, &newp, 10);
+ if (newp[0] == '.') {
+ p = newp + 1;
+ } else if (newp[0] == '\0') {
+ p = newp;
+ } else {
+ data_blob_free(blob);
+ return false;
+ }
+ if (v >= (1<<28)) blob->data[i++] = (0x80 | ((v>>28)&0x7f));
+ if (v >= (1<<21)) blob->data[i++] = (0x80 | ((v>>21)&0x7f));
+ if (v >= (1<<14)) blob->data[i++] = (0x80 | ((v>>14)&0x7f));
+ if (v >= (1<<7)) blob->data[i++] = (0x80 | ((v>>7)&0x7f));
+ blob->data[i++] = (v&0x7f);
+ }
+
+ blob->length = i;
+
+ return true;
+}
+
+/* read an object ID from a data blob */
+bool ber_read_OID_String(TALLOC_CTX *mem_ctx, DATA_BLOB blob, const char **OID)
+{
+ int i;
+ uint8_t *b;
+ uint_t v;
+ char *tmp_oid = NULL;
+
+ if (blob.length < 2) return false;
+
+ b = blob.data;
+
+ tmp_oid = talloc_asprintf(mem_ctx, "%u", b[0]/40);
+ if (!tmp_oid) goto nomem;
+ tmp_oid = talloc_asprintf_append_buffer(tmp_oid, ".%u", b[0]%40);
+ if (!tmp_oid) goto nomem;
+
+ for(i = 1, v = 0; i < blob.length; i++) {
+ v = (v<<7) | (b[i]&0x7f);
+ if ( ! (b[i] & 0x80)) {
+ tmp_oid = talloc_asprintf_append_buffer(tmp_oid, ".%u", v);
+ v = 0;
+ }
+ if (!tmp_oid) goto nomem;
+ }
+
+ if (v != 0) {
+ talloc_free(tmp_oid);
+ return false;
+ }
+
+ *OID = tmp_oid;
+ return true;
+
+nomem:
+ return false;
+}
+
+
diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c
new file mode 100644
index 0000000000..d5eac07b48
--- /dev/null
+++ b/source3/libsmb/async_smb.c
@@ -0,0 +1,999 @@
+/*
+ Unix SMB/CIFS implementation.
+ Infrastructure for async SMB client requests
+ Copyright (C) Volker Lendecke 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+static void cli_state_handler(struct event_context *event_ctx,
+ struct fd_event *event, uint16 flags, void *p);
+
+/**
+ * Fetch an error out of a NBT packet
+ * @param[in] buf The SMB packet
+ * @retval The error, converted to NTSTATUS
+ */
+
+NTSTATUS cli_pull_error(char *buf)
+{
+ uint32_t flags2 = SVAL(buf, smb_flg2);
+
+ if (flags2 & FLAGS2_32_BIT_ERROR_CODES) {
+ return NT_STATUS(IVAL(buf, smb_rcls));
+ }
+
+ /* if the client uses dos errors, but there is no error,
+ we should return no error here, otherwise it looks
+ like an unknown bad NT_STATUS. jmcd */
+ if (CVAL(buf, smb_rcls) == 0)
+ return NT_STATUS_OK;
+
+ return NT_STATUS_DOS(CVAL(buf, smb_rcls), SVAL(buf,smb_err));
+}
+
+/**
+ * Compatibility helper for the sync APIs: Fake NTSTATUS in cli->inbuf
+ * @param[in] cli The client connection that just received an error
+ * @param[in] status The error to set on "cli"
+ */
+
+void cli_set_error(struct cli_state *cli, NTSTATUS status)
+{
+ uint32_t flags2 = SVAL(cli->inbuf, smb_flg2);
+
+ if (NT_STATUS_IS_DOS(status)) {
+ SSVAL(cli->inbuf, smb_flg2,
+ flags2 & ~FLAGS2_32_BIT_ERROR_CODES);
+ SCVAL(cli->inbuf, smb_rcls, NT_STATUS_DOS_CLASS(status));
+ SSVAL(cli->inbuf, smb_err, NT_STATUS_DOS_CODE(status));
+ return;
+ }
+
+ SSVAL(cli->inbuf, smb_flg2, flags2 | FLAGS2_32_BIT_ERROR_CODES);
+ SIVAL(cli->inbuf, smb_rcls, NT_STATUS_V(status));
+ return;
+}
+
+/**
+ * Allocate a new mid
+ * @param[in] cli The client connection
+ * @retval The new, unused mid
+ */
+
+static uint16_t cli_new_mid(struct cli_state *cli)
+{
+ uint16_t result;
+ struct cli_request *req;
+
+ while (true) {
+ result = cli->mid++;
+ if (result == 0) {
+ continue;
+ }
+
+ for (req = cli->outstanding_requests; req; req = req->next) {
+ if (result == req->mid) {
+ break;
+ }
+ }
+
+ if (req == NULL) {
+ return result;
+ }
+ }
+}
+
+/**
+ * Print an async req that happens to be a cli_request
+ * @param[in] mem_ctx The TALLOC_CTX to put the result on
+ * @param[in] req The request to print
+ * @retval The string representation of "req"
+ */
+
+static char *cli_request_print(TALLOC_CTX *mem_ctx, struct async_req *req)
+{
+ char *result = async_req_print(mem_ctx, req);
+ struct cli_request *cli_req = talloc_get_type_abort(
+ req->private_data, struct cli_request);
+
+ if (result == NULL) {
+ return NULL;
+ }
+
+ return talloc_asprintf_append_buffer(
+ result, "mid=%d\n", cli_req->mid);
+}
+
+/**
+ * Destroy a cli_request
+ * @param[in] req The cli_request to kill
+ * @retval Can't fail
+ */
+
+static int cli_request_destructor(struct cli_request *req)
+{
+ if (req->enc_state != NULL) {
+ common_free_enc_buffer(req->enc_state, req->outbuf);
+ }
+ DLIST_REMOVE(req->cli->outstanding_requests, req);
+ if (req->cli->outstanding_requests == NULL) {
+ TALLOC_FREE(req->cli->fd_event);
+ }
+ return 0;
+}
+
+/**
+ * Are there already requests waiting in the chain_accumulator?
+ * @param[in] cli The cli_state we want to check
+ * @retval reply :-)
+ */
+
+bool cli_in_chain(struct cli_state *cli)
+{
+ if (cli->chain_accumulator == NULL) {
+ return false;
+ }
+
+ return (cli->chain_accumulator->num_async != 0);
+}
+
+/**
+ * Is the SMB command able to hold an AND_X successor
+ * @param[in] cmd The SMB command in question
+ * @retval Can we add a chained request after "cmd"?
+ */
+
+static bool is_andx_req(uint8_t cmd)
+{
+ switch (cmd) {
+ case SMBtconX:
+ case SMBlockingX:
+ case SMBopenX:
+ case SMBreadX:
+ case SMBwriteX:
+ case SMBsesssetupX:
+ case SMBulogoffX:
+ case SMBntcreateX:
+ return true;
+ break;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+/**
+ * @brief Find the smb_cmd offset of the last command pushed
+ * @param[in] buf The buffer we're building up
+ * @retval Where can we put our next andx cmd?
+ *
+ * While chaining requests, the "next" request we're looking at needs to put
+ * its SMB_Command before the data the previous request already built up added
+ * to the chain. Find the offset to the place where we have to put our cmd.
+ */
+
+static bool find_andx_cmd_ofs(char *buf, size_t *pofs)
+{
+ uint8_t cmd;
+ size_t ofs;
+
+ cmd = CVAL(buf, smb_com);
+
+ SMB_ASSERT(is_andx_req(cmd));
+
+ ofs = smb_vwv0;
+
+ while (CVAL(buf, ofs) != 0xff) {
+
+ if (!is_andx_req(CVAL(buf, ofs))) {
+ return false;
+ }
+
+ /*
+ * ofs is from start of smb header, so add the 4 length
+ * bytes. The next cmd is right after the wct field.
+ */
+ ofs = SVAL(buf, ofs+2) + 4 + 1;
+
+ SMB_ASSERT(ofs+4 < talloc_get_size(buf));
+ }
+
+ *pofs = ofs;
+ return true;
+}
+
+/**
+ * @brief Destroy an async_req that is the visible part of a cli_request
+ * @param[in] req The request to kill
+ * @retval Return 0 to make talloc happy
+ *
+ * This destructor is a bit tricky: Because a cli_request can host more than
+ * one async_req for chained requests, we need to make sure that the
+ * "cli_request" that we were part of is correctly destroyed at the right
+ * time. This is done by NULLing out ourself from the "async" member of our
+ * "cli_request". If there is none left, then also TALLOC_FREE() the
+ * cli_request, which was a talloc child of the client connection cli_state.
+ */
+
+static int cli_async_req_destructor(struct async_req *req)
+{
+ struct cli_request *cli_req = talloc_get_type_abort(
+ req->private_data, struct cli_request);
+ int i, pending;
+ bool found = false;
+
+ pending = 0;
+
+ for (i=0; i<cli_req->num_async; i++) {
+ if (cli_req->async[i] == req) {
+ cli_req->async[i] = NULL;
+ found = true;
+ }
+ if (cli_req->async[i] != NULL) {
+ pending += 1;
+ }
+ }
+
+ SMB_ASSERT(found);
+
+ if (pending == 0) {
+ TALLOC_FREE(cli_req);
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Chain up a request
+ * @param[in] mem_ctx The TALLOC_CTX for the result
+ * @param[in] ev The event context that will call us back
+ * @param[in] cli The cli_state we queue the request up for
+ * @param[in] smb_command The command that we want to issue
+ * @param[in] additional_flags open_and_x wants to add oplock header flags
+ * @param[in] wct How many words?
+ * @param[in] vwv The words, already in network order
+ * @param[in] num_bytes How many bytes?
+ * @param[in] bytes The data the request ships
+ *
+ * cli_request_chain() is the core of the SMB request marshalling routine. It
+ * will create a new async_req structure in the cli->chain_accumulator->async
+ * array and marshall the smb_cmd, the vwv array and the bytes into
+ * cli->chain_accumulator->outbuf.
+ */
+
+static struct async_req *cli_request_chain(TALLOC_CTX *mem_ctx,
+ struct event_context *ev,
+ struct cli_state *cli,
+ uint8_t smb_command,
+ uint8_t additional_flags,
+ uint8_t wct, const uint16_t *vwv,
+ uint16_t num_bytes,
+ const uint8_t *bytes)
+{
+ struct async_req **tmp_reqs;
+ char *tmp_buf;
+ struct cli_request *req;
+ size_t old_size, new_size;
+ size_t ofs;
+
+ req = cli->chain_accumulator;
+
+ tmp_reqs = TALLOC_REALLOC_ARRAY(req, req->async, struct async_req *,
+ req->num_async + 1);
+ if (tmp_reqs == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ return NULL;
+ }
+ req->async = tmp_reqs;
+ req->num_async += 1;
+
+ req->async[req->num_async-1] = async_req_new(mem_ctx, ev);
+ if (req->async[req->num_async-1] == NULL) {
+ DEBUG(0, ("async_req_new failed\n"));
+ req->num_async -= 1;
+ return NULL;
+ }
+ req->async[req->num_async-1]->private_data = req;
+ req->async[req->num_async-1]->print = cli_request_print;
+ talloc_set_destructor(req->async[req->num_async-1],
+ cli_async_req_destructor);
+
+ old_size = talloc_get_size(req->outbuf);
+
+ /*
+ * We need space for the wct field, the words, the byte count field
+ * and the bytes themselves.
+ */
+ new_size = old_size + 1 + wct * sizeof(uint16_t) + 2 + num_bytes;
+
+ if (new_size > 0xffff) {
+ DEBUG(1, ("cli_request_chain: %u bytes won't fit\n",
+ (unsigned)new_size));
+ goto fail;
+ }
+
+ tmp_buf = TALLOC_REALLOC_ARRAY(NULL, req->outbuf, char, new_size);
+ if (tmp_buf == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ goto fail;
+ }
+ req->outbuf = tmp_buf;
+
+ if (old_size == smb_wct) {
+ SCVAL(req->outbuf, smb_com, smb_command);
+ } else {
+ size_t andx_cmd_ofs;
+ if (!find_andx_cmd_ofs(req->outbuf, &andx_cmd_ofs)) {
+ DEBUG(1, ("invalid command chain\n"));
+ goto fail;
+ }
+ SCVAL(req->outbuf, andx_cmd_ofs, smb_command);
+ SSVAL(req->outbuf, andx_cmd_ofs + 2, old_size - 4);
+ }
+
+ ofs = old_size;
+
+ SCVAL(req->outbuf, ofs, wct);
+ ofs += 1;
+
+ memcpy(req->outbuf + ofs, vwv, sizeof(uint16_t) * wct);
+ ofs += sizeof(uint16_t) * wct;
+
+ SSVAL(req->outbuf, ofs, num_bytes);
+ ofs += sizeof(uint16_t);
+
+ memcpy(req->outbuf + ofs, bytes, num_bytes);
+
+ return req->async[req->num_async-1];
+
+ fail:
+ TALLOC_FREE(req->async[req->num_async-1]);
+ req->num_async -= 1;
+ return NULL;
+}
+
+/**
+ * @brief prepare a cli_state to accept a chain of requests
+ * @param[in] cli The cli_state we want to queue up in
+ * @param[in] ev The event_context that will call us back for the socket
+ * @param[in] size_hint How many bytes are expected, just an optimization
+ * @retval Did we have enough memory?
+ *
+ * cli_chain_cork() sets up a new cli_request in cli->chain_accumulator. If
+ * cli is used in an async fashion, i.e. if we have outstanding requests, then
+ * we do not have to create a fd event. If cli is used only with the sync
+ * helpers, we need to create the fd_event here.
+ *
+ * If you want to issue a chained request to the server, do a
+ * cli_chain_cork(), then do you cli_open_send(), cli_read_and_x_send(),
+ * cli_close_send() and so on. The async requests that come out of
+ * cli_xxx_send() are normal async requests with the difference that they
+ * won't be shipped individually. But the event_context will still trigger the
+ * req->async.fn to be called on every single request.
+ *
+ * You have to take care yourself that you only issue chainable requests in
+ * the middle of the chain.
+ */
+
+bool cli_chain_cork(struct cli_state *cli, struct event_context *ev,
+ size_t size_hint)
+{
+ struct cli_request *req = NULL;
+
+ SMB_ASSERT(cli->chain_accumulator == NULL);
+
+ if (cli->fd == -1) {
+ DEBUG(10, ("cli->fd closed\n"));
+ return false;
+ }
+
+ if (cli->fd_event == NULL) {
+ SMB_ASSERT(cli->outstanding_requests == NULL);
+ cli->fd_event = event_add_fd(ev, cli, cli->fd,
+ EVENT_FD_READ,
+ cli_state_handler, cli);
+ if (cli->fd_event == NULL) {
+ return false;
+ }
+ }
+
+ req = talloc(cli, struct cli_request);
+ if (req == NULL) {
+ goto fail;
+ }
+ req->cli = cli;
+
+ if (size_hint == 0) {
+ size_hint = 100;
+ }
+ req->outbuf = talloc_array(req, char, smb_wct + size_hint);
+ if (req->outbuf == NULL) {
+ goto fail;
+ }
+ req->outbuf = TALLOC_REALLOC_ARRAY(NULL, req->outbuf, char, smb_wct);
+
+ req->num_async = 0;
+ req->async = NULL;
+
+ req->enc_state = NULL;
+ req->recv_helper.fn = NULL;
+
+ SSVAL(req->outbuf, smb_tid, cli->cnum);
+ cli_setup_packet_buf(cli, req->outbuf);
+
+ req->mid = cli_new_mid(cli);
+
+ cli->chain_accumulator = req;
+
+ DEBUG(10, ("cli_chain_cork: mid=%d\n", req->mid));
+
+ return true;
+ fail:
+ TALLOC_FREE(req);
+ if (cli->outstanding_requests == NULL) {
+ TALLOC_FREE(cli->fd_event);
+ }
+ return false;
+}
+
+/**
+ * Ship a request queued up via cli_request_chain()
+ * @param[in] cl The connection
+ */
+
+void cli_chain_uncork(struct cli_state *cli)
+{
+ struct cli_request *req = cli->chain_accumulator;
+
+ SMB_ASSERT(req != NULL);
+
+ DLIST_ADD_END(cli->outstanding_requests, req, struct cli_request *);
+ talloc_set_destructor(req, cli_request_destructor);
+
+ cli->chain_accumulator = NULL;
+
+ SSVAL(req->outbuf, smb_mid, req->mid);
+ smb_setlen(req->outbuf, talloc_get_size(req->outbuf) - 4);
+
+ cli_calculate_sign_mac(cli, req->outbuf);
+
+ if (cli_encryption_on(cli)) {
+ NTSTATUS status;
+ char *enc_buf;
+
+ status = cli_encrypt_message(cli, req->outbuf, &enc_buf);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Error in encrypting client message. "
+ "Error %s\n", nt_errstr(status)));
+ TALLOC_FREE(req);
+ return;
+ }
+ req->outbuf = enc_buf;
+ req->enc_state = cli->trans_enc_state;
+ }
+
+ req->sent = 0;
+
+ event_fd_set_writeable(cli->fd_event);
+}
+
+/**
+ * @brief Send a request to the server
+ * @param[in] mem_ctx The TALLOC_CTX for the result
+ * @param[in] ev The event context that will call us back
+ * @param[in] cli The cli_state we queue the request up for
+ * @param[in] smb_command The command that we want to issue
+ * @param[in] additional_flags open_and_x wants to add oplock header flags
+ * @param[in] wct How many words?
+ * @param[in] vwv The words, already in network order
+ * @param[in] num_bytes How many bytes?
+ * @param[in] bytes The data the request ships
+ *
+ * This is the generic routine to be used by the cli_xxx_send routines.
+ */
+
+struct async_req *cli_request_send(TALLOC_CTX *mem_ctx,
+ struct event_context *ev,
+ struct cli_state *cli,
+ uint8_t smb_command,
+ uint8_t additional_flags,
+ uint8_t wct, const uint16_t *vwv,
+ uint16_t num_bytes, const uint8_t *bytes)
+{
+ struct async_req *result;
+ bool uncork = false;
+
+ if (cli->chain_accumulator == NULL) {
+ if (!cli_chain_cork(cli, ev,
+ wct * sizeof(uint16_t) + num_bytes + 3)) {
+ DEBUG(1, ("cli_chain_cork failed\n"));
+ return NULL;
+ }
+ uncork = true;
+ }
+
+ result = cli_request_chain(mem_ctx, ev, cli, smb_command,
+ additional_flags, wct, vwv,
+ num_bytes, bytes);
+
+ if (result == NULL) {
+ DEBUG(1, ("cli_request_chain failed\n"));
+ }
+
+ if (uncork) {
+ cli_chain_uncork(cli);
+ }
+
+ return result;
+}
+
+/**
+ * Figure out if there is an andx command behind the current one
+ * @param[in] buf The smb buffer to look at
+ * @param[in] ofs The offset to the wct field that is followed by the cmd
+ * @retval Is there a command following?
+ */
+
+static bool have_andx_command(const char *buf, uint16_t ofs)
+{
+ uint8_t wct;
+ size_t buflen = talloc_get_size(buf);
+
+ if ((ofs == buflen-1) || (ofs == buflen)) {
+ return false;
+ }
+
+ wct = CVAL(buf, ofs);
+ if (wct < 2) {
+ /*
+ * Not enough space for the command and a following pointer
+ */
+ return false;
+ }
+ return (CVAL(buf, ofs+1) != 0xff);
+}
+
+/**
+ * @brief Pull reply data out of a request
+ * @param[in] req The request that we just received a reply for
+ * @param[out] pwct How many words did the server send?
+ * @param[out] pvwv The words themselves
+ * @param[out] pnum_bytes How many bytes did the server send?
+ * @param[out] pbytes The bytes themselves
+ * @retval Was the reply formally correct?
+ */
+
+NTSTATUS cli_pull_reply(struct async_req *req,
+ uint8_t *pwct, uint16_t **pvwv,
+ uint16_t *pnum_bytes, uint8_t **pbytes)
+{
+ struct cli_request *cli_req = talloc_get_type_abort(
+ req->private_data, struct cli_request);
+ uint8_t wct, cmd;
+ uint16_t num_bytes;
+ size_t wct_ofs, bytes_offset;
+ int i, j;
+ NTSTATUS status;
+
+ for (i = 0; i < cli_req->num_async; i++) {
+ if (req == cli_req->async[i]) {
+ break;
+ }
+ }
+
+ if (i == cli_req->num_async) {
+ cli_set_error(cli_req->cli, NT_STATUS_INVALID_PARAMETER);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /**
+ * The status we pull here is only relevant for the last reply in the
+ * chain.
+ */
+
+ status = cli_pull_error(cli_req->inbuf);
+
+ if (i == 0) {
+ if (NT_STATUS_IS_ERR(status)
+ && !have_andx_command(cli_req->inbuf, smb_wct)) {
+ cli_set_error(cli_req->cli, status);
+ return status;
+ }
+ wct_ofs = smb_wct;
+ goto done;
+ }
+
+ cmd = CVAL(cli_req->inbuf, smb_com);
+ wct_ofs = smb_wct;
+
+ for (j = 0; j < i; j++) {
+ if (j < i-1) {
+ if (cmd == 0xff) {
+ return NT_STATUS_REQUEST_ABORTED;
+ }
+ if (!is_andx_req(cmd)) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ }
+
+ if (!have_andx_command(cli_req->inbuf, wct_ofs)) {
+ /*
+ * This request was not completed because a previous
+ * request in the chain had received an error.
+ */
+ return NT_STATUS_REQUEST_ABORTED;
+ }
+
+ wct_ofs = SVAL(cli_req->inbuf, wct_ofs + 3);
+
+ /*
+ * Skip the all-present length field. No overflow, we've just
+ * put a 16-bit value into a size_t.
+ */
+ wct_ofs += 4;
+
+ if (wct_ofs+2 > talloc_get_size(cli_req->inbuf)) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ cmd = CVAL(cli_req->inbuf, wct_ofs + 1);
+ }
+
+ if (!have_andx_command(cli_req->inbuf, wct_ofs)
+ && NT_STATUS_IS_ERR(status)) {
+ /*
+ * The last command takes the error code. All further commands
+ * down the requested chain will get a
+ * NT_STATUS_REQUEST_ABORTED.
+ */
+ return status;
+ }
+
+ done:
+ wct = CVAL(cli_req->inbuf, wct_ofs);
+
+ bytes_offset = wct_ofs + 1 + wct * sizeof(uint16_t);
+ num_bytes = SVAL(cli_req->inbuf, bytes_offset);
+
+ /*
+ * wct_ofs is a 16-bit value plus 4, wct is a 8-bit value, num_bytes
+ * is a 16-bit value. So bytes_offset being size_t should be far from
+ * wrapping.
+ */
+
+ if ((bytes_offset + 2 > talloc_get_size(cli_req->inbuf))
+ || (bytes_offset > 0xffff)) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ *pwct = wct;
+ *pvwv = (uint16_t *)(cli_req->inbuf + wct_ofs + 1);
+ *pnum_bytes = num_bytes;
+ *pbytes = (uint8_t *)cli_req->inbuf + bytes_offset + 2;
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * Decrypt a PDU, check the signature
+ * @param[in] cli The cli_state that received something
+ * @param[in] pdu The incoming bytes
+ * @retval error code
+ */
+
+
+static NTSTATUS validate_smb_crypto(struct cli_state *cli, char *pdu)
+{
+ NTSTATUS status;
+
+ if ((IVAL(pdu, 4) != 0x424d53ff) /* 0xFF"SMB" */
+ && (SVAL(pdu, 4) != 0x45ff)) /* 0xFF"E" */ {
+ DEBUG(10, ("Got non-SMB PDU\n"));
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ if (cli_encryption_on(cli) && CVAL(pdu, 0) == 0) {
+ uint16_t enc_ctx_num;
+
+ status = get_enc_ctx_num((uint8_t *)pdu, &enc_ctx_num);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("get_enc_ctx_num returned %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ if (enc_ctx_num != cli->trans_enc_state->enc_ctx_num) {
+ DEBUG(10, ("wrong enc_ctx %d, expected %d\n",
+ enc_ctx_num,
+ cli->trans_enc_state->enc_ctx_num));
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ status = common_decrypt_buffer(cli->trans_enc_state, pdu);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("common_decrypt_buffer returned %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+ }
+
+ if (!cli_check_sign_mac(cli, pdu)) {
+ DEBUG(10, ("cli_check_sign_mac failed\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * A PDU has arrived on cli->evt_inbuf
+ * @param[in] cli The cli_state that received something
+ */
+
+static void handle_incoming_pdu(struct cli_state *cli)
+{
+ struct cli_request *req;
+ uint16_t mid;
+ size_t raw_pdu_len, buf_len, pdu_len, rest_len;
+ char *pdu;
+ int i;
+ NTSTATUS status;
+
+ int num_async;
+
+ /*
+ * The encrypted PDU len might differ from the unencrypted one
+ */
+ raw_pdu_len = smb_len(cli->evt_inbuf) + 4;
+ buf_len = talloc_get_size(cli->evt_inbuf);
+ rest_len = buf_len - raw_pdu_len;
+
+ if (buf_len == raw_pdu_len) {
+ /*
+ * Optimal case: Exactly one PDU was in the socket buffer
+ */
+ pdu = cli->evt_inbuf;
+ cli->evt_inbuf = NULL;
+ }
+ else {
+ DEBUG(11, ("buf_len = %d, raw_pdu_len = %d, splitting "
+ "buffer\n", (int)buf_len, (int)raw_pdu_len));
+
+ if (raw_pdu_len < rest_len) {
+ /*
+ * The PDU is shorter, talloc_memdup that one.
+ */
+ pdu = (char *)talloc_memdup(
+ cli, cli->evt_inbuf, raw_pdu_len);
+
+ memmove(cli->evt_inbuf, cli->evt_inbuf + raw_pdu_len,
+ buf_len - raw_pdu_len);
+
+ cli->evt_inbuf = TALLOC_REALLOC_ARRAY(
+ NULL, cli->evt_inbuf, char, rest_len);
+
+ if (pdu == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto invalidate_requests;
+ }
+ }
+ else {
+ /*
+ * The PDU is larger than the rest, talloc_memdup the
+ * rest
+ */
+ pdu = cli->evt_inbuf;
+
+ cli->evt_inbuf = (char *)talloc_memdup(
+ cli, pdu + raw_pdu_len, rest_len);
+
+ if (cli->evt_inbuf == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto invalidate_requests;
+ }
+ }
+ }
+
+ status = validate_smb_crypto(cli, pdu);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto invalidate_requests;
+ }
+
+ mid = SVAL(pdu, smb_mid);
+
+ DEBUG(10, ("handle_incoming_pdu: got mid %d\n", mid));
+
+ for (req = cli->outstanding_requests; req; req = req->next) {
+ if (req->mid == mid) {
+ break;
+ }
+ }
+
+ pdu_len = smb_len(pdu) + 4;
+
+ if (req == NULL) {
+ DEBUG(3, ("Request for mid %d not found, dumping PDU\n", mid));
+
+ TALLOC_FREE(pdu);
+ return;
+ }
+
+ req->inbuf = talloc_move(req, &pdu);
+
+ /*
+ * Freeing the last async_req will free the req (see
+ * cli_async_req_destructor). So make a copy of req->num_async, we
+ * can't reference it in the last round.
+ */
+
+ num_async = req->num_async;
+
+ for (i=0; i<num_async; i++) {
+ /**
+ * A request might have been talloc_free()'ed before we arrive
+ * here. It will have removed itself from req->async via its
+ * destructor cli_async_req_destructor().
+ */
+ if (req->async[i] != NULL) {
+ if (req->recv_helper.fn != NULL) {
+ req->recv_helper.fn(req->async[i]);
+ } else {
+ async_req_done(req->async[i]);
+ }
+ }
+ }
+ return;
+
+ invalidate_requests:
+
+ DEBUG(10, ("handle_incoming_pdu: Aborting with %s\n",
+ nt_errstr(status)));
+
+ for (req = cli->outstanding_requests; req; req = req->next) {
+ async_req_error(req->async[0], status);
+ }
+ return;
+}
+
+/**
+ * fd event callback. This is the basic connection to the socket
+ * @param[in] event_ctx The event context that called us
+ * @param[in] event The event that fired
+ * @param[in] flags EVENT_FD_READ | EVENT_FD_WRITE
+ * @param[in] p private_data, in this case the cli_state
+ */
+
+static void cli_state_handler(struct event_context *event_ctx,
+ struct fd_event *event, uint16 flags, void *p)
+{
+ struct cli_state *cli = (struct cli_state *)p;
+ struct cli_request *req;
+ NTSTATUS status;
+
+ DEBUG(11, ("cli_state_handler called with flags %d\n", flags));
+
+ if (flags & EVENT_FD_READ) {
+ int res, available;
+ size_t old_size, new_size;
+ char *tmp;
+
+ res = ioctl(cli->fd, FIONREAD, &available);
+ if (res == -1) {
+ DEBUG(10, ("ioctl(FIONREAD) failed: %s\n",
+ strerror(errno)));
+ status = map_nt_error_from_unix(errno);
+ goto sock_error;
+ }
+
+ if (available == 0) {
+ /* EOF */
+ status = NT_STATUS_END_OF_FILE;
+ goto sock_error;
+ }
+
+ old_size = talloc_get_size(cli->evt_inbuf);
+ new_size = old_size + available;
+
+ if (new_size < old_size) {
+ /* wrap */
+ status = NT_STATUS_UNEXPECTED_IO_ERROR;
+ goto sock_error;
+ }
+
+ tmp = TALLOC_REALLOC_ARRAY(cli, cli->evt_inbuf, char,
+ new_size);
+ if (tmp == NULL) {
+ /* nomem */
+ status = NT_STATUS_NO_MEMORY;
+ goto sock_error;
+ }
+ cli->evt_inbuf = tmp;
+
+ res = recv(cli->fd, cli->evt_inbuf + old_size, available, 0);
+ if (res == -1) {
+ DEBUG(10, ("recv failed: %s\n", strerror(errno)));
+ status = map_nt_error_from_unix(errno);
+ goto sock_error;
+ }
+
+ DEBUG(11, ("cli_state_handler: received %d bytes, "
+ "smb_len(evt_inbuf) = %d\n", (int)res,
+ smb_len(cli->evt_inbuf)));
+
+ /* recv *might* have returned less than announced */
+ new_size = old_size + res;
+
+ /* shrink, so I don't expect errors here */
+ cli->evt_inbuf = TALLOC_REALLOC_ARRAY(cli, cli->evt_inbuf,
+ char, new_size);
+
+ while ((cli->evt_inbuf != NULL)
+ && ((smb_len(cli->evt_inbuf) + 4) <= new_size)) {
+ /*
+ * we've got a complete NBT level PDU in evt_inbuf
+ */
+ handle_incoming_pdu(cli);
+ new_size = talloc_get_size(cli->evt_inbuf);
+ }
+ }
+
+ if (flags & EVENT_FD_WRITE) {
+ size_t to_send;
+ ssize_t sent;
+
+ for (req = cli->outstanding_requests; req; req = req->next) {
+ to_send = smb_len(req->outbuf)+4;
+ if (to_send > req->sent) {
+ break;
+ }
+ }
+
+ if (req == NULL) {
+ if (cli->fd_event != NULL) {
+ event_fd_set_not_writeable(cli->fd_event);
+ }
+ return;
+ }
+
+ sent = send(cli->fd, req->outbuf + req->sent,
+ to_send - req->sent, 0);
+
+ if (sent < 0) {
+ status = map_nt_error_from_unix(errno);
+ goto sock_error;
+ }
+
+ req->sent += sent;
+
+ if (req->sent == to_send) {
+ return;
+ }
+ }
+ return;
+
+ sock_error:
+ for (req = cli->outstanding_requests; req; req = req->next) {
+ int i;
+ for (i=0; i<req->num_async; i++) {
+ async_req_error(req->async[i], status);
+ }
+ }
+ TALLOC_FREE(cli->fd_event);
+ close(cli->fd);
+ cli->fd = -1;
+}
diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c
new file mode 100644
index 0000000000..8ef14d7973
--- /dev/null
+++ b/source3/libsmb/cliconnect.c
@@ -0,0 +1,2011 @@
+/*
+ Unix SMB/CIFS implementation.
+ client connect/disconnect routines
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Andrew Bartlett 2001-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+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,"DOS LANMAN2.1"},
+ {PROTOCOL_LANMAN2,"LANMAN2.1"},
+ {PROTOCOL_LANMAN2,"Samba"},
+ {PROTOCOL_NT1,"NT LANMAN 1.0"},
+ {PROTOCOL_NT1,"NT LM 0.12"},
+ {-1,NULL}
+};
+
+static const char *star_smbserver_name = "*SMBSERVER";
+
+/**
+ * Set the user session key for a connection
+ * @param cli The cli structure to add it too
+ * @param session_key The session key used. (A copy of this is taken for the cli struct)
+ *
+ */
+
+static void cli_set_session_key (struct cli_state *cli, const DATA_BLOB session_key)
+{
+ cli->user_session_key = data_blob(session_key.data, session_key.length);
+}
+
+/****************************************************************************
+ Do an old lanman2 style session setup.
+****************************************************************************/
+
+static NTSTATUS cli_session_setup_lanman2(struct cli_state *cli,
+ const char *user,
+ const char *pass, size_t passlen,
+ const char *workgroup)
+{
+ DATA_BLOB session_key = data_blob_null;
+ DATA_BLOB lm_response = data_blob_null;
+ fstring pword;
+ char *p;
+
+ if (passlen > sizeof(pword)-1) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* LANMAN servers predate NT status codes and Unicode and ignore those
+ smb flags so we must disable the corresponding default capabilities
+ that would otherwise cause the Unicode and NT Status flags to be
+ set (and even returned by the server) */
+
+ cli->capabilities &= ~(CAP_UNICODE | CAP_STATUS32);
+
+ /* if in share level security then don't send a password now */
+ if (!(cli->sec_mode & NEGOTIATE_SECURITY_USER_LEVEL))
+ passlen = 0;
+
+ if (passlen > 0 && (cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) && passlen != 24) {
+ /* Encrypted mode needed, and non encrypted password supplied. */
+ lm_response = data_blob(NULL, 24);
+ if (!SMBencrypt(pass, cli->secblob.data,(uchar *)lm_response.data)) {
+ DEBUG(1, ("Password is > 14 chars in length, and is therefore incompatible with Lanman authentication\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ } else if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) && passlen == 24) {
+ /* Encrypted mode needed, and encrypted password supplied. */
+ lm_response = data_blob(pass, passlen);
+ } else if (passlen > 0) {
+ /* Plaintext mode needed, assume plaintext supplied. */
+ passlen = clistr_push(cli, pword, pass, sizeof(pword), STR_TERMINATE);
+ lm_response = data_blob(pass, passlen);
+ }
+
+ /* send a session setup command */
+ memset(cli->outbuf,'\0',smb_size);
+ cli_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,lm_response.length);
+
+ p = smb_buf(cli->outbuf);
+ memcpy(p,lm_response.data,lm_response.length);
+ p += lm_response.length;
+ 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);
+
+ if (!cli_send_smb(cli) || !cli_receive_smb(cli)) {
+ return cli_nt_error(cli);
+ }
+
+ show_msg(cli->inbuf);
+
+ if (cli_is_error(cli)) {
+ return cli_nt_error(cli);
+ }
+
+ /* use the returned vuid from now on */
+ cli->vuid = SVAL(cli->inbuf,smb_uid);
+ fstrcpy(cli->user_name, user);
+
+ if (session_key.data) {
+ /* Have plaintext orginal */
+ cli_set_session_key(cli, session_key);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ 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;
+
+ capabilities |= (cli->capabilities & (CAP_UNICODE|CAP_LARGE_FILES|CAP_LARGE_READX|CAP_LARGE_WRITEX|CAP_DFS));
+ return capabilities;
+}
+
+/****************************************************************************
+ Do a NT1 guest session setup.
+****************************************************************************/
+
+static NTSTATUS cli_session_setup_guest(struct cli_state *cli)
+{
+ char *p;
+ uint32 capabilities = cli_session_setup_capabilities(cli);
+
+ memset(cli->outbuf, '\0', smb_size);
+ 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);
+
+ if (!cli_send_smb(cli) || !cli_receive_smb(cli)) {
+ return cli_nt_error(cli);
+ }
+
+ show_msg(cli->inbuf);
+
+ if (cli_is_error(cli)) {
+ return cli_nt_error(cli);
+ }
+
+ 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);
+
+ if (strstr(cli->server_type, "Samba")) {
+ cli->is_samba = True;
+ }
+
+ fstrcpy(cli->user_name, "");
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Do a NT1 plaintext session setup.
+****************************************************************************/
+
+static NTSTATUS cli_session_setup_plaintext(struct cli_state *cli,
+ const char *user, const char *pass,
+ const char *workgroup)
+{
+ uint32 capabilities = cli_session_setup_capabilities(cli);
+ char *p;
+ fstring lanman;
+
+ fstr_sprintf( lanman, "Samba %s", SAMBA_VERSION_STRING);
+
+ memset(cli->outbuf, '\0', smb_size);
+ 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_vwv8,0);
+ SIVAL(cli->outbuf,smb_vwv11,capabilities);
+ p = smb_buf(cli->outbuf);
+
+ /* check wether to send the ASCII or UNICODE version of the password */
+
+ if ( (capabilities & CAP_UNICODE) == 0 ) {
+ p += clistr_push(cli, p, pass, -1, STR_TERMINATE); /* password */
+ SSVAL(cli->outbuf,smb_vwv7,PTR_DIFF(p, smb_buf(cli->outbuf)));
+ }
+ else {
+ /* For ucs2 passwords clistr_push calls ucs2_align, which causes
+ * the space taken by the unicode password to be one byte too
+ * long (as we're on an odd byte boundary here). Reduce the
+ * count by 1 to cope with this. Fixes smbclient against NetApp
+ * servers which can't cope. Fix from
+ * bryan.kolodziej@allenlund.com in bug #3840.
+ */
+ p += clistr_push(cli, p, pass, -1, STR_UNICODE|STR_TERMINATE); /* unicode password */
+ SSVAL(cli->outbuf,smb_vwv8,PTR_DIFF(p, smb_buf(cli->outbuf))-1);
+ }
+
+ 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, lanman, -1, STR_TERMINATE);
+ cli_setup_bcc(cli, p);
+
+ if (!cli_send_smb(cli) || !cli_receive_smb(cli)) {
+ return cli_nt_error(cli);
+ }
+
+ show_msg(cli->inbuf);
+
+ if (cli_is_error(cli)) {
+ return cli_nt_error(cli);
+ }
+
+ 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);
+
+ if (strstr(cli->server_type, "Samba")) {
+ cli->is_samba = True;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ do a NT1 NTLM/LM encrypted session setup - for when extended security
+ is not negotiated.
+ @param cli client state to create do session setup on
+ @param user username
+ @param pass *either* cleartext password (passlen !=24) or LM response.
+ @param ntpass NT response, implies ntpasslen >=24, implies pass is not clear
+ @param workgroup The user's domain.
+****************************************************************************/
+
+static NTSTATUS cli_session_setup_nt1(struct cli_state *cli, const char *user,
+ const char *pass, size_t passlen,
+ const char *ntpass, size_t ntpasslen,
+ const char *workgroup)
+{
+ uint32 capabilities = cli_session_setup_capabilities(cli);
+ DATA_BLOB lm_response = data_blob_null;
+ DATA_BLOB nt_response = data_blob_null;
+ DATA_BLOB session_key = data_blob_null;
+ NTSTATUS result;
+ char *p;
+
+ if (passlen == 0) {
+ /* do nothing - guest login */
+ } else if (passlen != 24) {
+ if (lp_client_ntlmv2_auth()) {
+ DATA_BLOB server_chal;
+ DATA_BLOB names_blob;
+ server_chal = data_blob(cli->secblob.data, MIN(cli->secblob.length, 8));
+
+ /* note that the 'workgroup' here is a best guess - we don't know
+ the server's domain at this point. The 'server name' is also
+ dodgy...
+ */
+ names_blob = NTLMv2_generate_names_blob(cli->called.name, workgroup);
+
+ if (!SMBNTLMv2encrypt(user, workgroup, pass, &server_chal,
+ &names_blob,
+ &lm_response, &nt_response, &session_key)) {
+ data_blob_free(&names_blob);
+ data_blob_free(&server_chal);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ data_blob_free(&names_blob);
+ data_blob_free(&server_chal);
+
+ } else {
+ uchar nt_hash[16];
+ E_md4hash(pass, nt_hash);
+
+#ifdef LANMAN_ONLY
+ nt_response = data_blob_null;
+#else
+ nt_response = data_blob(NULL, 24);
+ SMBNTencrypt(pass,cli->secblob.data,nt_response.data);
+#endif
+ /* non encrypted password supplied. Ignore ntpass. */
+ if (lp_client_lanman_auth()) {
+ lm_response = data_blob(NULL, 24);
+ if (!SMBencrypt(pass,cli->secblob.data, lm_response.data)) {
+ /* Oops, the LM response is invalid, just put
+ the NT response there instead */
+ data_blob_free(&lm_response);
+ lm_response = data_blob(nt_response.data, nt_response.length);
+ }
+ } else {
+ /* LM disabled, place NT# in LM field instead */
+ lm_response = data_blob(nt_response.data, nt_response.length);
+ }
+
+ session_key = data_blob(NULL, 16);
+#ifdef LANMAN_ONLY
+ E_deshash(pass, session_key.data);
+ memset(&session_key.data[8], '\0', 8);
+#else
+ SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data);
+#endif
+ }
+#ifdef LANMAN_ONLY
+ cli_simple_set_signing(cli, session_key, lm_response);
+#else
+ cli_simple_set_signing(cli, session_key, nt_response);
+#endif
+ } else {
+ /* pre-encrypted password supplied. Only used for
+ security=server, can't do
+ signing because we don't have original key */
+
+ lm_response = data_blob(pass, passlen);
+ nt_response = data_blob(ntpass, ntpasslen);
+ }
+
+ /* send a session setup command */
+ memset(cli->outbuf,'\0',smb_size);
+
+ 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,lm_response.length);
+ SSVAL(cli->outbuf,smb_vwv8,nt_response.length);
+ SIVAL(cli->outbuf,smb_vwv11,capabilities);
+ p = smb_buf(cli->outbuf);
+ if (lm_response.length) {
+ memcpy(p,lm_response.data, lm_response.length); p += lm_response.length;
+ }
+ if (nt_response.length) {
+ memcpy(p,nt_response.data, nt_response.length); p += nt_response.length;
+ }
+ p += clistr_push(cli, p, user, -1, STR_TERMINATE);
+
+ /* Upper case here might help some NTLMv2 implementations */
+ 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);
+
+ if (!cli_send_smb(cli) || !cli_receive_smb(cli)) {
+ result = cli_nt_error(cli);
+ goto end;
+ }
+
+ /* show_msg(cli->inbuf); */
+
+ if (cli_is_error(cli)) {
+ result = cli_nt_error(cli);
+ goto end;
+ }
+
+ /* 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);
+
+ if (strstr(cli->server_type, "Samba")) {
+ cli->is_samba = True;
+ }
+
+ fstrcpy(cli->user_name, user);
+
+ if (session_key.data) {
+ /* Have plaintext orginal */
+ cli_set_session_key(cli, session_key);
+ }
+
+ result = NT_STATUS_OK;
+end:
+ data_blob_free(&lm_response);
+ data_blob_free(&nt_response);
+ data_blob_free(&session_key);
+ return result;
+}
+
+/****************************************************************************
+ Send a extended security session setup blob
+****************************************************************************/
+
+static bool cli_session_setup_blob_send(struct cli_state *cli, DATA_BLOB blob)
+{
+ uint32 capabilities = cli_session_setup_capabilities(cli);
+ char *p;
+
+ capabilities |= CAP_EXTENDED_SECURITY;
+
+ /* send a session setup command */
+ memset(cli->outbuf,'\0',smb_size);
+
+ cli_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);
+ return cli_send_smb(cli);
+}
+
+/****************************************************************************
+ Send a extended security session setup blob, returning a reply blob.
+****************************************************************************/
+
+static DATA_BLOB cli_session_setup_blob_receive(struct cli_state *cli)
+{
+ DATA_BLOB blob2 = data_blob_null;
+ char *p;
+ size_t len;
+
+ 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
+/****************************************************************************
+ Send a extended security session setup blob, returning a reply blob.
+****************************************************************************/
+
+/* The following is calculated from :
+ * (smb_size-4) = 35
+ * (smb_wcnt * 2) = 24 (smb_wcnt == 12 in cli_session_setup_blob_send() )
+ * (strlen("Unix") + 1 + strlen("Samba") + 1) * 2 = 22 (unicode strings at
+ * end of packet.
+ */
+
+#define BASE_SESSSETUP_BLOB_PACKET_SIZE (35 + 24 + 22)
+
+static bool cli_session_setup_blob(struct cli_state *cli, DATA_BLOB blob, DATA_BLOB session_key_krb5)
+{
+ int32 remaining = blob.length;
+ int32 cur = 0;
+ DATA_BLOB send_blob = data_blob_null;
+ int32 max_blob_size = 0;
+ DATA_BLOB receive_blob = data_blob_null;
+
+ if (cli->max_xmit < BASE_SESSSETUP_BLOB_PACKET_SIZE + 1) {
+ DEBUG(0,("cli_session_setup_blob: cli->max_xmit too small "
+ "(was %u, need minimum %u)\n",
+ (unsigned int)cli->max_xmit,
+ BASE_SESSSETUP_BLOB_PACKET_SIZE));
+ cli_set_nt_error(cli, NT_STATUS_INVALID_PARAMETER);
+ return False;
+ }
+
+ max_blob_size = cli->max_xmit - BASE_SESSSETUP_BLOB_PACKET_SIZE;
+
+ while ( remaining > 0) {
+ if (remaining >= max_blob_size) {
+ send_blob.length = max_blob_size;
+ remaining -= max_blob_size;
+ } else {
+ DATA_BLOB null_blob = data_blob_null;
+
+ send_blob.length = remaining;
+ remaining = 0;
+
+ /* This is the last packet in the sequence - turn signing on. */
+ cli_simple_set_signing(cli, session_key_krb5, null_blob);
+ }
+
+ send_blob.data = &blob.data[cur];
+ cur += send_blob.length;
+
+ DEBUG(10, ("cli_session_setup_blob: Remaining (%u) sending (%u) current (%u)\n",
+ (unsigned int)remaining,
+ (unsigned int)send_blob.length,
+ (unsigned int)cur ));
+
+ if (!cli_session_setup_blob_send(cli, send_blob)) {
+ DEBUG(0, ("cli_session_setup_blob: send failed\n"));
+ return False;
+ }
+
+ receive_blob = cli_session_setup_blob_receive(cli);
+ data_blob_free(&receive_blob);
+
+ if (cli_is_error(cli) &&
+ !NT_STATUS_EQUAL( cli_get_nt_error(cli),
+ NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ DEBUG(0, ("cli_session_setup_blob: receive failed "
+ "(%s)\n", nt_errstr(cli_get_nt_error(cli))));
+ cli->vuid = 0;
+ return False;
+ }
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Use in-memory credentials cache
+****************************************************************************/
+
+static void use_in_memory_ccache(void) {
+ setenv(KRB5_ENV_CCNAME, "MEMORY:cliconnect", 1);
+}
+
+/****************************************************************************
+ Do a spnego/kerberos encrypted session setup.
+****************************************************************************/
+
+static ADS_STATUS cli_session_setup_kerberos(struct cli_state *cli, const char *principal, const char *workgroup)
+{
+ DATA_BLOB negTokenTarg;
+ DATA_BLOB session_key_krb5;
+ int rc;
+
+ DEBUG(2,("Doing kerberos session setup\n"));
+
+ /* generate the encapsulated kerberos5 ticket */
+ rc = spnego_gen_negTokenTarg(principal, 0, &negTokenTarg, &session_key_krb5, 0, NULL);
+
+ if (rc) {
+ DEBUG(1, ("cli_session_setup_kerberos: spnego_gen_negTokenTarg failed: %s\n",
+ error_message(rc)));
+ return ADS_ERROR_KRB5(rc);
+ }
+
+#if 0
+ file_save("negTokenTarg.dat", negTokenTarg.data, negTokenTarg.length);
+#endif
+
+ if (!cli_session_setup_blob(cli, negTokenTarg, session_key_krb5)) {
+ data_blob_free(&negTokenTarg);
+ data_blob_free(&session_key_krb5);
+ return ADS_ERROR_NT(cli_nt_error(cli));
+ }
+
+ cli_set_session_key(cli, session_key_krb5);
+
+ data_blob_free(&negTokenTarg);
+ data_blob_free(&session_key_krb5);
+
+ if (cli_is_error(cli)) {
+ if (NT_STATUS_IS_OK(cli_nt_error(cli))) {
+ return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+ }
+ }
+ return ADS_ERROR_NT(cli_nt_error(cli));
+}
+#endif /* HAVE_KRB5 */
+
+
+/****************************************************************************
+ Do a spnego/NTLMSSP encrypted session setup.
+****************************************************************************/
+
+static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *user,
+ const char *pass, const char *domain)
+{
+ struct ntlmssp_state *ntlmssp_state;
+ NTSTATUS nt_status;
+ int turn = 1;
+ DATA_BLOB msg1;
+ DATA_BLOB blob = data_blob_null;
+ DATA_BLOB blob_in = data_blob_null;
+ DATA_BLOB blob_out = data_blob_null;
+
+ cli_temp_set_signing(cli);
+
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_client_start(&ntlmssp_state))) {
+ return nt_status;
+ }
+ ntlmssp_want_feature(ntlmssp_state, NTLMSSP_FEATURE_SESSION_KEY);
+
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, user))) {
+ return nt_status;
+ }
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, domain))) {
+ return nt_status;
+ }
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_password(ntlmssp_state, pass))) {
+ return nt_status;
+ }
+
+ do {
+ nt_status = ntlmssp_update(ntlmssp_state,
+ blob_in, &blob_out);
+ data_blob_free(&blob_in);
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED) || NT_STATUS_IS_OK(nt_status)) {
+ if (turn == 1) {
+ /* and wrap it in a SPNEGO wrapper */
+ msg1 = gen_negTokenInit(OID_NTLMSSP, blob_out);
+ } else {
+ /* wrap it in SPNEGO */
+ msg1 = spnego_gen_auth(blob_out);
+ }
+
+ /* now send that blob on its way */
+ if (!cli_session_setup_blob_send(cli, msg1)) {
+ DEBUG(3, ("Failed to send NTLMSSP/SPNEGO blob to server!\n"));
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ } else {
+ blob = cli_session_setup_blob_receive(cli);
+
+ nt_status = cli_nt_error(cli);
+ if (cli_is_error(cli) && NT_STATUS_IS_OK(nt_status)) {
+ if (cli->smb_rw_error == SMB_READ_BAD_SIG) {
+ nt_status = NT_STATUS_ACCESS_DENIED;
+ } else {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+ }
+ data_blob_free(&msg1);
+ }
+
+ if (!blob.length) {
+ if (NT_STATUS_IS_OK(nt_status)) {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ }
+ } else if ((turn == 1) &&
+ NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ DATA_BLOB tmp_blob = data_blob_null;
+ /* the server might give us back two challenges */
+ if (!spnego_parse_challenge(blob, &blob_in,
+ &tmp_blob)) {
+ DEBUG(3,("Failed to parse challenges\n"));
+ nt_status = NT_STATUS_INVALID_PARAMETER;
+ }
+ data_blob_free(&tmp_blob);
+ } else {
+ if (!spnego_parse_auth_response(blob, nt_status, OID_NTLMSSP,
+ &blob_in)) {
+ DEBUG(3,("Failed to parse auth response\n"));
+ if (NT_STATUS_IS_OK(nt_status)
+ || NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED))
+ nt_status = NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+ data_blob_free(&blob);
+ data_blob_free(&blob_out);
+ turn++;
+ } while (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED));
+
+ data_blob_free(&blob_in);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+
+ DATA_BLOB key = data_blob(ntlmssp_state->session_key.data,
+ ntlmssp_state->session_key.length);
+ DATA_BLOB null_blob = data_blob_null;
+ bool res;
+
+ fstrcpy(cli->server_domain, ntlmssp_state->server_domain);
+ cli_set_session_key(cli, ntlmssp_state->session_key);
+
+ res = cli_simple_set_signing(cli, key, null_blob);
+
+ data_blob_free(&key);
+
+ if (res) {
+
+ /* 'resign' the last message, so we get the right sequence numbers
+ for checking the first reply from the server */
+ cli_calculate_sign_mac(cli, cli->outbuf);
+
+ if (!cli_check_sign_mac(cli, cli->inbuf)) {
+ nt_status = NT_STATUS_ACCESS_DENIED;
+ }
+ }
+ }
+
+ /* we have a reference conter on ntlmssp_state, if we are signing
+ then the state will be kept by the signing engine */
+
+ ntlmssp_end(&ntlmssp_state);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ cli->vuid = 0;
+ }
+ return nt_status;
+}
+
+/****************************************************************************
+ Do a spnego encrypted session setup.
+
+ user_domain: The shortname of the domain the user/machine is a member of.
+ dest_realm: The realm we're connecting to, if NULL we use our default realm.
+****************************************************************************/
+
+ADS_STATUS cli_session_setup_spnego(struct cli_state *cli, const char *user,
+ const char *pass, const char *user_domain,
+ const char * dest_realm)
+{
+ char *principal = NULL;
+ char *OIDs[ASN1_MAX_OIDS];
+ int i;
+ DATA_BLOB blob;
+ const char *p = NULL;
+ char *account = NULL;
+
+ DEBUG(3,("Doing spnego session setup (blob length=%lu)\n", (unsigned long)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
+
+ /* there is 16 bytes of GUID before the real spnego packet starts */
+ blob = data_blob(cli->secblob.data+16, cli->secblob.length-16);
+
+ /* The server sent us the first part of the SPNEGO exchange in the
+ * negprot reply. It is WRONG to depend on the principal sent in the
+ * negprot reply, but right now we do it. If we don't receive one,
+ * we try to best guess, then fall back to NTLM. */
+ if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
+ data_blob_free(&blob);
+ return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+ }
+ data_blob_free(&blob);
+
+ /* 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) {
+ cli->got_kerberos_mechanism = True;
+ }
+ free(OIDs[i]);
+ }
+
+ DEBUG(3,("got principal=%s\n", principal ? principal : "<null>"));
+
+ fstrcpy(cli->user_name, user);
+
+#ifdef HAVE_KRB5
+ /* If password is set we reauthenticate to kerberos server
+ * and do not store results */
+
+ if (cli->got_kerberos_mechanism && cli->use_kerberos) {
+ ADS_STATUS rc;
+
+ if (pass && *pass) {
+ int ret;
+
+ use_in_memory_ccache();
+ ret = kerberos_kinit_password(user, pass, 0 /* no time correction for now */, NULL);
+
+ if (ret){
+ SAFE_FREE(principal);
+ DEBUG(0, ("Kinit failed: %s\n", error_message(ret)));
+ if (cli->fallback_after_kerberos)
+ goto ntlmssp;
+ return ADS_ERROR_KRB5(ret);
+ }
+ }
+
+ /* If we get a bad principal, try to guess it if
+ we have a valid host NetBIOS name.
+ */
+ if (strequal(principal, ADS_IGNORE_PRINCIPAL)) {
+ SAFE_FREE(principal);
+ }
+
+ if (principal == NULL &&
+ !is_ipaddress(cli->desthost) &&
+ !strequal(star_smbserver_name,
+ cli->desthost)) {
+ char *realm = NULL;
+ char *machine = NULL;
+ char *host = NULL;
+ DEBUG(3,("cli_session_setup_spnego: got a "
+ "bad server principal, trying to guess ...\n"));
+
+ host = strchr_m(cli->desthost, '.');
+ if (host) {
+ machine = SMB_STRNDUP(cli->desthost,
+ host - cli->desthost);
+ } else {
+ machine = SMB_STRDUP(cli->desthost);
+ }
+ if (machine == NULL) {
+ return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ }
+
+ if (dest_realm) {
+ realm = SMB_STRDUP(dest_realm);
+ strupper_m(realm);
+ } else {
+ realm = kerberos_get_default_realm_from_ccache();
+ }
+ if (realm && *realm) {
+ if (asprintf(&principal, "%s$@%s",
+ machine, realm) < 0) {
+ SAFE_FREE(machine);
+ SAFE_FREE(realm);
+ return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ }
+ DEBUG(3,("cli_session_setup_spnego: guessed "
+ "server principal=%s\n",
+ principal ? principal : "<null>"));
+ }
+ SAFE_FREE(machine);
+ SAFE_FREE(realm);
+ }
+
+ if (principal) {
+ rc = cli_session_setup_kerberos(cli, principal,
+ dest_realm);
+ if (ADS_ERR_OK(rc) || !cli->fallback_after_kerberos) {
+ SAFE_FREE(principal);
+ return rc;
+ }
+ }
+ }
+#endif
+
+ SAFE_FREE(principal);
+
+ntlmssp:
+
+ account = talloc_strdup(talloc_tos(), user);
+ if (!account) {
+ return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ }
+
+ /* when falling back to ntlmssp while authenticating with a machine
+ * account strip off the realm - gd */
+
+ if ((p = strchr_m(user, '@')) != NULL) {
+ account[PTR_DIFF(p,user)] = '\0';
+ }
+
+ return ADS_ERROR_NT(cli_session_setup_ntlmssp(cli, account, pass, user_domain));
+}
+
+/****************************************************************************
+ 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.
+****************************************************************************/
+
+NTSTATUS cli_session_setup(struct cli_state *cli,
+ const char *user,
+ const char *pass, int passlen,
+ const char *ntpass, int ntpasslen,
+ const char *workgroup)
+{
+ char *p;
+ fstring user2;
+
+ if (user) {
+ fstrcpy(user2, user);
+ } else {
+ user2[0] ='\0';
+ }
+
+ if (!workgroup) {
+ workgroup = "";
+ }
+
+ /* allow for workgroups as part of the username */
+ 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 NT_STATUS_OK;
+ }
+
+ /* 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) {
+ if (!lp_client_lanman_auth() && passlen != 24 && (*pass)) {
+ DEBUG(1, ("Server requested LM password but 'client lanman auth'"
+ " is disabled\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0 &&
+ !lp_client_plaintext_auth() && (*pass)) {
+ DEBUG(1, ("Server requested plaintext password but "
+ "'client plaintext auth' is disabled\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return cli_session_setup_lanman2(cli, user, pass, passlen,
+ workgroup);
+ }
+
+ /* 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 & NEGOTIATE_SECURITY_USER_LEVEL) == 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 & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
+ if (!lp_client_plaintext_auth() && (*pass)) {
+ DEBUG(1, ("Server requested plaintext password but "
+ "'client plaintext auth' is disabled\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ return cli_session_setup_plaintext(cli, user, pass, workgroup);
+ }
+
+ /* if the server supports extended security then use SPNEGO */
+
+ if (cli->capabilities & CAP_EXTENDED_SECURITY) {
+ ADS_STATUS status = cli_session_setup_spnego(cli, user, pass,
+ workgroup, NULL);
+ if (!ADS_ERR_OK(status)) {
+ DEBUG(3, ("SPNEGO login failed: %s\n", ads_errstr(status)));
+ return ads_ntstatus(status);
+ }
+ } else {
+ NTSTATUS status;
+
+ /* otherwise do a NT1 style session setup */
+ status = cli_session_setup_nt1(cli, user, pass, passlen,
+ ntpass, ntpasslen, workgroup);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3,("cli_session_setup: NT1 session setup "
+ "failed: %s\n", nt_errstr(status)));
+ return status;
+ }
+ }
+
+ if (strstr(cli->server_type, "Samba")) {
+ cli->is_samba = True;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Send a uloggoff.
+*****************************************************************************/
+
+bool cli_ulogoff(struct cli_state *cli)
+{
+ memset(cli->outbuf,'\0',smb_size);
+ cli_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;
+
+ if (cli_is_error(cli)) {
+ return False;
+ }
+
+ cli->cnum = -1;
+ return True;
+}
+
+/****************************************************************************
+ 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;
+ 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 & NEGOTIATE_SECURITY_USER_LEVEL) {
+ passlen = 1;
+ pass = "";
+ } else if (!pass) {
+ DEBUG(1, ("Server not using user level security and no password supplied.\n"));
+ return False;
+ }
+
+ if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) &&
+ *pass && passlen != 24) {
+ if (!lp_client_lanman_auth()) {
+ DEBUG(1, ("Server requested LANMAN password "
+ "(share-level security) but "
+ "'client lanman auth' is disabled\n"));
+ return False;
+ }
+
+ /*
+ * Non-encrypted passwords - convert to DOS codepage before encryption.
+ */
+ passlen = 24;
+ SMBencrypt(pass,cli->secblob.data,(uchar *)pword);
+ } else {
+ if((cli->sec_mode & (NEGOTIATE_SECURITY_USER_LEVEL|NEGOTIATE_SECURITY_CHALLENGE_RESPONSE)) == 0) {
+ if (!lp_client_plaintext_auth() && (*pass)) {
+ DEBUG(1, ("Server requested plaintext "
+ "password but 'client plaintext "
+ "auth' is disabled\n"));
+ return False;
+ }
+
+ /*
+ * Non-encrypted passwords - convert to DOS codepage before using.
+ */
+ passlen = clistr_push(cli, pword, pass, sizeof(pword), STR_TERMINATE);
+
+ } else {
+ if (passlen) {
+ memcpy(pword, pass, passlen);
+ }
+ }
+ }
+
+ slprintf(fullshare, sizeof(fullshare)-1,
+ "\\\\%s\\%s", cli->desthost, share);
+
+ cli_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_vwv2,TCONX_FLAG_EXTENDED_RESPONSE);
+ SSVAL(cli->outbuf,smb_vwv3,passlen);
+
+ p = smb_buf(cli->outbuf);
+ if (passlen) {
+ memcpy(p,pword,passlen);
+ }
+ p += passlen;
+ p += clistr_push(cli, p, fullshare, -1, STR_TERMINATE |STR_UPPER);
+ p += clistr_push(cli, p, dev, -1, STR_TERMINATE |STR_UPPER | STR_ASCII);
+
+ 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 (cli->protocol >= PROTOCOL_NT1 &&
+ smb_buflen(cli->inbuf) == 3) {
+ /* almost certainly win95 - enable bug fixes */
+ cli->win95 = True;
+ }
+
+ /* Make sure that we have the optional support 16-bit field. WCT > 2 */
+ /* Avoids issues when connecting to Win9x boxes sharing files */
+
+ cli->dfsroot = False;
+ if ( (CVAL(cli->inbuf, smb_wct))>2 && cli->protocol >= PROTOCOL_LANMAN2 )
+ cli->dfsroot = (SVAL( cli->inbuf, smb_vwv2 ) & SMB_SHARE_IN_DFS) ? True : False;
+
+ 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);
+ cli_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;
+
+ if (cli_is_error(cli)) {
+ return False;
+ }
+
+ cli->cnum = -1;
+ return True;
+}
+
+/****************************************************************************
+ Send a negprot command.
+****************************************************************************/
+
+void cli_negprot_send(struct cli_state *cli)
+{
+ char *p;
+ int numprots;
+
+ if (cli->protocol < PROTOCOL_NT1)
+ cli->use_spnego = False;
+
+ memset(cli->outbuf,'\0',smb_size);
+
+ /* setup the protocol strings */
+ cli_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;
+
+ if (cli->protocol < PROTOCOL_NT1)
+ cli->use_spnego = False;
+
+ 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;
+
+ cli_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) && cli->sign_info.mandatory_signing) {
+ DEBUG(0,("cli_negprot: SMB signing is mandatory and the selected protocol level doesn't support it.\n"));
+ return False;
+ }
+
+ if (cli->protocol >= PROTOCOL_NT1) {
+ struct timespec ts;
+ /* 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 */
+ ts = interpret_long_date(cli->inbuf+smb_vwv11+1);
+ cli->servertime = ts.tv_sec;
+ 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);
+ }
+
+ /*
+ * As signing is slow we only turn it on if either the client or
+ * the server require it. JRA.
+ */
+
+ if (cli->sec_mode & NEGOTIATE_SECURITY_SIGNATURES_REQUIRED) {
+ /* Fail if server says signing is mandatory and we don't want to support it. */
+ if (!cli->sign_info.allow_smb_signing) {
+ DEBUG(0,("cli_negprot: SMB signing is mandatory and we have disabled it.\n"));
+ return False;
+ }
+ cli->sign_info.negotiated_smb_signing = True;
+ cli->sign_info.mandatory_signing = True;
+ } else if (cli->sign_info.mandatory_signing && cli->sign_info.allow_smb_signing) {
+ /* Fail if client says signing is mandatory and the server doesn't support it. */
+ if (!(cli->sec_mode & NEGOTIATE_SECURITY_SIGNATURES_ENABLED)) {
+ DEBUG(1,("cli_negprot: SMB signing is mandatory and the server doesn't support it.\n"));
+ return False;
+ }
+ cli->sign_info.negotiated_smb_signing = True;
+ cli->sign_info.mandatory_signing = True;
+ } else if (cli->sec_mode & NEGOTIATE_SECURITY_SIGNATURES_ENABLED) {
+ cli->sign_info.negotiated_smb_signing = True;
+ }
+
+ if (cli->capabilities & (CAP_LARGE_READX|CAP_LARGE_WRITEX)) {
+ SAFE_FREE(cli->outbuf);
+ SAFE_FREE(cli->inbuf);
+ cli->outbuf = (char *)SMB_MALLOC(CLI_SAMBA_MAX_LARGE_READX_SIZE+LARGE_WRITEX_HDR_SIZE+SAFETY_MARGIN);
+ cli->inbuf = (char *)SMB_MALLOC(CLI_SAMBA_MAX_LARGE_READX_SIZE+LARGE_WRITEX_HDR_SIZE+SAFETY_MARGIN);
+ cli->bufsize = CLI_SAMBA_MAX_LARGE_READX_SIZE + LARGE_WRITEX_HDR_SIZE;
+ }
+
+ } else if (cli->protocol >= PROTOCOL_LANMAN1) {
+ cli->use_spnego = False;
+ cli->sec_mode = SVAL(cli->inbuf,smb_vwv1);
+ cli->max_xmit = SVAL(cli->inbuf,smb_vwv2);
+ cli->max_mux = SVAL(cli->inbuf, smb_vwv3);
+ 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 = cli_make_unix_date(cli,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->use_spnego = False;
+ cli->sec_mode = 0;
+ cli->serverzone = get_time_zone(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;
+
+ 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);
+
+ /* 445 doesn't have session request */
+ if (cli->port == 445)
+ return True;
+
+ /* send a session request (RFC 1002) */
+ /* setup the packet length
+ * Remove four bytes from the length count, since the length
+ * field in the NBT Session Service header counts the number
+ * of bytes which follow. The cli_send_smb() function knows
+ * about this and accounts for those four bytes.
+ * CRH.
+ */
+ len -= 4;
+ _smb_setlen(cli->outbuf,len);
+ SCVAL(cli->outbuf,0,0x81);
+
+ 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;
+ };
+ */
+ uint16_t port = (CVAL(cli->inbuf,8)<<8)+CVAL(cli->inbuf,9);
+ struct in_addr dest_ip;
+
+ /* SESSION RETARGET */
+ putip((char *)&dest_ip,cli->inbuf+4);
+ in_addr_to_sockaddr_storage(&cli->dest_ss, dest_ip);
+
+ cli->fd = open_socket_out(SOCK_STREAM,
+ &cli->dest_ss,
+ port,
+ LONG_CONNECT_TIMEOUT);
+ if (cli->fd == -1)
+ return False;
+
+ DEBUG(3,("Retargeted\n"));
+
+ set_socket_options(cli->fd, lp_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 */
+
+ 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.
+****************************************************************************/
+
+NTSTATUS cli_connect(struct cli_state *cli,
+ const char *host,
+ struct sockaddr_storage *dest_ss)
+
+{
+ int name_type = 0x20;
+ TALLOC_CTX *frame = talloc_stackframe();
+ unsigned int num_addrs = 0;
+ unsigned int i = 0;
+ struct sockaddr_storage *ss_arr = NULL;
+ char *p = NULL;
+
+ /* reasonable default hostname */
+ if (!host) {
+ host = star_smbserver_name;
+ }
+
+ 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 (!dest_ss || is_zero_addr(dest_ss)) {
+ NTSTATUS status =resolve_name_list(frame,
+ cli->desthost,
+ name_type,
+ &ss_arr,
+ &num_addrs);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_BAD_NETWORK_NAME;
+ }
+ } else {
+ num_addrs = 1;
+ ss_arr = TALLOC_P(frame, struct sockaddr_storage);
+ if (!ss_arr) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ *ss_arr = *dest_ss;
+ }
+
+ for (i = 0; i < num_addrs; i++) {
+ cli->dest_ss = ss_arr[i];
+ if (getenv("LIBSMB_PROG")) {
+ cli->fd = sock_exec(getenv("LIBSMB_PROG"));
+ } else {
+ /* try 445 first, then 139 */
+ uint16_t port = cli->port?cli->port:445;
+ cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ss,
+ port, cli->timeout);
+ if (cli->fd == -1 && cli->port == 0) {
+ port = 139;
+ cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ss,
+ port, cli->timeout);
+ }
+ if (cli->fd != -1) {
+ cli->port = port;
+ }
+ }
+ if (cli->fd == -1) {
+ char addr[INET6_ADDRSTRLEN];
+ print_sockaddr(addr, sizeof(addr), &ss_arr[i]);
+ DEBUG(2,("Error connecting to %s (%s)\n",
+ dest_ss?addr:host,strerror(errno)));
+ } else {
+ /* Exit from loop on first connection. */
+ break;
+ }
+ }
+
+ if (cli->fd == -1) {
+ TALLOC_FREE(frame);
+ return map_nt_error_from_unix(errno);
+ }
+
+ if (dest_ss) {
+ *dest_ss = cli->dest_ss;
+ }
+
+ set_socket_options(cli->fd, lp_socket_options());
+
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+}
+
+/**
+ establishes a connection to after the negprot.
+ @param output_cli A fully initialised cli structure, non-null only on success
+ @param dest_host The netbios name of the remote host
+ @param dest_ss (optional) The the destination IP, NULL for name based lookup
+ @param port (optional) The destination port (0 for default)
+ @param retry bool. Did this connection fail with a retryable error ?
+
+*/
+NTSTATUS cli_start_connection(struct cli_state **output_cli,
+ const char *my_name,
+ const char *dest_host,
+ struct sockaddr_storage *dest_ss, int port,
+ int signing_state, int flags,
+ bool *retry)
+{
+ NTSTATUS nt_status;
+ struct nmb_name calling;
+ struct nmb_name called;
+ struct cli_state *cli;
+ struct sockaddr_storage ss;
+
+ if (retry)
+ *retry = False;
+
+ if (!my_name)
+ my_name = global_myname();
+
+ if (!(cli = cli_initialise())) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ make_nmb_name(&calling, my_name, 0x0);
+ make_nmb_name(&called , dest_host, 0x20);
+
+ if (cli_set_port(cli, port) != port) {
+ cli_shutdown(cli);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ cli_set_timeout(cli, 10000); /* 10 seconds. */
+
+ if (dest_ss) {
+ ss = *dest_ss;
+ } else {
+ zero_addr(&ss);
+ }
+
+again:
+
+ DEBUG(3,("Connecting to host=%s\n", dest_host));
+
+ nt_status = cli_connect(cli, dest_host, &ss);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ char addr[INET6_ADDRSTRLEN];
+ print_sockaddr(addr, sizeof(addr), &ss);
+ DEBUG(1,("cli_start_connection: failed to connect to %s (%s). Error %s\n",
+ nmb_namestr(&called), addr, nt_errstr(nt_status) ));
+ cli_shutdown(cli);
+ return nt_status;
+ }
+
+ if (retry)
+ *retry = True;
+
+ if (!cli_session_request(cli, &calling, &called)) {
+ char *p;
+ DEBUG(1,("session request to %s failed (%s)\n",
+ called.name, cli_errstr(cli)));
+ if ((p=strchr(called.name, '.')) && !is_ipaddress(called.name)) {
+ *p = 0;
+ goto again;
+ }
+ if (strcmp(called.name, star_smbserver_name)) {
+ make_nmb_name(&called , star_smbserver_name, 0x20);
+ goto again;
+ }
+ return NT_STATUS_BAD_NETWORK_NAME;
+ }
+
+ cli_setup_signing_state(cli, signing_state);
+
+ if (flags & CLI_FULL_CONNECTION_DONT_SPNEGO)
+ cli->use_spnego = False;
+ else if (flags & CLI_FULL_CONNECTION_USE_KERBEROS)
+ cli->use_kerberos = True;
+
+ if ((flags & CLI_FULL_CONNECTION_FALLBACK_AFTER_KERBEROS) &&
+ cli->use_kerberos) {
+ cli->fallback_after_kerberos = true;
+ }
+
+ if (!cli_negprot(cli)) {
+ DEBUG(1,("failed negprot\n"));
+ nt_status = cli_nt_error(cli);
+ if (NT_STATUS_IS_OK(nt_status)) {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ }
+ cli_shutdown(cli);
+ return nt_status;
+ }
+
+ *output_cli = cli;
+ return NT_STATUS_OK;
+}
+
+
+/**
+ establishes a connection right up to doing tconX, password specified.
+ @param output_cli A fully initialised cli structure, non-null only on success
+ @param dest_host The netbios name of the remote host
+ @param dest_ip (optional) The the destination IP, NULL for name based lookup
+ @param port (optional) The destination port (0 for default)
+ @param service (optional) The share to make the connection to. Should be 'unqualified' in any way.
+ @param service_type The 'type' of serivice.
+ @param user Username, unix string
+ @param domain User's domain
+ @param password User's password, unencrypted unix string.
+ @param retry bool. Did this connection fail with a retryable error ?
+*/
+
+NTSTATUS cli_full_connection(struct cli_state **output_cli,
+ const char *my_name,
+ const char *dest_host,
+ struct sockaddr_storage *dest_ss, int port,
+ const char *service, const char *service_type,
+ const char *user, const char *domain,
+ const char *password, int flags,
+ int signing_state,
+ bool *retry)
+{
+ NTSTATUS nt_status;
+ struct cli_state *cli = NULL;
+ int pw_len = password ? strlen(password)+1 : 0;
+
+ *output_cli = NULL;
+
+ if (password == NULL) {
+ password = "";
+ }
+
+ nt_status = cli_start_connection(&cli, my_name, dest_host,
+ dest_ss, port, signing_state,
+ flags, retry);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ nt_status = cli_session_setup(cli, user, password, pw_len, password,
+ pw_len, domain);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+
+ if (!(flags & CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK)) {
+ DEBUG(1,("failed session setup with %s\n",
+ nt_errstr(nt_status)));
+ cli_shutdown(cli);
+ return nt_status;
+ }
+
+ nt_status = cli_session_setup(cli, "", "", 0, "", 0, domain);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1,("anonymous failed session setup with %s\n",
+ nt_errstr(nt_status)));
+ cli_shutdown(cli);
+ return nt_status;
+ }
+ }
+
+ if (service) {
+ if (!cli_send_tconX(cli, service, service_type, password, pw_len)) {
+ nt_status = cli_nt_error(cli);
+ DEBUG(1,("failed tcon_X with %s\n", nt_errstr(nt_status)));
+ cli_shutdown(cli);
+ if (NT_STATUS_IS_OK(nt_status)) {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ }
+ return nt_status;
+ }
+ }
+
+ cli_init_creds(cli, user, domain, password);
+
+ *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 **ppcli, const char *srchost, const char *desthost,
+ struct sockaddr_storage *pdest_ss)
+{
+ 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, star_smbserver_name, 0x20);
+ } else {
+ make_nmb_name(&called, desthost, 0x20);
+ }
+
+ if (!cli_session_request(*ppcli, &calling, &called)) {
+ NTSTATUS status;
+ struct nmb_name smbservername;
+
+ make_nmb_name(&smbservername, star_smbserver_name, 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(*ppcli) ));
+ return False;
+ }
+
+ /* Try again... */
+ cli_shutdown(*ppcli);
+
+ *ppcli = cli_initialise();
+ if (!*ppcli) {
+ /* Out of memory... */
+ return False;
+ }
+
+ status = cli_connect(*ppcli, desthost, pdest_ss);
+ if (!NT_STATUS_IS_OK(status) ||
+ !cli_session_request(*ppcli, &calling, &smbservername)) {
+ DEBUG(0,("attempt_netbios_session_request: %s rejected the session for \
+name *SMBSERVER with error %s\n", desthost, cli_errstr(*ppcli) ));
+ return False;
+ }
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Send an old style tcon.
+****************************************************************************/
+NTSTATUS cli_raw_tcon(struct cli_state *cli,
+ const char *service, const char *pass, const char *dev,
+ uint16 *max_xmit, uint16 *tid)
+{
+ char *p;
+
+ if (!lp_client_plaintext_auth() && (*pass)) {
+ DEBUG(1, ("Server requested plaintext password but 'client "
+ "plaintext auth' is disabled\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ cli_set_message(cli->outbuf, 0, 0, True);
+ SCVAL(cli->outbuf,smb_com,SMBtcon);
+ cli_setup_packet(cli);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4; p += clistr_push(cli, p, service, -1, STR_TERMINATE | STR_NOALIGN);
+ *p++ = 4; p += clistr_push(cli, p, pass, -1, STR_TERMINATE | STR_NOALIGN);
+ *p++ = 4; p += clistr_push(cli, p, dev, -1, STR_TERMINATE | STR_NOALIGN);
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+
+ if (cli_is_error(cli)) {
+ return cli_nt_error(cli);
+ }
+
+ *max_xmit = SVAL(cli->inbuf, smb_vwv0);
+ *tid = SVAL(cli->inbuf, smb_vwv1);
+
+ return NT_STATUS_OK;
+}
+
+/* Return a cli_state pointing at the IPC$ share for the given server */
+
+struct cli_state *get_ipc_connect(char *server,
+ struct sockaddr_storage *server_ss,
+ const struct user_auth_info *user_info)
+{
+ struct cli_state *cli;
+ NTSTATUS nt_status;
+ uint32_t flags = CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK;
+
+ if (user_info->use_kerberos) {
+ flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
+ }
+
+ nt_status = cli_full_connection(&cli, NULL, server, server_ss, 0, "IPC$", "IPC",
+ user_info->username ? user_info->username : "",
+ lp_workgroup(),
+ user_info->password ? user_info->password : "",
+ flags,
+ Undefined, NULL);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ return cli;
+ } else if (is_ipaddress(server)) {
+ /* windows 9* needs a correct NMB name for connections */
+ fstring remote_name;
+
+ if (name_status_find("*", 0, 0, server_ss, remote_name)) {
+ cli = get_ipc_connect(remote_name, server_ss, user_info);
+ if (cli)
+ return cli;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Given the IP address of a master browser on the network, return its
+ * workgroup and connect to it.
+ *
+ * This function is provided to allow additional processing beyond what
+ * get_ipc_connect_master_ip_bcast() does, e.g. to retrieve the list of master
+ * browsers and obtain each master browsers' list of domains (in case the
+ * first master browser is recently on the network and has not yet
+ * synchronized with other master browsers and therefore does not yet have the
+ * entire network browse list)
+ */
+
+struct cli_state *get_ipc_connect_master_ip(TALLOC_CTX *ctx,
+ struct ip_service *mb_ip,
+ const struct user_auth_info *user_info,
+ char **pp_workgroup_out)
+{
+ char addr[INET6_ADDRSTRLEN];
+ fstring name;
+ struct cli_state *cli;
+ struct sockaddr_storage server_ss;
+
+ *pp_workgroup_out = NULL;
+
+ print_sockaddr(addr, sizeof(addr), &mb_ip->ss);
+ DEBUG(99, ("Looking up name of master browser %s\n",
+ addr));
+
+ /*
+ * Do a name status query to find out the name of the master browser.
+ * We use <01><02>__MSBROWSE__<02>#01 if *#00 fails because a domain
+ * master browser will not respond to a wildcard query (or, at least,
+ * an NT4 server acting as the domain master browser will not).
+ *
+ * We might be able to use ONLY the query on MSBROWSE, but that's not
+ * yet been tested with all Windows versions, so until it is, leave
+ * the original wildcard query as the first choice and fall back to
+ * MSBROWSE if the wildcard query fails.
+ */
+ if (!name_status_find("*", 0, 0x1d, &mb_ip->ss, name) &&
+ !name_status_find(MSBROWSE, 1, 0x1d, &mb_ip->ss, name)) {
+
+ DEBUG(99, ("Could not retrieve name status for %s\n",
+ addr));
+ return NULL;
+ }
+
+ if (!find_master_ip(name, &server_ss)) {
+ DEBUG(99, ("Could not find master ip for %s\n", name));
+ return NULL;
+ }
+
+ *pp_workgroup_out = talloc_strdup(ctx, name);
+
+ DEBUG(4, ("found master browser %s, %s\n", name, addr));
+
+ print_sockaddr(addr, sizeof(addr), &server_ss);
+ cli = get_ipc_connect(addr, &server_ss, user_info);
+
+ return cli;
+}
+
+/*
+ * Return the IP address and workgroup of a master browser on the network, and
+ * connect to it.
+ */
+
+struct cli_state *get_ipc_connect_master_ip_bcast(TALLOC_CTX *ctx,
+ const struct user_auth_info *user_info,
+ char **pp_workgroup_out)
+{
+ struct ip_service *ip_list;
+ struct cli_state *cli;
+ int i, count;
+
+ *pp_workgroup_out = NULL;
+
+ DEBUG(99, ("Do broadcast lookup for workgroups on local network\n"));
+
+ /* Go looking for workgroups by broadcasting on the local network */
+
+ if (!NT_STATUS_IS_OK(name_resolve_bcast(MSBROWSE, 1, &ip_list,
+ &count))) {
+ DEBUG(99, ("No master browsers responded\n"));
+ return False;
+ }
+
+ for (i = 0; i < count; i++) {
+ char addr[INET6_ADDRSTRLEN];
+ print_sockaddr(addr, sizeof(addr), &ip_list[i].ss);
+ DEBUG(99, ("Found master browser %s\n", addr));
+
+ cli = get_ipc_connect_master_ip(ctx, &ip_list[i],
+ user_info, pp_workgroup_out);
+ if (cli)
+ return(cli);
+ }
+
+ return NULL;
+}
diff --git a/source3/libsmb/clidfs.c b/source3/libsmb/clidfs.c
new file mode 100644
index 0000000000..7b63f9535e
--- /dev/null
+++ b/source3/libsmb/clidfs.c
@@ -0,0 +1,1125 @@
+/*
+ Unix SMB/CIFS implementation.
+ client connect/disconnect routines
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Gerald (Jerry) Carter 2004
+ Copyright (C) Jeremy Allison 2007
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/********************************************************************
+ Important point.
+
+ DFS paths are *always* of the form \server\share\<pathname> (the \ characters
+ are not C escaped here).
+
+ - but if we're using POSIX paths then <pathname> may contain
+ '/' separators, not '\\' separators. So cope with '\\' or '/'
+ as a separator when looking at the pathname part.... JRA.
+********************************************************************/
+
+struct client_connection {
+ struct client_connection *prev, *next;
+ struct cli_state *cli;
+ char *mount;
+};
+
+/* global state....globals reek! */
+int max_protocol = PROTOCOL_NT1;
+
+static struct cm_cred_struct {
+ char *username;
+ char *password;
+ bool got_pass;
+ bool use_kerberos;
+ bool fallback_after_kerberos;
+ int signing_state;
+} cm_creds;
+
+static void cm_set_password(const char *newpass);
+
+static int port;
+static int name_type = 0x20;
+static bool have_ip;
+static struct sockaddr_storage dest_ss;
+
+static struct client_connection *connections;
+
+static bool cli_check_msdfs_proxy(TALLOC_CTX *ctx,
+ struct cli_state *cli,
+ const char *sharename,
+ char **pp_newserver,
+ char **pp_newshare,
+ bool force_encrypt,
+ const char *username,
+ const char *password,
+ const char *domain);
+
+/********************************************************************
+ Ensure a connection is encrypted.
+********************************************************************/
+
+NTSTATUS cli_cm_force_encryption(struct cli_state *c,
+ const char *username,
+ const char *password,
+ const char *domain,
+ const char *sharename)
+{
+ NTSTATUS status = cli_force_encryption(c,
+ username,
+ password,
+ domain);
+
+ if (NT_STATUS_EQUAL(status,NT_STATUS_NOT_SUPPORTED)) {
+ d_printf("Encryption required and "
+ "server that doesn't support "
+ "UNIX extensions - failing connect\n");
+ } else if (NT_STATUS_EQUAL(status,NT_STATUS_UNKNOWN_REVISION)) {
+ d_printf("Encryption required and "
+ "can't get UNIX CIFS extensions "
+ "version from server.\n");
+ } else if (NT_STATUS_EQUAL(status,NT_STATUS_UNSUPPORTED_COMPRESSION)) {
+ d_printf("Encryption required and "
+ "share %s doesn't support "
+ "encryption.\n", sharename);
+ } else if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Encryption required and "
+ "setup failed with error %s.\n",
+ nt_errstr(status));
+ }
+
+ return status;
+}
+
+/********************************************************************
+ Return a connection to a server.
+********************************************************************/
+
+static struct cli_state *do_connect(TALLOC_CTX *ctx,
+ const char *server,
+ const char *share,
+ bool show_sessetup,
+ bool force_encrypt)
+{
+ struct cli_state *c = NULL;
+ struct nmb_name called, calling;
+ const char *server_n;
+ struct sockaddr_storage ss;
+ char *servicename;
+ char *sharename;
+ char *newserver, *newshare;
+ const char *username;
+ const char *password;
+ NTSTATUS status;
+
+ /* make a copy so we don't modify the global string 'service' */
+ servicename = talloc_strdup(ctx,share);
+ if (!servicename) {
+ return NULL;
+ }
+ sharename = servicename;
+ if (*sharename == '\\') {
+ server = sharename+2;
+ sharename = strchr_m(server,'\\');
+ if (!sharename) {
+ return NULL;
+ }
+ *sharename = 0;
+ sharename++;
+ }
+
+ server_n = server;
+
+ zero_addr(&ss);
+
+ make_nmb_name(&calling, global_myname(), 0x0);
+ make_nmb_name(&called , server, name_type);
+
+ again:
+ zero_addr(&ss);
+ if (have_ip)
+ ss = dest_ss;
+
+ /* have to open a new connection */
+ if (!(c=cli_initialise()) || (cli_set_port(c, port) != port)) {
+ d_printf("Connection to %s failed\n", server_n);
+ if (c) {
+ cli_shutdown(c);
+ }
+ return NULL;
+ }
+ status = cli_connect(c, server_n, &ss);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Connection to %s failed (Error %s)\n",
+ server_n,
+ nt_errstr(status));
+ cli_shutdown(c);
+ return NULL;
+ }
+
+ c->protocol = max_protocol;
+ c->use_kerberos = cm_creds.use_kerberos;
+ c->fallback_after_kerberos = cm_creds.fallback_after_kerberos;
+ cli_setup_signing_state(c, cm_creds.signing_state);
+
+ if (!cli_session_request(c, &calling, &called)) {
+ char *p;
+ d_printf("session request to %s failed (%s)\n",
+ called.name, cli_errstr(c));
+ cli_shutdown(c);
+ c = NULL;
+ if ((p=strchr_m(called.name, '.'))) {
+ *p = 0;
+ goto again;
+ }
+ if (strcmp(called.name, "*SMBSERVER")) {
+ make_nmb_name(&called , "*SMBSERVER", 0x20);
+ goto again;
+ }
+ return NULL;
+ }
+
+ DEBUG(4,(" session request ok\n"));
+
+ if (!cli_negprot(c)) {
+ d_printf("protocol negotiation failed\n");
+ cli_shutdown(c);
+ return NULL;
+ }
+
+ if (!cm_creds.got_pass && !cm_creds.use_kerberos) {
+ char *label = NULL;
+ char *pass;
+ label = talloc_asprintf(ctx, "Enter %s's password: ",
+ cm_creds.username);
+ pass = getpass(label);
+ if (pass) {
+ cm_set_password(pass);
+ }
+ TALLOC_FREE(label);
+ }
+
+ username = cm_creds.username ? cm_creds.username : "";
+ password = cm_creds.password ? cm_creds.password : "";
+
+ if (!NT_STATUS_IS_OK(cli_session_setup(c, username,
+ password, strlen(password),
+ password, strlen(password),
+ lp_workgroup()))) {
+ /* If a password was not supplied then
+ * try again with a null username. */
+ if (password[0] || !username[0] || cm_creds.use_kerberos ||
+ !NT_STATUS_IS_OK(cli_session_setup(c, "",
+ "", 0,
+ "", 0,
+ lp_workgroup()))) {
+ d_printf("session setup failed: %s\n", cli_errstr(c));
+ if (NT_STATUS_V(cli_nt_error(c)) ==
+ NT_STATUS_V(NT_STATUS_MORE_PROCESSING_REQUIRED))
+ d_printf("did you forget to run kinit?\n");
+ cli_shutdown(c);
+ return NULL;
+ }
+ d_printf("Anonymous login successful\n");
+ }
+
+ if ( show_sessetup ) {
+ if (*c->server_domain) {
+ DEBUG(0,("Domain=[%s] OS=[%s] Server=[%s]\n",
+ c->server_domain,c->server_os,c->server_type));
+ } else if (*c->server_os || *c->server_type) {
+ DEBUG(0,("OS=[%s] Server=[%s]\n",
+ c->server_os,c->server_type));
+ }
+ }
+ DEBUG(4,(" session setup ok\n"));
+
+ /* here's the fun part....to support 'msdfs proxy' shares
+ (on Samba or windows) we have to issues a TRANS_GET_DFS_REFERRAL
+ here before trying to connect to the original share.
+ check_dfs_proxy() will fail if it is a normal share. */
+
+ if ((c->capabilities & CAP_DFS) &&
+ cli_check_msdfs_proxy(ctx, c, sharename,
+ &newserver, &newshare,
+ force_encrypt,
+ username,
+ password,
+ lp_workgroup())) {
+ cli_shutdown(c);
+ return do_connect(ctx, newserver,
+ newshare, false, force_encrypt);
+ }
+
+ /* must be a normal share */
+
+ if (!cli_send_tconX(c, sharename, "?????",
+ password, strlen(password)+1)) {
+ d_printf("tree connect failed: %s\n", cli_errstr(c));
+ cli_shutdown(c);
+ return NULL;
+ }
+
+ if (force_encrypt) {
+ status = cli_cm_force_encryption(c,
+ username,
+ password,
+ lp_workgroup(),
+ sharename);
+ if (!NT_STATUS_IS_OK(status)) {
+ cli_shutdown(c);
+ return NULL;
+ }
+ }
+
+ DEBUG(4,(" tconx ok\n"));
+ return c;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void cli_cm_set_mntpoint(struct cli_state *c, const char *mnt)
+{
+ struct client_connection *p;
+ int i;
+
+ for (p=connections,i=0; p; p=p->next,i++) {
+ if (strequal(p->cli->desthost, c->desthost) &&
+ strequal(p->cli->share, c->share)) {
+ break;
+ }
+ }
+
+ if (p) {
+ char *name = clean_name(NULL, p->mount);
+ if (!name) {
+ return;
+ }
+ p->mount = talloc_strdup(p, name);
+ TALLOC_FREE(name);
+ }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+const char *cli_cm_get_mntpoint(struct cli_state *c)
+{
+ struct client_connection *p;
+ int i;
+
+ for (p=connections,i=0; p; p=p->next,i++) {
+ if (strequal(p->cli->desthost, c->desthost) &&
+ strequal(p->cli->share, c->share)) {
+ break;
+ }
+ }
+
+ if (p) {
+ return p->mount;
+ }
+ return NULL;
+}
+
+/********************************************************************
+ Add a new connection to the list
+********************************************************************/
+
+static struct cli_state *cli_cm_connect(TALLOC_CTX *ctx,
+ struct cli_state *referring_cli,
+ const char *server,
+ const char *share,
+ bool show_hdr,
+ bool force_encrypt)
+{
+ struct client_connection *node;
+
+ /* NB This must be the null context here... JRA. */
+ node = TALLOC_ZERO_ARRAY(NULL, struct client_connection, 1);
+ if (!node) {
+ return NULL;
+ }
+
+ node->cli = do_connect(ctx, server, share, show_hdr, force_encrypt);
+
+ if ( !node->cli ) {
+ TALLOC_FREE( node );
+ return NULL;
+ }
+
+ DLIST_ADD( connections, node );
+
+ cli_cm_set_mntpoint(node->cli, "");
+
+ if (referring_cli && referring_cli->posix_capabilities) {
+ uint16 major, minor;
+ uint32 caplow, caphigh;
+ if (cli_unix_extensions_version(node->cli, &major,
+ &minor, &caplow, &caphigh)) {
+ cli_set_unix_extensions_capabilities(node->cli,
+ major, minor,
+ caplow, caphigh);
+ }
+ }
+
+ return node->cli;
+}
+
+/********************************************************************
+ Return a connection to a server.
+********************************************************************/
+
+static struct cli_state *cli_cm_find(const char *server, const char *share)
+{
+ struct client_connection *p;
+
+ for (p=connections; p; p=p->next) {
+ if ( strequal(server, p->cli->desthost) &&
+ strequal(share,p->cli->share)) {
+ return p->cli;
+ }
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Open a client connection to a \\server\share. Set's the current *cli
+ global variable as a side-effect (but only if the connection is successful).
+****************************************************************************/
+
+struct cli_state *cli_cm_open(TALLOC_CTX *ctx,
+ struct cli_state *referring_cli,
+ const char *server,
+ const char *share,
+ bool show_hdr,
+ bool force_encrypt)
+{
+ struct cli_state *c;
+
+ /* try to reuse an existing connection */
+
+ c = cli_cm_find(server, share);
+ if (!c) {
+ c = cli_cm_connect(ctx, referring_cli,
+ server, share, show_hdr, force_encrypt);
+ }
+
+ return c;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+void cli_cm_shutdown(void)
+{
+ struct client_connection *p, *x;
+
+ for (p=connections; p;) {
+ cli_shutdown(p->cli);
+ x = p;
+ p = p->next;
+
+ TALLOC_FREE(x);
+ }
+
+ connections = NULL;
+ return;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+void cli_cm_display(void)
+{
+ struct client_connection *p;
+ int i;
+
+ for ( p=connections,i=0; p; p=p->next,i++ ) {
+ d_printf("%d:\tserver=%s, share=%s\n",
+ i, p->cli->desthost, p->cli->share );
+ }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static void cm_set_password(const char *newpass)
+{
+ SAFE_FREE(cm_creds.password);
+ cm_creds.password = SMB_STRDUP(newpass);
+ if (cm_creds.password) {
+ cm_creds.got_pass = true;
+ }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+void cli_cm_set_credentials(void)
+{
+ SAFE_FREE(cm_creds.username);
+ cm_creds.username = SMB_STRDUP(get_cmdline_auth_info_username());
+
+ if (get_cmdline_auth_info_got_pass()) {
+ cm_set_password(get_cmdline_auth_info_password());
+ }
+
+ cm_creds.use_kerberos = get_cmdline_auth_info_use_kerberos();
+ cm_creds.fallback_after_kerberos = false;
+ cm_creds.signing_state = get_cmdline_auth_info_signing_state();
+}
+
+/****************************************************************************
+****************************************************************************/
+
+void cli_cm_set_port(int port_number)
+{
+ port = port_number;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+void cli_cm_set_dest_name_type(int type)
+{
+ name_type = type;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+void cli_cm_set_signing_state(int state)
+{
+ cm_creds.signing_state = state;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+void cli_cm_set_username(const char *username)
+{
+ SAFE_FREE(cm_creds.username);
+ cm_creds.username = SMB_STRDUP(username);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+void cli_cm_set_password(const char *newpass)
+{
+ SAFE_FREE(cm_creds.password);
+ cm_creds.password = SMB_STRDUP(newpass);
+ if (cm_creds.password) {
+ cm_creds.got_pass = true;
+ }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+void cli_cm_set_use_kerberos(void)
+{
+ cm_creds.use_kerberos = true;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+void cli_cm_set_fallback_after_kerberos(void)
+{
+ cm_creds.fallback_after_kerberos = true;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+void cli_cm_set_dest_ss(struct sockaddr_storage *pss)
+{
+ dest_ss = *pss;
+ have_ip = true;
+}
+
+/**********************************************************************
+ split a dfs path into the server, share name, and extrapath components
+**********************************************************************/
+
+static void split_dfs_path(TALLOC_CTX *ctx,
+ const char *nodepath,
+ char **pp_server,
+ char **pp_share,
+ char **pp_extrapath)
+{
+ char *p, *q;
+ char *path;
+
+ *pp_server = NULL;
+ *pp_share = NULL;
+ *pp_extrapath = NULL;
+
+ path = talloc_strdup(ctx, nodepath);
+ if (!path) {
+ return;
+ }
+
+ if ( path[0] != '\\' ) {
+ return;
+ }
+
+ p = strchr_m( path + 1, '\\' );
+ if ( !p ) {
+ return;
+ }
+
+ *p = '\0';
+ p++;
+
+ /* Look for any extra/deep path */
+ q = strchr_m(p, '\\');
+ if (q != NULL) {
+ *q = '\0';
+ q++;
+ *pp_extrapath = talloc_strdup(ctx, q);
+ } else {
+ *pp_extrapath = talloc_strdup(ctx, "");
+ }
+
+ *pp_share = talloc_strdup(ctx, p);
+ *pp_server = talloc_strdup(ctx, &path[1]);
+}
+
+/****************************************************************************
+ Return the original path truncated at the directory component before
+ the first wildcard character. Trust the caller to provide a NULL
+ terminated string
+****************************************************************************/
+
+static char *clean_path(TALLOC_CTX *ctx, const char *path)
+{
+ size_t len;
+ char *p1, *p2, *p;
+ char *path_out;
+
+ /* No absolute paths. */
+ while (IS_DIRECTORY_SEP(*path)) {
+ path++;
+ }
+
+ path_out = talloc_strdup(ctx, path);
+ if (!path_out) {
+ return NULL;
+ }
+
+ p1 = strchr_m(path_out, '*');
+ p2 = strchr_m(path_out, '?');
+
+ if (p1 || p2) {
+ if (p1 && p2) {
+ p = MIN(p1,p2);
+ } else if (!p1) {
+ p = p2;
+ } else {
+ p = p1;
+ }
+ *p = '\0';
+
+ /* Now go back to the start of this component. */
+ p1 = strrchr_m(path_out, '/');
+ p2 = strrchr_m(path_out, '\\');
+ p = MAX(p1,p2);
+ if (p) {
+ *p = '\0';
+ }
+ }
+
+ /* Strip any trailing separator */
+
+ len = strlen(path_out);
+ if ( (len > 0) && IS_DIRECTORY_SEP(path_out[len-1])) {
+ path_out[len-1] = '\0';
+ }
+
+ return path_out;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static char *cli_dfs_make_full_path(TALLOC_CTX *ctx,
+ struct cli_state *cli,
+ const char *dir)
+{
+ /* Ensure the extrapath doesn't start with a separator. */
+ while (IS_DIRECTORY_SEP(*dir)) {
+ dir++;
+ }
+
+ return talloc_asprintf(ctx, "\\%s\\%s\\%s",
+ cli->desthost, cli->share, dir);
+}
+
+/********************************************************************
+ check for dfs referral
+********************************************************************/
+
+static bool cli_dfs_check_error( struct cli_state *cli, NTSTATUS status )
+{
+ uint32 flgs2 = SVAL(cli->inbuf,smb_flg2);
+
+ /* only deal with DS when we negotiated NT_STATUS codes and UNICODE */
+
+ if (!((flgs2&FLAGS2_32_BIT_ERROR_CODES) &&
+ (flgs2&FLAGS2_UNICODE_STRINGS)))
+ return false;
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS(IVAL(cli->inbuf,smb_rcls))))
+ return true;
+
+ return false;
+}
+
+/********************************************************************
+ Get the dfs referral link.
+********************************************************************/
+
+bool cli_dfs_get_referral(TALLOC_CTX *ctx,
+ struct cli_state *cli,
+ const char *path,
+ CLIENT_DFS_REFERRAL**refs,
+ size_t *num_refs,
+ uint16 *consumed)
+{
+ unsigned int data_len = 0;
+ unsigned int param_len = 0;
+ uint16 setup = TRANSACT2_GET_DFS_REFERRAL;
+ char *param;
+ char *rparam=NULL, *rdata=NULL;
+ char *p;
+ char *endp;
+ size_t pathlen = 2*(strlen(path)+1);
+ uint16 num_referrals;
+ CLIENT_DFS_REFERRAL *referrals = NULL;
+ bool ret = false;
+
+ *num_refs = 0;
+ *refs = NULL;
+
+ param = SMB_MALLOC_ARRAY(char, 2+pathlen+2);
+ if (!param) {
+ return false;
+ }
+ SSVAL(param, 0, 0x03); /* max referral level */
+ p = &param[2];
+
+ p += clistr_push(cli, p, path, pathlen, 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, 2, /* param, length, max */
+ NULL, 0, cli->max_xmit /* data, length, max */
+ )) {
+ SAFE_FREE(param);
+ return false;
+ }
+
+ SAFE_FREE(param);
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len)) {
+ return false;
+ }
+
+ if (data_len < 4) {
+ goto out;
+ }
+
+ endp = rdata + data_len;
+
+ *consumed = SVAL(rdata, 0);
+ num_referrals = SVAL(rdata, 2);
+
+ if (num_referrals != 0) {
+ uint16 ref_version;
+ uint16 ref_size;
+ int i;
+ uint16 node_offset;
+
+ referrals = TALLOC_ARRAY(ctx, CLIENT_DFS_REFERRAL,
+ num_referrals);
+
+ if (!referrals) {
+ goto out;
+ }
+ /* start at the referrals array */
+
+ p = rdata+8;
+ for (i=0; i<num_referrals && p < endp; i++) {
+ if (p + 18 > endp) {
+ goto out;
+ }
+ ref_version = SVAL(p, 0);
+ ref_size = SVAL(p, 2);
+ node_offset = SVAL(p, 16);
+
+ if (ref_version != 3) {
+ p += ref_size;
+ continue;
+ }
+
+ referrals[i].proximity = SVAL(p, 8);
+ referrals[i].ttl = SVAL(p, 10);
+
+ if (p + node_offset > endp) {
+ goto out;
+ }
+ clistr_pull_talloc(ctx, cli, &referrals[i].dfspath,
+ p+node_offset, -1,
+ STR_TERMINATE|STR_UNICODE );
+
+ if (!referrals[i].dfspath) {
+ goto out;
+ }
+ p += ref_size;
+ }
+ if (i < num_referrals) {
+ goto out;
+ }
+ }
+
+ ret = true;
+
+ *num_refs = num_referrals;
+ *refs = referrals;
+
+ out:
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ return ret;
+}
+
+/********************************************************************
+********************************************************************/
+
+bool cli_resolve_path(TALLOC_CTX *ctx,
+ const char *mountpt,
+ struct cli_state *rootcli,
+ const char *path,
+ struct cli_state **targetcli,
+ char **pp_targetpath)
+{
+ CLIENT_DFS_REFERRAL *refs = NULL;
+ size_t num_refs = 0;
+ uint16 consumed;
+ struct cli_state *cli_ipc = NULL;
+ char *dfs_path = NULL;
+ char *cleanpath = NULL;
+ char *extrapath = NULL;
+ int pathlen;
+ char *server = NULL;
+ char *share = NULL;
+ struct cli_state *newcli = NULL;
+ char *newpath = NULL;
+ char *newmount = NULL;
+ char *ppath = NULL;
+ SMB_STRUCT_STAT sbuf;
+ uint32 attributes;
+
+ if ( !rootcli || !path || !targetcli ) {
+ return false;
+ }
+
+ /* Don't do anything if this is not a DFS root. */
+
+ if ( !rootcli->dfsroot) {
+ *targetcli = rootcli;
+ *pp_targetpath = talloc_strdup(ctx, path);
+ if (!*pp_targetpath) {
+ return false;
+ }
+ return true;
+ }
+
+ *targetcli = NULL;
+
+ /* Send a trans2_query_path_info to check for a referral. */
+
+ cleanpath = clean_path(ctx, path);
+ if (!cleanpath) {
+ return false;
+ }
+
+ dfs_path = cli_dfs_make_full_path(ctx, rootcli, cleanpath);
+ if (!dfs_path) {
+ return false;
+ }
+
+ if (cli_qpathinfo_basic( rootcli, dfs_path, &sbuf, &attributes)) {
+ /* This is an ordinary path, just return it. */
+ *targetcli = rootcli;
+ *pp_targetpath = talloc_strdup(ctx, path);
+ if (!*pp_targetpath) {
+ return false;
+ }
+ goto done;
+ }
+
+ /* Special case where client asked for a path that does not exist */
+
+ if (cli_dfs_check_error(rootcli, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ *targetcli = rootcli;
+ *pp_targetpath = talloc_strdup(ctx, path);
+ if (!*pp_targetpath) {
+ return false;
+ }
+ goto done;
+ }
+
+ /* We got an error, check for DFS referral. */
+
+ if (!cli_dfs_check_error(rootcli, NT_STATUS_PATH_NOT_COVERED)) {
+ return false;
+ }
+
+ /* Check for the referral. */
+
+ if (!(cli_ipc = cli_cm_open(ctx, rootcli,
+ rootcli->desthost,
+ "IPC$", false,
+ (rootcli->trans_enc_state != NULL)))) {
+ return false;
+ }
+
+ if (!cli_dfs_get_referral(ctx, cli_ipc, dfs_path, &refs,
+ &num_refs, &consumed) || !num_refs) {
+ return false;
+ }
+
+ /* Just store the first referral for now. */
+
+ if (!refs[0].dfspath) {
+ return false;
+ }
+ split_dfs_path(ctx, refs[0].dfspath, &server, &share, &extrapath );
+
+ if (!server || !share) {
+ return false;
+ }
+
+ /* Make sure to recreate the original string including any wildcards. */
+
+ dfs_path = cli_dfs_make_full_path(ctx, rootcli, path);
+ if (!dfs_path) {
+ return false;
+ }
+ pathlen = strlen(dfs_path)*2;
+ consumed = MIN(pathlen, consumed);
+ *pp_targetpath = talloc_strdup(ctx, &dfs_path[consumed/2]);
+ if (!*pp_targetpath) {
+ return false;
+ }
+ dfs_path[consumed/2] = '\0';
+
+ /*
+ * *pp_targetpath is now the unconsumed part of the path.
+ * dfs_path is now the consumed part of the path
+ * (in \server\share\path format).
+ */
+
+ /* Open the connection to the target server & share */
+ if ((*targetcli = cli_cm_open(ctx, rootcli,
+ server,
+ share,
+ false,
+ (rootcli->trans_enc_state != NULL))) == NULL) {
+ d_printf("Unable to follow dfs referral [\\%s\\%s]\n",
+ server, share );
+ return false;
+ }
+
+ if (extrapath && strlen(extrapath) > 0) {
+ *pp_targetpath = talloc_asprintf(ctx,
+ "%s%s",
+ extrapath,
+ *pp_targetpath);
+ if (!*pp_targetpath) {
+ return false;
+ }
+ }
+
+ /* parse out the consumed mount path */
+ /* trim off the \server\share\ */
+
+ ppath = dfs_path;
+
+ if (*ppath != '\\') {
+ d_printf("cli_resolve_path: "
+ "dfs_path (%s) not in correct format.\n",
+ dfs_path );
+ return false;
+ }
+
+ ppath++; /* Now pointing at start of server name. */
+
+ if ((ppath = strchr_m( dfs_path, '\\' )) == NULL) {
+ return false;
+ }
+
+ ppath++; /* Now pointing at start of share name. */
+
+ if ((ppath = strchr_m( ppath+1, '\\' )) == NULL) {
+ return false;
+ }
+
+ ppath++; /* Now pointing at path component. */
+
+ newmount = talloc_asprintf(ctx, "%s\\%s", mountpt, ppath );
+ if (!newmount) {
+ return false;
+ }
+
+ cli_cm_set_mntpoint(*targetcli, newmount);
+
+ /* Check for another dfs referral, note that we are not
+ checking for loops here. */
+
+ if (!strequal(*pp_targetpath, "\\") && !strequal(*pp_targetpath, "/")) {
+ if (cli_resolve_path(ctx,
+ newmount,
+ *targetcli,
+ *pp_targetpath,
+ &newcli,
+ &newpath)) {
+ /*
+ * When cli_resolve_path returns true here it's always
+ * returning the complete path in newpath, so we're done
+ * here.
+ */
+ *targetcli = newcli;
+ *pp_targetpath = newpath;
+ return true;
+ }
+ }
+
+ done:
+
+ /* If returning true ensure we return a dfs root full path. */
+ if ((*targetcli)->dfsroot) {
+ dfs_path = talloc_strdup(ctx, *pp_targetpath);
+ if (!dfs_path) {
+ return false;
+ }
+ *pp_targetpath = cli_dfs_make_full_path(ctx, *targetcli, dfs_path);
+ }
+
+ return true;
+}
+
+/********************************************************************
+********************************************************************/
+
+static bool cli_check_msdfs_proxy(TALLOC_CTX *ctx,
+ struct cli_state *cli,
+ const char *sharename,
+ char **pp_newserver,
+ char **pp_newshare,
+ bool force_encrypt,
+ const char *username,
+ const char *password,
+ const char *domain)
+{
+ CLIENT_DFS_REFERRAL *refs = NULL;
+ size_t num_refs = 0;
+ uint16 consumed;
+ char *fullpath = NULL;
+ bool res;
+ uint16 cnum;
+ char *newextrapath = NULL;
+
+ if (!cli || !sharename) {
+ return false;
+ }
+
+ cnum = cli->cnum;
+
+ /* special case. never check for a referral on the IPC$ share */
+
+ if (strequal(sharename, "IPC$")) {
+ return false;
+ }
+
+ /* send a trans2_query_path_info to check for a referral */
+
+ fullpath = talloc_asprintf(ctx, "\\%s\\%s", cli->desthost, sharename );
+ if (!fullpath) {
+ return false;
+ }
+
+ /* check for the referral */
+
+ if (!cli_send_tconX(cli, "IPC$", "IPC", NULL, 0)) {
+ return false;
+ }
+
+ if (force_encrypt) {
+ NTSTATUS status = cli_cm_force_encryption(cli,
+ username,
+ password,
+ lp_workgroup(),
+ "IPC$");
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ }
+
+ res = cli_dfs_get_referral(ctx, cli, fullpath, &refs, &num_refs, &consumed);
+
+ if (!cli_tdis(cli)) {
+ return false;
+ }
+
+ cli->cnum = cnum;
+
+ if (!res || !num_refs) {
+ return false;
+ }
+
+ if (!refs[0].dfspath) {
+ return false;
+ }
+
+ split_dfs_path(ctx, refs[0].dfspath, pp_newserver,
+ pp_newshare, &newextrapath );
+
+ if ((*pp_newserver == NULL) || (*pp_newshare == NULL)) {
+ return false;
+ }
+
+ /* check that this is not a self-referral */
+
+ if (strequal(cli->desthost, *pp_newserver) &&
+ strequal(sharename, *pp_newshare)) {
+ return false;
+ }
+
+ return true;
+}
diff --git a/source3/libsmb/clidgram.c b/source3/libsmb/clidgram.c
new file mode 100644
index 0000000000..8b35a69def
--- /dev/null
+++ b/source3/libsmb/clidgram.c
@@ -0,0 +1,357 @@
+/*
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/*
+ * cli_send_mailslot, send a mailslot for client code ...
+ */
+
+bool cli_send_mailslot(struct messaging_context *msg_ctx,
+ bool unique, const char *mailslot,
+ uint16 priority,
+ char *buf, int len,
+ const char *srcname, int src_type,
+ const char *dstname, int dest_type,
+ const struct sockaddr_storage *dest_ss)
+{
+ struct packet_struct p;
+ struct dgram_packet *dgram = &p.packet.dgram;
+ char *ptr, *p2;
+ char tmp[4];
+ pid_t nmbd_pid;
+ char addr[INET6_ADDRSTRLEN];
+
+ if ((nmbd_pid = pidfile_pid("nmbd")) == 0) {
+ DEBUG(3, ("No nmbd found\n"));
+ return False;
+ }
+
+ if (dest_ss->ss_family != AF_INET) {
+ DEBUG(3, ("cli_send_mailslot: can't send to IPv6 address.\n"));
+ return false;
+ }
+
+ 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);
+ /* source ip is filled by nmbd */
+ 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);
+
+ if (smb_size + 17*2 + strlen(mailslot) + 1 + len > MAX_DGRAM_SIZE) {
+ DEBUG(0, ("cli_send_mailslot: Cannot write beyond end of packet\n"));
+ return False;
+ }
+
+ cli_set_message(ptr,17,strlen(mailslot) + 1 + 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,priority);
+ SSVAL(ptr,smb_vwv16,2);
+ p2 = smb_buf(ptr);
+ fstrcpy(p2,mailslot);
+ p2 = skip_string(ptr,MAX_DGRAM_SIZE,p2);
+ if (!p2) {
+ return False;
+ }
+
+ memcpy(p2,buf,len);
+ p2 += len;
+
+ dgram->datasize = PTR_DIFF(p2,ptr+4); /* +4 for tcp length. */
+
+ p.packet_type = DGRAM_PACKET;
+ p.ip = ((const struct sockaddr_in *)dest_ss)->sin_addr;
+ p.timestamp = time(NULL);
+
+ DEBUG(4,("send_mailslot: Sending to mailslot %s from %s ",
+ mailslot, nmb_namestr(&dgram->source_name)));
+ print_sockaddr(addr, sizeof(addr), dest_ss);
+
+ DEBUGADD(4,("to %s IP %s\n", nmb_namestr(&dgram->dest_name), addr));
+
+ return NT_STATUS_IS_OK(messaging_send_buf(msg_ctx,
+ pid_to_procid(nmbd_pid),
+ MSG_SEND_PACKET,
+ (uint8 *)&p, sizeof(p)));
+}
+
+static const char *mailslot_name(TALLOC_CTX *mem_ctx, struct in_addr dc_ip)
+{
+ return talloc_asprintf(mem_ctx, "%s%X",
+ NBT_MAILSLOT_GETDC, dc_ip.s_addr);
+}
+
+bool send_getdc_request(TALLOC_CTX *mem_ctx,
+ struct messaging_context *msg_ctx,
+ struct sockaddr_storage *dc_ss,
+ const char *domain_name,
+ const DOM_SID *sid,
+ uint32_t nt_version)
+{
+ struct in_addr dc_ip;
+ const char *my_acct_name = NULL;
+ const char *my_mailslot = NULL;
+ struct nbt_ntlogon_packet packet;
+ struct nbt_ntlogon_sam_logon *s;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ struct dom_sid my_sid;
+
+ ZERO_STRUCT(packet);
+ ZERO_STRUCT(my_sid);
+
+ if (dc_ss->ss_family != AF_INET) {
+ return false;
+ }
+
+ if (sid) {
+ my_sid = *sid;
+ }
+
+ dc_ip = ((struct sockaddr_in *)dc_ss)->sin_addr;
+ my_mailslot = mailslot_name(mem_ctx, dc_ip);
+ if (!my_mailslot) {
+ return false;
+ }
+
+ my_acct_name = talloc_asprintf(mem_ctx, "%s$", global_myname());
+ if (!my_acct_name) {
+ return false;
+ }
+
+ packet.command = NTLOGON_SAM_LOGON;
+ s = &packet.req.logon;
+
+ s->request_count = 0;
+ s->computer_name = global_myname();
+ s->user_name = my_acct_name;
+ s->mailslot_name = my_mailslot;
+ s->acct_control = ACB_WSTRUST;
+ s->sid = my_sid;
+ s->nt_version = nt_version;
+ s->lmnt_token = 0xffff;
+ s->lm20_token = 0xffff;
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_DEBUG(nbt_ntlogon_packet, &packet);
+ }
+
+ ndr_err = ndr_push_struct_blob(&blob, mem_ctx, &packet,
+ (ndr_push_flags_fn_t)ndr_push_nbt_ntlogon_packet);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return false;
+ }
+
+ return cli_send_mailslot(msg_ctx,
+ false, NBT_MAILSLOT_NTLOGON, 0,
+ (char *)blob.data, blob.length,
+ global_myname(), 0, domain_name, 0x1c,
+ dc_ss);
+}
+
+bool receive_getdc_response(TALLOC_CTX *mem_ctx,
+ struct sockaddr_storage *dc_ss,
+ const char *domain_name,
+ uint32_t *nt_version,
+ const char **dc_name,
+ union nbt_cldap_netlogon **reply)
+{
+ struct packet_struct *packet;
+ const char *my_mailslot = NULL;
+ struct in_addr dc_ip;
+ DATA_BLOB blob;
+ union nbt_cldap_netlogon r;
+ union dgram_message_body p;
+ enum ndr_err_code ndr_err;
+
+ const char *returned_dc = NULL;
+ const char *returned_domain = NULL;
+
+ if (dc_ss->ss_family != AF_INET) {
+ return false;
+ }
+
+ dc_ip = ((struct sockaddr_in *)dc_ss)->sin_addr;
+
+ my_mailslot = mailslot_name(mem_ctx, dc_ip);
+ if (!my_mailslot) {
+ return false;
+ }
+
+ packet = receive_unexpected(DGRAM_PACKET, 0, my_mailslot);
+
+ if (packet == NULL) {
+ DEBUG(5, ("Did not receive packet for %s\n", my_mailslot));
+ return False;
+ }
+
+ DEBUG(5, ("Received packet for %s\n", my_mailslot));
+
+ blob = data_blob_const(packet->packet.dgram.data,
+ packet->packet.dgram.datasize);
+
+ if (blob.length < 4) {
+ DEBUG(0,("invalid length: %d\n", (int)blob.length));
+ return false;
+ }
+
+ if (RIVAL(blob.data,0) != DGRAM_SMB) {
+ DEBUG(0,("invalid packet\n"));
+ return false;
+ }
+
+ blob.data += 4;
+ blob.length -= 4;
+
+ ndr_err = ndr_pull_union_blob_all(&blob, mem_ctx, &p, DGRAM_SMB,
+ (ndr_pull_flags_fn_t)ndr_pull_dgram_smb_packet);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(0,("failed to parse packet\n"));
+ return false;
+ }
+
+ if (p.smb.smb_command != SMB_TRANSACTION) {
+ DEBUG(0,("invalid smb_command: %d\n", p.smb.smb_command));
+ return false;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_DEBUG(dgram_smb_packet, &p);
+ }
+
+ blob = p.smb.body.trans.data;
+
+ if (!pull_mailslot_cldap_reply(mem_ctx, &blob,
+ &r, nt_version))
+ {
+ return false;
+ }
+
+ switch (*nt_version) {
+ case 1:
+ case 16:
+ case 17:
+
+ returned_domain = r.logon1.domain_name;
+ returned_dc = r.logon1.pdc_name;
+ break;
+ case 2:
+ case 3:
+ case 18:
+ case 19:
+ returned_domain = r.logon3.domain_name;
+ returned_dc = r.logon3.pdc_name;
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ returned_domain = r.logon5.domain;
+ returned_dc = r.logon5.pdc_name;
+ break;
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ returned_domain = r.logon13.domain;
+ returned_dc = r.logon13.pdc_name;
+ break;
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ returned_domain = r.logon15.domain;
+ returned_dc = r.logon15.pdc_name;
+ break;
+ case 29:
+ case 30:
+ case 31:
+ returned_domain = r.logon29.domain;
+ returned_dc = r.logon29.pdc_name;
+ break;
+ default:
+ return false;
+ }
+
+ if (!strequal(returned_domain, domain_name)) {
+ DEBUG(3, ("GetDC: Expected domain %s, got %s\n",
+ domain_name, returned_domain));
+ return false;
+ }
+
+ *dc_name = talloc_strdup(mem_ctx, returned_dc);
+ if (!*dc_name) {
+ return false;
+ }
+
+ if (**dc_name == '\\') *dc_name += 1;
+ if (**dc_name == '\\') *dc_name += 1;
+
+ if (reply) {
+ *reply = (union nbt_cldap_netlogon *)talloc_memdup(
+ mem_ctx, &r, sizeof(union nbt_cldap_netlogon));
+ if (!*reply) {
+ return false;
+ }
+ }
+
+ DEBUG(10, ("GetDC gave name %s for domain %s\n",
+ *dc_name, returned_domain));
+
+ return True;
+}
+
diff --git a/source3/libsmb/clientgen.c b/source3/libsmb/clientgen.c
new file mode 100644
index 0000000000..9d65fb4e94
--- /dev/null
+++ b/source3/libsmb/clientgen.c
@@ -0,0 +1,789 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client generic functions
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Jeremy Allison 2007.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/*******************************************************************
+ Setup the word count and byte count for a client smb message.
+********************************************************************/
+
+int cli_set_message(char *buf,int num_words,int num_bytes,bool zero)
+{
+ if (zero && (num_words || num_bytes)) {
+ memset(buf + smb_size,'\0',num_words*2 + num_bytes);
+ }
+ SCVAL(buf,smb_wct,num_words);
+ SSVAL(buf,smb_vwv + num_words*SIZEOFWORD,num_bytes);
+ smb_setlen(buf,smb_size + num_words*2 + num_bytes - 4);
+ return (smb_size + num_words*2 + num_bytes);
+}
+
+/****************************************************************************
+ Change the timeout (in milliseconds).
+****************************************************************************/
+
+unsigned int cli_set_timeout(struct cli_state *cli, unsigned int timeout)
+{
+ unsigned int old_timeout = cli->timeout;
+ cli->timeout = timeout;
+ return old_timeout;
+}
+
+/****************************************************************************
+ Change the port number used to call on.
+****************************************************************************/
+
+int cli_set_port(struct cli_state *cli, int port)
+{
+ cli->port = port;
+ return port;
+}
+
+/****************************************************************************
+ Read an smb from a fd ignoring all keepalive packets.
+ The timeout is in milliseconds
+
+ This is exactly the same as receive_smb except that it never returns
+ a session keepalive packet (just as receive_smb used to do).
+ receive_smb was changed to return keepalives as the oplock processing means this call
+ should never go into a blocking read.
+****************************************************************************/
+
+static ssize_t client_receive_smb(struct cli_state *cli, size_t maxlen)
+{
+ size_t len;
+
+ for(;;) {
+ NTSTATUS status;
+
+ set_smb_read_error(&cli->smb_rw_error, SMB_READ_OK);
+
+ status = receive_smb_raw(cli->fd, cli->inbuf, cli->bufsize,
+ cli->timeout, maxlen, &len);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10,("client_receive_smb failed\n"));
+ show_msg(cli->inbuf);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) {
+ set_smb_read_error(&cli->smb_rw_error,
+ SMB_READ_EOF);
+ return -1;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ set_smb_read_error(&cli->smb_rw_error,
+ SMB_READ_TIMEOUT);
+ return -1;
+ }
+
+ set_smb_read_error(&cli->smb_rw_error, SMB_READ_ERROR);
+ return -1;
+ }
+
+ /*
+ * I don't believe len can be < 0 with NT_STATUS_OK
+ * returned above, but this check doesn't hurt. JRA.
+ */
+
+ if ((ssize_t)len < 0) {
+ return len;
+ }
+
+ /* Ignore session keepalive packets. */
+ if(CVAL(cli->inbuf,0) != SMBkeepalive) {
+ break;
+ }
+ }
+
+ if (cli_encryption_on(cli)) {
+ NTSTATUS status = cli_decrypt_message(cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("SMB decryption failed on incoming packet! Error %s\n",
+ nt_errstr(status)));
+ cli->smb_rw_error = SMB_READ_BAD_DECRYPT;
+ return -1;
+ }
+ }
+
+ show_msg(cli->inbuf);
+ return len;
+}
+
+/****************************************************************************
+ Recv an smb.
+****************************************************************************/
+
+bool cli_receive_smb(struct cli_state *cli)
+{
+ ssize_t len;
+
+ /* fd == -1 causes segfaults -- Tom (tom@ninja.nl) */
+ if (cli->fd == -1)
+ return false;
+
+ again:
+ len = client_receive_smb(cli, 0);
+
+ if (len > 0) {
+ /* 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 (len < 0) {
+ DEBUG(0, ("Receiving SMB: Server stopped responding\n"));
+ close(cli->fd);
+ cli->fd = -1;
+ return false;
+ }
+
+ if (!cli_check_sign_mac(cli, cli->inbuf)) {
+ /*
+ * If we get a signature failure in sessionsetup, then
+ * the server sometimes just reflects the sent signature
+ * back to us. Detect this and allow the upper layer to
+ * retrieve the correct Windows error message.
+ */
+ if (CVAL(cli->outbuf,smb_com) == SMBsesssetupX &&
+ (smb_len(cli->inbuf) > (smb_ss_field + 8 - 4)) &&
+ (SVAL(cli->inbuf,smb_flg2) & FLAGS2_SMB_SECURITY_SIGNATURES) &&
+ memcmp(&cli->outbuf[smb_ss_field],&cli->inbuf[smb_ss_field],8) == 0 &&
+ cli_is_error(cli)) {
+
+ /*
+ * Reflected signature on login error.
+ * Set bad sig but don't close fd.
+ */
+ cli->smb_rw_error = SMB_READ_BAD_SIG;
+ return true;
+ }
+
+ DEBUG(0, ("SMB Signature verification failed on incoming packet!\n"));
+ cli->smb_rw_error = SMB_READ_BAD_SIG;
+ close(cli->fd);
+ cli->fd = -1;
+ return false;
+ };
+ return true;
+}
+
+/****************************************************************************
+ Read the data portion of a readX smb.
+ The timeout is in milliseconds
+****************************************************************************/
+
+ssize_t cli_receive_smb_data(struct cli_state *cli, char *buffer, size_t len)
+{
+ NTSTATUS status;
+
+ set_smb_read_error(&cli->smb_rw_error, SMB_READ_OK);
+
+ status = read_socket_with_timeout(
+ cli->fd, buffer, len, len, cli->timeout, NULL);
+ if (NT_STATUS_IS_OK(status)) {
+ return len;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) {
+ set_smb_read_error(&cli->smb_rw_error, SMB_READ_EOF);
+ return -1;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ set_smb_read_error(&cli->smb_rw_error, SMB_READ_TIMEOUT);
+ return -1;
+ }
+
+ set_smb_read_error(&cli->smb_rw_error, SMB_READ_ERROR);
+ return -1;
+}
+
+static ssize_t write_socket(int fd, const char *buf, size_t len)
+{
+ ssize_t ret=0;
+
+ DEBUG(6,("write_socket(%d,%d)\n",fd,(int)len));
+ ret = write_data(fd,buf,len);
+
+ DEBUG(6,("write_socket(%d,%d) wrote %d\n",fd,(int)len,(int)ret));
+ if(ret <= 0)
+ DEBUG(0,("write_socket: Error writing %d bytes to socket %d: ERRNO = %s\n",
+ (int)len, fd, strerror(errno) ));
+
+ 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;
+ char *buf_out = cli->outbuf;
+ bool enc_on = cli_encryption_on(cli);
+
+ /* fd == -1 causes segfaults -- Tom (tom@ninja.nl) */
+ if (cli->fd == -1)
+ return false;
+
+ cli_calculate_sign_mac(cli, cli->outbuf);
+
+ if (enc_on) {
+ NTSTATUS status = cli_encrypt_message(cli, cli->outbuf,
+ &buf_out);
+ if (!NT_STATUS_IS_OK(status)) {
+ close(cli->fd);
+ cli->fd = -1;
+ cli->smb_rw_error = SMB_WRITE_ERROR;
+ DEBUG(0,("Error in encrypting client message. Error %s\n",
+ nt_errstr(status) ));
+ return false;
+ }
+ }
+
+ len = smb_len(buf_out) + 4;
+
+ while (nwritten < len) {
+ ret = write_socket(cli->fd,buf_out+nwritten,len - nwritten);
+ if (ret <= 0) {
+ if (enc_on) {
+ cli_free_enc_buffer(cli, buf_out);
+ }
+ close(cli->fd);
+ cli->fd = -1;
+ cli->smb_rw_error = SMB_WRITE_ERROR;
+ DEBUG(0,("Error writing %d bytes to client. %d (%s)\n",
+ (int)len,(int)ret, strerror(errno) ));
+ return false;
+ }
+ nwritten += ret;
+ }
+
+ if (enc_on) {
+ cli_free_enc_buffer(cli, buf_out);
+ }
+
+ /* Increment the mid so we can tell between responses. */
+ cli->mid++;
+ if (!cli->mid)
+ cli->mid++;
+ return true;
+}
+
+/****************************************************************************
+ Send a "direct" writeX smb to a fd.
+****************************************************************************/
+
+bool cli_send_smb_direct_writeX(struct cli_state *cli,
+ const char *p,
+ size_t extradata)
+{
+ /* First length to send is the offset to the data. */
+ size_t len = SVAL(cli->outbuf,smb_vwv11) + 4;
+ size_t nwritten=0;
+ ssize_t ret;
+
+ /* fd == -1 causes segfaults -- Tom (tom@ninja.nl) */
+ if (cli->fd == -1) {
+ return false;
+ }
+
+ if (client_is_signing_on(cli)) {
+ DEBUG(0,("cli_send_smb_large: cannot send signed packet.\n"));
+ return false;
+ }
+
+ while (nwritten < len) {
+ ret = write_socket(cli->fd,cli->outbuf+nwritten,len - nwritten);
+ if (ret <= 0) {
+ close(cli->fd);
+ cli->fd = -1;
+ cli->smb_rw_error = SMB_WRITE_ERROR;
+ DEBUG(0,("Error writing %d bytes to client. %d (%s)\n",
+ (int)len,(int)ret, strerror(errno) ));
+ return false;
+ }
+ nwritten += ret;
+ }
+
+ /* Now write the extra data. */
+ nwritten=0;
+ while (nwritten < extradata) {
+ ret = write_socket(cli->fd,p+nwritten,extradata - nwritten);
+ if (ret <= 0) {
+ close(cli->fd);
+ cli->fd = -1;
+ cli->smb_rw_error = SMB_WRITE_ERROR;
+ DEBUG(0,("Error writing %d extradata "
+ "bytes to client. %d (%s)\n",
+ (int)extradata,(int)ret, strerror(errno) ));
+ return false;
+ }
+ nwritten += ret;
+ }
+
+ /* Increment the mid so we can tell between responses. */
+ cli->mid++;
+ if (!cli->mid)
+ cli->mid++;
+ return true;
+}
+
+/****************************************************************************
+ Setup basics in a outgoing packet.
+****************************************************************************/
+
+void cli_setup_packet_buf(struct cli_state *cli, char *buf)
+{
+ uint16 flags2;
+ cli->rap_error = 0;
+ SIVAL(buf,smb_rcls,0);
+ SSVAL(buf,smb_pid,cli->pid);
+ memset(buf+smb_pidhigh, 0, 12);
+ SSVAL(buf,smb_uid,cli->vuid);
+ SSVAL(buf,smb_mid,cli->mid);
+
+ if (cli->protocol <= PROTOCOL_CORE) {
+ return;
+ }
+
+ if (cli->case_sensitive) {
+ SCVAL(buf,smb_flg,0x0);
+ } else {
+ /* Default setting, case insensitive. */
+ SCVAL(buf,smb_flg,0x8);
+ }
+ flags2 = FLAGS2_LONG_PATH_COMPONENTS;
+ if (cli->capabilities & CAP_UNICODE)
+ flags2 |= FLAGS2_UNICODE_STRINGS;
+ if ((cli->capabilities & CAP_DFS) && cli->dfsroot)
+ flags2 |= FLAGS2_DFS_PATHNAMES;
+ if (cli->capabilities & CAP_STATUS32)
+ flags2 |= FLAGS2_32_BIT_ERROR_CODES;
+ if (cli->use_spnego)
+ flags2 |= FLAGS2_EXTENDED_SECURITY;
+ SSVAL(buf,smb_flg2, flags2);
+}
+
+void cli_setup_packet(struct cli_state *cli)
+{
+ cli_setup_packet_buf(cli, cli->outbuf);
+}
+
+/****************************************************************************
+ 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 char *username, const char *domain, const char *password)
+{
+ fstrcpy(cli->domain, domain);
+ fstrcpy(cli->user_name, username);
+ pwd_set_cleartext(&cli->pwd, password);
+ if (!*username) {
+ cli->pwd.null_pwd = true;
+ }
+
+ DEBUG(10,("cli_init_creds: user %s domain %s\n", cli->user_name, cli->domain));
+}
+
+/****************************************************************************
+ Set the signing state (used from the command line).
+****************************************************************************/
+
+void cli_setup_signing_state(struct cli_state *cli, int signing_state)
+{
+ if (signing_state == Undefined)
+ return;
+
+ if (signing_state == false) {
+ cli->sign_info.allow_smb_signing = false;
+ cli->sign_info.mandatory_signing = false;
+ return;
+ }
+
+ cli->sign_info.allow_smb_signing = true;
+
+ if (signing_state == Required)
+ cli->sign_info.mandatory_signing = true;
+}
+
+/****************************************************************************
+ Initialise a client structure. Always returns a malloc'ed struct.
+****************************************************************************/
+
+struct cli_state *cli_initialise(void)
+{
+ struct cli_state *cli = NULL;
+
+ /* 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;
+ }
+
+ cli = talloc(NULL, struct cli_state);
+ if (!cli) {
+ return NULL;
+ }
+
+ 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 *)SMB_MALLOC(cli->bufsize+SAFETY_MARGIN);
+ cli->inbuf = (char *)SMB_MALLOC(cli->bufsize+SAFETY_MARGIN);
+ cli->oplock_handler = cli_oplock_ack;
+ cli->case_sensitive = false;
+ cli->smb_rw_error = SMB_READ_OK;
+
+ cli->use_spnego = lp_client_use_spnego();
+
+ cli->capabilities = CAP_UNICODE | CAP_STATUS32 | CAP_DFS;
+
+ /* 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 (lp_client_signing())
+ cli->sign_info.allow_smb_signing = true;
+
+ if (lp_client_signing() == Required)
+ cli->sign_info.mandatory_signing = true;
+
+ if (!cli->outbuf || !cli->inbuf)
+ goto error;
+
+ memset(cli->outbuf, 0, cli->bufsize);
+ memset(cli->inbuf, 0, cli->bufsize);
+
+
+#if defined(DEVELOPER)
+ /* just because we over-allocate, doesn't mean it's right to use it */
+ clobber_region(FUNCTION_MACRO, __LINE__, cli->outbuf+cli->bufsize, SAFETY_MARGIN);
+ clobber_region(FUNCTION_MACRO, __LINE__, cli->inbuf+cli->bufsize, SAFETY_MARGIN);
+#endif
+
+ /* initialise signing */
+ cli_null_set_signing(cli);
+
+ cli->initialised = 1;
+
+ return cli;
+
+ /* Clean up after malloc() error */
+
+ error:
+
+ SAFE_FREE(cli->inbuf);
+ SAFE_FREE(cli->outbuf);
+ SAFE_FREE(cli);
+ return NULL;
+}
+
+/****************************************************************************
+ Close all pipes open on this session.
+****************************************************************************/
+
+void cli_nt_pipes_close(struct cli_state *cli)
+{
+ while (cli->pipe_list != NULL) {
+ /*
+ * No TALLOC_FREE here!
+ */
+ talloc_free(cli->pipe_list);
+ }
+}
+
+/****************************************************************************
+ Shutdown a client structure.
+****************************************************************************/
+
+void cli_shutdown(struct cli_state *cli)
+{
+ cli_nt_pipes_close(cli);
+
+ /*
+ * tell our peer to free his resources. Wihtout this, when an
+ * application attempts to do a graceful shutdown and calls
+ * smbc_free_context() to clean up all connections, some connections
+ * can remain active on the peer end, until some (long) timeout period
+ * later. This tree disconnect forces the peer to clean up, since the
+ * connection will be going away.
+ *
+ * Also, do not do tree disconnect when cli->smb_rw_error is SMB_DO_NOT_DO_TDIS
+ * the only user for this so far is smbmount which passes opened connection
+ * down to kernel's smbfs module.
+ */
+ if ( (cli->cnum != (uint16)-1) && (cli->smb_rw_error != SMB_DO_NOT_DO_TDIS ) ) {
+ cli_tdis(cli);
+ }
+
+ SAFE_FREE(cli->outbuf);
+ SAFE_FREE(cli->inbuf);
+
+ cli_free_signing_context(cli);
+ data_blob_free(&cli->secblob);
+ data_blob_free(&cli->user_session_key);
+
+ if (cli->fd != -1) {
+ close(cli->fd);
+ }
+ cli->fd = -1;
+ cli->smb_rw_error = SMB_READ_OK;
+
+ TALLOC_FREE(cli);
+}
+
+/****************************************************************************
+ Set socket options on a open connection.
+****************************************************************************/
+
+void cli_sockopt(struct cli_state *cli, const 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;
+}
+
+/****************************************************************************
+ Set the case sensitivity flag on the packets. Returns old state.
+****************************************************************************/
+
+bool cli_set_case_sensitive(struct cli_state *cli, bool case_sensitive)
+{
+ bool ret = cli->case_sensitive;
+ cli->case_sensitive = case_sensitive;
+ return ret;
+}
+
+/****************************************************************************
+Send a keepalive packet to the server
+****************************************************************************/
+
+bool cli_send_keepalive(struct cli_state *cli)
+{
+ if (cli->fd == -1) {
+ DEBUG(3, ("cli_send_keepalive: fd == -1\n"));
+ return false;
+ }
+ if (!send_keepalive(cli->fd)) {
+ close(cli->fd);
+ cli->fd = -1;
+ DEBUG(0,("Error sending keepalive packet to client.\n"));
+ return false;
+ }
+ return true;
+}
+
+/**
+ * @brief: Collect a echo reply
+ * @param[in] req The corresponding async request
+ *
+ * There might be more than one echo reply. This helper pulls the reply out of
+ * the data stream. If all expected replies have arrived, declare the
+ * async_req done.
+ */
+
+static void cli_echo_recv_helper(struct async_req *req)
+{
+ struct cli_request *cli_req;
+ uint8_t wct;
+ uint16_t *vwv;
+ uint16_t num_bytes;
+ uint8_t *bytes;
+ NTSTATUS status;
+
+ status = cli_pull_reply(req, &wct, &vwv, &num_bytes, &bytes);
+ if (!NT_STATUS_IS_OK(status)) {
+ async_req_error(req, status);
+ return;
+ }
+
+ cli_req = talloc_get_type_abort(req->private_data, struct cli_request);
+
+ if ((num_bytes != cli_req->data.echo.data.length)
+ || (memcmp(cli_req->data.echo.data.data, bytes,
+ num_bytes) != 0)) {
+ async_req_error(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ cli_req->data.echo.num_echos -= 1;
+
+ if (cli_req->data.echo.num_echos == 0) {
+ client_set_trans_sign_state_off(cli_req->cli, cli_req->mid);
+ async_req_done(req);
+ return;
+ }
+
+ return;
+}
+
+/**
+ * @brief Send SMBEcho requests
+ * @param[in] mem_ctx The memory context to put the async_req on
+ * @param[in] ev The event context that will call us back
+ * @param[in] cli The connection to send the echo to
+ * @param[in] num_echos How many times do we want to get the reply?
+ * @param[in] data The data we want to get back
+ * @retval The async request
+ */
+
+struct async_req *cli_echo_send(TALLOC_CTX *mem_ctx, struct event_context *ev,
+ struct cli_state *cli, uint16_t num_echos,
+ DATA_BLOB data)
+{
+ uint16_t vwv[1];
+ uint8_t *data_copy;
+ struct async_req *result;
+ struct cli_request *req;
+
+ SSVAL(vwv, 0, num_echos);
+
+ data_copy = (uint8_t *)talloc_memdup(mem_ctx, data.data, data.length);
+ if (data_copy == NULL) {
+ return NULL;
+ }
+
+ result = cli_request_send(mem_ctx, ev, cli, SMBecho, 0, 1, vwv,
+ data.length, data.data);
+ if (result == NULL) {
+ TALLOC_FREE(data_copy);
+ return NULL;
+ }
+ req = talloc_get_type_abort(result->private_data, struct cli_request);
+
+ client_set_trans_sign_state_on(cli, req->mid);
+
+ req->data.echo.num_echos = num_echos;
+ req->data.echo.data.data = talloc_move(req, &data_copy);
+ req->data.echo.data.length = data.length;
+
+ req->recv_helper.fn = cli_echo_recv_helper;
+
+ return result;
+}
+
+/**
+ * Get the result out from an echo request
+ * @param[in] req The async_req from cli_echo_send
+ * @retval Did the server reply correctly?
+ */
+
+NTSTATUS cli_echo_recv(struct async_req *req)
+{
+ SMB_ASSERT(req->state >= ASYNC_REQ_DONE);
+ if (req->state == ASYNC_REQ_ERROR) {
+ return req->status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * @brief Send/Receive SMBEcho requests
+ * @param[in] mem_ctx The memory context to put the async_req on
+ * @param[in] ev The event context that will call us back
+ * @param[in] cli The connection to send the echo to
+ * @param[in] num_echos How many times do we want to get the reply?
+ * @param[in] data The data we want to get back
+ * @retval Did the server reply correctly?
+ */
+
+NTSTATUS cli_echo(struct cli_state *cli, uint16_t num_echos, DATA_BLOB data)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct event_context *ev;
+ struct async_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (cli->fd_event != NULL) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ cli_set_error(cli, NT_STATUS_INVALID_PARAMETER);
+ goto fail;
+ }
+
+ ev = event_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+
+ req = cli_echo_send(frame, ev, cli, num_echos, data);
+ if (req == NULL) {
+ goto fail;
+ }
+
+ while (req->state < ASYNC_REQ_DONE) {
+ event_loop_once(ev);
+ }
+
+ status = cli_echo_recv(req);
+
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
diff --git a/source3/libsmb/clierror.c b/source3/libsmb/clierror.c
new file mode 100644
index 0000000000..36746419f7
--- /dev/null
+++ b/source3/libsmb/clierror.c
@@ -0,0 +1,497 @@
+/*
+ Unix SMB/CIFS implementation.
+ client error handling routines
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Jelmer Vernooij 2003
+ Copyright (C) Jeremy Allison 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/*****************************************************
+ 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;
+ const 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 const char *cli_smb_errstr(struct cli_state *cli)
+{
+ return smb_dos_errstr(cli->inbuf);
+}
+
+/****************************************************************************
+ Convert a socket error into an NTSTATUS.
+****************************************************************************/
+
+static NTSTATUS cli_smb_rw_error_to_ntstatus(struct cli_state *cli)
+{
+ switch(cli->smb_rw_error) {
+ case SMB_READ_TIMEOUT:
+ return NT_STATUS_IO_TIMEOUT;
+ case SMB_READ_EOF:
+ return NT_STATUS_END_OF_FILE;
+ /* What we shoud really do for read/write errors is convert from errno. */
+ /* FIXME. JRA. */
+ case SMB_READ_ERROR:
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ case SMB_WRITE_ERROR:
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ case SMB_READ_BAD_SIG:
+ return NT_STATUS_INVALID_PARAMETER;
+ case SMB_NO_MEMORY:
+ return NT_STATUS_NO_MEMORY;
+ default:
+ break;
+ }
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/***************************************************************************
+ 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.
+****************************************************************************/
+
+const char *cli_errstr(struct cli_state *cli)
+{
+ fstring cli_error_message;
+ uint32 flgs2 = SVAL(cli->inbuf,smb_flg2), errnum;
+ uint8 errclass;
+ int i;
+ char *result;
+
+ if (!cli->initialised) {
+ fstrcpy(cli_error_message, "[Programmer's error] cli_errstr called on unitialized cli_stat struct!\n");
+ goto done;
+ }
+
+ /* Was it server socket error ? */
+ if (cli->fd == -1 && cli->smb_rw_error) {
+ switch(cli->smb_rw_error) {
+ case SMB_READ_TIMEOUT:
+ slprintf(cli_error_message, sizeof(cli_error_message) - 1,
+ "Call timed out: server did not respond after %d milliseconds",
+ cli->timeout);
+ break;
+ case SMB_READ_EOF:
+ slprintf(cli_error_message, sizeof(cli_error_message) - 1,
+ "Call returned zero bytes (EOF)" );
+ break;
+ case SMB_READ_ERROR:
+ slprintf(cli_error_message, sizeof(cli_error_message) - 1,
+ "Read error: %s", strerror(errno) );
+ break;
+ case SMB_WRITE_ERROR:
+ slprintf(cli_error_message, sizeof(cli_error_message) - 1,
+ "Write error: %s", strerror(errno) );
+ break;
+ case SMB_READ_BAD_SIG:
+ slprintf(cli_error_message, sizeof(cli_error_message) - 1,
+ "Server packet had invalid SMB signature!");
+ break;
+ case SMB_NO_MEMORY:
+ slprintf(cli_error_message, sizeof(cli_error_message) - 1,
+ "Out of memory");
+ break;
+ default:
+ slprintf(cli_error_message, sizeof(cli_error_message) - 1,
+ "Unknown error code %d\n", cli->smb_rw_error );
+ break;
+ }
+ goto done;
+ }
+
+ /* 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);
+
+ goto done;
+ }
+
+ /* 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);
+
+ done:
+ result = talloc_strdup(talloc_tos(), cli_error_message);
+ SMB_ASSERT(result);
+ return result;
+}
+
+
+/****************************************************************************
+ 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);
+
+ /* Deal with socket errors first. */
+ if (cli->fd == -1 && cli->smb_rw_error) {
+ return cli_smb_rw_error_to_ntstatus(cli);
+ }
+
+ if (!(flgs2 & FLAGS2_32_BIT_ERROR_CODES)) {
+ int e_class = CVAL(cli->inbuf,smb_rcls);
+ int code = SVAL(cli->inbuf,smb_err);
+ return dos_to_ntstatus(e_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;
+
+ if(!cli->initialised) {
+ return;
+ }
+
+ /* Deal with socket errors first. */
+ if (cli->fd == -1 && cli->smb_rw_error) {
+ NTSTATUS status = cli_smb_rw_error_to_ntstatus(cli);
+ ntstatus_to_dos( status, eclass, ecode);
+ 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;
+ }
+
+ *eclass = CVAL(cli->inbuf,smb_rcls);
+ *ecode = SVAL(cli->inbuf,smb_err);
+}
+
+/* Return a UNIX errno from a NT status code */
+static const struct {
+ NTSTATUS status;
+ int error;
+} nt_errno_map[] = {
+ {NT_STATUS_ACCESS_VIOLATION, EACCES},
+ {NT_STATUS_INVALID_HANDLE, EBADF},
+ {NT_STATUS_ACCESS_DENIED, EACCES},
+ {NT_STATUS_OBJECT_NAME_NOT_FOUND, ENOENT},
+ {NT_STATUS_OBJECT_PATH_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_UNSUCCESSFUL, EINVAL},
+ {NT_STATUS_NOT_IMPLEMENTED, ENOSYS},
+ {NT_STATUS_IN_PAGE_ERROR, EFAULT},
+ {NT_STATUS_BAD_NETWORK_NAME, ENOENT},
+#ifdef EDQUOT
+ {NT_STATUS_PAGEFILE_QUOTA, EDQUOT},
+ {NT_STATUS_QUOTA_EXCEEDED, EDQUOT},
+ {NT_STATUS_REGISTRY_QUOTA_LIMIT, EDQUOT},
+ {NT_STATUS_LICENSE_QUOTA_EXCEEDED, EDQUOT},
+#endif
+#ifdef ETIME
+ {NT_STATUS_TIMER_NOT_CANCELED, ETIME},
+#endif
+ {NT_STATUS_INVALID_PARAMETER, EINVAL},
+ {NT_STATUS_NO_SUCH_DEVICE, ENODEV},
+ {NT_STATUS_NO_SUCH_FILE, ENOENT},
+#ifdef ENODATA
+ {NT_STATUS_END_OF_FILE, ENODATA},
+#endif
+#ifdef ENOMEDIUM
+ {NT_STATUS_NO_MEDIA_IN_DEVICE, ENOMEDIUM},
+ {NT_STATUS_NO_MEDIA, ENOMEDIUM},
+#endif
+ {NT_STATUS_NONEXISTENT_SECTOR, ESPIPE},
+ {NT_STATUS_NO_MEMORY, ENOMEM},
+ {NT_STATUS_CONFLICTING_ADDRESSES, EADDRINUSE},
+ {NT_STATUS_NOT_MAPPED_VIEW, EINVAL},
+ {NT_STATUS_UNABLE_TO_FREE_VM, EADDRINUSE},
+ {NT_STATUS_ACCESS_DENIED, EACCES},
+ {NT_STATUS_BUFFER_TOO_SMALL, ENOBUFS},
+ {NT_STATUS_WRONG_PASSWORD, EACCES},
+ {NT_STATUS_LOGON_FAILURE, EACCES},
+ {NT_STATUS_INVALID_WORKSTATION, EACCES},
+ {NT_STATUS_INVALID_LOGON_HOURS, EACCES},
+ {NT_STATUS_PASSWORD_EXPIRED, EACCES},
+ {NT_STATUS_ACCOUNT_DISABLED, EACCES},
+ {NT_STATUS_DISK_FULL, ENOSPC},
+ {NT_STATUS_INVALID_PIPE_STATE, EPIPE},
+ {NT_STATUS_PIPE_BUSY, EPIPE},
+ {NT_STATUS_PIPE_DISCONNECTED, EPIPE},
+ {NT_STATUS_PIPE_NOT_AVAILABLE, ENOSYS},
+ {NT_STATUS_FILE_IS_A_DIRECTORY, EISDIR},
+ {NT_STATUS_NOT_SUPPORTED, ENOSYS},
+ {NT_STATUS_NOT_A_DIRECTORY, ENOTDIR},
+ {NT_STATUS_DIRECTORY_NOT_EMPTY, ENOTEMPTY},
+ {NT_STATUS_NETWORK_UNREACHABLE, ENETUNREACH},
+ {NT_STATUS_HOST_UNREACHABLE, EHOSTUNREACH},
+ {NT_STATUS_CONNECTION_ABORTED, ECONNABORTED},
+ {NT_STATUS_CONNECTION_REFUSED, ECONNREFUSED},
+ {NT_STATUS_TOO_MANY_LINKS, EMLINK},
+ {NT_STATUS_NETWORK_BUSY, EBUSY},
+ {NT_STATUS_DEVICE_DOES_NOT_EXIST, ENODEV},
+#ifdef ELIBACC
+ {NT_STATUS_DLL_NOT_FOUND, ELIBACC},
+#endif
+ {NT_STATUS_PIPE_BROKEN, EPIPE},
+ {NT_STATUS_REMOTE_NOT_LISTENING, ECONNREFUSED},
+ {NT_STATUS_NETWORK_ACCESS_DENIED, EACCES},
+ {NT_STATUS_TOO_MANY_OPENED_FILES, EMFILE},
+#ifdef EPROTO
+ {NT_STATUS_DEVICE_PROTOCOL_ERROR, EPROTO},
+#endif
+ {NT_STATUS_FLOAT_OVERFLOW, ERANGE},
+ {NT_STATUS_FLOAT_UNDERFLOW, ERANGE},
+ {NT_STATUS_INTEGER_OVERFLOW, ERANGE},
+ {NT_STATUS_MEDIA_WRITE_PROTECTED, EROFS},
+ {NT_STATUS_PIPE_CONNECTED, EISCONN},
+ {NT_STATUS_MEMORY_NOT_ALLOCATED, EFAULT},
+ {NT_STATUS_FLOAT_INEXACT_RESULT, ERANGE},
+ {NT_STATUS_ILL_FORMED_PASSWORD, EACCES},
+ {NT_STATUS_PASSWORD_RESTRICTION, EACCES},
+ {NT_STATUS_ACCOUNT_RESTRICTION, EACCES},
+ {NT_STATUS_PORT_CONNECTION_REFUSED, ECONNREFUSED},
+ {NT_STATUS_NAME_TOO_LONG, ENAMETOOLONG},
+ {NT_STATUS_REMOTE_DISCONNECT, ESHUTDOWN},
+ {NT_STATUS_CONNECTION_DISCONNECTED, ECONNABORTED},
+ {NT_STATUS_CONNECTION_RESET, ENETRESET},
+#ifdef ENOTUNIQ
+ {NT_STATUS_IP_ADDRESS_CONFLICT1, ENOTUNIQ},
+ {NT_STATUS_IP_ADDRESS_CONFLICT2, ENOTUNIQ},
+#endif
+ {NT_STATUS_PORT_MESSAGE_TOO_LONG, EMSGSIZE},
+ {NT_STATUS_PROTOCOL_UNREACHABLE, ENOPROTOOPT},
+ {NT_STATUS_ADDRESS_ALREADY_EXISTS, EADDRINUSE},
+ {NT_STATUS_PORT_UNREACHABLE, EHOSTUNREACH},
+ {NT_STATUS_IO_TIMEOUT, ETIMEDOUT},
+ {NT_STATUS_RETRY, EAGAIN},
+#ifdef ENOTUNIQ
+ {NT_STATUS_DUPLICATE_NAME, ENOTUNIQ},
+#endif
+#ifdef ECOMM
+ {NT_STATUS_NET_WRITE_FAULT, ECOMM},
+#endif
+#ifdef EXDEV
+ {NT_STATUS_NOT_SAME_DEVICE, EXDEV},
+#endif
+ {NT_STATUS(0), 0}
+};
+
+/****************************************************************************
+ The following mappings need tidying up and moving into libsmb/errormap.c...
+****************************************************************************/
+
+static 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_nt_error(cli)) {
+ status = cli_nt_error(cli);
+ return cli_errno_from_nt(status);
+ }
+
+ if (cli_is_dos_error(cli)) {
+ uint8 eclass;
+ uint32 ecode;
+
+ cli_dos_error(cli, &eclass, &ecode);
+ status = dos_to_ntstatus(eclass, ecode);
+ return cli_errno_from_nt(status);
+ }
+
+ /*
+ * Yuck! A special case for this Vista error. Since its high-order
+ * byte isn't 0xc0, it doesn't match cli_is_nt_error() above.
+ */
+ status = cli_nt_error(cli);
+ if (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_INACCESSIBLE_SYSTEM_SHORTCUT)) {
+ return EACCES;
+ }
+
+ /* for other cases */
+ return EINVAL;
+}
+
+/* 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;
+
+ /* A socket error is always an error. */
+ if (cli->fd == -1 && cli->smb_rw_error != 0) {
+ return True;
+ }
+
+ 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);
+
+ /* A socket error is always an NT error. */
+ if (cli->fd == -1 && cli->smb_rw_error != 0) {
+ return True;
+ }
+
+ 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);
+
+ /* A socket error is always a DOS error. */
+ if (cli->fd == -1 && cli->smb_rw_error != 0) {
+ return True;
+ }
+
+ return cli_is_error(cli) && !(flgs2 & FLAGS2_32_BIT_ERROR_CODES);
+}
+
+/* Return the last error always as an NTSTATUS. */
+
+NTSTATUS cli_get_nt_error(struct cli_state *cli)
+{
+ if (cli_is_nt_error(cli)) {
+ return cli_nt_error(cli);
+ } else if (cli_is_dos_error(cli)) {
+ uint32 ecode;
+ uint8 eclass;
+ cli_dos_error(cli, &eclass, &ecode);
+ return dos_to_ntstatus(eclass, ecode);
+ } else {
+ /* Something went wrong, we don't know what. */
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+}
+
+/* Push an error code into the inbuf to be returned on the next
+ * query. */
+
+void cli_set_nt_error(struct cli_state *cli, NTSTATUS status)
+{
+ SSVAL(cli->inbuf,smb_flg2, SVAL(cli->inbuf,smb_flg2)|FLAGS2_32_BIT_ERROR_CODES);
+ SIVAL(cli->inbuf, smb_rcls, NT_STATUS_V(status));
+}
+
+/* Reset an error. */
+
+void cli_reset_error(struct cli_state *cli)
+{
+ if (SVAL(cli->inbuf,smb_flg2) & FLAGS2_32_BIT_ERROR_CODES) {
+ SIVAL(cli->inbuf, smb_rcls, NT_STATUS_V(NT_STATUS_OK));
+ } else {
+ SCVAL(cli->inbuf,smb_rcls,0);
+ SSVAL(cli->inbuf,smb_err,0);
+ }
+}
diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c
new file mode 100644
index 0000000000..d3819af444
--- /dev/null
+++ b/source3/libsmb/clifile.c
@@ -0,0 +1,2281 @@
+/*
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+ Hard/Symlink a file (UNIX extensions).
+ Creates new name (sym)linked to oldname.
+****************************************************************************/
+
+static bool cli_link_internal(struct cli_state *cli, const char *oldname, const char *newname, bool hard_link)
+{
+ unsigned int data_len = 0;
+ unsigned int param_len = 0;
+ uint16 setup = TRANSACT2_SETPATHINFO;
+ char *param;
+ char *data;
+ char *rparam=NULL, *rdata=NULL;
+ char *p;
+ size_t oldlen = 2*(strlen(oldname)+1);
+ size_t newlen = 2*(strlen(newname)+1);
+
+ param = SMB_MALLOC_ARRAY(char, 6+newlen+2);
+
+ if (!param) {
+ return false;
+ }
+
+ data = SMB_MALLOC_ARRAY(char, oldlen+2);
+
+ if (!data) {
+ SAFE_FREE(param);
+ return false;
+ }
+
+ SSVAL(param,0,hard_link ? SMB_SET_FILE_UNIX_HLINK : SMB_SET_FILE_UNIX_LINK);
+ SIVAL(param,2,0);
+ p = &param[6];
+
+ p += clistr_push(cli, p, newname, newlen, STR_TERMINATE);
+ param_len = PTR_DIFF(p, param);
+
+ p = data;
+ p += clistr_push(cli, p, oldname, oldlen, 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 */
+ )) {
+ SAFE_FREE(data);
+ SAFE_FREE(param);
+ return false;
+ }
+
+ SAFE_FREE(data);
+ SAFE_FREE(param);
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len)) {
+ return false;
+ }
+
+ SAFE_FREE(data);
+ SAFE_FREE(param);
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+
+ return true;
+}
+
+/****************************************************************************
+ Map standard UNIX permissions onto wire representations.
+****************************************************************************/
+
+uint32 unix_perms_to_wire(mode_t perms)
+{
+ unsigned int 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_ISUID) ? UNIX_SET_UID : 0);
+#endif
+ return ret;
+}
+
+/****************************************************************************
+ Map wire permissions to standard UNIX.
+****************************************************************************/
+
+mode_t wire_perms_to_unix(uint32 perms)
+{
+ mode_t ret = (mode_t)0;
+
+ ret |= ((perms & UNIX_X_OTH) ? S_IXOTH : 0);
+ ret |= ((perms & UNIX_W_OTH) ? S_IWOTH : 0);
+ ret |= ((perms & UNIX_R_OTH) ? S_IROTH : 0);
+ ret |= ((perms & UNIX_X_GRP) ? S_IXGRP : 0);
+ ret |= ((perms & UNIX_W_GRP) ? S_IWGRP : 0);
+ ret |= ((perms & UNIX_R_GRP) ? S_IRGRP : 0);
+ ret |= ((perms & UNIX_X_USR) ? S_IXUSR : 0);
+ ret |= ((perms & UNIX_W_USR) ? S_IWUSR : 0);
+ ret |= ((perms & UNIX_R_USR) ? S_IRUSR : 0);
+#ifdef S_ISVTX
+ ret |= ((perms & UNIX_STICKY) ? S_ISVTX : 0);
+#endif
+#ifdef S_ISGID
+ ret |= ((perms & UNIX_SET_GID) ? S_ISGID : 0);
+#endif
+#ifdef S_ISUID
+ ret |= ((perms & UNIX_SET_UID) ? S_ISUID : 0);
+#endif
+ return ret;
+}
+
+/****************************************************************************
+ Return the file type from the wire filetype for UNIX extensions.
+****************************************************************************/
+
+static mode_t unix_filetype_from_wire(uint32 wire_type)
+{
+ switch (wire_type) {
+ case UNIX_TYPE_FILE:
+ return S_IFREG;
+ case UNIX_TYPE_DIR:
+ return S_IFDIR;
+#ifdef S_IFLNK
+ case UNIX_TYPE_SYMLINK:
+ return S_IFLNK;
+#endif
+#ifdef S_IFCHR
+ case UNIX_TYPE_CHARDEV:
+ return S_IFCHR;
+#endif
+#ifdef S_IFBLK
+ case UNIX_TYPE_BLKDEV:
+ return S_IFBLK;
+#endif
+#ifdef S_IFIFO
+ case UNIX_TYPE_FIFO:
+ return S_IFIFO;
+#endif
+#ifdef S_IFSOCK
+ case UNIX_TYPE_SOCKET:
+ return S_IFSOCK;
+#endif
+ default:
+ return (mode_t)0;
+ }
+}
+
+/****************************************************************************
+ Do a POSIX getfacl (UNIX extensions).
+****************************************************************************/
+
+bool cli_unix_getfacl(struct cli_state *cli, const char *name, size_t *prb_size, char **retbuf)
+{
+ unsigned int param_len = 0;
+ unsigned int data_len = 0;
+ uint16 setup = TRANSACT2_QPATHINFO;
+ char *param;
+ size_t nlen = 2*(strlen(name)+1);
+ char *rparam=NULL, *rdata=NULL;
+ char *p;
+
+ param = SMB_MALLOC_ARRAY(char, 6+nlen+2);
+ if (!param) {
+ return false;
+ }
+
+ p = param;
+ memset(p, '\0', 6);
+ SSVAL(p, 0, SMB_QUERY_POSIX_ACL);
+ p += 6;
+ p += clistr_push(cli, p, name, nlen, 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, 2, /* param, length, max */
+ NULL, 0, cli->max_xmit /* data, length, max */
+ )) {
+ SAFE_FREE(param);
+ return false;
+ }
+
+ SAFE_FREE(param);
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len)) {
+ return false;
+ }
+
+ if (data_len < 6) {
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ return false;
+ }
+
+ SAFE_FREE(rparam);
+ *retbuf = rdata;
+ *prb_size = (size_t)data_len;
+
+ return true;
+}
+
+/****************************************************************************
+ Stat a file (UNIX extensions).
+****************************************************************************/
+
+bool cli_unix_stat(struct cli_state *cli, const char *name, SMB_STRUCT_STAT *sbuf)
+{
+ unsigned int param_len = 0;
+ unsigned int data_len = 0;
+ uint16 setup = TRANSACT2_QPATHINFO;
+ char *param;
+ size_t nlen = 2*(strlen(name)+1);
+ char *rparam=NULL, *rdata=NULL;
+ char *p;
+
+ ZERO_STRUCTP(sbuf);
+
+ param = SMB_MALLOC_ARRAY(char, 6+nlen+2);
+ if (!param) {
+ return false;
+ }
+ p = param;
+ memset(p, '\0', 6);
+ SSVAL(p, 0, SMB_QUERY_FILE_UNIX_BASIC);
+ p += 6;
+ p += clistr_push(cli, p, name, nlen, 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, 2, /* param, length, max */
+ NULL, 0, cli->max_xmit /* data, length, max */
+ )) {
+ SAFE_FREE(param);
+ return false;
+ }
+
+ SAFE_FREE(param);
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len)) {
+ return false;
+ }
+
+ if (data_len < 96) {
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ return false;
+ }
+
+ sbuf->st_size = IVAL2_TO_SMB_BIG_UINT(rdata,0); /* total size, in bytes */
+ sbuf->st_blocks = IVAL2_TO_SMB_BIG_UINT(rdata,8); /* number of blocks allocated */
+#if defined (HAVE_STAT_ST_BLOCKS) && defined(STAT_ST_BLOCKSIZE)
+ sbuf->st_blocks /= STAT_ST_BLOCKSIZE;
+#else
+ /* assume 512 byte blocks */
+ sbuf->st_blocks /= 512;
+#endif
+ set_ctimespec(sbuf, interpret_long_date(rdata + 16)); /* time of last change */
+ set_atimespec(sbuf, interpret_long_date(rdata + 24)); /* time of last access */
+ set_mtimespec(sbuf, interpret_long_date(rdata + 32)); /* time of last modification */
+
+ sbuf->st_uid = (uid_t) IVAL(rdata,40); /* user ID of owner */
+ sbuf->st_gid = (gid_t) IVAL(rdata,48); /* group ID of owner */
+ sbuf->st_mode |= unix_filetype_from_wire(IVAL(rdata, 56));
+#if defined(HAVE_MAKEDEV)
+ {
+ uint32 dev_major = IVAL(rdata,60);
+ uint32 dev_minor = IVAL(rdata,68);
+ sbuf->st_rdev = makedev(dev_major, dev_minor);
+ }
+#endif
+ sbuf->st_ino = (SMB_INO_T)IVAL2_TO_SMB_BIG_UINT(rdata,76); /* inode */
+ sbuf->st_mode |= wire_perms_to_unix(IVAL(rdata,84)); /* protection */
+ sbuf->st_nlink = IVAL(rdata,92); /* number of hard links */
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+
+ return true;
+}
+
+/****************************************************************************
+ Symlink a file (UNIX extensions).
+****************************************************************************/
+
+bool cli_unix_symlink(struct cli_state *cli, const char *oldname, const char *newname)
+{
+ return cli_link_internal(cli, oldname, newname, False);
+}
+
+/****************************************************************************
+ Hard a file (UNIX extensions).
+****************************************************************************/
+
+bool cli_unix_hardlink(struct cli_state *cli, const char *oldname, const char *newname)
+{
+ return cli_link_internal(cli, oldname, newname, 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)
+{
+ unsigned int data_len = 0;
+ unsigned int param_len = 0;
+ uint16 setup = TRANSACT2_SETPATHINFO;
+ size_t nlen = 2*(strlen(fname)+1);
+ char *param;
+ char data[100];
+ char *rparam=NULL, *rdata=NULL;
+ char *p;
+
+ param = SMB_MALLOC_ARRAY(char, 6+nlen+2);
+ if (!param) {
+ return false;
+ }
+ memset(param, '\0', 6);
+ memset(data, 0, sizeof(data));
+
+ SSVAL(param,0,SMB_SET_FILE_UNIX_BASIC);
+ p = &param[6];
+
+ p += clistr_push(cli, p, fname, nlen, STR_TERMINATE);
+ param_len = PTR_DIFF(p, param);
+
+ memset(data, 0xff, 40); /* Set all sizes/times to no change. */
+
+ 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 */
+ )) {
+ SAFE_FREE(param);
+ return False;
+ }
+
+ SAFE_FREE(param);
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_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);
+
+ cli_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,
+ cli->bufsize - PTR_DIFF(p,cli->outbuf), STR_TERMINATE);
+ *p++ = 4;
+ p += clistr_push(cli, p, fname_dst,
+ cli->bufsize - PTR_DIFF(p,cli->outbuf), 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;
+}
+
+/****************************************************************************
+ NT Rename a file.
+****************************************************************************/
+
+bool cli_ntrename(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);
+
+ cli_set_message(cli->outbuf, 4, 0, true);
+
+ SCVAL(cli->outbuf,smb_com,SMBntrename);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0,aSYSTEM | aHIDDEN | aDIR);
+ SSVAL(cli->outbuf,smb_vwv1, RENAME_FLAG_RENAME);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+ p += clistr_push(cli, p, fname_src,
+ cli->bufsize - PTR_DIFF(p,cli->outbuf), STR_TERMINATE);
+ *p++ = 4;
+ p += clistr_push(cli, p, fname_dst,
+ cli->bufsize - PTR_DIFF(p,cli->outbuf), 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;
+}
+
+/****************************************************************************
+ NT hardlink a file.
+****************************************************************************/
+
+bool cli_nt_hardlink(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);
+
+ cli_set_message(cli->outbuf, 4, 0, true);
+
+ SCVAL(cli->outbuf,smb_com,SMBntrename);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0,aSYSTEM | aHIDDEN | aDIR);
+ SSVAL(cli->outbuf,smb_vwv1, RENAME_FLAG_HARD_LINK);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+ p += clistr_push(cli, p, fname_src,
+ cli->bufsize - PTR_DIFF(p,cli->outbuf), STR_TERMINATE);
+ *p++ = 4;
+ p += clistr_push(cli, p, fname_dst,
+ cli->bufsize - PTR_DIFF(p,cli->outbuf), 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_full(struct cli_state *cli, const char *fname, uint16 attrs)
+{
+ char *p;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ cli_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, attrs);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+ p += clistr_push(cli, p, fname,
+ cli->bufsize - PTR_DIFF(p,cli->outbuf), 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)
+{
+ return cli_unlink_full(cli, fname, aSYSTEM | aHIDDEN);
+}
+
+/****************************************************************************
+ 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);
+
+ cli_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,
+ cli->bufsize - PTR_DIFF(p,cli->outbuf), 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);
+
+ cli_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,
+ cli->bufsize - PTR_DIFF(p,cli->outbuf), 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)
+{
+ unsigned int data_len = 1;
+ unsigned int param_len = 6;
+ uint16 setup = TRANSACT2_SETFILEINFO;
+ char param[6];
+ 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, &param_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 CreatFlags, uint32 DesiredAccess,
+ uint32 FileAttributes, uint32 ShareAccess,
+ uint32 CreateDisposition, uint32 CreateOptions,
+ uint8 SecurityFlags)
+{
+ char *p;
+ int len;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ cli_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)
+ CreatFlags |= (REQUEST_OPLOCK|REQUEST_BATCH_OPLOCK);
+
+ SIVAL(cli->outbuf,smb_ntcreate_Flags, CreatFlags);
+ 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);
+ SCVAL(cli->outbuf,smb_ntcreate_SecurityFlags, SecurityFlags);
+
+ 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,
+ cli->bufsize - PTR_DIFF(p,cli->outbuf), 0);
+ p += len;
+ SSVAL(cli->outbuf,smb_ntcreate_NameLength, len);
+ /* sigh. this copes with broken netapp filer behaviour */
+ p += clistr_push(cli, p, "",
+ cli->bufsize - PTR_DIFF(p,cli->outbuf), 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, 0, DesiredAccess, 0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, 0x0, 0x0);
+}
+
+uint8_t *smb_bytes_push_str(uint8_t *buf, bool ucs2, const char *str)
+{
+ size_t buflen = talloc_get_size(buf);
+ char *converted;
+ size_t converted_size;
+
+ /*
+ * We're pushing into an SMB buffer, align odd
+ */
+ if (ucs2 && (buflen % 2 == 0)) {
+ buf = TALLOC_REALLOC_ARRAY(NULL, buf, uint8_t, buflen + 1);
+ if (buf == NULL) {
+ return NULL;
+ }
+ buf[buflen] = '\0';
+ buflen += 1;
+ }
+
+ if (!convert_string_allocate(talloc_tos(), CH_UNIX,
+ ucs2 ? CH_UTF16LE : CH_DOS,
+ str, strlen(str)+1, &converted,
+ &converted_size, true)) {
+ return NULL;
+ }
+
+ buf = TALLOC_REALLOC_ARRAY(NULL, buf, uint8_t,
+ buflen + converted_size);
+ if (buf == NULL) {
+ return NULL;
+ }
+
+ memcpy(buf + buflen, converted, converted_size);
+
+ TALLOC_FREE(converted);
+ return buf;
+}
+
+/****************************************************************************
+ Open a file
+ WARNING: if you open with O_WRONLY then getattrE won't work!
+****************************************************************************/
+
+struct async_req *cli_open_send(TALLOC_CTX *mem_ctx, struct event_context *ev,
+ struct cli_state *cli,
+ const char *fname, int flags, int share_mode)
+{
+ unsigned openfn = 0;
+ unsigned accessmode = 0;
+ uint8_t additional_flags = 0;
+ uint8_t *bytes;
+ uint16_t vwv[15];
+ struct async_req *result;
+
+ 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;
+ }
+
+ SCVAL(vwv + 0, 0, 0xFF);
+ SCVAL(vwv + 0, 1, 0);
+ SSVAL(vwv + 1, 0, 0);
+ SSVAL(vwv + 2, 0, 0); /* no additional info */
+ SSVAL(vwv + 3, 0, accessmode);
+ SSVAL(vwv + 4, 0, aSYSTEM | aHIDDEN);
+ SSVAL(vwv + 5, 0, 0);
+ SIVAL(vwv + 6, 0, 0);
+ SSVAL(vwv + 8, 0, openfn);
+ SIVAL(vwv + 9, 0, 0);
+ SIVAL(vwv + 11, 0, 0);
+ SIVAL(vwv + 13, 0, 0);
+
+ if (cli->use_oplocks) {
+ /* if using oplocks then ask for a batch oplock via
+ core and extended methods */
+ additional_flags =
+ FLAG_REQUEST_OPLOCK|FLAG_REQUEST_BATCH_OPLOCK;
+ SSVAL(vwv+2, 0, SVAL(vwv+2, 0) | 6);
+ }
+
+ bytes = talloc_array(talloc_tos(), uint8_t, 0);
+ if (bytes == NULL) {
+ return NULL;
+ }
+
+ bytes = smb_bytes_push_str(
+ bytes, (cli->capabilities & CAP_UNICODE) != 0, fname);
+ if (bytes == NULL) {
+ return NULL;
+ }
+
+ result = cli_request_send(mem_ctx, ev, cli, SMBopenX, additional_flags,
+ 15, vwv, talloc_get_size(bytes), bytes);
+ TALLOC_FREE(bytes);
+ return result;
+}
+
+NTSTATUS cli_open_recv(struct async_req *req, int *fnum)
+{
+ uint8_t wct;
+ uint16_t *vwv;
+ uint16_t num_bytes;
+ uint8_t *bytes;
+ NTSTATUS status;
+
+ SMB_ASSERT(req->state >= ASYNC_REQ_DONE);
+ if (req->state == ASYNC_REQ_ERROR) {
+ return req->status;
+ }
+
+ status = cli_pull_reply(req, &wct, &vwv, &num_bytes, &bytes);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (wct < 3) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ *fnum = SVAL(vwv+2, 0);
+
+ return NT_STATUS_OK;
+}
+
+int cli_open(struct cli_state *cli, const char *fname, int flags,
+ int share_mode)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct event_context *ev;
+ struct async_req *req;
+ int result = -1;
+
+ if (cli->fd_event != NULL) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ cli_set_error(cli, NT_STATUS_INVALID_PARAMETER);
+ goto fail;
+ }
+
+ ev = event_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+
+ req = cli_open_send(frame, ev, cli, fname, flags, share_mode);
+ if (req == NULL) {
+ goto fail;
+ }
+
+ while (req->state < ASYNC_REQ_DONE) {
+ event_loop_once(ev);
+ }
+
+ cli_open_recv(req, &result);
+ fail:
+ TALLOC_FREE(frame);
+ return result;
+}
+
+/****************************************************************************
+ Close a file.
+****************************************************************************/
+
+struct async_req *cli_close_send(TALLOC_CTX *mem_ctx, struct event_context *ev,
+ struct cli_state *cli, int fnum)
+{
+ uint16_t vwv[3];
+
+ SSVAL(vwv+0, 0, fnum);
+ SIVALS(vwv+1, 0, -1);
+
+ return cli_request_send(mem_ctx, ev, cli, SMBclose, 0, 3, vwv,
+ 0, NULL);
+}
+
+NTSTATUS cli_close_recv(struct async_req *req)
+{
+ uint8_t wct;
+ uint16_t *vwv;
+ uint16_t num_bytes;
+ uint8_t *bytes;
+
+ SMB_ASSERT(req->state >= ASYNC_REQ_DONE);
+ if (req->state == ASYNC_REQ_ERROR) {
+ return req->status;
+ }
+
+ return cli_pull_reply(req, &wct, &vwv, &num_bytes, &bytes);
+}
+
+bool cli_close(struct cli_state *cli, int fnum)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct event_context *ev;
+ struct async_req *req;
+ bool result = false;
+
+ if (cli->fd_event != NULL) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ cli_set_error(cli, NT_STATUS_INVALID_PARAMETER);
+ goto fail;
+ }
+
+ ev = event_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+
+ req = cli_close_send(frame, ev, cli, fnum);
+ if (req == NULL) {
+ goto fail;
+ }
+
+ while (req->state < ASYNC_REQ_DONE) {
+ event_loop_once(ev);
+ }
+
+ result = NT_STATUS_IS_OK(cli_close_recv(req));
+ fail:
+ TALLOC_FREE(frame);
+ return result;
+}
+
+/****************************************************************************
+ Truncate a file to a specified size
+****************************************************************************/
+
+bool cli_ftruncate(struct cli_state *cli, int fnum, uint64_t size)
+{
+ unsigned int param_len = 6;
+ unsigned int data_len = 8;
+ uint16 setup = TRANSACT2_SETFILEINFO;
+ char param[6];
+ unsigned char data[8];
+ char *rparam=NULL, *rdata=NULL;
+ int saved_timeout = cli->timeout;
+
+ SSVAL(param,0,fnum);
+ SSVAL(param,2,SMB_SET_FILE_END_OF_FILE_INFO);
+ SSVAL(param,4,0);
+
+ SBVAL(data, 0, size);
+
+ 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,/* data, length, ... */
+ cli->max_xmit)) { /* ... max */
+ cli->timeout = saved_timeout;
+ return False;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len)) {
+ cli->timeout = saved_timeout;
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ return False;
+ }
+
+ cli->timeout = saved_timeout;
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+
+ return True;
+}
+
+
+/****************************************************************************
+ 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);
+
+ cli_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.
+ note that timeout is in units of 2 milliseconds
+****************************************************************************/
+
+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);
+
+ cli_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 + 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.
+****************************************************************************/
+
+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);
+
+ cli_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);
+
+ cli_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);
+
+ cli_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;
+}
+
+/****************************************************************************
+ Get/unlock a POSIX lock on a file - internal function.
+****************************************************************************/
+
+static bool cli_posix_lock_internal(struct cli_state *cli, int fnum,
+ SMB_BIG_UINT offset, SMB_BIG_UINT len, bool wait_lock, enum brl_type lock_type)
+{
+ unsigned int param_len = 4;
+ unsigned int data_len = POSIX_LOCK_DATA_SIZE;
+ uint16 setup = TRANSACT2_SETFILEINFO;
+ char param[4];
+ unsigned char data[POSIX_LOCK_DATA_SIZE];
+ char *rparam=NULL, *rdata=NULL;
+ int saved_timeout = cli->timeout;
+
+ SSVAL(param,0,fnum);
+ SSVAL(param,2,SMB_SET_POSIX_LOCK);
+
+ switch (lock_type) {
+ case READ_LOCK:
+ SSVAL(data, POSIX_LOCK_TYPE_OFFSET, POSIX_LOCK_TYPE_READ);
+ break;
+ case WRITE_LOCK:
+ SSVAL(data, POSIX_LOCK_TYPE_OFFSET, POSIX_LOCK_TYPE_WRITE);
+ break;
+ case UNLOCK_LOCK:
+ SSVAL(data, POSIX_LOCK_TYPE_OFFSET, POSIX_LOCK_TYPE_UNLOCK);
+ break;
+ default:
+ return False;
+ }
+
+ if (wait_lock) {
+ SSVAL(data, POSIX_LOCK_FLAGS_OFFSET, POSIX_LOCK_FLAG_WAIT);
+ cli->timeout = 0x7FFFFFFF;
+ } else {
+ SSVAL(data, POSIX_LOCK_FLAGS_OFFSET, POSIX_LOCK_FLAG_NOWAIT);
+ }
+
+ SIVAL(data, POSIX_LOCK_PID_OFFSET, cli->pid);
+ SOFF_T(data, POSIX_LOCK_START_OFFSET, offset);
+ SOFF_T(data, POSIX_LOCK_LEN_OFFSET, len);
+
+ 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 */
+ )) {
+ cli->timeout = saved_timeout;
+ return False;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len)) {
+ cli->timeout = saved_timeout;
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ return False;
+ }
+
+ cli->timeout = saved_timeout;
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+
+ return True;
+}
+
+/****************************************************************************
+ POSIX Lock a file.
+****************************************************************************/
+
+bool cli_posix_lock(struct cli_state *cli, int fnum,
+ SMB_BIG_UINT offset, SMB_BIG_UINT len,
+ bool wait_lock, enum brl_type lock_type)
+{
+ if (lock_type != READ_LOCK && lock_type != WRITE_LOCK) {
+ return False;
+ }
+ return cli_posix_lock_internal(cli, fnum, offset, len, wait_lock, lock_type);
+}
+
+/****************************************************************************
+ POSIX Unlock a file.
+****************************************************************************/
+
+bool cli_posix_unlock(struct cli_state *cli, int fnum, SMB_BIG_UINT offset, SMB_BIG_UINT len)
+{
+ return cli_posix_lock_internal(cli, fnum, offset, len, False, UNLOCK_LOCK);
+}
+
+/****************************************************************************
+ POSIX Get any lock covering a file.
+****************************************************************************/
+
+bool cli_posix_getlock(struct cli_state *cli, int fnum, SMB_BIG_UINT *poffset, SMB_BIG_UINT *plen)
+{
+ return True;
+}
+
+/****************************************************************************
+ Do a SMBgetattrE call.
+****************************************************************************/
+
+bool cli_getattrE(struct cli_state *cli, int fd,
+ uint16 *attr, SMB_OFF_T *size,
+ time_t *change_time,
+ time_t *access_time,
+ time_t *write_time)
+{
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ cli_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 (change_time) {
+ *change_time = cli_make_unix_date2(cli, cli->inbuf+smb_vwv0);
+ }
+
+ if (access_time) {
+ *access_time = cli_make_unix_date2(cli, cli->inbuf+smb_vwv2);
+ }
+
+ if (write_time) {
+ *write_time = cli_make_unix_date2(cli, cli->inbuf+smb_vwv4);
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Do a SMBgetatr call
+****************************************************************************/
+
+bool cli_getatr(struct cli_state *cli, const char *fname,
+ uint16 *attr, SMB_OFF_T *size, time_t *write_time)
+{
+ char *p;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ cli_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,
+ cli->bufsize - PTR_DIFF(p,cli->outbuf), 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 (write_time) {
+ *write_time = cli_make_unix_date3(cli, cli->inbuf+smb_vwv1);
+ }
+
+ if (attr) {
+ *attr = SVAL(cli->inbuf,smb_vwv0);
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ Do a SMBsetattrE call.
+****************************************************************************/
+
+bool cli_setattrE(struct cli_state *cli, int fd,
+ time_t change_time,
+ time_t access_time,
+ time_t write_time)
+
+{
+ char *p;
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ cli_set_message(cli->outbuf,7,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBsetattrE);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf,smb_vwv0, fd);
+ cli_put_dos_date2(cli, cli->outbuf,smb_vwv1, change_time);
+ cli_put_dos_date2(cli, cli->outbuf,smb_vwv3, access_time);
+ cli_put_dos_date2(cli, cli->outbuf,smb_vwv5, write_time);
+
+ p = smb_buf(cli->outbuf);
+ *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;
+}
+
+/****************************************************************************
+ 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);
+
+ cli_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);
+ cli_put_dos_date3(cli, cli->outbuf,smb_vwv1, t);
+
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+ p += clistr_push(cli, p, fname,
+ cli->bufsize - PTR_DIFF(p,cli->outbuf), 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)
+{
+ char *path2 = NULL;
+ char *p;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ path2 = talloc_strdup(frame, path);
+ if (!path2) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+ trim_char(path2,'\0','\\');
+ if (!*path2) {
+ path2 = talloc_strdup(frame, "\\");
+ if (!path2) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+ }
+
+ memset(cli->outbuf,'\0',smb_size);
+ cli_set_message(cli->outbuf,0,0,True);
+ SCVAL(cli->outbuf,smb_com,SMBcheckpath);
+ SSVAL(cli->outbuf,smb_tid,cli->cnum);
+ cli_setup_packet(cli);
+ p = smb_buf(cli->outbuf);
+ *p++ = 4;
+ p += clistr_push(cli, p, path2,
+ cli->bufsize - PTR_DIFF(p,cli->outbuf), STR_TERMINATE);
+
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ TALLOC_FREE(frame);
+ return False;
+ }
+
+ TALLOC_FREE(frame);
+
+ 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);
+ cli_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);
+
+ cli_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,
+ cli->bufsize - PTR_DIFF(p,cli->outbuf), 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 || len > PATH_MAX) return -1;
+
+ if (tmp_path) {
+ char *path2 = SMB_MALLOC_ARRAY(char, len+1);
+ if (!path2) {
+ return -1;
+ }
+ clistr_pull(cli, path2, p,
+ len+1, len, STR_ASCII);
+ *tmp_path = path2;
+ }
+
+ return SVAL(cli->inbuf,smb_vwv0);
+}
+
+/*
+ send a raw ioctl - used by the torture code
+*/
+NTSTATUS cli_raw_ioctl(struct cli_state *cli, int fnum, uint32 code, DATA_BLOB *blob)
+{
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ cli_set_message(cli->outbuf, 3, 0, True);
+ SCVAL(cli->outbuf,smb_com,SMBioctl);
+ cli_setup_packet(cli);
+
+ SSVAL(cli->outbuf, smb_vwv0, fnum);
+ SSVAL(cli->outbuf, smb_vwv1, code>>16);
+ SSVAL(cli->outbuf, smb_vwv2, (code&0xFFFF));
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli)) {
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+
+ if (cli_is_error(cli)) {
+ return cli_nt_error(cli);
+ }
+
+ *blob = data_blob_null;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************
+ Set an extended attribute utility fn.
+*********************************************************/
+
+static bool cli_set_ea(struct cli_state *cli, uint16 setup, char *param, unsigned int param_len,
+ const char *ea_name, const char *ea_val, size_t ea_len)
+{
+ unsigned int data_len = 0;
+ char *data = NULL;
+ char *rparam=NULL, *rdata=NULL;
+ char *p;
+ size_t ea_namelen = strlen(ea_name);
+
+ if (ea_namelen == 0 && ea_len == 0) {
+ data_len = 4;
+ data = (char *)SMB_MALLOC(data_len);
+ if (!data) {
+ return False;
+ }
+ p = data;
+ SIVAL(p,0,data_len);
+ } else {
+ data_len = 4 + 4 + ea_namelen + 1 + ea_len;
+ data = (char *)SMB_MALLOC(data_len);
+ if (!data) {
+ return False;
+ }
+ p = data;
+ SIVAL(p,0,data_len);
+ p += 4;
+ SCVAL(p, 0, 0); /* EA flags. */
+ SCVAL(p, 1, ea_namelen);
+ SSVAL(p, 2, ea_len);
+ memcpy(p+4, ea_name, ea_namelen+1); /* Copy in the name. */
+ memcpy(p+4+ea_namelen+1, ea_val, ea_len);
+ }
+
+ 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 */
+ data, data_len, cli->max_xmit /* data, length, max */
+ )) {
+ SAFE_FREE(data);
+ return False;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len)) {
+ SAFE_FREE(data);
+ return false;
+ }
+
+ SAFE_FREE(data);
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+
+ return True;
+}
+
+/*********************************************************
+ Set an extended attribute on a pathname.
+*********************************************************/
+
+bool cli_set_ea_path(struct cli_state *cli, const char *path, const char *ea_name, const char *ea_val, size_t ea_len)
+{
+ uint16 setup = TRANSACT2_SETPATHINFO;
+ unsigned int param_len = 0;
+ char *param;
+ size_t srclen = 2*(strlen(path)+1);
+ char *p;
+ bool ret;
+
+ param = SMB_MALLOC_ARRAY(char, 6+srclen+2);
+ if (!param) {
+ return false;
+ }
+ memset(param, '\0', 6);
+ SSVAL(param,0,SMB_INFO_SET_EA);
+ p = &param[6];
+
+ p += clistr_push(cli, p, path, srclen, STR_TERMINATE);
+ param_len = PTR_DIFF(p, param);
+
+ ret = cli_set_ea(cli, setup, param, param_len, ea_name, ea_val, ea_len);
+ SAFE_FREE(param);
+ return ret;
+}
+
+/*********************************************************
+ Set an extended attribute on an fnum.
+*********************************************************/
+
+bool cli_set_ea_fnum(struct cli_state *cli, int fnum, const char *ea_name, const char *ea_val, size_t ea_len)
+{
+ char param[6];
+ uint16 setup = TRANSACT2_SETFILEINFO;
+
+ memset(param, 0, 6);
+ SSVAL(param,0,fnum);
+ SSVAL(param,2,SMB_INFO_SET_EA);
+
+ return cli_set_ea(cli, setup, param, 6, ea_name, ea_val, ea_len);
+}
+
+/*********************************************************
+ Get an extended attribute list utility fn.
+*********************************************************/
+
+static bool cli_get_ea_list(struct cli_state *cli,
+ uint16 setup, char *param, unsigned int param_len,
+ TALLOC_CTX *ctx,
+ size_t *pnum_eas,
+ struct ea_struct **pea_list)
+{
+ unsigned int data_len = 0;
+ unsigned int rparam_len, rdata_len;
+ char *rparam=NULL, *rdata=NULL;
+ char *p;
+ size_t ea_size;
+ size_t num_eas;
+ bool ret = False;
+ struct ea_struct *ea_list;
+
+ *pnum_eas = 0;
+ if (pea_list) {
+ *pea_list = NULL;
+ }
+
+ 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, &rparam_len,
+ &rdata, &rdata_len)) {
+ return False;
+ }
+
+ if (!rdata || rdata_len < 4) {
+ goto out;
+ }
+
+ ea_size = (size_t)IVAL(rdata,0);
+ if (ea_size > rdata_len) {
+ goto out;
+ }
+
+ if (ea_size == 0) {
+ /* No EA's present. */
+ ret = True;
+ goto out;
+ }
+
+ p = rdata + 4;
+ ea_size -= 4;
+
+ /* Validate the EA list and count it. */
+ for (num_eas = 0; ea_size >= 4; num_eas++) {
+ unsigned int ea_namelen = CVAL(p,1);
+ unsigned int ea_valuelen = SVAL(p,2);
+ if (ea_namelen == 0) {
+ goto out;
+ }
+ if (4 + ea_namelen + 1 + ea_valuelen > ea_size) {
+ goto out;
+ }
+ ea_size -= 4 + ea_namelen + 1 + ea_valuelen;
+ p += 4 + ea_namelen + 1 + ea_valuelen;
+ }
+
+ if (num_eas == 0) {
+ ret = True;
+ goto out;
+ }
+
+ *pnum_eas = num_eas;
+ if (!pea_list) {
+ /* Caller only wants number of EA's. */
+ ret = True;
+ goto out;
+ }
+
+ ea_list = TALLOC_ARRAY(ctx, struct ea_struct, num_eas);
+ if (!ea_list) {
+ goto out;
+ }
+
+ ea_size = (size_t)IVAL(rdata,0);
+ p = rdata + 4;
+
+ for (num_eas = 0; num_eas < *pnum_eas; num_eas++ ) {
+ struct ea_struct *ea = &ea_list[num_eas];
+ fstring unix_ea_name;
+ unsigned int ea_namelen = CVAL(p,1);
+ unsigned int ea_valuelen = SVAL(p,2);
+
+ ea->flags = CVAL(p,0);
+ unix_ea_name[0] = '\0';
+ pull_ascii_fstring(unix_ea_name, p + 4);
+ ea->name = talloc_strdup(ctx, unix_ea_name);
+ /* Ensure the value is null terminated (in case it's a string). */
+ ea->value = data_blob_talloc(ctx, NULL, ea_valuelen + 1);
+ if (!ea->value.data) {
+ goto out;
+ }
+ if (ea_valuelen) {
+ memcpy(ea->value.data, p+4+ea_namelen+1, ea_valuelen);
+ }
+ ea->value.data[ea_valuelen] = 0;
+ ea->value.length--;
+ p += 4 + ea_namelen + 1 + ea_valuelen;
+ }
+
+ *pea_list = ea_list;
+ ret = True;
+
+ out :
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ return ret;
+}
+
+/*********************************************************
+ Get an extended attribute list from a pathname.
+*********************************************************/
+
+bool cli_get_ea_list_path(struct cli_state *cli, const char *path,
+ TALLOC_CTX *ctx,
+ size_t *pnum_eas,
+ struct ea_struct **pea_list)
+{
+ uint16 setup = TRANSACT2_QPATHINFO;
+ unsigned int param_len = 0;
+ char *param;
+ char *p;
+ size_t srclen = 2*(strlen(path)+1);
+ bool ret;
+
+ param = SMB_MALLOC_ARRAY(char, 6+srclen+2);
+ if (!param) {
+ return false;
+ }
+ p = param;
+ memset(p, 0, 6);
+ SSVAL(p, 0, SMB_INFO_QUERY_ALL_EAS);
+ p += 6;
+ p += clistr_push(cli, p, path, srclen, STR_TERMINATE);
+ param_len = PTR_DIFF(p, param);
+
+ ret = cli_get_ea_list(cli, setup, param, param_len, ctx, pnum_eas, pea_list);
+ SAFE_FREE(param);
+ return ret;
+}
+
+/*********************************************************
+ Get an extended attribute list from an fnum.
+*********************************************************/
+
+bool cli_get_ea_list_fnum(struct cli_state *cli, int fnum,
+ TALLOC_CTX *ctx,
+ size_t *pnum_eas,
+ struct ea_struct **pea_list)
+{
+ uint16 setup = TRANSACT2_QFILEINFO;
+ char param[6];
+
+ memset(param, 0, 6);
+ SSVAL(param,0,fnum);
+ SSVAL(param,2,SMB_INFO_SET_EA);
+
+ return cli_get_ea_list(cli, setup, param, 6, ctx, pnum_eas, pea_list);
+}
+
+/****************************************************************************
+ Convert open "flags" arg to uint32 on wire.
+****************************************************************************/
+
+static uint32 open_flags_to_wire(int flags)
+{
+ int open_mode = flags & O_ACCMODE;
+ uint32 ret = 0;
+
+ switch (open_mode) {
+ case O_WRONLY:
+ ret |= SMB_O_WRONLY;
+ break;
+ case O_RDWR:
+ ret |= SMB_O_RDWR;
+ break;
+ default:
+ case O_RDONLY:
+ ret |= SMB_O_RDONLY;
+ break;
+ }
+
+ if (flags & O_CREAT) {
+ ret |= SMB_O_CREAT;
+ }
+ if (flags & O_EXCL) {
+ ret |= SMB_O_EXCL;
+ }
+ if (flags & O_TRUNC) {
+ ret |= SMB_O_TRUNC;
+ }
+#if defined(O_SYNC)
+ if (flags & O_SYNC) {
+ ret |= SMB_O_SYNC;
+ }
+#endif /* O_SYNC */
+ if (flags & O_APPEND) {
+ ret |= SMB_O_APPEND;
+ }
+#if defined(O_DIRECT)
+ if (flags & O_DIRECT) {
+ ret |= SMB_O_DIRECT;
+ }
+#endif
+#if defined(O_DIRECTORY)
+ if (flags & O_DIRECTORY) {
+ ret &= ~(SMB_O_RDONLY|SMB_O_RDWR|SMB_O_WRONLY);
+ ret |= SMB_O_DIRECTORY;
+ }
+#endif
+ return ret;
+}
+
+/****************************************************************************
+ Open a file - POSIX semantics. Returns fnum. Doesn't request oplock.
+****************************************************************************/
+
+static int cli_posix_open_internal(struct cli_state *cli, const char *fname, int flags, mode_t mode, bool is_dir)
+{
+ unsigned int data_len = 0;
+ unsigned int param_len = 0;
+ uint16 setup = TRANSACT2_SETPATHINFO;
+ char *param;
+ char data[18];
+ char *rparam=NULL, *rdata=NULL;
+ char *p;
+ int fnum = -1;
+ uint32 wire_flags = open_flags_to_wire(flags);
+ size_t srclen = 2*(strlen(fname)+1);
+
+ param = SMB_MALLOC_ARRAY(char, 6+srclen+2);
+ if (!param) {
+ return false;
+ }
+ memset(param, '\0', 6);
+ SSVAL(param,0, SMB_POSIX_PATH_OPEN);
+ p = &param[6];
+
+ p += clistr_push(cli, p, fname, srclen, STR_TERMINATE);
+ param_len = PTR_DIFF(p, param);
+
+ if (is_dir) {
+ wire_flags &= ~(SMB_O_RDONLY|SMB_O_RDWR|SMB_O_WRONLY);
+ wire_flags |= SMB_O_DIRECTORY;
+ }
+
+ p = data;
+ SIVAL(p,0,0); /* No oplock. */
+ SIVAL(p,4,wire_flags);
+ SIVAL(p,8,unix_perms_to_wire(mode));
+ SIVAL(p,12,0); /* Top bits of perms currently undefined. */
+ SSVAL(p,16,SMB_NO_INFO_LEVEL_RETURNED); /* No info level returned. */
+
+ data_len = 18;
+
+ 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 */
+ )) {
+ SAFE_FREE(param);
+ return -1;
+ }
+
+ SAFE_FREE(param);
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len)) {
+ return -1;
+ }
+
+ fnum = SVAL(rdata,2);
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+
+ return fnum;
+}
+
+/****************************************************************************
+ open - POSIX semantics.
+****************************************************************************/
+
+int cli_posix_open(struct cli_state *cli, const char *fname, int flags, mode_t mode)
+{
+ return cli_posix_open_internal(cli, fname, flags, mode, False);
+}
+
+/****************************************************************************
+ mkdir - POSIX semantics.
+****************************************************************************/
+
+int cli_posix_mkdir(struct cli_state *cli, const char *fname, mode_t mode)
+{
+ return (cli_posix_open_internal(cli, fname, O_CREAT, mode, True) == -1) ? -1 : 0;
+}
+
+/****************************************************************************
+ unlink or rmdir - POSIX semantics.
+****************************************************************************/
+
+static bool cli_posix_unlink_internal(struct cli_state *cli, const char *fname, bool is_dir)
+{
+ unsigned int data_len = 0;
+ unsigned int param_len = 0;
+ uint16 setup = TRANSACT2_SETPATHINFO;
+ char *param;
+ char data[2];
+ char *rparam=NULL, *rdata=NULL;
+ char *p;
+ size_t srclen = 2*(strlen(fname)+1);
+
+ param = SMB_MALLOC_ARRAY(char, 6+srclen+2);
+ if (!param) {
+ return false;
+ }
+ memset(param, '\0', 6);
+ SSVAL(param,0, SMB_POSIX_PATH_UNLINK);
+ p = &param[6];
+
+ p += clistr_push(cli, p, fname, srclen, STR_TERMINATE);
+ param_len = PTR_DIFF(p, param);
+
+ SSVAL(data, 0, is_dir ? SMB_POSIX_UNLINK_DIRECTORY_TARGET :
+ SMB_POSIX_UNLINK_FILE_TARGET);
+ data_len = 2;
+
+ 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 */
+ )) {
+ SAFE_FREE(param);
+ return False;
+ }
+
+ SAFE_FREE(param);
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len)) {
+ return False;
+ }
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+
+ return True;
+}
+
+/****************************************************************************
+ unlink - POSIX semantics.
+****************************************************************************/
+
+bool cli_posix_unlink(struct cli_state *cli, const char *fname)
+{
+ return cli_posix_unlink_internal(cli, fname, False);
+}
+
+/****************************************************************************
+ rmdir - POSIX semantics.
+****************************************************************************/
+
+int cli_posix_rmdir(struct cli_state *cli, const char *fname)
+{
+ return cli_posix_unlink_internal(cli, fname, True);
+}
diff --git a/source3/libsmb/clifsinfo.c b/source3/libsmb/clifsinfo.c
new file mode 100644
index 0000000000..5e73b61cd2
--- /dev/null
+++ b/source3/libsmb/clifsinfo.c
@@ -0,0 +1,664 @@
+/*
+ Unix SMB/CIFS implementation.
+ FS info functions
+ Copyright (C) Stefan (metze) Metzmacher 2003
+ Copyright (C) Jeremy Allison 2007
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+ Get UNIX extensions version info.
+****************************************************************************/
+
+bool cli_unix_extensions_version(struct cli_state *cli, uint16 *pmajor, uint16 *pminor,
+ uint32 *pcaplow, uint32 *pcaphigh)
+{
+ bool ret = False;
+ uint16 setup;
+ char param[2];
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int rparam_count=0, rdata_count=0;
+
+ setup = TRANSACT2_QFSINFO;
+
+ SSVAL(param,0,SMB_QUERY_CIFS_UNIX_INFO);
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL,
+ 0, 0,
+ &setup, 1, 0,
+ param, 2, 0,
+ NULL, 0, 560)) {
+ goto cleanup;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ goto cleanup;
+ }
+
+ if (cli_is_error(cli)) {
+ ret = False;
+ goto cleanup;
+ } else {
+ ret = True;
+ }
+
+ if (rdata_count < 12) {
+ goto cleanup;
+ }
+
+ *pmajor = SVAL(rdata,0);
+ *pminor = SVAL(rdata,2);
+ cli->posix_capabilities = *pcaplow = IVAL(rdata,4);
+ *pcaphigh = IVAL(rdata,8);
+
+ /* todo: but not yet needed
+ * return the other stuff
+ */
+
+cleanup:
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return ret;
+}
+
+/****************************************************************************
+ Set UNIX extensions capabilities.
+****************************************************************************/
+
+bool cli_set_unix_extensions_capabilities(struct cli_state *cli, uint16 major, uint16 minor,
+ uint32 caplow, uint32 caphigh)
+{
+ bool ret = False;
+ uint16 setup;
+ char param[4];
+ char data[12];
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int rparam_count=0, rdata_count=0;
+
+ setup = TRANSACT2_SETFSINFO;
+
+ SSVAL(param,0,0);
+ SSVAL(param,2,SMB_SET_CIFS_UNIX_INFO);
+
+ SSVAL(data,0,major);
+ SSVAL(data,2,minor);
+ SIVAL(data,4,caplow);
+ SIVAL(data,8,caphigh);
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL,
+ 0, 0,
+ &setup, 1, 0,
+ param, 4, 0,
+ data, 12, 560)) {
+ goto cleanup;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ goto cleanup;
+ }
+
+ if (cli_is_error(cli)) {
+ ret = False;
+ goto cleanup;
+ } else {
+ ret = True;
+ }
+
+cleanup:
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return ret;
+}
+
+bool cli_get_fs_attr_info(struct cli_state *cli, uint32 *fs_attr)
+{
+ bool ret = False;
+ uint16 setup;
+ char param[2];
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int rparam_count=0, rdata_count=0;
+
+ if (!cli||!fs_attr)
+ smb_panic("cli_get_fs_attr_info() called with NULL Pionter!");
+
+ setup = TRANSACT2_QFSINFO;
+
+ SSVAL(param,0,SMB_QUERY_FS_ATTRIBUTE_INFO);
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL,
+ 0, 0,
+ &setup, 1, 0,
+ param, 2, 0,
+ NULL, 0, 560)) {
+ goto cleanup;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ goto cleanup;
+ }
+
+ if (cli_is_error(cli)) {
+ ret = False;
+ goto cleanup;
+ } else {
+ ret = True;
+ }
+
+ if (rdata_count < 12) {
+ goto cleanup;
+ }
+
+ *fs_attr = IVAL(rdata,0);
+
+ /* todo: but not yet needed
+ * return the other stuff
+ */
+
+cleanup:
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return ret;
+}
+
+bool cli_get_fs_volume_info_old(struct cli_state *cli, fstring volume_name, uint32 *pserial_number)
+{
+ bool ret = False;
+ uint16 setup;
+ char param[2];
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int rparam_count=0, rdata_count=0;
+ unsigned char nlen;
+
+ setup = TRANSACT2_QFSINFO;
+
+ SSVAL(param,0,SMB_INFO_VOLUME);
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL,
+ 0, 0,
+ &setup, 1, 0,
+ param, 2, 0,
+ NULL, 0, 560)) {
+ goto cleanup;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ goto cleanup;
+ }
+
+ if (cli_is_error(cli)) {
+ ret = False;
+ goto cleanup;
+ } else {
+ ret = True;
+ }
+
+ if (rdata_count < 5) {
+ goto cleanup;
+ }
+
+ if (pserial_number) {
+ *pserial_number = IVAL(rdata,0);
+ }
+ nlen = CVAL(rdata,l2_vol_cch);
+ clistr_pull(cli, volume_name, rdata + l2_vol_szVolLabel, sizeof(fstring), nlen, STR_NOALIGN);
+
+ /* todo: but not yet needed
+ * return the other stuff
+ */
+
+cleanup:
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return ret;
+}
+
+bool cli_get_fs_volume_info(struct cli_state *cli, fstring volume_name, uint32 *pserial_number, time_t *pdate)
+{
+ bool ret = False;
+ uint16 setup;
+ char param[2];
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int rparam_count=0, rdata_count=0;
+ unsigned int nlen;
+
+ setup = TRANSACT2_QFSINFO;
+
+ SSVAL(param,0,SMB_QUERY_FS_VOLUME_INFO);
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL,
+ 0, 0,
+ &setup, 1, 0,
+ param, 2, 0,
+ NULL, 0, 560)) {
+ goto cleanup;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ goto cleanup;
+ }
+
+ if (cli_is_error(cli)) {
+ ret = False;
+ goto cleanup;
+ } else {
+ ret = True;
+ }
+
+ if (rdata_count < 19) {
+ goto cleanup;
+ }
+
+ if (pdate) {
+ struct timespec ts;
+ ts = interpret_long_date(rdata);
+ *pdate = ts.tv_sec;
+ }
+ if (pserial_number) {
+ *pserial_number = IVAL(rdata,8);
+ }
+ nlen = IVAL(rdata,12);
+ clistr_pull(cli, volume_name, rdata + 18, sizeof(fstring), nlen, STR_UNICODE);
+
+ /* todo: but not yet needed
+ * return the other stuff
+ */
+
+cleanup:
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return ret;
+}
+
+/******************************************************************************
+ Send/receive the request encryption blob.
+******************************************************************************/
+
+static NTSTATUS enc_blob_send_receive(struct cli_state *cli, DATA_BLOB *in, DATA_BLOB *out, DATA_BLOB *param_out)
+{
+ uint16 setup;
+ char param[4];
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int rparam_count=0, rdata_count=0;
+ NTSTATUS status = NT_STATUS_OK;
+
+ setup = TRANSACT2_SETFSINFO;
+
+ SSVAL(param,0,0);
+ SSVAL(param,2,SMB_REQUEST_TRANSPORT_ENCRYPTION);
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL,
+ 0, 0,
+ &setup, 1, 0,
+ param, 4, 0,
+ (char *)in->data, in->length, CLI_BUFFER_SIZE)) {
+ status = cli_nt_error(cli);
+ goto out;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ status = cli_nt_error(cli);
+ goto out;
+ }
+
+ if (cli_is_error(cli)) {
+ status = cli_nt_error(cli);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ goto out;
+ }
+ }
+
+ *out = data_blob(rdata, rdata_count);
+ *param_out = data_blob(rparam, rparam_count);
+
+ out:
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+ return status;
+}
+
+/******************************************************************************
+ Make a client state struct.
+******************************************************************************/
+
+static struct smb_trans_enc_state *make_cli_enc_state(enum smb_trans_enc_type smb_enc_type)
+{
+ struct smb_trans_enc_state *es = NULL;
+ es = SMB_MALLOC_P(struct smb_trans_enc_state);
+ if (!es) {
+ return NULL;
+ }
+ ZERO_STRUCTP(es);
+ es->smb_enc_type = smb_enc_type;
+
+#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
+ if (smb_enc_type == SMB_TRANS_ENC_GSS) {
+ es->s.gss_state = SMB_MALLOC_P(struct smb_tran_enc_state_gss);
+ if (!es->s.gss_state) {
+ SAFE_FREE(es);
+ return NULL;
+ }
+ ZERO_STRUCTP(es->s.gss_state);
+ }
+#endif
+ return es;
+}
+
+/******************************************************************************
+ Start a raw ntlmssp encryption.
+******************************************************************************/
+
+NTSTATUS cli_raw_ntlm_smb_encryption_start(struct cli_state *cli,
+ const char *user,
+ const char *pass,
+ const char *domain)
+{
+ DATA_BLOB blob_in = data_blob_null;
+ DATA_BLOB blob_out = data_blob_null;
+ DATA_BLOB param_out = data_blob_null;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ struct smb_trans_enc_state *es = make_cli_enc_state(SMB_TRANS_ENC_NTLM);
+
+ if (!es) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = ntlmssp_client_start(&es->s.ntlmssp_state);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ ntlmssp_want_feature(es->s.ntlmssp_state, NTLMSSP_FEATURE_SESSION_KEY);
+ es->s.ntlmssp_state->neg_flags |= (NTLMSSP_NEGOTIATE_SIGN|NTLMSSP_NEGOTIATE_SEAL);
+
+ if (!NT_STATUS_IS_OK(status = ntlmssp_set_username(es->s.ntlmssp_state, user))) {
+ goto fail;
+ }
+ if (!NT_STATUS_IS_OK(status = ntlmssp_set_domain(es->s.ntlmssp_state, domain))) {
+ goto fail;
+ }
+ if (!NT_STATUS_IS_OK(status = ntlmssp_set_password(es->s.ntlmssp_state, pass))) {
+ goto fail;
+ }
+
+ do {
+ status = ntlmssp_update(es->s.ntlmssp_state, blob_in, &blob_out);
+ data_blob_free(&blob_in);
+ data_blob_free(&param_out);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) || NT_STATUS_IS_OK(status)) {
+ NTSTATUS trans_status = enc_blob_send_receive(cli,
+ &blob_out,
+ &blob_in,
+ &param_out);
+ if (!NT_STATUS_EQUAL(trans_status,
+ NT_STATUS_MORE_PROCESSING_REQUIRED) &&
+ !NT_STATUS_IS_OK(trans_status)) {
+ status = trans_status;
+ } else {
+ if (param_out.length == 2) {
+ es->enc_ctx_num = SVAL(param_out.data, 0);
+ }
+ }
+ }
+ data_blob_free(&blob_out);
+ } while (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED));
+
+ data_blob_free(&blob_in);
+
+ if (NT_STATUS_IS_OK(status)) {
+ /* Replace the old state, if any. */
+ if (cli->trans_enc_state) {
+ common_free_encryption_state(&cli->trans_enc_state);
+ }
+ cli->trans_enc_state = es;
+ cli->trans_enc_state->enc_on = True;
+ es = NULL;
+ }
+
+ fail:
+
+ common_free_encryption_state(&es);
+ return status;
+}
+
+#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
+
+#ifndef SMB_GSS_REQUIRED_FLAGS
+#define SMB_GSS_REQUIRED_FLAGS (GSS_C_CONF_FLAG|GSS_C_INTEG_FLAG|GSS_C_MUTUAL_FLAG|GSS_C_REPLAY_FLAG|GSS_C_SEQUENCE_FLAG)
+#endif
+
+/******************************************************************************
+ Get client gss blob to send to a server.
+******************************************************************************/
+
+static NTSTATUS make_cli_gss_blob(struct smb_trans_enc_state *es,
+ const char *service,
+ const char *host,
+ NTSTATUS status_in,
+ DATA_BLOB spnego_blob_in,
+ DATA_BLOB *p_blob_out)
+{
+ const char *krb_mechs[] = {OID_KERBEROS5, NULL};
+ OM_uint32 ret;
+ OM_uint32 min;
+ gss_name_t srv_name;
+ gss_buffer_desc input_name;
+ gss_buffer_desc *p_tok_in;
+ gss_buffer_desc tok_out, tok_in;
+ DATA_BLOB blob_out = data_blob_null;
+ DATA_BLOB blob_in = data_blob_null;
+ char *host_princ_s = NULL;
+ OM_uint32 ret_flags = 0;
+ NTSTATUS status = NT_STATUS_OK;
+
+ gss_OID_desc nt_hostbased_service =
+ {10, CONST_DISCARD(char *,"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04")};
+
+ memset(&tok_out, '\0', sizeof(tok_out));
+
+ /* Get a ticket for the service@host */
+ if (asprintf(&host_princ_s, "%s@%s", service, host) == -1) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ input_name.value = host_princ_s;
+ input_name.length = strlen(host_princ_s) + 1;
+
+ ret = gss_import_name(&min,
+ &input_name,
+ &nt_hostbased_service,
+ &srv_name);
+
+ if (ret != GSS_S_COMPLETE) {
+ SAFE_FREE(host_princ_s);
+ return map_nt_error_from_gss(ret, min);
+ }
+
+ if (spnego_blob_in.length == 0) {
+ p_tok_in = GSS_C_NO_BUFFER;
+ } else {
+ /* Remove the SPNEGO wrapper */
+ if (!spnego_parse_auth_response(spnego_blob_in, status_in, OID_KERBEROS5, &blob_in)) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto fail;
+ }
+ tok_in.value = blob_in.data;
+ tok_in.length = blob_in.length;
+ p_tok_in = &tok_in;
+ }
+
+ ret = gss_init_sec_context(&min,
+ GSS_C_NO_CREDENTIAL, /* Use our default cred. */
+ &es->s.gss_state->gss_ctx,
+ srv_name,
+ GSS_C_NO_OID, /* default OID. */
+ GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG,
+ GSS_C_INDEFINITE, /* requested ticket lifetime. */
+ NULL, /* no channel bindings */
+ p_tok_in,
+ NULL, /* ignore mech type */
+ &tok_out,
+ &ret_flags,
+ NULL); /* ignore time_rec */
+
+ status = map_nt_error_from_gss(ret, min);
+ if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ ADS_STATUS adss = ADS_ERROR_GSS(ret, min);
+ DEBUG(10,("make_cli_gss_blob: gss_init_sec_context failed with %s\n",
+ ads_errstr(adss)));
+ goto fail;
+ }
+
+ if ((ret_flags & SMB_GSS_REQUIRED_FLAGS) != SMB_GSS_REQUIRED_FLAGS) {
+ status = NT_STATUS_ACCESS_DENIED;
+ }
+
+ blob_out = data_blob(tok_out.value, tok_out.length);
+
+ /* Wrap in an SPNEGO wrapper */
+ *p_blob_out = gen_negTokenTarg(krb_mechs, blob_out);
+
+ fail:
+
+ data_blob_free(&blob_out);
+ data_blob_free(&blob_in);
+ SAFE_FREE(host_princ_s);
+ gss_release_name(&min, &srv_name);
+ if (tok_out.value) {
+ gss_release_buffer(&min, &tok_out);
+ }
+ return status;
+}
+
+/******************************************************************************
+ Start a SPNEGO gssapi encryption context.
+******************************************************************************/
+
+NTSTATUS cli_gss_smb_encryption_start(struct cli_state *cli)
+{
+ DATA_BLOB blob_recv = data_blob_null;
+ DATA_BLOB blob_send = data_blob_null;
+ DATA_BLOB param_out = data_blob_null;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ fstring fqdn;
+ const char *servicename;
+ struct smb_trans_enc_state *es = make_cli_enc_state(SMB_TRANS_ENC_GSS);
+
+ if (!es) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ name_to_fqdn(fqdn, cli->desthost);
+ strlower_m(fqdn);
+
+ servicename = "cifs";
+ status = make_cli_gss_blob(es, servicename, fqdn, NT_STATUS_OK, blob_recv, &blob_send);
+ if (!NT_STATUS_EQUAL(status,NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ servicename = "host";
+ status = make_cli_gss_blob(es, servicename, fqdn, NT_STATUS_OK, blob_recv, &blob_send);
+ if (!NT_STATUS_EQUAL(status,NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ goto fail;
+ }
+ }
+
+ do {
+ data_blob_free(&blob_recv);
+ status = enc_blob_send_receive(cli, &blob_send, &blob_recv, &param_out);
+ if (param_out.length == 2) {
+ es->enc_ctx_num = SVAL(param_out.data, 0);
+ }
+ data_blob_free(&blob_send);
+ status = make_cli_gss_blob(es, servicename, fqdn, status, blob_recv, &blob_send);
+ } while (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED));
+ data_blob_free(&blob_recv);
+
+ if (NT_STATUS_IS_OK(status)) {
+ /* Replace the old state, if any. */
+ if (cli->trans_enc_state) {
+ common_free_encryption_state(&cli->trans_enc_state);
+ }
+ cli->trans_enc_state = es;
+ cli->trans_enc_state->enc_on = True;
+ es = NULL;
+ }
+
+ fail:
+
+ common_free_encryption_state(&es);
+ return status;
+}
+#else
+NTSTATUS cli_gss_smb_encryption_start(struct cli_state *cli)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+#endif
+
+/********************************************************************
+ Ensure a connection is encrypted.
+********************************************************************/
+
+NTSTATUS cli_force_encryption(struct cli_state *c,
+ const char *username,
+ const char *password,
+ const char *domain)
+{
+ uint16 major, minor;
+ uint32 caplow, caphigh;
+
+ if (!SERVER_HAS_UNIX_CIFS(c)) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ if (!cli_unix_extensions_version(c, &major, &minor, &caplow, &caphigh)) {
+ return NT_STATUS_UNKNOWN_REVISION;
+ }
+
+ if (!(caplow & CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP)) {
+ return NT_STATUS_UNSUPPORTED_COMPRESSION;
+ }
+
+ if (c->use_kerberos) {
+ return cli_gss_smb_encryption_start(c);
+ }
+ return cli_raw_ntlm_smb_encryption_start(c,
+ username,
+ password,
+ domain);
+}
diff --git a/source3/libsmb/clikrb5.c b/source3/libsmb/clikrb5.c
new file mode 100644
index 0000000000..b8afb57977
--- /dev/null
+++ b/source3/libsmb/clikrb5.c
@@ -0,0 +1,1923 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple kerberos5 routines for active directory
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Luke Howard 2002-2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+ Copyright (C) Guenther Deschner 2005-2007
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#define KRB5_PRIVATE 1 /* this file uses PRIVATE interfaces! */
+#define KRB5_DEPRECATED 1 /* this file uses DEPRECATED interfaces! */
+
+#include "includes.h"
+
+#ifdef HAVE_KRB5
+
+#define GSSAPI_CHECKSUM 0x8003 /* Checksum type value for Kerberos */
+#define GSSAPI_BNDLENGTH 16 /* Bind Length (rfc-1964 pg.3) */
+#define GSSAPI_CHECKSUM_SIZE (12+GSSAPI_BNDLENGTH)
+
+#if defined(TKT_FLG_OK_AS_DELEGATE ) && defined(HAVE_KRB5_FWD_TGT_CREDS) && defined(HAVE_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE) && defined(KRB5_AUTH_CONTEXT_USE_SUBKEY)
+static krb5_error_code ads_krb5_get_fwd_ticket( krb5_context context,
+ krb5_auth_context *auth_context,
+ krb5_creds *credsp,
+ krb5_ccache ccache,
+ krb5_data *authenticator);
+#endif
+
+/**************************************************************
+ Wrappers around kerberos string functions that convert from
+ utf8 -> unix charset and vica versa.
+**************************************************************/
+
+/**************************************************************
+ krb5_parse_name that takes a UNIX charset.
+**************************************************************/
+
+ krb5_error_code smb_krb5_parse_name(krb5_context context,
+ const char *name, /* in unix charset */
+ krb5_principal *principal)
+{
+ krb5_error_code ret;
+ char *utf8_name;
+ size_t converted_size;
+
+ if (!push_utf8_allocate(&utf8_name, name, &converted_size)) {
+ return ENOMEM;
+ }
+
+ ret = krb5_parse_name(context, utf8_name, principal);
+ SAFE_FREE(utf8_name);
+ return ret;
+}
+
+#ifdef HAVE_KRB5_PARSE_NAME_NOREALM
+/**************************************************************
+ krb5_parse_name_norealm that takes a UNIX charset.
+**************************************************************/
+
+static krb5_error_code smb_krb5_parse_name_norealm_conv(krb5_context context,
+ const char *name, /* in unix charset */
+ krb5_principal *principal)
+{
+ krb5_error_code ret;
+ char *utf8_name;
+ size_t converted_size;
+
+ *principal = NULL;
+ if (!push_utf8_allocate(&utf8_name, name, &converted_size)) {
+ return ENOMEM;
+ }
+
+ ret = krb5_parse_name_norealm(context, utf8_name, principal);
+ SAFE_FREE(utf8_name);
+ return ret;
+}
+#endif
+
+/**************************************************************
+ krb5_parse_name that returns a UNIX charset name. Must
+ be freed with normal free() call.
+**************************************************************/
+
+ krb5_error_code smb_krb5_unparse_name(krb5_context context,
+ krb5_const_principal principal,
+ char **unix_name)
+{
+ krb5_error_code ret;
+ char *utf8_name;
+ size_t converted_size;
+
+ *unix_name = NULL;
+ ret = krb5_unparse_name(context, principal, &utf8_name);
+ if (ret) {
+ return ret;
+ }
+
+ if (!pull_utf8_allocate(unix_name, utf8_name, &converted_size)) {
+ krb5_free_unparsed_name(context, utf8_name);
+ return ENOMEM;
+ }
+ krb5_free_unparsed_name(context, utf8_name);
+ return 0;
+}
+
+#ifndef HAVE_KRB5_SET_REAL_TIME
+/*
+ * This function is not in the Heimdal mainline.
+ */
+ krb5_error_code krb5_set_real_time(krb5_context context, int32_t seconds, int32_t microseconds)
+{
+ krb5_error_code ret;
+ int32_t sec, usec;
+
+ ret = krb5_us_timeofday(context, &sec, &usec);
+ if (ret)
+ return ret;
+
+ context->kdc_sec_offset = seconds - sec;
+ context->kdc_usec_offset = microseconds - usec;
+
+ return 0;
+}
+#endif
+
+#if !defined(HAVE_KRB5_SET_DEFAULT_TGS_KTYPES)
+
+#if defined(HAVE_KRB5_SET_DEFAULT_TGS_ENCTYPES)
+
+/* With MIT kerberos, we should use krb5_set_default_tgs_enctypes in preference
+ * to krb5_set_default_tgs_ktypes. See
+ * http://lists.samba.org/archive/samba-technical/2006-July/048271.html
+ *
+ * If the MIT libraries are not exporting internal symbols, we will end up in
+ * this branch, which is correct. Otherwise we will continue to use the
+ * internal symbol
+ */
+ krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc)
+{
+ return krb5_set_default_tgs_enctypes(ctx, enc);
+}
+
+#elif defined(HAVE_KRB5_SET_DEFAULT_IN_TKT_ETYPES)
+
+/* Heimdal */
+ krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc)
+{
+ return krb5_set_default_in_tkt_etypes(ctx, enc);
+}
+
+#endif /* HAVE_KRB5_SET_DEFAULT_TGS_ENCTYPES */
+
+#endif /* HAVE_KRB5_SET_DEFAULT_TGS_KTYPES */
+
+#if defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS)
+/* HEIMDAL */
+ bool setup_kaddr( krb5_address *pkaddr, struct sockaddr_storage *paddr)
+{
+ memset(pkaddr, '\0', sizeof(krb5_address));
+#if defined(HAVE_IPV6) && defined(KRB5_ADDRESS_INET6)
+ if (paddr->ss_family == AF_INET6) {
+ pkaddr->addr_type = KRB5_ADDRESS_INET6;
+ pkaddr->address.length = sizeof(((struct sockaddr_in6 *)paddr)->sin6_addr);
+ pkaddr->address.data = (char *)&(((struct sockaddr_in6 *)paddr)->sin6_addr);
+ return true;
+ }
+#endif
+ if (paddr->ss_family == AF_INET) {
+ pkaddr->addr_type = KRB5_ADDRESS_INET;
+ pkaddr->address.length = sizeof(((struct sockaddr_in *)paddr)->sin_addr);
+ pkaddr->address.data = (char *)&(((struct sockaddr_in *)paddr)->sin_addr);
+ return true;
+ }
+ return false;
+}
+#elif defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS)
+/* MIT */
+ bool setup_kaddr( krb5_address *pkaddr, struct sockaddr_storage *paddr)
+{
+ memset(pkaddr, '\0', sizeof(krb5_address));
+#if defined(HAVE_IPV6) && defined(ADDRTYPE_INET6)
+ if (paddr->ss_family == AF_INET6) {
+ pkaddr->addrtype = ADDRTYPE_INET6;
+ pkaddr->length = sizeof(((struct sockaddr_in6 *)paddr)->sin6_addr);
+ pkaddr->contents = (krb5_octet *)&(((struct sockaddr_in6 *)paddr)->sin6_addr);
+ return true;
+ }
+#endif
+ if (paddr->ss_family == AF_INET) {
+ pkaddr->addrtype = ADDRTYPE_INET;
+ pkaddr->length = sizeof(((struct sockaddr_in *)paddr)->sin_addr);
+ pkaddr->contents = (krb5_octet *)&(((struct sockaddr_in *)paddr)->sin_addr);
+ return true;
+ }
+ return false;
+}
+#else
+#error UNKNOWN_ADDRTYPE
+#endif
+
+#if defined(HAVE_KRB5_PRINCIPAL2SALT) && defined(HAVE_KRB5_USE_ENCTYPE) && defined(HAVE_KRB5_STRING_TO_KEY) && defined(HAVE_KRB5_ENCRYPT_BLOCK)
+static int create_kerberos_key_from_string_direct(krb5_context context,
+ krb5_principal host_princ,
+ krb5_data *password,
+ krb5_keyblock *key,
+ krb5_enctype enctype)
+{
+ int ret = 0;
+ krb5_data salt;
+ krb5_encrypt_block eblock;
+
+ ret = krb5_principal2salt(context, host_princ, &salt);
+ if (ret) {
+ DEBUG(1,("krb5_principal2salt failed (%s)\n", error_message(ret)));
+ return ret;
+ }
+ krb5_use_enctype(context, &eblock, enctype);
+ ret = krb5_string_to_key(context, &eblock, key, password, &salt);
+ SAFE_FREE(salt.data);
+
+ return ret;
+}
+#elif defined(HAVE_KRB5_GET_PW_SALT) && defined(HAVE_KRB5_STRING_TO_KEY_SALT)
+static int create_kerberos_key_from_string_direct(krb5_context context,
+ krb5_principal host_princ,
+ krb5_data *password,
+ krb5_keyblock *key,
+ krb5_enctype enctype)
+{
+ int ret;
+ krb5_salt salt;
+
+ ret = krb5_get_pw_salt(context, host_princ, &salt);
+ if (ret) {
+ DEBUG(1,("krb5_get_pw_salt failed (%s)\n", error_message(ret)));
+ return ret;
+ }
+
+ ret = krb5_string_to_key_salt(context, enctype, (const char *)password->data, salt, key);
+ krb5_free_salt(context, salt);
+
+ return ret;
+}
+#else
+#error UNKNOWN_CREATE_KEY_FUNCTIONS
+#endif
+
+ int create_kerberos_key_from_string(krb5_context context,
+ krb5_principal host_princ,
+ krb5_data *password,
+ krb5_keyblock *key,
+ krb5_enctype enctype,
+ bool no_salt)
+{
+ krb5_principal salt_princ = NULL;
+ int ret;
+ /*
+ * Check if we've determined that the KDC is salting keys for this
+ * principal/enctype in a non-obvious way. If it is, try to match
+ * its behavior.
+ */
+ if (no_salt) {
+ KRB5_KEY_DATA(key) = (KRB5_KEY_DATA_CAST *)SMB_MALLOC(password->length);
+ if (!KRB5_KEY_DATA(key)) {
+ return ENOMEM;
+ }
+ memcpy(KRB5_KEY_DATA(key), password->data, password->length);
+ KRB5_KEY_LENGTH(key) = password->length;
+ KRB5_KEY_TYPE(key) = enctype;
+ return 0;
+ }
+ salt_princ = kerberos_fetch_salt_princ_for_host_princ(context, host_princ, enctype);
+ ret = create_kerberos_key_from_string_direct(context, salt_princ ? salt_princ : host_princ, password, key, enctype);
+ if (salt_princ) {
+ krb5_free_principal(context, salt_princ);
+ }
+ return ret;
+}
+
+#if defined(HAVE_KRB5_GET_PERMITTED_ENCTYPES)
+ krb5_error_code get_kerberos_allowed_etypes(krb5_context context,
+ krb5_enctype **enctypes)
+{
+ return krb5_get_permitted_enctypes(context, enctypes);
+}
+#elif defined(HAVE_KRB5_GET_DEFAULT_IN_TKT_ETYPES)
+ krb5_error_code get_kerberos_allowed_etypes(krb5_context context,
+ krb5_enctype **enctypes)
+{
+ return krb5_get_default_in_tkt_etypes(context, enctypes);
+}
+#else
+#error UNKNOWN_GET_ENCTYPES_FUNCTIONS
+#endif
+
+#if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY)
+ krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_keyblock *keyblock)
+{
+ return krb5_auth_con_setkey(context, auth_context, keyblock);
+}
+#endif
+
+bool unwrap_edata_ntstatus(TALLOC_CTX *mem_ctx,
+ DATA_BLOB *edata,
+ DATA_BLOB *edata_out)
+{
+ DATA_BLOB edata_contents;
+ ASN1_DATA data;
+ int edata_type;
+
+ if (!edata->length) {
+ return False;
+ }
+
+ asn1_load(&data, *edata);
+ asn1_start_tag(&data, ASN1_SEQUENCE(0));
+ asn1_start_tag(&data, ASN1_CONTEXT(1));
+ asn1_read_Integer(&data, &edata_type);
+
+ if (edata_type != KRB5_PADATA_PW_SALT) {
+ DEBUG(0,("edata is not of required type %d but of type %d\n",
+ KRB5_PADATA_PW_SALT, edata_type));
+ asn1_free(&data);
+ return False;
+ }
+
+ asn1_start_tag(&data, ASN1_CONTEXT(2));
+ asn1_read_OctetString(&data, &edata_contents);
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+ asn1_free(&data);
+
+ *edata_out = data_blob_talloc(mem_ctx, edata_contents.data, edata_contents.length);
+
+ data_blob_free(&edata_contents);
+
+ return True;
+}
+
+
+bool unwrap_pac(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data, DATA_BLOB *unwrapped_pac_data)
+{
+ DATA_BLOB pac_contents;
+ ASN1_DATA data;
+ int data_type;
+
+ if (!auth_data->length) {
+ return False;
+ }
+
+ asn1_load(&data, *auth_data);
+ asn1_start_tag(&data, ASN1_SEQUENCE(0));
+ asn1_start_tag(&data, ASN1_SEQUENCE(0));
+ asn1_start_tag(&data, ASN1_CONTEXT(0));
+ asn1_read_Integer(&data, &data_type);
+
+ if (data_type != KRB5_AUTHDATA_WIN2K_PAC ) {
+ DEBUG(10,("authorization data is not a Windows PAC (type: %d)\n", data_type));
+ asn1_free(&data);
+ return False;
+ }
+
+ asn1_end_tag(&data);
+ asn1_start_tag(&data, ASN1_CONTEXT(1));
+ asn1_read_OctetString(&data, &pac_contents);
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+ asn1_free(&data);
+
+ *unwrapped_pac_data = data_blob_talloc(mem_ctx, pac_contents.data, pac_contents.length);
+
+ data_blob_free(&pac_contents);
+
+ return True;
+}
+
+ bool get_auth_data_from_tkt(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data, krb5_ticket *tkt)
+{
+ DATA_BLOB auth_data_wrapped;
+ bool got_auth_data_pac = False;
+ int i;
+
+#if defined(HAVE_KRB5_TKT_ENC_PART2)
+ if (tkt->enc_part2 && tkt->enc_part2->authorization_data &&
+ tkt->enc_part2->authorization_data[0] &&
+ tkt->enc_part2->authorization_data[0]->length)
+ {
+ for (i = 0; tkt->enc_part2->authorization_data[i] != NULL; i++) {
+
+ if (tkt->enc_part2->authorization_data[i]->ad_type !=
+ KRB5_AUTHDATA_IF_RELEVANT) {
+ DEBUG(10,("get_auth_data_from_tkt: ad_type is %d\n",
+ tkt->enc_part2->authorization_data[i]->ad_type));
+ continue;
+ }
+
+ auth_data_wrapped = data_blob(tkt->enc_part2->authorization_data[i]->contents,
+ tkt->enc_part2->authorization_data[i]->length);
+
+ /* check if it is a PAC */
+ got_auth_data_pac = unwrap_pac(mem_ctx, &auth_data_wrapped, auth_data);
+ data_blob_free(&auth_data_wrapped);
+
+ if (got_auth_data_pac) {
+ return true;
+ }
+ }
+
+ return got_auth_data_pac;
+ }
+
+#else
+ if (tkt->ticket.authorization_data &&
+ tkt->ticket.authorization_data->len)
+ {
+ for (i = 0; i < tkt->ticket.authorization_data->len; i++) {
+
+ if (tkt->ticket.authorization_data->val[i].ad_type !=
+ KRB5_AUTHDATA_IF_RELEVANT) {
+ DEBUG(10,("get_auth_data_from_tkt: ad_type is %d\n",
+ tkt->ticket.authorization_data->val[i].ad_type));
+ continue;
+ }
+
+ auth_data_wrapped = data_blob(tkt->ticket.authorization_data->val[i].ad_data.data,
+ tkt->ticket.authorization_data->val[i].ad_data.length);
+
+ /* check if it is a PAC */
+ got_auth_data_pac = unwrap_pac(mem_ctx, &auth_data_wrapped, auth_data);
+ data_blob_free(&auth_data_wrapped);
+
+ if (got_auth_data_pac) {
+ return true;
+ }
+ }
+
+ return got_auth_data_pac;
+ }
+#endif
+ return False;
+}
+
+ krb5_const_principal get_principal_from_tkt(krb5_ticket *tkt)
+{
+#if defined(HAVE_KRB5_TKT_ENC_PART2)
+ return tkt->enc_part2->client;
+#else
+ return tkt->client;
+#endif
+}
+
+#if !defined(HAVE_KRB5_LOCATE_KDC)
+
+/* krb5_locate_kdc is an internal MIT symbol. MIT are not yet willing to commit
+ * to a public interface for this functionality, so we have to be able to live
+ * without it if the MIT libraries are hiding their internal symbols.
+ */
+
+#if defined(KRB5_KRBHST_INIT)
+/* Heimdal */
+ krb5_error_code smb_krb5_locate_kdc(krb5_context ctx, const krb5_data *realm, struct sockaddr **addr_pp, int *naddrs, int get_masters)
+{
+ krb5_krbhst_handle hnd;
+ krb5_krbhst_info *hinfo;
+ krb5_error_code rc;
+ int num_kdcs, i;
+ struct sockaddr *sa;
+ struct addrinfo *ai;
+
+ *addr_pp = NULL;
+ *naddrs = 0;
+
+ rc = krb5_krbhst_init(ctx, realm->data, KRB5_KRBHST_KDC, &hnd);
+ if (rc) {
+ DEBUG(0, ("smb_krb5_locate_kdc: krb5_krbhst_init failed (%s)\n", error_message(rc)));
+ return rc;
+ }
+
+ for ( num_kdcs = 0; (rc = krb5_krbhst_next(ctx, hnd, &hinfo) == 0); num_kdcs++)
+ ;
+
+ krb5_krbhst_reset(ctx, hnd);
+
+ if (!num_kdcs) {
+ DEBUG(0, ("smb_krb5_locate_kdc: zero kdcs found !\n"));
+ krb5_krbhst_free(ctx, hnd);
+ return -1;
+ }
+
+ sa = SMB_MALLOC_ARRAY( struct sockaddr, num_kdcs );
+ if (!sa) {
+ DEBUG(0, ("smb_krb5_locate_kdc: malloc failed\n"));
+ krb5_krbhst_free(ctx, hnd);
+ naddrs = 0;
+ return -1;
+ }
+
+ memset(sa, '\0', sizeof(struct sockaddr) * num_kdcs );
+
+ for (i = 0; i < num_kdcs && (rc = krb5_krbhst_next(ctx, hnd, &hinfo) == 0); i++) {
+
+#if defined(HAVE_KRB5_KRBHST_GET_ADDRINFO)
+ rc = krb5_krbhst_get_addrinfo(ctx, hinfo, &ai);
+ if (rc) {
+ DEBUG(0,("krb5_krbhst_get_addrinfo failed: %s\n", error_message(rc)));
+ continue;
+ }
+#endif
+ if (hinfo->ai && hinfo->ai->ai_family == AF_INET)
+ memcpy(&sa[i], hinfo->ai->ai_addr, sizeof(struct sockaddr));
+ }
+
+ krb5_krbhst_free(ctx, hnd);
+
+ *naddrs = num_kdcs;
+ *addr_pp = sa;
+ return 0;
+}
+
+#else /* ! defined(KRB5_KRBHST_INIT) */
+
+ krb5_error_code smb_krb5_locate_kdc(krb5_context ctx, const krb5_data *realm,
+ struct sockaddr **addr_pp, int *naddrs, int get_masters)
+{
+ DEBUG(0, ("unable to explicitly locate the KDC on this platform\n"));
+ return KRB5_KDC_UNREACH;
+}
+
+#endif /* KRB5_KRBHST_INIT */
+
+#else /* ! HAVE_KRB5_LOCATE_KDC */
+
+ krb5_error_code smb_krb5_locate_kdc(krb5_context ctx, const krb5_data *realm,
+ struct sockaddr **addr_pp, int *naddrs, int get_masters)
+{
+ return krb5_locate_kdc(ctx, realm, addr_pp, naddrs, get_masters);
+}
+
+#endif /* HAVE_KRB5_LOCATE_KDC */
+
+#if !defined(HAVE_KRB5_FREE_UNPARSED_NAME)
+ void krb5_free_unparsed_name(krb5_context context, char *val)
+{
+ SAFE_FREE(val);
+}
+#endif
+
+ void kerberos_free_data_contents(krb5_context context, krb5_data *pdata)
+{
+#if defined(HAVE_KRB5_FREE_DATA_CONTENTS)
+ if (pdata->data) {
+ krb5_free_data_contents(context, pdata);
+ }
+#else
+ SAFE_FREE(pdata->data);
+#endif
+}
+
+ void kerberos_set_creds_enctype(krb5_creds *pcreds, int enctype)
+{
+#if defined(HAVE_KRB5_KEYBLOCK_IN_CREDS)
+ KRB5_KEY_TYPE((&pcreds->keyblock)) = enctype;
+#elif defined(HAVE_KRB5_SESSION_IN_CREDS)
+ KRB5_KEY_TYPE((&pcreds->session)) = enctype;
+#else
+#error UNKNOWN_KEYBLOCK_MEMBER_IN_KRB5_CREDS_STRUCT
+#endif
+}
+
+ bool kerberos_compatible_enctypes(krb5_context context,
+ krb5_enctype enctype1,
+ krb5_enctype enctype2)
+{
+#if defined(HAVE_KRB5_C_ENCTYPE_COMPARE)
+ krb5_boolean similar = 0;
+
+ krb5_c_enctype_compare(context, enctype1, enctype2, &similar);
+ return similar ? True : False;
+#elif defined(HAVE_KRB5_ENCTYPES_COMPATIBLE_KEYS)
+ return krb5_enctypes_compatible_keys(context, enctype1, enctype2) ? True : False;
+#endif
+}
+
+static bool ads_cleanup_expired_creds(krb5_context context,
+ krb5_ccache ccache,
+ krb5_creds *credsp)
+{
+ krb5_error_code retval;
+ const char *cc_type = krb5_cc_get_type(context, ccache);
+
+ DEBUG(3, ("ads_cleanup_expired_creds: Ticket in ccache[%s:%s] expiration %s\n",
+ cc_type, krb5_cc_get_name(context, ccache),
+ http_timestring(credsp->times.endtime)));
+
+ /* we will probably need new tickets if the current ones
+ will expire within 10 seconds.
+ */
+ if (credsp->times.endtime >= (time(NULL) + 10))
+ return False;
+
+ /* heimdal won't remove creds from a file ccache, and
+ perhaps we shouldn't anyway, since internally we
+ use memory ccaches, and a FILE one probably means that
+ we're using creds obtained outside of our exectuable
+ */
+ if (strequal(cc_type, "FILE")) {
+ DEBUG(5, ("ads_cleanup_expired_creds: We do not remove creds from a %s ccache\n", cc_type));
+ return False;
+ }
+
+ retval = krb5_cc_remove_cred(context, ccache, 0, credsp);
+ if (retval) {
+ DEBUG(1, ("ads_cleanup_expired_creds: krb5_cc_remove_cred failed, err %s\n",
+ error_message(retval)));
+ /* If we have an error in this, we want to display it,
+ but continue as though we deleted it */
+ }
+ return True;
+}
+
+/*
+ we can't use krb5_mk_req because w2k wants the service to be in a particular format
+*/
+static krb5_error_code ads_krb5_mk_req(krb5_context context,
+ krb5_auth_context *auth_context,
+ const krb5_flags ap_req_options,
+ const char *principal,
+ krb5_ccache ccache,
+ krb5_data *outbuf,
+ time_t *expire_time)
+{
+ krb5_error_code retval;
+ krb5_principal server;
+ krb5_creds * credsp;
+ krb5_creds creds;
+ krb5_data in_data;
+ bool creds_ready = False;
+ int i = 0, maxtries = 3;
+
+ ZERO_STRUCT(in_data);
+
+ retval = smb_krb5_parse_name(context, principal, &server);
+ if (retval) {
+ DEBUG(1,("ads_krb5_mk_req: Failed to parse principal %s\n", principal));
+ return retval;
+ }
+
+ /* obtain ticket & session key */
+ ZERO_STRUCT(creds);
+ if ((retval = krb5_copy_principal(context, server, &creds.server))) {
+ DEBUG(1,("ads_krb5_mk_req: krb5_copy_principal failed (%s)\n",
+ error_message(retval)));
+ goto cleanup_princ;
+ }
+
+ if ((retval = krb5_cc_get_principal(context, ccache, &creds.client))) {
+ /* This can commonly fail on smbd startup with no ticket in the cache.
+ * Report at higher level than 1. */
+ DEBUG(3,("ads_krb5_mk_req: krb5_cc_get_principal failed (%s)\n",
+ error_message(retval)));
+ goto cleanup_creds;
+ }
+
+ while (!creds_ready && (i < maxtries)) {
+
+ if ((retval = krb5_get_credentials(context, 0, ccache,
+ &creds, &credsp))) {
+ DEBUG(1,("ads_krb5_mk_req: krb5_get_credentials failed for %s (%s)\n",
+ principal, error_message(retval)));
+ goto cleanup_creds;
+ }
+
+ /* cope with ticket being in the future due to clock skew */
+ if ((unsigned)credsp->times.starttime > time(NULL)) {
+ time_t t = time(NULL);
+ int time_offset =(int)((unsigned)credsp->times.starttime-t);
+ DEBUG(4,("ads_krb5_mk_req: Advancing clock by %d seconds to cope with clock skew\n", time_offset));
+ krb5_set_real_time(context, t + time_offset + 1, 0);
+ }
+
+ if (!ads_cleanup_expired_creds(context, ccache, credsp)) {
+ creds_ready = True;
+ }
+
+ i++;
+ }
+
+ DEBUG(10,("ads_krb5_mk_req: Ticket (%s) in ccache (%s:%s) is valid until: (%s - %u)\n",
+ principal, krb5_cc_get_type(context, ccache), krb5_cc_get_name(context, ccache),
+ http_timestring((unsigned)credsp->times.endtime),
+ (unsigned)credsp->times.endtime));
+
+ if (expire_time) {
+ *expire_time = (time_t)credsp->times.endtime;
+ }
+
+#if defined(TKT_FLG_OK_AS_DELEGATE ) && defined(HAVE_KRB5_FWD_TGT_CREDS) && defined(HAVE_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE) && defined(KRB5_AUTH_CONTEXT_USE_SUBKEY)
+ if( credsp->ticket_flags & TKT_FLG_OK_AS_DELEGATE ) {
+ /* Fetch a forwarded TGT from the KDC so that we can hand off a 2nd ticket
+ as part of the kerberos exchange. */
+
+ DEBUG( 3, ("ads_krb5_mk_req: server marked as OK to delegate to, building forwardable TGT\n") );
+
+ if( *auth_context == NULL ) {
+ /* Allocate if it has not yet been allocated. */
+ retval = krb5_auth_con_init( context, auth_context );
+ if (retval) {
+ DEBUG(1,("ads_krb5_mk_req: krb5_auth_con_init failed (%s)\n",
+ error_message(retval)));
+ goto cleanup_creds;
+ }
+ }
+
+ retval = krb5_auth_con_setuseruserkey( context, *auth_context, &credsp->keyblock );
+ if (retval) {
+ DEBUG(1,("ads_krb5_mk_req: krb5_auth_con_setuseruserkey failed (%s)\n",
+ error_message(retval)));
+ goto cleanup_creds;
+ }
+
+ /* Must use a subkey for forwarded tickets. */
+ retval = krb5_auth_con_setflags( context, *auth_context, KRB5_AUTH_CONTEXT_USE_SUBKEY);
+ if (retval) {
+ DEBUG(1,("ads_krb5_mk_req: krb5_auth_con_setflags failed (%s)\n",
+ error_message(retval)));
+ goto cleanup_creds;
+ }
+
+ retval = ads_krb5_get_fwd_ticket( context,
+ auth_context,
+ credsp,
+ ccache,
+ &in_data );
+ if (retval) {
+ DEBUG( 3, ("ads_krb5_get_fwd_ticket failed (%s)\n",
+ error_message( retval ) ) );
+
+ /*
+ * This is not fatal. Delete the *auth_context and continue
+ * with krb5_mk_req_extended to get a non-forwardable ticket.
+ */
+
+ if (in_data.data) {
+ free( in_data.data );
+ in_data.data = NULL;
+ in_data.length = 0;
+ }
+ krb5_auth_con_free(context, *auth_context);
+ *auth_context = NULL;
+ }
+ }
+#endif
+
+ retval = krb5_mk_req_extended(context, auth_context, ap_req_options,
+ &in_data, credsp, outbuf);
+ if (retval) {
+ DEBUG(1,("ads_krb5_mk_req: krb5_mk_req_extended failed (%s)\n",
+ error_message(retval)));
+ }
+
+ if (in_data.data) {
+ free( in_data.data );
+ in_data.length = 0;
+ }
+
+ 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
+*/
+int cli_krb5_get_ticket(const char *principal, time_t time_offset,
+ DATA_BLOB *ticket, DATA_BLOB *session_key_krb5,
+ uint32 extra_ap_opts, const char *ccname,
+ time_t *tgs_expire)
+
+{
+ krb5_error_code retval;
+ krb5_data packet;
+ krb5_context context = NULL;
+ krb5_ccache ccdef = NULL;
+ krb5_auth_context auth_context = NULL;
+ krb5_enctype enc_types[] = {
+#ifdef ENCTYPE_ARCFOUR_HMAC
+ ENCTYPE_ARCFOUR_HMAC,
+#endif
+ ENCTYPE_DES_CBC_MD5,
+ ENCTYPE_DES_CBC_CRC,
+ ENCTYPE_NULL};
+
+ initialize_krb5_error_table();
+ retval = krb5_init_context(&context);
+ if (retval) {
+ DEBUG(1,("cli_krb5_get_ticket: krb5_init_context failed (%s)\n",
+ error_message(retval)));
+ goto failed;
+ }
+
+ if (time_offset != 0) {
+ krb5_set_real_time(context, time(NULL) + time_offset, 0);
+ }
+
+ if ((retval = krb5_cc_resolve(context, ccname ?
+ ccname : krb5_cc_default_name(context), &ccdef))) {
+ DEBUG(1,("cli_krb5_get_ticket: krb5_cc_default failed (%s)\n",
+ error_message(retval)));
+ goto failed;
+ }
+
+ if ((retval = krb5_set_default_tgs_ktypes(context, enc_types))) {
+ DEBUG(1,("cli_krb5_get_ticket: krb5_set_default_tgs_ktypes failed (%s)\n",
+ error_message(retval)));
+ goto failed;
+ }
+
+ if ((retval = ads_krb5_mk_req(context,
+ &auth_context,
+ AP_OPTS_USE_SUBKEY | (krb5_flags)extra_ap_opts,
+ principal,
+ ccdef, &packet,
+ tgs_expire))) {
+ goto failed;
+ }
+
+ get_krb5_smb_session_key(context, auth_context, session_key_krb5, False);
+
+ *ticket = data_blob(packet.data, packet.length);
+
+ kerberos_free_data_contents(context, &packet);
+
+failed:
+
+ if ( context ) {
+ if (ccdef)
+ krb5_cc_close(context, ccdef);
+ if (auth_context)
+ krb5_auth_con_free(context, auth_context);
+ krb5_free_context(context);
+ }
+
+ return retval;
+}
+
+ bool get_krb5_smb_session_key(krb5_context context, krb5_auth_context auth_context, DATA_BLOB *session_key, bool remote)
+ {
+ krb5_keyblock *skey;
+ krb5_error_code err;
+ bool ret = False;
+
+ if (remote)
+ err = krb5_auth_con_getremotesubkey(context, auth_context, &skey);
+ else
+ err = krb5_auth_con_getlocalsubkey(context, auth_context, &skey);
+ if (err == 0 && skey != NULL) {
+ DEBUG(10, ("Got KRB5 session key of length %d\n", (int)KRB5_KEY_LENGTH(skey)));
+ *session_key = data_blob(KRB5_KEY_DATA(skey), KRB5_KEY_LENGTH(skey));
+ dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length);
+
+ ret = True;
+
+ krb5_free_keyblock(context, skey);
+ } else {
+ DEBUG(10, ("KRB5 error getting session key %d\n", err));
+ }
+
+ return ret;
+ }
+
+
+#if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING) && !defined(HAVE_KRB5_PRINC_COMPONENT)
+ const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i );
+
+ const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i )
+{
+ static krb5_data kdata;
+
+ kdata.data = (char *)krb5_principal_get_comp_string(context, principal, i);
+ kdata.length = strlen((const char *)kdata.data);
+ return &kdata;
+}
+#endif
+
+ krb5_error_code smb_krb5_kt_free_entry(krb5_context context, krb5_keytab_entry *kt_entry)
+{
+#if defined(HAVE_KRB5_KT_FREE_ENTRY)
+ return krb5_kt_free_entry(context, kt_entry);
+#elif defined(HAVE_KRB5_FREE_KEYTAB_ENTRY_CONTENTS)
+ return krb5_free_keytab_entry_contents(context, kt_entry);
+#else
+#error UNKNOWN_KT_FREE_FUNCTION
+#endif
+}
+
+ void smb_krb5_checksum_from_pac_sig(krb5_checksum *cksum,
+ struct PAC_SIGNATURE_DATA *sig)
+{
+#ifdef HAVE_CHECKSUM_IN_KRB5_CHECKSUM
+ cksum->cksumtype = (krb5_cksumtype)sig->type;
+ cksum->checksum.length = sig->signature.length;
+ cksum->checksum.data = sig->signature.data;
+#else
+ cksum->checksum_type = (krb5_cksumtype)sig->type;
+ cksum->length = sig->signature.length;
+ cksum->contents = sig->signature.data;
+#endif
+}
+
+ krb5_error_code smb_krb5_verify_checksum(krb5_context context,
+ const krb5_keyblock *keyblock,
+ krb5_keyusage usage,
+ krb5_checksum *cksum,
+ uint8 *data,
+ size_t length)
+{
+ krb5_error_code ret;
+
+ /* verify the checksum */
+
+ /* welcome to the wonderful world of samba's kerberos abstraction layer:
+ *
+ * function heimdal 0.6.1rc3 heimdal 0.7 MIT krb 1.4.2
+ * -----------------------------------------------------------------------------
+ * krb5_c_verify_checksum - works works
+ * krb5_verify_checksum works (6 args) works (6 args) broken (7 args)
+ */
+
+#if defined(HAVE_KRB5_C_VERIFY_CHECKSUM)
+ {
+ krb5_boolean checksum_valid = False;
+ krb5_data input;
+
+ input.data = (char *)data;
+ input.length = length;
+
+ ret = krb5_c_verify_checksum(context,
+ keyblock,
+ usage,
+ &input,
+ cksum,
+ &checksum_valid);
+ if (ret) {
+ DEBUG(3,("smb_krb5_verify_checksum: krb5_c_verify_checksum() failed: %s\n",
+ error_message(ret)));
+ return ret;
+ }
+
+ if (!checksum_valid)
+ ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+ }
+
+#elif KRB5_VERIFY_CHECKSUM_ARGS == 6 && defined(HAVE_KRB5_CRYPTO_INIT) && defined(HAVE_KRB5_CRYPTO) && defined(HAVE_KRB5_CRYPTO_DESTROY)
+
+ /* Warning: MIT's krb5_verify_checksum cannot be used as it will use a key
+ * without enctype and it ignores any key_usage types - Guenther */
+
+ {
+
+ krb5_crypto crypto;
+ ret = krb5_crypto_init(context,
+ keyblock,
+ 0,
+ &crypto);
+ if (ret) {
+ DEBUG(0,("smb_krb5_verify_checksum: krb5_crypto_init() failed: %s\n",
+ error_message(ret)));
+ return ret;
+ }
+
+ ret = krb5_verify_checksum(context,
+ crypto,
+ usage,
+ data,
+ length,
+ cksum);
+
+ krb5_crypto_destroy(context, crypto);
+ }
+
+#else
+#error UNKNOWN_KRB5_VERIFY_CHECKSUM_FUNCTION
+#endif
+
+ return ret;
+}
+
+ time_t get_authtime_from_tkt(krb5_ticket *tkt)
+{
+#if defined(HAVE_KRB5_TKT_ENC_PART2)
+ return tkt->enc_part2->times.authtime;
+#else
+ return tkt->ticket.authtime;
+#endif
+}
+
+#ifdef HAVE_KRB5_DECODE_AP_REQ /* Heimdal */
+static int get_kvno_from_ap_req(krb5_ap_req *ap_req)
+{
+#ifdef HAVE_TICKET_POINTER_IN_KRB5_AP_REQ /* MIT */
+ if (ap_req->ticket->enc_part.kvno)
+ return ap_req->ticket->enc_part.kvno;
+#else /* Heimdal */
+ if (ap_req->ticket.enc_part.kvno)
+ return *ap_req->ticket.enc_part.kvno;
+#endif
+ return 0;
+}
+
+static krb5_enctype get_enctype_from_ap_req(krb5_ap_req *ap_req)
+{
+#ifdef HAVE_ETYPE_IN_ENCRYPTEDDATA /* Heimdal */
+ return ap_req->ticket.enc_part.etype;
+#else /* MIT */
+ return ap_req->ticket->enc_part.enctype;
+#endif
+}
+#endif /* HAVE_KRB5_DECODE_AP_REQ */
+
+static krb5_error_code
+get_key_from_keytab(krb5_context context,
+ krb5_const_principal server,
+ krb5_enctype enctype,
+ krb5_kvno kvno,
+ krb5_keyblock **out_key)
+{
+ krb5_keytab_entry entry;
+ krb5_error_code ret;
+ krb5_keytab keytab;
+ char *name = NULL;
+ krb5_keyblock *keyp;
+
+ /* We have to open a new keytab handle here, as MIT does
+ an implicit open/getnext/close on krb5_kt_get_entry. We
+ may be in the middle of a keytab enumeration when this is
+ called. JRA. */
+
+ ret = smb_krb5_open_keytab(context, NULL, False, &keytab);
+ if (ret) {
+ DEBUG(1,("get_key_from_keytab: smb_krb5_open_keytab failed (%s)\n", error_message(ret)));
+ return ret;
+ }
+
+ if ( DEBUGLEVEL >= 10 ) {
+ if (smb_krb5_unparse_name(context, server, &name) == 0) {
+ DEBUG(10,("get_key_from_keytab: will look for kvno %d, enctype %d and name: %s\n",
+ kvno, enctype, name));
+ SAFE_FREE(name);
+ }
+ }
+
+ ret = krb5_kt_get_entry(context,
+ keytab,
+ server,
+ kvno,
+ enctype,
+ &entry);
+
+ if (ret) {
+ DEBUG(0,("get_key_from_keytab: failed to retrieve key: %s\n", error_message(ret)));
+ goto out;
+ }
+
+ keyp = KRB5_KT_KEY(&entry);
+
+ ret = krb5_copy_keyblock(context, keyp, out_key);
+ if (ret) {
+ DEBUG(0,("get_key_from_keytab: failed to copy key: %s\n", error_message(ret)));
+ goto out;
+ }
+
+ smb_krb5_kt_free_entry(context, &entry);
+
+out:
+ krb5_kt_close(context, keytab);
+ return ret;
+}
+
+/* Prototypes */
+
+ krb5_error_code smb_krb5_get_keyinfo_from_ap_req(krb5_context context,
+ const krb5_data *inbuf,
+ krb5_kvno *kvno,
+ krb5_enctype *enctype)
+{
+#ifdef HAVE_KRB5_DECODE_AP_REQ /* Heimdal */
+ {
+ krb5_error_code ret;
+ krb5_ap_req ap_req;
+
+ ret = krb5_decode_ap_req(context, inbuf, &ap_req);
+ if (ret)
+ return ret;
+
+ *kvno = get_kvno_from_ap_req(&ap_req);
+ *enctype = get_enctype_from_ap_req(&ap_req);
+
+ free_AP_REQ(&ap_req);
+ return 0;
+ }
+#endif
+
+ /* Possibly not an appropriate error code. */
+ return KRB5KDC_ERR_BADOPTION;
+}
+
+ krb5_error_code krb5_rd_req_return_keyblock_from_keytab(krb5_context context,
+ krb5_auth_context *auth_context,
+ const krb5_data *inbuf,
+ krb5_const_principal server,
+ krb5_keytab keytab,
+ krb5_flags *ap_req_options,
+ krb5_ticket **ticket,
+ krb5_keyblock **keyblock)
+{
+ krb5_error_code ret;
+ krb5_kvno kvno;
+ krb5_enctype enctype;
+ krb5_keyblock *local_keyblock;
+
+ ret = krb5_rd_req(context,
+ auth_context,
+ inbuf,
+ server,
+ keytab,
+ ap_req_options,
+ ticket);
+ if (ret) {
+ return ret;
+ }
+
+#ifdef KRB5_TICKET_HAS_KEYINFO
+ enctype = (*ticket)->enc_part.enctype;
+ kvno = (*ticket)->enc_part.kvno;
+#else
+ ret = smb_krb5_get_keyinfo_from_ap_req(context, inbuf, &kvno, &enctype);
+ if (ret) {
+ return ret;
+ }
+#endif
+
+ ret = get_key_from_keytab(context,
+ server,
+ enctype,
+ kvno,
+ &local_keyblock);
+ if (ret) {
+ DEBUG(0,("krb5_rd_req_return_keyblock_from_keytab: failed to call get_key_from_keytab\n"));
+ goto out;
+ }
+
+out:
+ if (ret && local_keyblock != NULL) {
+ krb5_free_keyblock(context, local_keyblock);
+ } else {
+ *keyblock = local_keyblock;
+ }
+
+ return ret;
+}
+
+ krb5_error_code smb_krb5_parse_name_norealm(krb5_context context,
+ const char *name,
+ krb5_principal *principal)
+{
+#ifdef HAVE_KRB5_PARSE_NAME_NOREALM
+ return smb_krb5_parse_name_norealm_conv(context, name, principal);
+#endif
+
+ /* we are cheating here because parse_name will in fact set the realm.
+ * We don't care as the only caller of smb_krb5_parse_name_norealm
+ * ignores the realm anyway when calling
+ * smb_krb5_principal_compare_any_realm later - Guenther */
+
+ return smb_krb5_parse_name(context, name, principal);
+}
+
+ bool smb_krb5_principal_compare_any_realm(krb5_context context,
+ krb5_const_principal princ1,
+ krb5_const_principal princ2)
+{
+#ifdef HAVE_KRB5_PRINCIPAL_COMPARE_ANY_REALM
+
+ return krb5_principal_compare_any_realm(context, princ1, princ2);
+
+/* krb5_princ_size is a macro in MIT */
+#elif defined(HAVE_KRB5_PRINC_SIZE) || defined(krb5_princ_size)
+
+ int i, len1, len2;
+ const krb5_data *p1, *p2;
+
+ len1 = krb5_princ_size(context, princ1);
+ len2 = krb5_princ_size(context, princ2);
+
+ if (len1 != len2)
+ return False;
+
+ for (i = 0; i < len1; i++) {
+
+ p1 = krb5_princ_component(context, CONST_DISCARD(krb5_principal, princ1), i);
+ p2 = krb5_princ_component(context, CONST_DISCARD(krb5_principal, princ2), i);
+
+ if (p1->length != p2->length || memcmp(p1->data, p2->data, p1->length))
+ return False;
+ }
+
+ return True;
+#else
+#error NO_SUITABLE_PRINCIPAL_COMPARE_FUNCTION
+#endif
+}
+
+ krb5_error_code smb_krb5_renew_ticket(const char *ccache_string, /* FILE:/tmp/krb5cc_0 */
+ const char *client_string, /* gd@BER.SUSE.DE */
+ const char *service_string, /* krbtgt/BER.SUSE.DE@BER.SUSE.DE */
+ time_t *expire_time)
+{
+ krb5_error_code ret;
+ krb5_context context = NULL;
+ krb5_ccache ccache = NULL;
+ krb5_principal client = NULL;
+ krb5_creds creds, creds_in, *creds_out = NULL;
+
+ ZERO_STRUCT(creds);
+ ZERO_STRUCT(creds_in);
+
+ initialize_krb5_error_table();
+ ret = krb5_init_context(&context);
+ if (ret) {
+ goto done;
+ }
+
+ if (!ccache_string) {
+ ccache_string = krb5_cc_default_name(context);
+ }
+
+ if (!ccache_string) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ DEBUG(10,("smb_krb5_renew_ticket: using %s as ccache\n", ccache_string));
+
+ /* FIXME: we should not fall back to defaults */
+ ret = krb5_cc_resolve(context, CONST_DISCARD(char *, ccache_string), &ccache);
+ if (ret) {
+ goto done;
+ }
+
+ if (client_string) {
+ ret = smb_krb5_parse_name(context, client_string, &client);
+ if (ret) {
+ goto done;
+ }
+ } else {
+ ret = krb5_cc_get_principal(context, ccache, &client);
+ if (ret) {
+ goto done;
+ }
+ }
+
+#ifdef HAVE_KRB5_GET_RENEWED_CREDS /* MIT */
+ {
+ ret = krb5_get_renewed_creds(context, &creds, client, ccache, CONST_DISCARD(char *, service_string));
+ if (ret) {
+ DEBUG(10,("smb_krb5_renew_ticket: krb5_get_kdc_cred failed: %s\n", error_message(ret)));
+ goto done;
+ }
+ }
+#elif defined(HAVE_KRB5_GET_KDC_CRED) /* Heimdal */
+ {
+ krb5_kdc_flags flags;
+ krb5_realm *client_realm = NULL;
+
+ ret = krb5_copy_principal(context, client, &creds_in.client);
+ if (ret) {
+ goto done;
+ }
+
+ if (service_string) {
+ ret = smb_krb5_parse_name(context, service_string, &creds_in.server);
+ if (ret) {
+ goto done;
+ }
+ } else {
+ /* build tgt service by default */
+ client_realm = krb5_princ_realm(context, creds_in.client);
+ if (!client_realm) {
+ ret = ENOMEM;
+ goto done;
+ }
+ ret = krb5_make_principal(context, &creds_in.server, *client_realm, KRB5_TGS_NAME, *client_realm, NULL);
+ if (ret) {
+ goto done;
+ }
+ }
+
+ flags.i = 0;
+ flags.b.renewable = flags.b.renew = True;
+
+ ret = krb5_get_kdc_cred(context, ccache, flags, NULL, NULL, &creds_in, &creds_out);
+ if (ret) {
+ DEBUG(10,("smb_krb5_renew_ticket: krb5_get_kdc_cred failed: %s\n", error_message(ret)));
+ goto done;
+ }
+
+ creds = *creds_out;
+ }
+#else
+#error NO_SUITABLE_KRB5_TICKET_RENEW_FUNCTION_AVAILABLE
+#endif
+
+ /* hm, doesn't that create a new one if the old one wasn't there? - Guenther */
+ ret = krb5_cc_initialize(context, ccache, client);
+ if (ret) {
+ goto done;
+ }
+
+ ret = krb5_cc_store_cred(context, ccache, &creds);
+
+ if (expire_time) {
+ *expire_time = (time_t) creds.times.endtime;
+ }
+
+done:
+ krb5_free_cred_contents(context, &creds_in);
+
+ if (creds_out) {
+ krb5_free_creds(context, creds_out);
+ } else {
+ krb5_free_cred_contents(context, &creds);
+ }
+
+ if (client) {
+ krb5_free_principal(context, client);
+ }
+ if (ccache) {
+ krb5_cc_close(context, ccache);
+ }
+ if (context) {
+ krb5_free_context(context);
+ }
+
+ return ret;
+}
+
+ krb5_error_code smb_krb5_free_addresses(krb5_context context, smb_krb5_addresses *addr)
+{
+ krb5_error_code ret = 0;
+ if (addr == NULL) {
+ return ret;
+ }
+#if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
+ krb5_free_addresses(context, addr->addrs);
+#elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
+ ret = krb5_free_addresses(context, addr->addrs);
+ SAFE_FREE(addr->addrs);
+#endif
+ SAFE_FREE(addr);
+ addr = NULL;
+ return ret;
+}
+
+ krb5_error_code smb_krb5_gen_netbios_krb5_address(smb_krb5_addresses **kerb_addr)
+{
+ krb5_error_code ret = 0;
+ nstring buf;
+#if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
+ krb5_address **addrs = NULL;
+#elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
+ krb5_addresses *addrs = NULL;
+#endif
+
+ *kerb_addr = (smb_krb5_addresses *)SMB_MALLOC(sizeof(smb_krb5_addresses));
+ if (*kerb_addr == NULL) {
+ return ENOMEM;
+ }
+
+ put_name(buf, global_myname(), ' ', 0x20);
+
+#if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
+ {
+ int num_addr = 2;
+
+ addrs = (krb5_address **)SMB_MALLOC(sizeof(krb5_address *) * num_addr);
+ if (addrs == NULL) {
+ SAFE_FREE(kerb_addr);
+ return ENOMEM;
+ }
+
+ memset(addrs, 0, sizeof(krb5_address *) * num_addr);
+
+ addrs[0] = (krb5_address *)SMB_MALLOC(sizeof(krb5_address));
+ if (addrs[0] == NULL) {
+ SAFE_FREE(addrs);
+ SAFE_FREE(kerb_addr);
+ return ENOMEM;
+ }
+
+ addrs[0]->magic = KV5M_ADDRESS;
+ addrs[0]->addrtype = KRB5_ADDR_NETBIOS;
+ addrs[0]->length = MAX_NETBIOSNAME_LEN;
+ addrs[0]->contents = (unsigned char *)SMB_MALLOC(addrs[0]->length);
+ if (addrs[0]->contents == NULL) {
+ SAFE_FREE(addrs[0]);
+ SAFE_FREE(addrs);
+ SAFE_FREE(kerb_addr);
+ return ENOMEM;
+ }
+
+ memcpy(addrs[0]->contents, buf, addrs[0]->length);
+
+ addrs[1] = NULL;
+ }
+#elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
+ {
+ addrs = (krb5_addresses *)SMB_MALLOC(sizeof(krb5_addresses));
+ if (addrs == NULL) {
+ SAFE_FREE(kerb_addr);
+ return ENOMEM;
+ }
+
+ memset(addrs, 0, sizeof(krb5_addresses));
+
+ addrs->len = 1;
+ addrs->val = (krb5_address *)SMB_MALLOC(sizeof(krb5_address));
+ if (addrs->val == NULL) {
+ SAFE_FREE(addrs);
+ SAFE_FREE(kerb_addr);
+ return ENOMEM;
+ }
+
+ addrs->val[0].addr_type = KRB5_ADDR_NETBIOS;
+ addrs->val[0].address.length = MAX_NETBIOSNAME_LEN;
+ addrs->val[0].address.data = (unsigned char *)SMB_MALLOC(addrs->val[0].address.length);
+ if (addrs->val[0].address.data == NULL) {
+ SAFE_FREE(addrs->val);
+ SAFE_FREE(addrs);
+ SAFE_FREE(kerb_addr);
+ return ENOMEM;
+ }
+
+ memcpy(addrs->val[0].address.data, buf, addrs->val[0].address.length);
+ }
+#else
+#error UNKNOWN_KRB5_ADDRESS_FORMAT
+#endif
+ (*kerb_addr)->addrs = addrs;
+
+ return ret;
+}
+
+ void smb_krb5_free_error(krb5_context context, krb5_error *krberror)
+{
+#ifdef HAVE_KRB5_FREE_ERROR_CONTENTS /* Heimdal */
+ krb5_free_error_contents(context, krberror);
+#else /* MIT */
+ krb5_free_error(context, krberror);
+#endif
+}
+
+ krb5_error_code handle_krberror_packet(krb5_context context,
+ krb5_data *packet)
+{
+ krb5_error_code ret;
+ bool got_error_code = False;
+
+ DEBUG(10,("handle_krberror_packet: got error packet\n"));
+
+#ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR /* Heimdal */
+ {
+ krb5_error krberror;
+
+ if ((ret = krb5_rd_error(context, packet, &krberror))) {
+ DEBUG(10,("handle_krberror_packet: krb5_rd_error failed with: %s\n",
+ error_message(ret)));
+ return ret;
+ }
+
+ if (krberror.e_data == NULL || krberror.e_data->data == NULL) {
+ ret = (krb5_error_code) krberror.error_code;
+ got_error_code = True;
+ }
+
+ smb_krb5_free_error(context, &krberror);
+ }
+#else /* MIT */
+ {
+ krb5_error *krberror;
+
+ if ((ret = krb5_rd_error(context, packet, &krberror))) {
+ DEBUG(10,("handle_krberror_packet: krb5_rd_error failed with: %s\n",
+ error_message(ret)));
+ return ret;
+ }
+
+ if (krberror->e_data.data == NULL) {
+ ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error;
+ got_error_code = True;
+ }
+ smb_krb5_free_error(context, krberror);
+ }
+#endif
+ if (got_error_code) {
+ DEBUG(5,("handle_krberror_packet: got KERBERR from kpasswd: %s (%d)\n",
+ error_message(ret), ret));
+ }
+ return ret;
+}
+
+ krb5_error_code smb_krb5_get_init_creds_opt_alloc(krb5_context context,
+ krb5_get_init_creds_opt **opt)
+{
+#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC
+ /* Heimdal or modern MIT version */
+ return krb5_get_init_creds_opt_alloc(context, opt);
+#else
+ /* Historical MIT version */
+ krb5_get_init_creds_opt *my_opt;
+
+ *opt = NULL;
+
+ if ((my_opt = SMB_MALLOC_P(krb5_get_init_creds_opt)) == NULL) {
+ return ENOMEM;
+ }
+
+ krb5_get_init_creds_opt_init(my_opt);
+
+ *opt = my_opt;
+ return 0;
+#endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC */
+}
+
+ void smb_krb5_get_init_creds_opt_free(krb5_context context,
+ krb5_get_init_creds_opt *opt)
+{
+#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_FREE
+
+#ifdef KRB5_CREDS_OPT_FREE_REQUIRES_CONTEXT
+ /* Modern MIT or Heimdal version */
+ krb5_get_init_creds_opt_free(context, opt);
+#else
+ /* Heimdal version */
+ krb5_get_init_creds_opt_free(opt);
+#endif /* KRB5_CREDS_OPT_FREE_REQUIRES_CONTEXT */
+
+#else /* HAVE_KRB5_GET_INIT_CREDS_OPT_FREE */
+ /* Historical MIT version */
+ SAFE_FREE(opt);
+ opt = NULL;
+#endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_FREE */
+}
+
+ krb5_enctype smb_get_enctype_from_kt_entry(krb5_keytab_entry *kt_entry)
+{
+ return KRB5_KEY_TYPE(KRB5_KT_KEY(kt_entry));
+}
+
+
+/* caller needs to free etype_s */
+ krb5_error_code smb_krb5_enctype_to_string(krb5_context context,
+ krb5_enctype enctype,
+ char **etype_s)
+{
+#ifdef HAVE_KRB5_ENCTYPE_TO_STRING_WITH_KRB5_CONTEXT_ARG
+ return krb5_enctype_to_string(context, enctype, etype_s); /* Heimdal */
+#elif defined(HAVE_KRB5_ENCTYPE_TO_STRING_WITH_SIZE_T_ARG)
+ char buf[256];
+ krb5_error_code ret = krb5_enctype_to_string(enctype, buf, 256); /* MIT */
+ if (ret) {
+ return ret;
+ }
+ *etype_s = SMB_STRDUP(buf);
+ if (!*etype_s) {
+ return ENOMEM;
+ }
+ return ret;
+#else
+#error UNKNOWN_KRB5_ENCTYPE_TO_STRING_FUNCTION
+#endif
+}
+
+ krb5_error_code smb_krb5_mk_error(krb5_context context,
+ krb5_error_code error_code,
+ const krb5_principal server,
+ krb5_data *reply)
+{
+#ifdef HAVE_SHORT_KRB5_MK_ERROR_INTERFACE /* MIT */
+ /*
+ * The MIT interface is *terrible*.
+ * We have to construct this ourselves...
+ */
+ krb5_error e;
+
+ memset(&e, 0, sizeof(e));
+ krb5_us_timeofday(context, &e.stime, &e.susec);
+ e.server = server;
+#if defined(krb5_err_base)
+ e.error = error_code - krb5_err_base;
+#elif defined(ERROR_TABLE_BASE_krb5)
+ e.error = error_code - ERROR_TABLE_BASE_krb5;
+#else
+ e.error = error_code; /* Almost certainly wrong, but what can we do... ? */
+#endif
+
+ return krb5_mk_error(context, &e, reply);
+#else /* Heimdal. */
+ return krb5_mk_error(context,
+ error_code,
+ NULL,
+ NULL, /* e_data */
+ NULL,
+ server,
+ NULL,
+ NULL,
+ reply);
+#endif
+}
+
+/**********************************************************************
+ * Open a krb5 keytab with flags, handles readonly or readwrite access and
+ * allows to process non-default keytab names.
+ * @param context krb5_context
+ * @param keytab_name_req string
+ * @param write_access bool if writable keytab is required
+ * @param krb5_keytab pointer to krb5_keytab (close with krb5_kt_close())
+ * @return krb5_error_code
+**********************************************************************/
+
+/* This MAX_NAME_LEN is a constant defined in krb5.h */
+#ifndef MAX_KEYTAB_NAME_LEN
+#define MAX_KEYTAB_NAME_LEN 1100
+#endif
+
+ krb5_error_code smb_krb5_open_keytab(krb5_context context,
+ const char *keytab_name_req,
+ bool write_access,
+ krb5_keytab *keytab)
+{
+ krb5_error_code ret = 0;
+ TALLOC_CTX *mem_ctx;
+ char keytab_string[MAX_KEYTAB_NAME_LEN];
+ char *kt_str = NULL;
+ bool found_valid_name = False;
+ const char *pragma = "FILE";
+ const char *tmp = NULL;
+
+ if (!write_access && !keytab_name_req) {
+ /* caller just wants to read the default keytab readonly, so be it */
+ return krb5_kt_default(context, keytab);
+ }
+
+ mem_ctx = talloc_init("smb_krb5_open_keytab");
+ if (!mem_ctx) {
+ return ENOMEM;
+ }
+
+#ifdef HAVE_WRFILE_KEYTAB
+ if (write_access) {
+ pragma = "WRFILE";
+ }
+#endif
+
+ if (keytab_name_req) {
+
+ if (strlen(keytab_name_req) > MAX_KEYTAB_NAME_LEN) {
+ ret = KRB5_CONFIG_NOTENUFSPACE;
+ goto out;
+ }
+
+ if ((strncmp(keytab_name_req, "WRFILE:/", 8) == 0) ||
+ (strncmp(keytab_name_req, "FILE:/", 6) == 0)) {
+ tmp = keytab_name_req;
+ goto resolve;
+ }
+
+ if (keytab_name_req[0] != '/') {
+ ret = KRB5_KT_BADNAME;
+ goto out;
+ }
+
+ tmp = talloc_asprintf(mem_ctx, "%s:%s", pragma, keytab_name_req);
+ if (!tmp) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ goto resolve;
+ }
+
+ /* we need to handle more complex keytab_strings, like:
+ * "ANY:FILE:/etc/krb5.keytab,krb4:/etc/srvtab" */
+
+ ret = krb5_kt_default_name(context, &keytab_string[0], MAX_KEYTAB_NAME_LEN - 2);
+ if (ret) {
+ goto out;
+ }
+
+ DEBUG(10,("smb_krb5_open_keytab: krb5_kt_default_name returned %s\n", keytab_string));
+
+ tmp = talloc_strdup(mem_ctx, keytab_string);
+ if (!tmp) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ if (strncmp(tmp, "ANY:", 4) == 0) {
+ tmp += 4;
+ }
+
+ memset(&keytab_string, '\0', sizeof(keytab_string));
+
+ while (next_token_talloc(mem_ctx, &tmp, &kt_str, ",")) {
+ if (strncmp(kt_str, "WRFILE:", 7) == 0) {
+ found_valid_name = True;
+ tmp = kt_str;
+ tmp += 7;
+ }
+
+ if (strncmp(kt_str, "FILE:", 5) == 0) {
+ found_valid_name = True;
+ tmp = kt_str;
+ tmp += 5;
+ }
+
+ if (found_valid_name) {
+ if (tmp[0] != '/') {
+ ret = KRB5_KT_BADNAME;
+ goto out;
+ }
+
+ tmp = talloc_asprintf(mem_ctx, "%s:%s", pragma, tmp);
+ if (!tmp) {
+ ret = ENOMEM;
+ goto out;
+ }
+ break;
+ }
+ }
+
+ if (!found_valid_name) {
+ ret = KRB5_KT_UNKNOWN_TYPE;
+ goto out;
+ }
+
+ resolve:
+ DEBUG(10,("smb_krb5_open_keytab: resolving: %s\n", tmp));
+ ret = krb5_kt_resolve(context, tmp, keytab);
+
+ out:
+ TALLOC_FREE(mem_ctx);
+ return ret;
+}
+
+krb5_error_code smb_krb5_keytab_name(TALLOC_CTX *mem_ctx,
+ krb5_context context,
+ krb5_keytab keytab,
+ const char **keytab_name)
+{
+ char keytab_string[MAX_KEYTAB_NAME_LEN];
+ krb5_error_code ret = 0;
+
+ ret = krb5_kt_get_name(context, keytab,
+ keytab_string, MAX_KEYTAB_NAME_LEN - 2);
+ if (ret) {
+ return ret;
+ }
+
+ *keytab_name = talloc_strdup(mem_ctx, keytab_string);
+ if (!*keytab_name) {
+ return ENOMEM;
+ }
+
+ return ret;
+}
+
+#if defined(TKT_FLG_OK_AS_DELEGATE ) && defined(HAVE_KRB5_FWD_TGT_CREDS) && defined(HAVE_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE) && defined(KRB5_AUTH_CONTEXT_USE_SUBKEY)
+/**************************************************************
+Routine: ads_krb5_get_fwd_ticket
+ Description:
+ When a service ticket is flagged as trusted
+ for delegation we should provide a forwardable
+ ticket so that the remote host can act on our
+ behalf. This is done by taking the 2nd forwardable
+ TGT and storing it in the GSS-API authenticator
+ "checksum". This routine will populate
+ the krb5_data authenticator with this TGT.
+ Parameters:
+ krb5_context context: The kerberos context for this authentication.
+ krb5_auth_context: The authentication context.
+ krb5_creds *credsp: The ticket credentials (AS-REP).
+ krb5_ccache ccache: The credentials cache.
+ krb5_data &authenticator: The checksum field that will store the TGT, and
+ authenticator.data must be freed by the caller.
+
+ Returns:
+ krb5_error_code: 0 if no errors, otherwise set.
+**************************************************************/
+
+static krb5_error_code ads_krb5_get_fwd_ticket( krb5_context context,
+ krb5_auth_context *auth_context,
+ krb5_creds *credsp,
+ krb5_ccache ccache,
+ krb5_data *authenticator)
+{
+ krb5_data fwdData;
+ krb5_error_code retval = 0;
+ char *pChksum = NULL;
+ char *p = NULL;
+
+ ZERO_STRUCT(fwdData);
+ ZERO_STRUCTP(authenticator);
+
+ retval = krb5_fwd_tgt_creds(context,/* Krb5 context [in] */
+ *auth_context, /* Authentication context [in] */
+ CONST_DISCARD(char *, KRB5_TGS_NAME), /* Ticket service name ("krbtgt") [in] */
+ credsp->client, /* Client principal for the tgt [in] */
+ credsp->server, /* Server principal for the tgt [in] */
+ ccache, /* Credential cache to use for storage [in] */
+ 1, /* Turn on for "Forwardable ticket" [in] */
+ &fwdData ); /* Resulting response [out] */
+
+
+ if (retval) {
+ DEBUG(1,("ads_krb5_get_fwd_ticket: krb5_fwd_tgt_creds failed (%s)\n",
+ error_message(retval)));
+ goto out;
+ }
+
+ if ((unsigned int)GSSAPI_CHECKSUM_SIZE + (unsigned int)fwdData.length <
+ (unsigned int)GSSAPI_CHECKSUM_SIZE) {
+ retval = EINVAL;
+ goto out;
+ }
+
+ /* We're going to allocate a gssChecksum structure with a little
+ extra data the length of the kerberos credentials length
+ (APPLICATION 22) so that we can pack it on the end of the structure.
+ */
+
+ pChksum = (char *)SMB_MALLOC(GSSAPI_CHECKSUM_SIZE + fwdData.length );
+ if (!pChksum) {
+ retval = ENOMEM;
+ goto out;
+ }
+
+ p = pChksum;
+
+ SIVAL(p, 0, GSSAPI_BNDLENGTH);
+ p += 4;
+
+ /* Zero out the bindings fields */
+ memset(p, '\0', GSSAPI_BNDLENGTH );
+ p += GSSAPI_BNDLENGTH;
+
+ SIVAL(p, 0, GSS_C_DELEG_FLAG );
+ p += 4;
+ SSVAL(p, 0, 1 );
+ p += 2;
+ SSVAL(p, 0, fwdData.length );
+ p += 2;
+
+ /* Migrate the kerberos KRB_CRED data to the checksum delegation */
+ memcpy(p, fwdData.data, fwdData.length );
+ p += fwdData.length;
+
+ /* We need to do this in order to allow our GSS-API */
+ retval = krb5_auth_con_set_req_cksumtype( context, *auth_context, GSSAPI_CHECKSUM );
+ if (retval) {
+ goto out;
+ }
+
+ /* We now have a service ticket, now turn it into an AP-REQ. */
+ authenticator->length = fwdData.length + GSSAPI_CHECKSUM_SIZE;
+
+ /* Caller should call free() when they're done with this. */
+ authenticator->data = (char *)pChksum;
+
+ out:
+
+ /* Remove that input data, we never needed it anyway. */
+ if (fwdData.length > 0) {
+ krb5_free_data_contents( context, &fwdData );
+ }
+
+ return retval;
+}
+#endif
+
+#else /* HAVE_KRB5 */
+ /* this saves a few linking headaches */
+ int cli_krb5_get_ticket(const char *principal, time_t time_offset,
+ DATA_BLOB *ticket, DATA_BLOB *session_key_krb5, uint32 extra_ap_opts,
+ const char *ccname, time_t *tgs_expire)
+{
+ DEBUG(0,("NO KERBEROS SUPPORT\n"));
+ return 1;
+}
+
+#endif
diff --git a/source3/libsmb/clilist.c b/source3/libsmb/clilist.c
new file mode 100644
index 0000000000..50918458b0
--- /dev/null
+++ b/source3/libsmb/clilist.c
@@ -0,0 +1,683 @@
+/*
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+ Calculate a safe next_entry_offset.
+****************************************************************************/
+
+static size_t calc_next_entry_offset(const char *base, const char *pdata_end)
+{
+ size_t next_entry_offset = (size_t)IVAL(base,0);
+
+ if (next_entry_offset == 0 ||
+ base + next_entry_offset < base ||
+ base + next_entry_offset > pdata_end) {
+ next_entry_offset = pdata_end - base;
+ }
+ return next_entry_offset;
+}
+
+/****************************************************************************
+ 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 size_t interpret_long_filename(TALLOC_CTX *ctx,
+ struct cli_state *cli,
+ int level,
+ const char *p,
+ const char *pdata_end,
+ file_info *finfo,
+ uint32 *p_resume_key,
+ DATA_BLOB *p_last_name_raw)
+{
+ int len;
+ size_t ret;
+ const char *base = p;
+
+ data_blob_free(p_last_name_raw);
+
+ if (p_resume_key) {
+ *p_resume_key = 0;
+ }
+ ZERO_STRUCTP(finfo);
+ finfo->cli = cli;
+
+ switch (level) {
+ case 1: /* OS/2 understands this */
+ /* these dates are converted to GMT by
+ make_unix_date */
+ if (pdata_end - base < 27) {
+ return pdata_end - base;
+ }
+ finfo->ctime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+4));
+ finfo->atime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+8));
+ finfo->mtime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, 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);
+
+ /* We can safely use +1 here (which is required by OS/2)
+ * instead of +2 as the STR_TERMINATE flag below is
+ * actually used as the length calculation.
+ * The len+2 is merely an upper bound.
+ * Due to the explicit 2 byte null termination
+ * in cli_receive_trans/cli_receive_nt_trans
+ * we know this is safe. JRA + kukks
+ */
+
+ if (p + len + 1 > pdata_end) {
+ return pdata_end - base;
+ }
+
+ /* the len+2 below looks strange but it is
+ important to cope with the differences
+ between win2000 and win9x for this call
+ (tridge) */
+ ret = clistr_pull_talloc(ctx,
+ cli,
+ &finfo->name,
+ p,
+ len+2,
+ STR_TERMINATE);
+ if (ret == (size_t)-1) {
+ return pdata_end - base;
+ }
+ p += ret;
+ return PTR_DIFF(p, base);
+
+ case 2: /* this is what OS/2 uses mostly */
+ /* these dates are converted to GMT by
+ make_unix_date */
+ if (pdata_end - base < 31) {
+ return pdata_end - base;
+ }
+ finfo->ctime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+4));
+ finfo->atime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+8));
+ finfo->mtime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+12));
+ finfo->size = IVAL(p,16);
+ finfo->mode = CVAL(p,24);
+ len = CVAL(p, 30);
+ p += 31;
+ /* check for unisys! */
+ if (p + len + 1 > pdata_end) {
+ return pdata_end - base;
+ }
+ ret = clistr_pull_talloc(ctx,
+ cli,
+ &finfo->name,
+ p,
+ len,
+ STR_NOALIGN);
+ if (ret == (size_t)-1) {
+ return pdata_end - base;
+ }
+ p += ret;
+ return PTR_DIFF(p, base) + 1;
+
+ case 260: /* NT uses this, but also accepts 2 */
+ {
+ size_t namelen, slen;
+
+ if (pdata_end - base < 94) {
+ return pdata_end - base;
+ }
+
+ p += 4; /* next entry offset */
+
+ if (p_resume_key) {
+ *p_resume_key = IVAL(p,0);
+ }
+ p += 4; /* fileindex */
+
+ /* Offset zero is "create time", not "change time". */
+ p += 8;
+ finfo->atime_ts = interpret_long_date(p);
+ p += 8;
+ finfo->mtime_ts = interpret_long_date(p);
+ p += 8;
+ finfo->ctime_ts = interpret_long_date(p);
+ p += 8;
+ finfo->size = IVAL2_TO_SMB_BIG_UINT(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);
+ if (slen > 24) {
+ /* Bad short name length. */
+ return pdata_end - base;
+ }
+ 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? */
+ if (p + namelen < p || p + namelen > pdata_end) {
+ return pdata_end - base;
+ }
+ ret = clistr_pull_talloc(ctx,
+ cli,
+ &finfo->name,
+ p,
+ namelen,
+ 0);
+ if (ret == (size_t)-1) {
+ return pdata_end - base;
+ }
+
+ /* To be robust in the face of unicode conversion failures
+ we need to copy the raw bytes of the last name seen here.
+ Namelen doesn't include the terminating unicode null, so
+ copy it here. */
+
+ if (p_last_name_raw) {
+ *p_last_name_raw = data_blob(NULL, namelen+2);
+ memcpy(p_last_name_raw->data, p, namelen);
+ SSVAL(p_last_name_raw->data, namelen, 0);
+ }
+ return calc_next_entry_offset(base, pdata_end);
+ }
+ }
+
+ DEBUG(1,("Unknown long filename format %d\n",level));
+ return calc_next_entry_offset(base, pdata_end);
+}
+
+/****************************************************************************
+ 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)(const char *, file_info *, const char *, void *), void *state)
+{
+#if 1
+ int max_matches = 1366; /* Match W2k - was 512. */
+#else
+ int max_matches = 512;
+#endif
+ int info_level;
+ char *p, *p2, *rdata_end;
+ char *mask = NULL;
+ file_info finfo;
+ int i;
+ char *dirlist = NULL;
+ int dirlist_len = 0;
+ int total_received = -1;
+ bool First = True;
+ int ff_searchcount=0;
+ int ff_eos=0;
+ int ff_dir_handle=0;
+ int loop_count = 0;
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int param_len, data_len;
+ uint16 setup;
+ char *param;
+ const char *mnt;
+ uint32 resume_key = 0;
+ TALLOC_CTX *frame = talloc_stackframe();
+ DATA_BLOB last_name_raw = data_blob(NULL, 0);
+
+ /* NT uses 260, OS/2 uses 2. Both accept 1. */
+ info_level = (cli->capabilities&CAP_NT_SMBS)?260:1;
+
+ mask = SMB_STRDUP(Mask);
+ if (!mask) {
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ while (ff_eos == 0) {
+ size_t nlen = 2*(strlen(mask)+1);
+
+ loop_count++;
+ if (loop_count > 200) {
+ DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
+ break;
+ }
+
+ param = SMB_MALLOC_ARRAY(char, 12+nlen+last_name_raw.length+2);
+ if (!param) {
+ break;
+ }
+
+ if (First) {
+ setup = TRANSACT2_FINDFIRST;
+ SSVAL(param,0,attribute); /* attribute */
+ SSVAL(param,2,max_matches); /* max count */
+ SSVAL(param,4,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END)); /* resume required + close on end */
+ SSVAL(param,6,info_level);
+ SIVAL(param,8,0);
+ p = param+12;
+ p += clistr_push(cli, param+12, mask,
+ nlen, STR_TERMINATE);
+ } else {
+ setup = TRANSACT2_FINDNEXT;
+ SSVAL(param,0,ff_dir_handle);
+ SSVAL(param,2,max_matches); /* max count */
+ SSVAL(param,4,info_level);
+ /* For W2K servers serving out FAT filesystems we *must* set the
+ resume key. If it's not FAT then it's returned as zero. */
+ SIVAL(param,6,resume_key); /* ff_resume_key */
+ /* NB. *DON'T* use continue here. If you do it seems that W2K and bretheren
+ can miss filenames. Use last filename continue instead. JRA */
+ SSVAL(param,10,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END)); /* resume required + close on end */
+ p = param+12;
+ if (last_name_raw.length) {
+ memcpy(p, last_name_raw.data, last_name_raw.length);
+ p += last_name_raw.length;
+ } else {
+ p += clistr_push(cli, param+12, mask,
+ nlen, 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,
+#if 0
+ /* w2k value. */
+ MIN(16384,cli->max_xmit) /* data, length, max. */
+#else
+ cli->max_xmit /* data, length, max. */
+#endif
+ )) {
+ SAFE_FREE(param);
+ TALLOC_FREE(frame);
+ break;
+ }
+
+ SAFE_FREE(param);
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_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;
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+
+ cli_dos_error(cli, &eclass, &ecode);
+
+ /*
+ * OS/2 might return "no more files",
+ * which just tells us, that searchcount is zero
+ * in this search.
+ * Guenter Kukkukk <linux@kukkukk.com>
+ */
+
+ if (eclass == ERRDOS && ecode == ERRnofiles) {
+ ff_searchcount = 0;
+ cli_reset_error(cli);
+ break;
+ }
+
+ if (eclass != ERRSRV || ecode != ERRerror)
+ break;
+ smb_msleep(100);
+ continue;
+ }
+
+ if (cli_is_error(cli) || !rdata || !rparam) {
+ SAFE_FREE(rdata);
+ SAFE_FREE(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);
+ } else {
+ ff_searchcount = SVAL(p,0);
+ ff_eos = SVAL(p,2);
+ }
+
+ if (ff_searchcount == 0) {
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ break;
+ }
+
+ /* point to the data bytes */
+ p = rdata;
+ rdata_end = rdata + data_len;
+
+ /* we might need the lastname for continuations */
+ for (p2=p,i=0;i<ff_searchcount && p2 < rdata_end;i++) {
+ if ((info_level == 260) && (i == ff_searchcount-1)) {
+ /* Last entry - fixup the last offset length. */
+ SIVAL(p2,0,PTR_DIFF((rdata + data_len),p2));
+ }
+ p2 += interpret_long_filename(frame,
+ cli,
+ info_level,
+ p2,
+ rdata_end,
+ &finfo,
+ &resume_key,
+ &last_name_raw);
+
+ if (!finfo.name) {
+ DEBUG(0,("cli_list_new: Error: unable to parse name from info level %d\n",
+ info_level));
+ ff_eos = 1;
+ break;
+ }
+ if (!First && *mask && strcsequal(finfo.name, mask)) {
+ DEBUG(0,("Error: Looping in FIND_NEXT as name %s has already been seen?\n",
+ finfo.name));
+ ff_eos = 1;
+ break;
+ }
+ }
+
+ SAFE_FREE(mask);
+ if (ff_searchcount > 0) {
+ mask = SMB_STRDUP(finfo.name);
+ } else {
+ mask = SMB_STRDUP("");
+ }
+ if (!mask) {
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ break;
+ }
+
+ /* grab the data for later use */
+ /* and add them to the dirlist pool */
+ dirlist = (char *)SMB_REALLOC(dirlist,dirlist_len + data_len);
+
+ if (!dirlist) {
+ DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ break;
+ }
+
+ 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;
+ }
+
+ mnt = cli_cm_get_mntpoint( cli );
+
+ /* see if the server disconnected or the connection otherwise failed */
+ if (cli_is_error(cli)) {
+ total_received = -1;
+ } else {
+ /* no connection problem. let user function add each entry */
+ rdata_end = dirlist + dirlist_len;
+ for (p=dirlist,i=0;i<total_received;i++) {
+ p += interpret_long_filename(frame,
+ cli,
+ info_level,
+ p,
+ rdata_end,
+ &finfo,
+ NULL,
+ NULL);
+ if (!finfo.name) {
+ DEBUG(0,("cli_list_new: unable to parse name from info level %d\n",
+ info_level));
+ break;
+ }
+ fn(mnt,&finfo, Mask, state);
+ }
+ }
+
+ /* free up the dirlist buffer and last name raw blob */
+ SAFE_FREE(dirlist);
+ data_blob_free(&last_name_raw);
+ SAFE_FREE(mask);
+ TALLOC_FREE(frame);
+ return(total_received);
+}
+
+/****************************************************************************
+ Interpret a short filename structure.
+ The length of the structure is returned.
+****************************************************************************/
+
+static bool interpret_short_filename(TALLOC_CTX *ctx,
+ struct cli_state *cli,
+ char *p,
+ file_info *finfo)
+{
+ size_t ret;
+ ZERO_STRUCTP(finfo);
+
+ finfo->cli = cli;
+ finfo->mode = CVAL(p,21);
+
+ /* this date is converted to GMT by make_unix_date */
+ finfo->ctime_ts.tv_sec = cli_make_unix_date(cli, p+22);
+ finfo->ctime_ts.tv_nsec = 0;
+ finfo->mtime_ts.tv_sec = finfo->atime_ts.tv_sec = finfo->ctime_ts.tv_sec;
+ finfo->mtime_ts.tv_nsec = finfo->atime_ts.tv_nsec = 0;
+ finfo->size = IVAL(p,26);
+ ret = clistr_pull_talloc(ctx,
+ cli,
+ &finfo->name,
+ p+30,
+ 12,
+ STR_ASCII);
+ if (ret == (size_t)-1) {
+ return false;
+ }
+
+ if (finfo->name) {
+ strlcpy(finfo->short_name,
+ finfo->name,
+ sizeof(finfo->short_name));
+ }
+ return true;
+ 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)(const char *, 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 *dirlist = NULL;
+ char *mask = NULL;
+ TALLOC_CTX *frame = NULL;
+
+ ZERO_ARRAY(status);
+
+ mask = SMB_STRDUP(Mask);
+ if (!mask) {
+ return -1;
+ }
+
+ while (1) {
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ cli_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:"",
+ cli->bufsize - PTR_DIFF(p,cli->outbuf),
+ 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;
+
+ /* Ensure we received enough data. */
+ if ((cli->inbuf+4+smb_len(cli->inbuf) - (smb_buf(cli->inbuf)+3)) <
+ received*DIR_STRUCT_SIZE) {
+ break;
+ }
+
+ first = False;
+
+ dirlist = (char *)SMB_REALLOC(
+ dirlist,(num_received + received)*DIR_STRUCT_SIZE);
+ if (!dirlist) {
+ DEBUG(0,("cli_list_old: failed to expand dirlist"));
+ SAFE_FREE(mask);
+ return 0;
+ }
+
+ 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);
+
+ cli_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)));
+ }
+ }
+
+ frame = talloc_stackframe();
+ for (p=dirlist,i=0;i<num_received;i++) {
+ file_info finfo;
+ if (!interpret_short_filename(frame, cli, p, &finfo)) {
+ break;
+ }
+ p += DIR_STRUCT_SIZE;
+ fn("\\", &finfo, Mask, state);
+ }
+ TALLOC_FREE(frame);
+
+ SAFE_FREE(mask);
+ 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)(const char *, 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..808190e79c
--- /dev/null
+++ b/source3/libsmb/climessage.c
@@ -0,0 +1,162 @@
+/*
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+ Start a message sequence.
+****************************************************************************/
+
+int cli_message_start_build(struct cli_state *cli, const char *host, const char *username)
+{
+ char *p;
+
+ /* construct a SMBsendstrt command */
+ memset(cli->outbuf,'\0',smb_size);
+ cli_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,
+ cli->bufsize - PTR_DIFF(p,cli->outbuf), STR_ASCII|STR_TERMINATE);
+ *p++ = 4;
+ p += clistr_push(cli, p, host,
+ cli->bufsize - PTR_DIFF(p,cli->outbuf), STR_ASCII|STR_TERMINATE);
+
+ cli_setup_bcc(cli, p);
+
+ return(PTR_DIFF(p, cli->outbuf));
+}
+
+bool cli_message_start(struct cli_state *cli, const char *host, const char *username,
+ int *grp)
+{
+ cli_message_start_build(cli, host, username);
+ 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
+****************************************************************************/
+
+int cli_message_text_build(struct cli_state *cli, const char *msg, int len, int grp)
+{
+ char *msgdos;
+ size_t lendos;
+ char *p;
+
+ memset(cli->outbuf,'\0',smb_size);
+ cli_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;
+
+ if (!convert_string_allocate(NULL, CH_UNIX, CH_DOS, msg, len,
+ (void **)(void *)&msgdos, &lendos, True) || !msgdos) {
+ DEBUG(3,("Conversion failed, sending message in UNIX charset\n"));
+ SSVAL(p, 0, len); p += 2;
+ if (len > cli->bufsize - PTR_DIFF(p,cli->outbuf)) {
+ return -1;
+ }
+ memcpy(p, msg, len);
+ p += len;
+ } else {
+ SSVAL(p, 0, lendos); p += 2;
+ if (lendos > cli->bufsize - PTR_DIFF(p,cli->outbuf)) {
+ return -1;
+ }
+ memcpy(p, msgdos, lendos);
+ p += lendos;
+ SAFE_FREE(msgdos);
+ }
+
+ cli_setup_bcc(cli, p);
+
+ return(PTR_DIFF(p, cli->outbuf));
+}
+
+bool cli_message_text(struct cli_state *cli, const char *msg, int len, int grp)
+{
+ cli_message_text_build(cli, msg, len, grp);
+
+ cli_send_smb(cli);
+
+ if (!cli_receive_smb(cli)) {
+ return False;
+ }
+
+ if (cli_is_error(cli)) return False;
+
+ return True;
+}
+
+/****************************************************************************
+ End a message.
+****************************************************************************/
+
+int cli_message_end_build(struct cli_state *cli, int grp)
+{
+ char *p;
+
+ memset(cli->outbuf,'\0',smb_size);
+ cli_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);
+
+ p = smb_buf(cli->outbuf);
+
+ return(PTR_DIFF(p, cli->outbuf));
+}
+
+bool cli_message_end(struct cli_state *cli, int grp)
+{
+ cli_message_end_build(cli, grp);
+
+ 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..ef8b396461
--- /dev/null
+++ b/source3/libsmb/clioplock.c
@@ -0,0 +1,66 @@
+/*
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+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;
+ char buf[smb_size+16];
+ bool ret;
+
+ cli->outbuf = buf;
+
+ memset(buf,'\0',smb_size);
+ cli_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..223ddb4186
--- /dev/null
+++ b/source3/libsmb/cliprint.c
@@ -0,0 +1,260 @@
+/*
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/*****************************************************************************
+ 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 const 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;
+ unsigned int rdrcnt, rprcnt;
+ char param[1024];
+ 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;
+ safe_strcpy_base(p,"zWrLeh", param, sizeof(param)); /* parameter description? */
+ p = skip_string(param,sizeof(param),p);
+ safe_strcpy_base(p,"WWzWWDDzz", param, sizeof(param)); /* returned data format */
+ p = skip_string(param,sizeof(param),p);
+ safe_strcpy_base(p,cli->share, param, sizeof(param)); /* name of queue */
+ p = skip_string(param,sizeof(param),p);
+ 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;
+ safe_strcpy_base(p,"", param,sizeof(param)); /* subformat */
+ p = skip_string(param,sizeof(param),p);
+
+ 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 = cli_make_unix_date3(cli, 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;
+ unsigned int rdrcnt,rprcnt;
+ int ret = -1;
+ char param[1024];
+
+ memset(param,'\0',sizeof(param));
+
+ p = param;
+ SSVAL(p,0,81); /* DosPrintJobDel() */
+ p += 2;
+ safe_strcpy_base(p,"W", param,sizeof(param));
+ p = skip_string(param,sizeof(param),p);
+ safe_strcpy_base(p,"", param,sizeof(param));
+ p = skip_string(param,sizeof(param),p);
+ 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;
+}
+
+
+/****************************************************************************
+ Open a spool file
+****************************************************************************/
+
+int cli_spl_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);
+
+ cli_set_message(cli->outbuf,15,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBsplopen);
+ 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_spl_close(struct cli_state *cli, int fnum)
+{
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ cli_set_message(cli->outbuf,3,0,True);
+
+ SCVAL(cli->outbuf,smb_com,SMBsplclose);
+ 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);
+}
diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c
new file mode 100644
index 0000000000..f369d28dff
--- /dev/null
+++ b/source3/libsmb/cliquota.c
@@ -0,0 +1,642 @@
+/*
+ Unix SMB/CIFS implementation.
+ client quota functions
+ Copyright (C) Stefan (metze) Metzmacher 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+bool cli_get_quota_handle(struct cli_state *cli, int *quota_fnum)
+{
+ *quota_fnum = cli_nt_create_full(cli, FAKE_FILE_NAME_QUOTA_WIN32,
+ 0x00000016, DESIRED_ACCESS_PIPE,
+ 0x00000000, FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_OPEN, 0x00000000, 0x03);
+
+ if (*quota_fnum == (-1)) {
+ return False;
+ }
+
+ return True;
+}
+
+void free_ntquota_list(SMB_NTQUOTA_LIST **qt_list)
+{
+ if (!qt_list)
+ return;
+
+ if ((*qt_list)->mem_ctx)
+ talloc_destroy((*qt_list)->mem_ctx);
+
+ (*qt_list) = NULL;
+
+ return;
+}
+
+static bool parse_user_quota_record(const char *rdata, unsigned int rdata_count, unsigned int *offset, SMB_NTQUOTA_STRUCT *pqt)
+{
+ int sid_len;
+ SMB_NTQUOTA_STRUCT qt;
+
+ ZERO_STRUCT(qt);
+
+ if (!rdata||!offset||!pqt) {
+ smb_panic("parse_quota_record: called with NULL POINTER!");
+ }
+
+ if (rdata_count < 40) {
+ return False;
+ }
+
+ /* offset to next quota record.
+ * 4 bytes IVAL(rdata,0)
+ * unused here...
+ */
+ *offset = IVAL(rdata,0);
+
+ /* sid len */
+ sid_len = IVAL(rdata,4);
+
+ if (rdata_count < 40+sid_len) {
+ return False;
+ }
+
+ /* unknown 8 bytes in pdata
+ * maybe its the change time in NTTIME
+ */
+
+ /* the used space 8 bytes (SMB_BIG_UINT)*/
+ qt.usedspace = (SMB_BIG_UINT)IVAL(rdata,16);
+#ifdef LARGE_SMB_OFF_T
+ qt.usedspace |= (((SMB_BIG_UINT)IVAL(rdata,20)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if ((IVAL(rdata,20) != 0)&&
+ ((qt.usedspace != 0xFFFFFFFF)||
+ (IVAL(rdata,20)!=0xFFFFFFFF))) {
+ /* more than 32 bits? */
+ return False;
+ }
+#endif /* LARGE_SMB_OFF_T */
+
+ /* the soft quotas 8 bytes (SMB_BIG_UINT)*/
+ qt.softlim = (SMB_BIG_UINT)IVAL(rdata,24);
+#ifdef LARGE_SMB_OFF_T
+ qt.softlim |= (((SMB_BIG_UINT)IVAL(rdata,28)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if ((IVAL(rdata,28) != 0)&&
+ ((qt.softlim != 0xFFFFFFFF)||
+ (IVAL(rdata,28)!=0xFFFFFFFF))) {
+ /* more than 32 bits? */
+ return False;
+ }
+#endif /* LARGE_SMB_OFF_T */
+
+ /* the hard quotas 8 bytes (SMB_BIG_UINT)*/
+ qt.hardlim = (SMB_BIG_UINT)IVAL(rdata,32);
+#ifdef LARGE_SMB_OFF_T
+ qt.hardlim |= (((SMB_BIG_UINT)IVAL(rdata,36)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if ((IVAL(rdata,36) != 0)&&
+ ((qt.hardlim != 0xFFFFFFFF)||
+ (IVAL(rdata,36)!=0xFFFFFFFF))) {
+ /* more than 32 bits? */
+ return False;
+ }
+#endif /* LARGE_SMB_OFF_T */
+
+ sid_parse(rdata+40,sid_len,&qt.sid);
+
+ qt.qtype = SMB_USER_QUOTA_TYPE;
+
+ *pqt = qt;
+
+ return True;
+}
+
+bool cli_get_user_quota(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_STRUCT *pqt)
+{
+ bool ret = False;
+ uint16 setup;
+ char params[16];
+ unsigned int data_len;
+ char data[SID_MAX_SIZE+8];
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int rparam_count=0, rdata_count=0;
+ unsigned int sid_len;
+ unsigned int offset;
+
+ if (!cli||!pqt) {
+ smb_panic("cli_get_user_quota() called with NULL Pointer!");
+ }
+
+ setup = NT_TRANSACT_GET_USER_QUOTA;
+
+ SSVAL(params, 0,quota_fnum);
+ SSVAL(params, 2,TRANSACT_GET_USER_QUOTA_FOR_SID);
+ SIVAL(params, 4,0x00000024);
+ SIVAL(params, 8,0x00000000);
+ SIVAL(params,12,0x00000024);
+
+ sid_len = ndr_size_dom_sid(&pqt->sid, 0);
+ data_len = sid_len+8;
+ SIVAL(data, 0, 0x00000000);
+ SIVAL(data, 4, sid_len);
+ sid_linearize(data+8, sid_len, &pqt->sid);
+
+ if (!cli_send_nt_trans(cli,
+ NT_TRANSACT_GET_USER_QUOTA,
+ 0,
+ &setup, 1, 0,
+ params, 16, 4,
+ data, data_len, 112)) {
+ DEBUG(1,("Failed to send NT_TRANSACT_GET_USER_QUOTA\n"));
+ goto cleanup;
+ }
+
+
+ if (!cli_receive_nt_trans(cli,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ DEBUG(1,("Failed to recv NT_TRANSACT_GET_USER_QUOTA\n"));
+ goto cleanup;
+ }
+
+ if (cli_is_error(cli)) {
+ ret = False;
+ goto cleanup;
+ } else {
+ ret = True;
+ }
+
+ if ((rparam&&rdata)&&(rparam_count>=4&&rdata_count>=8)) {
+ ret = parse_user_quota_record(rdata, rdata_count, &offset, pqt);
+ } else {
+ DEBUG(0,("Got INVALID NT_TRANSACT_GET_USER_QUOTA reply.\n"));
+ ret = False;
+ }
+
+ cleanup:
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+ return ret;
+}
+
+bool cli_set_user_quota(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_STRUCT *pqt)
+{
+ bool ret = False;
+ uint16 setup;
+ char params[2];
+ char data[112];
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int rparam_count=0, rdata_count=0;
+ unsigned int sid_len;
+ memset(data,'\0',112);
+
+ if (!cli||!pqt) {
+ smb_panic("cli_set_user_quota() called with NULL Pointer!");
+ }
+
+ setup = NT_TRANSACT_SET_USER_QUOTA;
+
+ SSVAL(params,0,quota_fnum);
+
+ sid_len = ndr_size_dom_sid(&pqt->sid, 0);
+ SIVAL(data,0,0);
+ SIVAL(data,4,sid_len);
+ SBIG_UINT(data, 8,(SMB_BIG_UINT)0);
+ SBIG_UINT(data,16,pqt->usedspace);
+ SBIG_UINT(data,24,pqt->softlim);
+ SBIG_UINT(data,32,pqt->hardlim);
+ sid_linearize(data+40, sid_len, &pqt->sid);
+
+ if (!cli_send_nt_trans(cli,
+ NT_TRANSACT_SET_USER_QUOTA,
+ 0,
+ &setup, 1, 0,
+ params, 2, 0,
+ data, 112, 0)) {
+ DEBUG(1,("Failed to send NT_TRANSACT_SET_USER_QUOTA\n"));
+ goto cleanup;
+ }
+
+
+ if (!cli_receive_nt_trans(cli,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ DEBUG(1,("NT_TRANSACT_SET_USER_QUOTA failed\n"));
+ goto cleanup;
+ }
+
+ if (cli_is_error(cli)) {
+ ret = False;
+ goto cleanup;
+ } else {
+ ret = True;
+ }
+
+ cleanup:
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+ return ret;
+}
+
+bool cli_list_user_quota(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_LIST **pqt_list)
+{
+ bool ret = False;
+ uint16 setup;
+ char params[16];
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int rparam_count=0, rdata_count=0;
+ unsigned int offset;
+ const char *curdata = NULL;
+ unsigned int curdata_count = 0;
+ TALLOC_CTX *mem_ctx = NULL;
+ SMB_NTQUOTA_STRUCT qt;
+ SMB_NTQUOTA_LIST *tmp_list_ent;
+
+ if (!cli||!pqt_list) {
+ smb_panic("cli_list_user_quota() called with NULL Pointer!");
+ }
+
+ setup = NT_TRANSACT_GET_USER_QUOTA;
+
+ SSVAL(params, 0,quota_fnum);
+ SSVAL(params, 2,TRANSACT_GET_USER_QUOTA_LIST_START);
+ SIVAL(params, 4,0x00000000);
+ SIVAL(params, 8,0x00000000);
+ SIVAL(params,12,0x00000000);
+
+ if (!cli_send_nt_trans(cli,
+ NT_TRANSACT_GET_USER_QUOTA,
+ 0,
+ &setup, 1, 0,
+ params, 16, 4,
+ NULL, 0, 2048)) {
+ DEBUG(1,("Failed to send NT_TRANSACT_GET_USER_QUOTA\n"));
+ goto cleanup;
+ }
+
+
+ if (!cli_receive_nt_trans(cli,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ DEBUG(1,("Failed to recv NT_TRANSACT_GET_USER_QUOTA\n"));
+ goto cleanup;
+ }
+
+ if (cli_is_error(cli)) {
+ ret = False;
+ goto cleanup;
+ } else {
+ ret = True;
+ }
+
+ if (rdata_count == 0) {
+ *pqt_list = NULL;
+ return True;
+ }
+
+ if ((mem_ctx=talloc_init("SMB_USER_QUOTA_LIST"))==NULL) {
+ DEBUG(0,("talloc_init() failed\n"));
+ return (-1);
+ }
+
+ offset = 1;
+ for (curdata=rdata,curdata_count=rdata_count;
+ ((curdata)&&(curdata_count>=8)&&(offset>0));
+ curdata +=offset,curdata_count -= offset) {
+ ZERO_STRUCT(qt);
+ if (!parse_user_quota_record(curdata, curdata_count, &offset, &qt)) {
+ DEBUG(1,("Failed to parse the quota record\n"));
+ goto cleanup;
+ }
+
+ if ((tmp_list_ent=TALLOC_ZERO_P(mem_ctx,SMB_NTQUOTA_LIST))==NULL) {
+ DEBUG(0,("TALLOC_ZERO() failed\n"));
+ talloc_destroy(mem_ctx);
+ return (-1);
+ }
+
+ if ((tmp_list_ent->quotas=TALLOC_ZERO_P(mem_ctx,SMB_NTQUOTA_STRUCT))==NULL) {
+ DEBUG(0,("TALLOC_ZERO() failed\n"));
+ talloc_destroy(mem_ctx);
+ return (-1);
+ }
+
+ memcpy(tmp_list_ent->quotas,&qt,sizeof(qt));
+ tmp_list_ent->mem_ctx = mem_ctx;
+
+ DLIST_ADD((*pqt_list),tmp_list_ent);
+ }
+
+ SSVAL(params, 2,TRANSACT_GET_USER_QUOTA_LIST_CONTINUE);
+ while(1) {
+ if (!cli_send_nt_trans(cli,
+ NT_TRANSACT_GET_USER_QUOTA,
+ 0,
+ &setup, 1, 0,
+ params, 16, 4,
+ NULL, 0, 2048)) {
+ DEBUG(1,("Failed to send NT_TRANSACT_GET_USER_QUOTA\n"));
+ goto cleanup;
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+ if (!cli_receive_nt_trans(cli,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ DEBUG(1,("Failed to recv NT_TRANSACT_GET_USER_QUOTA\n"));
+ goto cleanup;
+ }
+
+ if (cli_is_error(cli)) {
+ ret = False;
+ goto cleanup;
+ } else {
+ ret = True;
+ }
+
+ if (rdata_count == 0) {
+ break;
+ }
+
+ offset = 1;
+ for (curdata=rdata,curdata_count=rdata_count;
+ ((curdata)&&(curdata_count>=8)&&(offset>0));
+ curdata +=offset,curdata_count -= offset) {
+ ZERO_STRUCT(qt);
+ if (!parse_user_quota_record(curdata, curdata_count, &offset, &qt)) {
+ DEBUG(1,("Failed to parse the quota record\n"));
+ goto cleanup;
+ }
+
+ if ((tmp_list_ent=TALLOC_ZERO_P(mem_ctx,SMB_NTQUOTA_LIST))==NULL) {
+ DEBUG(0,("TALLOC_ZERO() failed\n"));
+ talloc_destroy(mem_ctx);
+ goto cleanup;
+ }
+
+ if ((tmp_list_ent->quotas=TALLOC_ZERO_P(mem_ctx,SMB_NTQUOTA_STRUCT))==NULL) {
+ DEBUG(0,("TALLOC_ZERO() failed\n"));
+ talloc_destroy(mem_ctx);
+ goto cleanup;
+ }
+
+ memcpy(tmp_list_ent->quotas,&qt,sizeof(qt));
+ tmp_list_ent->mem_ctx = mem_ctx;
+
+ DLIST_ADD((*pqt_list),tmp_list_ent);
+ }
+ }
+
+
+ ret = True;
+ cleanup:
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return ret;
+}
+
+bool cli_get_fs_quota_info(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_STRUCT *pqt)
+{
+ bool ret = False;
+ uint16 setup;
+ char param[2];
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int rparam_count=0, rdata_count=0;
+ SMB_NTQUOTA_STRUCT qt;
+ ZERO_STRUCT(qt);
+
+ if (!cli||!pqt) {
+ smb_panic("cli_get_fs_quota_info() called with NULL Pointer!");
+ }
+
+ setup = TRANSACT2_QFSINFO;
+
+ SSVAL(param,0,SMB_FS_QUOTA_INFORMATION);
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL,
+ 0, 0,
+ &setup, 1, 0,
+ param, 2, 0,
+ NULL, 0, 560)) {
+ goto cleanup;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ goto cleanup;
+ }
+
+ if (cli_is_error(cli)) {
+ ret = False;
+ goto cleanup;
+ } else {
+ ret = True;
+ }
+
+ if (rdata_count < 48) {
+ goto cleanup;
+ }
+
+ /* unknown_1 24 NULL bytes in pdata*/
+
+ /* the soft quotas 8 bytes (SMB_BIG_UINT)*/
+ qt.softlim = (SMB_BIG_UINT)IVAL(rdata,24);
+#ifdef LARGE_SMB_OFF_T
+ qt.softlim |= (((SMB_BIG_UINT)IVAL(rdata,28)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if ((IVAL(rdata,28) != 0)&&
+ ((qt.softlim != 0xFFFFFFFF)||
+ (IVAL(rdata,28)!=0xFFFFFFFF))) {
+ /* more than 32 bits? */
+ goto cleanup;
+ }
+#endif /* LARGE_SMB_OFF_T */
+
+ /* the hard quotas 8 bytes (SMB_BIG_UINT)*/
+ qt.hardlim = (SMB_BIG_UINT)IVAL(rdata,32);
+#ifdef LARGE_SMB_OFF_T
+ qt.hardlim |= (((SMB_BIG_UINT)IVAL(rdata,36)) << 32);
+#else /* LARGE_SMB_OFF_T */
+ if ((IVAL(rdata,36) != 0)&&
+ ((qt.hardlim != 0xFFFFFFFF)||
+ (IVAL(rdata,36)!=0xFFFFFFFF))) {
+ /* more than 32 bits? */
+ goto cleanup;
+ }
+#endif /* LARGE_SMB_OFF_T */
+
+ /* quota_flags 2 bytes **/
+ qt.qflags = SVAL(rdata,40);
+
+ qt.qtype = SMB_USER_FS_QUOTA_TYPE;
+
+ *pqt = qt;
+
+ ret = True;
+cleanup:
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return ret;
+}
+
+bool cli_set_fs_quota_info(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_STRUCT *pqt)
+{
+ bool ret = False;
+ uint16 setup;
+ char param[4];
+ char data[48];
+ char *rparam=NULL, *rdata=NULL;
+ unsigned int rparam_count=0, rdata_count=0;
+ SMB_NTQUOTA_STRUCT qt;
+ ZERO_STRUCT(qt);
+ memset(data,'\0',48);
+
+ if (!cli||!pqt) {
+ smb_panic("cli_set_fs_quota_info() called with NULL Pointer!");
+ }
+
+ setup = TRANSACT2_SETFSINFO;
+
+ SSVAL(param,0,quota_fnum);
+ SSVAL(param,2,SMB_FS_QUOTA_INFORMATION);
+
+ /* Unknown1 24 NULL bytes*/
+
+ /* Default Soft Quota 8 bytes */
+ SBIG_UINT(data,24,pqt->softlim);
+
+ /* Default Hard Quota 8 bytes */
+ SBIG_UINT(data,32,pqt->hardlim);
+
+ /* Quota flag 2 bytes */
+ SSVAL(data,40,pqt->qflags);
+
+ /* Unknown3 6 NULL bytes */
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL,
+ 0, 0,
+ &setup, 1, 0,
+ param, 4, 0,
+ data, 48, 0)) {
+ goto cleanup;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &rparam_count,
+ &rdata, &rdata_count)) {
+ goto cleanup;
+ }
+
+ if (cli_is_error(cli)) {
+ ret = False;
+ goto cleanup;
+ } else {
+ ret = True;
+ }
+
+cleanup:
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return ret;
+}
+
+static const char *quota_str_static(SMB_BIG_UINT val, bool special, bool _numeric)
+{
+ const char *result;
+
+ if (!_numeric&&special&&(val == SMB_NTQUOTAS_NO_LIMIT)) {
+ return "NO LIMIT";
+ }
+#if defined(HAVE_LONGLONG)
+ result = talloc_asprintf(talloc_tos(), "%llu", val);
+#else
+ result = talloc_asprintf(talloc_tos(), "%lu", val);
+#endif
+ SMB_ASSERT(result != NULL);
+ return result;
+}
+
+void dump_ntquota(SMB_NTQUOTA_STRUCT *qt, bool _verbose, bool _numeric, void (*_sidtostring)(fstring str, DOM_SID *sid, bool _numeric))
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!qt) {
+ smb_panic("dump_ntquota() called with NULL pointer");
+ }
+
+ switch (qt->qtype) {
+ case SMB_USER_FS_QUOTA_TYPE:
+ {
+ d_printf("File System QUOTAS:\n");
+ d_printf("Limits:\n");
+ d_printf(" Default Soft Limit: %15s\n",quota_str_static(qt->softlim,True,_numeric));
+ d_printf(" Default Hard Limit: %15s\n",quota_str_static(qt->hardlim,True,_numeric));
+ d_printf("Quota Flags:\n");
+ d_printf(" Quotas Enabled: %s\n",
+ ((qt->qflags&QUOTAS_ENABLED)||(qt->qflags&QUOTAS_DENY_DISK))?"On":"Off");
+ d_printf(" Deny Disk: %s\n",(qt->qflags&QUOTAS_DENY_DISK)?"On":"Off");
+ d_printf(" Log Soft Limit: %s\n",(qt->qflags&QUOTAS_LOG_THRESHOLD)?"On":"Off");
+ d_printf(" Log Hard Limit: %s\n",(qt->qflags&QUOTAS_LOG_LIMIT)?"On":"Off");
+ }
+ break;
+ case SMB_USER_QUOTA_TYPE:
+ {
+ fstring username_str = {0};
+
+ if (_sidtostring) {
+ _sidtostring(username_str,&qt->sid,_numeric);
+ } else {
+ sid_to_fstring(username_str, &qt->sid);
+ }
+
+ if (_verbose) {
+ d_printf("Quotas for User: %s\n",username_str);
+ d_printf("Used Space: %15s\n",quota_str_static(qt->usedspace,False,_numeric));
+ d_printf("Soft Limit: %15s\n",quota_str_static(qt->softlim,True,_numeric));
+ d_printf("Hard Limit: %15s\n",quota_str_static(qt->hardlim,True,_numeric));
+ } else {
+ d_printf("%-30s: ",username_str);
+ d_printf("%15s/",quota_str_static(qt->usedspace,False,_numeric));
+ d_printf("%15s/",quota_str_static(qt->softlim,True,_numeric));
+ d_printf("%15s\n",quota_str_static(qt->hardlim,True,_numeric));
+ }
+ }
+ break;
+ default:
+ d_printf("dump_ntquota() invalid qtype(%d)\n",qt->qtype);
+ }
+ TALLOC_FREE(frame);
+ return;
+}
+
+void dump_ntquota_list(SMB_NTQUOTA_LIST **qtl, bool _verbose, bool _numeric, void (*_sidtostring)(fstring str, DOM_SID *sid, bool _numeric))
+{
+ SMB_NTQUOTA_LIST *cur;
+
+ for (cur = *qtl;cur;cur = cur->next) {
+ if (cur->quotas)
+ dump_ntquota(cur->quotas,_verbose,_numeric,_sidtostring);
+ }
+}
diff --git a/source3/libsmb/clirap.c b/source3/libsmb/clirap.c
new file mode 100644
index 0000000000..eee8636fdd
--- /dev/null
+++ b/source3/libsmb/clirap.c
@@ -0,0 +1,1264 @@
+/*
+ Unix SMB/CIFS implementation.
+ client RAP calls
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Gerald (Jerry) Carter 2004
+ Copyright (C) James Peach 2007
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+ Call a remote api on an arbitrary pipe. takes param, data and setup buffers.
+****************************************************************************/
+
+bool cli_api_pipe(struct cli_state *cli, const 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, (unsigned int *)rparam_count,
+ rdata, (unsigned 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, unsigned int *rprcnt,
+ char **rdata, unsigned 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;
+ unsigned int rdrcnt,rprcnt;
+ char param[1024];
+
+ memset(param, 0, sizeof(param));
+
+ /* send a SMBtrans command with api NetWkstaUserLogon */
+ p = param;
+ SSVAL(p,0,132); /* api number */
+ p += 2;
+ strlcpy(p,"OOWb54WrLh",sizeof(param)-PTR_DIFF(p,param));
+ p = skip_string(param,sizeof(param),p);
+ strlcpy(p,"WB21BWDWWDDDDDDDzzzD",sizeof(param)-PTR_DIFF(p,param));
+ p = skip_string(param,sizeof(param),p);
+ SSVAL(p,0,1);
+ p += 2;
+ strlcpy(p,user,sizeof(param)-PTR_DIFF(p,param));
+ strupper_m(p);
+ p += 21;
+ p++;
+ p += 15;
+ p++;
+ strlcpy(p, workstation,sizeof(param)-PTR_DIFF(p,param));
+ strupper_m(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);
+ /* The cli->eff_name field used to be set here
+ but it wasn't used anywhere else. */
+ } 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;
+ unsigned int rdrcnt,rprcnt;
+ char param[1024];
+ int count = -1;
+
+ /* now send a SMBtrans command with api RNetShareEnum */
+ p = param;
+ SSVAL(p,0,0); /* api number */
+ p += 2;
+ strlcpy(p,"WrLeh",sizeof(param)-PTR_DIFF(p,param));
+ p = skip_string(param,sizeof(param),p);
+ strlcpy(p,"B13BWz",sizeof(param)-PTR_DIFF(p,param));
+ p = skip_string(param,sizeof(param),p);
+ 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;
+ char *rdata_end = rdata + rdrcnt;
+
+ count=SVAL(rparam,4);
+ p = rdata;
+
+ for (i=0;i<count;i++,p+=20) {
+ char *sname;
+ int type;
+ int comment_offset;
+ const char *cmnt;
+ const char *p1;
+ char *s1, *s2;
+ size_t len;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (p + 20 > rdata_end) {
+ TALLOC_FREE(frame);
+ break;
+ }
+
+ sname = p;
+ type = SVAL(p,14);
+ comment_offset = (IVAL(p,16) & 0xFFFF) - converter;
+ if (comment_offset < 0 ||
+ comment_offset > (int)rdrcnt) {
+ TALLOC_FREE(frame);
+ break;
+ }
+ cmnt = comment_offset?(rdata+comment_offset):"";
+
+ /* Work out the comment length. */
+ for (p1 = cmnt, len = 0; *p1 &&
+ p1 < rdata_end; len++)
+ p1++;
+ if (!*p1) {
+ len++;
+ }
+ pull_string_talloc(frame,rdata,0,
+ &s1,sname,14,STR_ASCII);
+ pull_string_talloc(frame,rdata,0,
+ &s2,cmnt,len,STR_ASCII);
+ if (!s1 || !s2) {
+ TALLOC_FREE(frame);
+ continue;
+ }
+
+ fn(s1, type, s2, state);
+
+ TALLOC_FREE(frame);
+ }
+ } 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;
+ char *rdata_end = NULL;
+ unsigned int rdrcnt,rprcnt;
+ char *p;
+ char param[1024];
+ int uLevel = 1;
+ size_t len;
+ uint32 func = RAP_NetServerEnum2;
+ char *last_entry = NULL;
+ int total_cnt = 0;
+ int return_cnt = 0;
+ int res;
+
+ errno = 0; /* reset */
+
+ /*
+ * This may take more than one transaction, so we should loop until
+ * we no longer get a more data to process or we have all of the
+ * items.
+ */
+ do {
+ /* send a SMBtrans command with api NetServerEnum */
+ p = param;
+ SIVAL(p,0,func); /* api number */
+ p += 2;
+ /* Next time through we need to use the continue api */
+ func = RAP_NetServerEnum3;
+
+ if (last_entry) {
+ strlcpy(p,"WrLehDOz", sizeof(param)-PTR_DIFF(p,param));
+ } else {
+ strlcpy(p,"WrLehDz", sizeof(param)-PTR_DIFF(p,param));
+ }
+
+ p = skip_string(param, sizeof(param), p);
+ strlcpy(p,"B16BBDz", sizeof(param)-PTR_DIFF(p,param));
+
+ p = skip_string(param, sizeof(param), p);
+ SSVAL(p,0,uLevel);
+ SSVAL(p,2,CLI_BUFFER_SIZE);
+ p += 4;
+ SIVAL(p,0,stype);
+ p += 4;
+
+ /* If we have more data, tell the server where
+ * to continue from.
+ */
+ len = push_ascii(p,
+ last_entry ? last_entry : workgroup,
+ sizeof(param) - PTR_DIFF(p,param) - 1,
+ STR_TERMINATE|STR_UPPER);
+
+ if (len == (size_t)-1) {
+ SAFE_FREE(last_entry);
+ return false;
+ }
+ p += 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 */
+
+ /* break out of the loop on error */
+ res = -1;
+ break;
+ }
+
+ rdata_end = rdata + rdrcnt;
+ res = rparam ? SVAL(rparam,0) : -1;
+
+ if (res == 0 || res == ERRmoredata ||
+ (res != -1 && cli_errno(cli) == 0)) {
+ char *sname = NULL;
+ int i, count;
+ int converter=SVAL(rparam,2);
+
+ /* Get the number of items returned in this buffer */
+ count = SVAL(rparam, 4);
+
+ /* The next field contains the number of items left,
+ * including those returned in this buffer. So the
+ * first time through this should contain all of the
+ * entries.
+ */
+ if (total_cnt == 0) {
+ total_cnt = SVAL(rparam, 6);
+ }
+
+ /* Keep track of how many we have read */
+ return_cnt += count;
+ p = rdata;
+
+ /* The last name in the previous NetServerEnum reply is
+ * sent back to server in the NetServerEnum3 request
+ * (last_entry). The next reply should repeat this entry
+ * as the first element. We have no proof that this is
+ * always true, but from traces that seems to be the
+ * behavior from Window Servers. So first lets do a lot
+ * of checking, just being paranoid. If the string
+ * matches then we already saw this entry so skip it.
+ *
+ * NOTE: sv1_name field must be null terminated and has
+ * a max size of 16 (NetBIOS Name).
+ */
+ if (last_entry && count && p &&
+ (strncmp(last_entry, p, 16) == 0)) {
+ count -= 1; /* Skip this entry */
+ return_cnt = -1; /* Not part of total, so don't count. */
+ p = rdata + 26; /* Skip the whole record */
+ }
+
+ for (i = 0; i < count; i++, p += 26) {
+ int comment_offset;
+ const char *cmnt;
+ const char *p1;
+ char *s1, *s2;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (p + 26 > rdata_end) {
+ TALLOC_FREE(frame);
+ break;
+ }
+
+ sname = p;
+ comment_offset = (IVAL(p,22) & 0xFFFF)-converter;
+ cmnt = comment_offset?(rdata+comment_offset):"";
+
+ if (comment_offset < 0 || comment_offset > (int)rdrcnt) {
+ TALLOC_FREE(frame);
+ continue;
+ }
+
+ /* Work out the comment length. */
+ for (p1 = cmnt, len = 0; *p1 &&
+ p1 < rdata_end; len++)
+ p1++;
+ if (!*p1) {
+ len++;
+ }
+
+ stype = IVAL(p,18) & ~SV_TYPE_LOCAL_LIST_ONLY;
+
+ pull_string_talloc(frame,rdata,0,
+ &s1,sname,16,STR_ASCII);
+ pull_string_talloc(frame,rdata,0,
+ &s2,cmnt,len,STR_ASCII);
+
+ if (!s1 || !s2) {
+ TALLOC_FREE(frame);
+ continue;
+ }
+
+ fn(s1, stype, s2, state);
+ TALLOC_FREE(frame);
+ }
+
+ /* We are done with the old last entry, so now we can free it */
+ if (last_entry) {
+ SAFE_FREE(last_entry); /* This will set it to null */
+ }
+
+ /* We always make a copy of the last entry if we have one */
+ if (sname) {
+ last_entry = smb_xstrdup(sname);
+ }
+
+ /* If we have more data, but no last entry then error out */
+ if (!last_entry && (res == ERRmoredata)) {
+ errno = EINVAL;
+ res = 0;
+ }
+
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+ } while ((res == ERRmoredata) && (total_cnt > return_cnt));
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+ SAFE_FREE(last_entry);
+
+ if (res == -1) {
+ errno = cli_errno(cli);
+ } else {
+ if (!return_cnt) {
+ /* this is a very special case, when the domain master for the
+ work group isn't part of the work group itself, there is something
+ wild going on */
+ errno = ENOENT;
+ }
+ }
+
+ return(return_cnt > 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[1024];
+ unsigned char data[532];
+ char *p = param;
+ unsigned char old_pw_hash[16];
+ unsigned char new_pw_hash[16];
+ unsigned int data_len;
+ unsigned int param_len = 0;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+
+ 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;
+ strlcpy(p, "zsT", sizeof(param)-PTR_DIFF(p,param));
+ p = skip_string(param,sizeof(param),p);
+ strlcpy(p, "B516B16", sizeof(param)-PTR_DIFF(p,param));
+ p = skip_string(param,sizeof(param),p);
+ strlcpy(p,user, sizeof(param)-PTR_DIFF(p,param));
+ p = skip_string(param,sizeof(param),p);
+ 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().
+ */
+ E_deshash(old_password, old_pw_hash);
+
+ encode_pw_buffer(data, new_password, STR_ASCII);
+
+#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);
+
+ /*
+ * Now place the old password hash in the data.
+ */
+ E_deshash(new_password, 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 */
+ (char *)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)) {
+ DEBUG(0,("cli_oem_change_password: Failed to recieve reply to password change for user %s\n",
+ user ));
+ return False;
+ }
+
+ 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 *change_time,
+ time_t *access_time,
+ time_t *write_time,
+ SMB_OFF_T *size,
+ uint16 *mode)
+{
+ unsigned int data_len = 0;
+ unsigned int param_len = 0;
+ unsigned int rparam_len, rdata_len;
+ uint16 setup = TRANSACT2_QPATHINFO;
+ char *param;
+ char *rparam=NULL, *rdata=NULL;
+ int count=8;
+ bool ret;
+ time_t (*date_fn)(struct cli_state *, const void *);
+ char *p;
+ size_t nlen = 2*(strlen(fname)+1);
+
+ param = SMB_MALLOC_ARRAY(char, 6+nlen+2);
+ if (!param) {
+ return false;
+ }
+ p = param;
+ memset(p, '\0', 6);
+ SSVAL(p, 0, SMB_INFO_STANDARD);
+ p += 6;
+ p += clistr_push(cli, p, fname, nlen, 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;
+ smb_msleep(100);
+ }
+ } while (count-- && ret==False);
+
+ SAFE_FREE(param);
+ if (!ret || !rdata || rdata_len < 22) {
+ return False;
+ }
+
+ if (cli->win95) {
+ date_fn = cli_make_unix_date;
+ } else {
+ date_fn = cli_make_unix_date2;
+ }
+
+ if (change_time) {
+ *change_time = date_fn(cli, rdata+0);
+ }
+ if (access_time) {
+ *access_time = date_fn(cli, rdata+4);
+ }
+ if (write_time) {
+ *write_time = date_fn(cli, 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 setpathinfo call.
+****************************************************************************/
+
+bool cli_setpathinfo(struct cli_state *cli, const char *fname,
+ time_t create_time,
+ time_t access_time,
+ time_t write_time,
+ time_t change_time,
+ uint16 mode)
+{
+ unsigned int data_len = 0;
+ unsigned int param_len = 0;
+ unsigned int rparam_len, rdata_len;
+ uint16 setup = TRANSACT2_SETPATHINFO;
+ char *param;
+ char data[40];
+ char *rparam=NULL, *rdata=NULL;
+ int count=8;
+ bool ret;
+ char *p;
+ size_t nlen = 2*(strlen(fname)+1);
+
+ param = SMB_MALLOC_ARRAY(char, 6+nlen+2);
+ if (!param) {
+ return false;
+ }
+ memset(param, '\0', 6);
+ memset(data, 0, sizeof(data));
+
+ p = param;
+
+ /* Add the information level */
+ SSVAL(p, 0, SMB_FILE_BASIC_INFORMATION);
+
+ /* Skip reserved */
+ p += 6;
+
+ /* Add the file name */
+ p += clistr_push(cli, p, fname, nlen, STR_TERMINATE);
+
+ param_len = PTR_DIFF(p, param);
+
+ p = data;
+
+ /*
+ * Add the create, last access, modification, and status change times
+ */
+ put_long_date(p, create_time);
+ p += 8;
+
+ put_long_date(p, access_time);
+ p += 8;
+
+ put_long_date(p, write_time);
+ p += 8;
+
+ put_long_date(p, change_time);
+ p += 8;
+
+ /* Add attributes */
+ SIVAL(p, 0, mode);
+ p += 4;
+
+ /* Add padding */
+ SIVAL(p, 0, 0);
+ p += 4;
+
+ data_len = PTR_DIFF(p, data);
+
+ 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 */
+ data, 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;
+ smb_msleep(100);
+ }
+ } while (count-- && ret==False);
+
+ SAFE_FREE(param);
+ if (!ret) {
+ return False;
+ }
+
+ 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,
+ struct timespec *create_time,
+ struct timespec *access_time,
+ struct timespec *write_time,
+ struct timespec *change_time,
+ SMB_OFF_T *size, uint16 *mode,
+ SMB_INO_T *ino)
+{
+ unsigned int data_len = 0;
+ unsigned int param_len = 0;
+ uint16 setup = TRANSACT2_QPATHINFO;
+ char *param;
+ char *rparam=NULL, *rdata=NULL;
+ char *p;
+ size_t nlen = 2*(strlen(fname)+1);
+
+ param = SMB_MALLOC_ARRAY(char, 6+nlen+2);
+ if (!param) {
+ return false;
+ }
+ p = param;
+ memset(param, '\0', 6);
+ SSVAL(p, 0, SMB_QUERY_FILE_ALL_INFO);
+ p += 6;
+ p += clistr_push(cli, p, fname, nlen, 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 */
+ )) {
+ SAFE_FREE(param);
+ return False;
+ }
+
+ SAFE_FREE(param);
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len)) {
+ return False;
+ }
+
+ if (!rdata || data_len < 22) {
+ return False;
+ }
+
+ if (create_time) {
+ *create_time = interpret_long_date(rdata+0);
+ }
+ if (access_time) {
+ *access_time = interpret_long_date(rdata+8);
+ }
+ if (write_time) {
+ *write_time = interpret_long_date(rdata+16);
+ }
+ if (change_time) {
+ *change_time = interpret_long_date(rdata+24);
+ }
+ if (mode) {
+ *mode = SVAL(rdata, 32);
+ }
+ if (size) {
+ *size = IVAL2_TO_SMB_BIG_UINT(rdata,48);
+ }
+ if (ino) {
+ *ino = IVAL(rdata, 64);
+ }
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ return True;
+}
+
+/****************************************************************************
+ Get the stream info
+****************************************************************************/
+
+bool cli_qpathinfo_streams(struct cli_state *cli, const char *fname,
+ TALLOC_CTX *mem_ctx,
+ unsigned int *pnum_streams,
+ struct stream_struct **pstreams)
+{
+ unsigned int data_len = 0;
+ unsigned int param_len = 0;
+ uint16 setup = TRANSACT2_QPATHINFO;
+ char *param;
+ char *rparam=NULL, *rdata=NULL;
+ char *p;
+ unsigned int num_streams;
+ struct stream_struct *streams;
+ unsigned int ofs;
+ size_t namelen = 2*(strlen(fname)+1);
+
+ param = SMB_MALLOC_ARRAY(char, 6+namelen+2);
+ if (param == NULL) {
+ return false;
+ }
+ p = param;
+ memset(p, 0, 6);
+ SSVAL(p, 0, SMB_FILE_STREAM_INFORMATION);
+ p += 6;
+ p += clistr_push(cli, p, fname, namelen, STR_TERMINATE);
+
+ param_len = PTR_DIFF(p, param);
+
+ if (!cli_send_trans(cli, SMBtrans2,
+ NULL, /* name */
+ -1, 0, /* fid, flags */
+ &setup, 1, 0, /* setup, len, max */
+ param, param_len, 10, /* param, len, max */
+ NULL, data_len, cli->max_xmit /* data, len, max */
+ )) {
+ return false;
+ }
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len)) {
+ return false;
+ }
+
+ if (!rdata) {
+ SAFE_FREE(rparam);
+ return false;
+ }
+
+ num_streams = 0;
+ streams = NULL;
+ ofs = 0;
+
+ while ((data_len > ofs) && (data_len - ofs >= 24)) {
+ uint32_t nlen, len;
+ size_t size;
+ void *vstr;
+ struct stream_struct *tmp;
+ uint8_t *tmp_buf;
+
+ tmp = TALLOC_REALLOC_ARRAY(mem_ctx, streams,
+ struct stream_struct,
+ num_streams+1);
+
+ if (tmp == NULL) {
+ goto fail;
+ }
+ streams = tmp;
+
+ nlen = IVAL(rdata, ofs + 0x04);
+
+ streams[num_streams].size = IVAL_TO_SMB_OFF_T(
+ rdata, ofs + 0x08);
+ streams[num_streams].alloc_size = IVAL_TO_SMB_OFF_T(
+ rdata, ofs + 0x10);
+
+ if (nlen > data_len - (ofs + 24)) {
+ goto fail;
+ }
+
+ /*
+ * We need to null-terminate src, how do I do this with
+ * convert_string_talloc??
+ */
+
+ tmp_buf = TALLOC_ARRAY(streams, uint8_t, nlen+2);
+ if (tmp_buf == NULL) {
+ goto fail;
+ }
+
+ memcpy(tmp_buf, rdata+ofs+24, nlen);
+ tmp_buf[nlen] = 0;
+ tmp_buf[nlen+1] = 0;
+
+ if (!convert_string_talloc(streams, CH_UTF16, CH_UNIX, tmp_buf,
+ nlen+2, &vstr, &size, false))
+ {
+ TALLOC_FREE(tmp_buf);
+ goto fail;
+ }
+
+ TALLOC_FREE(tmp_buf);
+ streams[num_streams].name = (char *)vstr;
+ num_streams++;
+
+ len = IVAL(rdata, ofs);
+ if (len > data_len - ofs) {
+ goto fail;
+ }
+ if (len == 0) break;
+ ofs += len;
+ }
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+
+ *pnum_streams = num_streams;
+ *pstreams = streams;
+ return true;
+
+ fail:
+ TALLOC_FREE(streams);
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ return false;
+}
+
+/****************************************************************************
+ Send a qfileinfo QUERY_FILE_NAME_INFO call.
+****************************************************************************/
+
+bool cli_qfilename(struct cli_state *cli, int fnum, char *name, size_t namelen)
+{
+ unsigned int data_len = 0;
+ unsigned int param_len = 0;
+ uint16 setup = TRANSACT2_QFILEINFO;
+ char param[4];
+ char *rparam=NULL, *rdata=NULL;
+
+ param_len = 4;
+ 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, &param_len,
+ &rdata, &data_len)) {
+ return False;
+ }
+
+ if (!rdata || data_len < 4) {
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+ return False;
+ }
+
+ clistr_pull(cli, name, rdata+4, namelen, IVAL(rdata, 0), STR_UNICODE);
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return True;
+}
+
+/****************************************************************************
+ Send a qfileinfo call.
+****************************************************************************/
+
+bool cli_qfileinfo(struct cli_state *cli, int fnum,
+ uint16 *mode, SMB_OFF_T *size,
+ struct timespec *create_time,
+ struct timespec *access_time,
+ struct timespec *write_time,
+ struct timespec *change_time,
+ SMB_INO_T *ino)
+{
+ unsigned int data_len = 0;
+ unsigned int param_len = 0;
+ uint16 setup;
+ uint8_t param[4];
+ uint8_t *rparam=NULL, *rdata=NULL;
+ NTSTATUS status;
+
+ /* if its a win95 server then fail this - win95 totally screws it
+ up */
+ if (cli->win95) return False;
+
+ param_len = 4;
+
+ SSVAL(param, 0, fnum);
+ SSVAL(param, 2, SMB_QUERY_FILE_ALL_INFO);
+
+ SSVAL(&setup, 0, TRANSACT2_QFILEINFO);
+
+ status = cli_trans(talloc_tos(), cli, SMBtrans2,
+ NULL, -1, 0, 0, /* name, fid, function, flags */
+ &setup, 1, 0, /* setup, length, max */
+ param, param_len, 2, /* param, length, max */
+ NULL, 0, MIN(cli->max_xmit, 0xffff), /* data, length, max */
+ NULL, NULL, /* rsetup, length */
+ &rparam, &param_len, /* rparam, length */
+ &rdata, &data_len);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ if (!rdata || data_len < 68) {
+ return False;
+ }
+
+ if (create_time) {
+ *create_time = interpret_long_date((char *)rdata+0);
+ }
+ if (access_time) {
+ *access_time = interpret_long_date((char *)rdata+8);
+ }
+ if (write_time) {
+ *write_time = interpret_long_date((char *)rdata+16);
+ }
+ if (change_time) {
+ *change_time = interpret_long_date((char *)rdata+24);
+ }
+ if (mode) {
+ *mode = SVAL(rdata, 32);
+ }
+ if (size) {
+ *size = IVAL2_TO_SMB_BIG_UINT(rdata,48);
+ }
+ if (ino) {
+ *ino = IVAL(rdata, 64);
+ }
+
+ TALLOC_FREE(rdata);
+ TALLOC_FREE(rparam);
+ return True;
+}
+
+/****************************************************************************
+ Send a qpathinfo BASIC_INFO call.
+****************************************************************************/
+
+bool cli_qpathinfo_basic( struct cli_state *cli, const char *name,
+ SMB_STRUCT_STAT *sbuf, uint32 *attributes )
+{
+ unsigned int param_len = 0;
+ unsigned int data_len = 0;
+ uint16 setup = TRANSACT2_QPATHINFO;
+ char *param;
+ char *rparam=NULL, *rdata=NULL;
+ char *p;
+ char *path;
+ int len;
+ size_t nlen;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ path = talloc_strdup(frame, name);
+ if (!path) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+ /* cleanup */
+
+ len = strlen(path);
+ if ( path[len-1] == '\\' || path[len-1] == '/') {
+ path[len-1] = '\0';
+ }
+ nlen = 2*(strlen(path)+1);
+
+ param = TALLOC_ARRAY(frame,char,6+nlen+2);
+ if (!param) {
+ return false;
+ }
+ p = param;
+ memset(param, '\0', 6);
+
+ SSVAL(p, 0, SMB_QUERY_FILE_BASIC_INFO);
+ p += 6;
+ p += clistr_push(cli, p, path, nlen, 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, 2, /* param, length, max */
+ NULL, 0, cli->max_xmit /* data, length, max */
+ )) {
+ TALLOC_FREE(frame);
+ return False;
+ }
+
+ TALLOC_FREE(frame);
+
+ if (!cli_receive_trans(cli, SMBtrans2,
+ &rparam, &param_len,
+ &rdata, &data_len)) {
+ return False;
+ }
+
+ if (data_len < 36) {
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ return False;
+ }
+
+ set_atimespec(sbuf, interpret_long_date( rdata+8 )); /* Access time. */
+ set_mtimespec(sbuf, interpret_long_date( rdata+16 )); /* Write time. */
+ set_ctimespec(sbuf, interpret_long_date( rdata+24 )); /* Change time. */
+
+ *attributes = IVAL( rdata, 32 );
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return True;
+}
+
+/****************************************************************************
+ Send a qfileinfo call.
+****************************************************************************/
+
+bool cli_qfileinfo_test(struct cli_state *cli, int fnum, int level, char **poutdata, uint32 *poutlen)
+{
+ unsigned int data_len = 0;
+ unsigned int param_len = 0;
+ uint16 setup = TRANSACT2_QFILEINFO;
+ char param[4];
+ char *rparam=NULL, *rdata=NULL;
+
+ *poutdata = NULL;
+ *poutlen = 0;
+
+ /* if its a win95 server then fail this - win95 totally screws it
+ up */
+ if (cli->win95)
+ return False;
+
+ param_len = 4;
+
+ 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, &param_len,
+ &rdata, &data_len)) {
+ return False;
+ }
+
+ *poutdata = (char *)memdup(rdata, data_len);
+ if (!*poutdata) {
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ return False;
+ }
+
+ *poutlen = 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)
+{
+ unsigned int data_len = 0;
+ unsigned int param_len = 0;
+ uint16 setup = TRANSACT2_QPATHINFO;
+ char *param;
+ char *rparam=NULL, *rdata=NULL;
+ int count=8;
+ char *p;
+ bool ret;
+ unsigned int len;
+ size_t nlen = 2*(strlen(fname)+1);
+
+ param = SMB_MALLOC_ARRAY(char, 6+nlen+2);
+ if (!param) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ p = param;
+ memset(param, '\0', 6);
+ SSVAL(p, 0, SMB_QUERY_FILE_ALT_NAME_INFO);
+ p += 6;
+ p += clistr_push(cli, p, fname, nlen, 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, &param_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;
+ smb_msleep(100);
+ }
+ } while (count-- && ret==False);
+
+ SAFE_FREE(param);
+
+ 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, STR_UNICODE);
+
+ 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..a15fa5f7d8
--- /dev/null
+++ b/source3/libsmb/clirap2.c
@@ -0,0 +1,2675 @@
+/*
+ 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)
+ Copyright (C) 2007 Jeremy Allison. jra@samba.org
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*****************************************************/
+/* */
+/* 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) */
+/* */
+/*****************************************************/
+
+#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,endp) \
+ do {\
+ if (p+1 < endp) {\
+ b = CVAL(p,0);\
+ }\
+ p++;\
+ } while(0)
+
+#define PUTWORD(p,w) do {SSVAL(p,0,w); p += WORDSIZE;} while(0)
+
+#define GETWORD(p,w,endp) \
+ do {\
+ if (p+WORDSIZE < endp) {\
+ 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,endp) \
+ do {\
+ if (p+DWORDSIZE < endp) {\
+ d = IVAL(p,0);\
+ }\
+ p += DWORDSIZE;\
+ } while(0)
+
+#define GETRES(p,endp) ((p && p+2 < endp) ? 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 = push_skip_string(p);\
+ } 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 dest from src, return increment past string */
+
+static size_t rap_getstring(TALLOC_CTX *ctx, char *src, char **dest, const char *endp)
+{
+ char *p1;
+ size_t len;
+
+ *dest = NULL;
+ for (p1 = src, len = 0; *p1 && p1 < endp; len++)
+ p1++;
+ if (!*p1) {
+ len++;
+ }
+ pull_string_talloc(ctx,src,0,dest,src,len,STR_ASCII);
+ return len;
+}
+
+/* get fixed length l string dest from src, return increment for src */
+
+static size_t rap_getstringf(char *src, char *dest, size_t l, size_t dlen, char *endp)
+{
+ char *p1;
+ size_t len;
+
+ if (dlen) {
+ dest[0] = '\0';
+ }
+ for (p1 = src, len = 0; *p1 && p1 < endp; len++) {
+ p1++;
+ }
+ if (!*p1) {
+ len++;
+ }
+ if (len > l) {
+ len = l;
+ }
+ if (len) {
+ pull_ascii(dest,src,len,len,STR_ASCII);
+ }
+ return l;
+}
+
+/* get string dest from offset (obtained at p) from rdata r - converter c */
+static size_t rap_getstringp(TALLOC_CTX *ctx, char *p, char **dest, char *r, uint16_t c, char *endp)
+{
+ uint32_t off = 0;
+ const char *src;
+ size_t len=0;
+
+ *dest = NULL;
+ if (p+4 < endp) {
+ GETDWORD(p,off,endp);
+ off &= 0x0000FFFF; /* mask the obsolete segment number from the offset */
+ off -= c;
+ }
+ if (r+off > endp || r+off < r) {
+ src="";
+ len=1;
+ } else {
+ const char *p1;
+ src=r+off;
+ for (p1 = src, len = 0; *p1 && p1 < endp; len++) {
+ p1++;
+ }
+ if (!*p1) {
+ len++;
+ }
+ }
+ pull_string_talloc(ctx,src,0,dest,src,len,STR_ASCII);
+ return len;
+}
+
+static char *make_header(char *param, uint16 apinum, const char *reqfmt, const 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;
+ unsigned int rdrcnt,rprcnt;
+ int res = -1;
+ 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 */
+ {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam,endp);
+
+ 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;
+ unsigned int rdrcnt,rprcnt;
+ int res = -1;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetGroupAdd_REQ) /* req string */
+ +sizeof(RAP_GROUP_INFO_L1) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* reserved word */
+
+ /* 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;
+ char *data;
+ size_t data_size;
+
+ /* Allocate data. */
+ data_size = MAX(soffset + strlen(grinfo->comment) + 1, 1024);
+
+ data = SMB_MALLOC_ARRAY(char, data_size);
+ if (!data) {
+ DEBUG (1, ("Malloc fail\n"));
+ return -1;
+ }
+
+ /* 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 */
+ {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+
+ 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(data);
+ 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;
+ unsigned 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)) {
+ char *endp = rparam + rdrcnt;
+
+ res = GETRES(rparam, endp);
+ 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) {
+ DEBUG(4,("NetGroupEnum no data returned\n"));
+ goto out;
+ }
+
+ if (res == 0 || res == ERRmoredata) {
+ char *endp = rparam + rprcnt;
+ int i, converter = 0, count = 0;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ p = rparam + WORDSIZE; /* skip result */
+ GETWORD(p, converter, endp);
+ GETWORD(p, count, endp);
+
+ endp = rdata + rdrcnt;
+ for (i=0,p=rdata; i<count && p < endp;i++) {
+ char *comment = NULL;
+ char groupname[RAP_GROUPNAME_LEN];
+
+ p += rap_getstringf(p,
+ groupname,
+ RAP_GROUPNAME_LEN,
+ RAP_GROUPNAME_LEN,
+ endp);
+ p++; /* pad byte */
+ p += rap_getstringp(frame,
+ p,
+ &comment,
+ rdata,
+ converter,
+ endp);
+
+ if (!comment || !groupname[0]) {
+ break;
+ }
+
+ fn(groupname, comment, cli);
+ }
+ TALLOC_FREE(frame);
+ } else {
+ DEBUG(4,("NetGroupEnum res=%d\n", res));
+ }
+
+ out:
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+int cli_RNetGroupEnum0(struct cli_state *cli,
+ void (*fn)(const char *, void *),
+ void *state)
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetGroupEnum_REQ) /* parm string */
+ +sizeof(RAP_GROUP_INFO_L0) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ int res = -1;
+
+ memset(param, '\0', sizeof(param));
+ p = make_header(param, RAP_WGroupEnum,
+ RAP_NetGroupEnum_REQ, RAP_GROUP_INFO_L0);
+ PUTWORD(p,0); /* Info level 0 */ /* Hmmm. I *very* much suspect this
+ is the resume count, at least
+ that's what smbd believes... */
+ 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)) {
+ char *endp = rparam+rprcnt;
+ res = GETRES(rparam,endp);
+ 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) {
+ DEBUG(4,("NetGroupEnum no data returned\n"));
+ goto out;
+ }
+
+ if (res == 0 || res == ERRmoredata) {
+ char *endp = rparam + rprcnt;
+ int i, count = 0;
+
+ p = rparam + WORDSIZE + WORDSIZE; /* skip result and converter */
+ GETWORD(p, count, endp);
+
+ endp = rdata + rdrcnt;
+ for (i=0,p=rdata; i<count && p < endp;i++) {
+ char groupname[RAP_GROUPNAME_LEN];
+
+ p += rap_getstringf(p,
+ groupname,
+ RAP_GROUPNAME_LEN,
+ RAP_GROUPNAME_LEN,
+ endp);
+ if (groupname[0]) {
+ fn(groupname, cli);
+ }
+ }
+ } else {
+ DEBUG(4,("NetGroupEnum res=%d\n", res));
+ }
+
+ out:
+
+ 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;
+ unsigned int rdrcnt,rprcnt;
+ int res = -1;
+ 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 */
+ {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam,endp);
+
+ 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;
+ unsigned int rdrcnt,rprcnt;
+ int res = -1;
+ 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 */
+ {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam,endp);
+
+ 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;
+ unsigned 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)) {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam,endp);
+ cli->rap_error = res;
+ if (res != 0) {
+ DEBUG(1,("NetGroupGetUsers gave error %d\n", res));
+ }
+ }
+
+ if (!rdata) {
+ DEBUG(4,("NetGroupGetUsers no data returned\n"));
+ goto out;
+ }
+
+ if (res == 0 || res == ERRmoredata) {
+ char *endp = rparam + rprcnt;
+ int i, count = 0;
+ char username[RAP_USERNAME_LEN];
+
+ p = rparam + WORDSIZE + WORDSIZE;
+ GETWORD(p, count, endp);
+
+ endp = rdata + rdrcnt;
+ for (i=0,p=rdata; i<count && p < endp; i++) {
+ p += rap_getstringf(p,
+ username,
+ RAP_USERNAME_LEN,
+ RAP_USERNAME_LEN,
+ endp);
+ if (username[0]) {
+ fn(username, state);
+ }
+ }
+ } else {
+ DEBUG(4,("NetGroupGetUsers res=%d\n", res));
+ }
+
+ out:
+
+ 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;
+ unsigned 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)) {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam,endp);
+ cli->rap_error = res;
+ if (res != 0) {
+ DEBUG(1,("NetUserGetGroups gave error %d\n", res));
+ }
+ }
+
+ if (!rdata) {
+ DEBUG(4,("NetUserGetGroups no data returned\n"));
+ goto out;
+ }
+
+ if (res == 0 || res == ERRmoredata) {
+ char *endp = rparam + rprcnt;
+ int i, count = 0;
+ char groupname[RAP_GROUPNAME_LEN];
+
+ p = rparam + WORDSIZE + WORDSIZE;
+ GETWORD(p, count, endp);
+
+ endp = rdata + rdrcnt;
+ for (i=0,p=rdata; i<count && p < endp; i++) {
+ p += rap_getstringf(p,
+ groupname,
+ RAP_GROUPNAME_LEN,
+ RAP_GROUPNAME_LEN,
+ endp);
+ if (groupname[0]) {
+ fn(groupname, state);
+ }
+ }
+ } else {
+ DEBUG(4,("NetUserGetGroups res=%d\n", res));
+ }
+
+ out:
+
+ 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;
+ unsigned int rdrcnt,rprcnt;
+ int res = -1;
+ 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 */
+ {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam,endp);
+
+ 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;
+ unsigned int rdrcnt,rprcnt;
+ int res = -1;
+ 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 */
+ {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+
+ 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;
+ unsigned 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)) {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam,endp);
+ cli->rap_error = res;
+ if (cli->rap_error != 0) {
+ DEBUG(1,("NetUserEnum gave error %d\n", cli->rap_error));
+ }
+ }
+
+ if (!rdata) {
+ DEBUG(4,("NetUserEnum no data returned\n"));
+ goto out;
+ }
+
+ if (res == 0 || res == ERRmoredata) {
+ int i, converter = 0, count = 0;
+ char username[RAP_USERNAME_LEN];
+ char userpw[RAP_UPASSWD_LEN];
+ char *endp = rparam + rprcnt;
+ char *comment, *homedir, *logonscript;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ p = rparam + WORDSIZE; /* skip result */
+ GETWORD(p, converter, endp);
+ GETWORD(p, count, endp);
+
+ endp = rdata + rdrcnt;
+ for (i=0,p=rdata;i<count && p < endp;i++) {
+ p += rap_getstringf(p,
+ username,
+ RAP_USERNAME_LEN,
+ RAP_USERNAME_LEN,
+ endp);
+ p++; /* pad byte */
+ p += rap_getstringf(p,
+ userpw,
+ RAP_UPASSWD_LEN,
+ RAP_UPASSWD_LEN,
+ endp);
+ p += DWORDSIZE; /* skip password age */
+ p += WORDSIZE; /* skip priv: 0=guest, 1=user, 2=admin */
+ p += rap_getstringp(frame,
+ p,
+ &homedir,
+ rdata,
+ converter,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &comment,
+ rdata,
+ converter,
+ endp);
+ p += WORDSIZE; /* skip flags */
+ p += rap_getstringp(frame,
+ p,
+ &logonscript,
+ rdata,
+ converter,
+ endp);
+ if (username[0] && comment &&
+ homedir && logonscript) {
+ fn(username,
+ comment,
+ homedir,
+ logonscript,
+ cli);
+ }
+ }
+ TALLOC_FREE(frame);
+ } else {
+ DEBUG(4,("NetUserEnum res=%d\n", res));
+ }
+
+ out:
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+int cli_RNetUserEnum0(struct cli_state *cli,
+ void (*fn)(const char *, void *),
+ void *state)
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetUserEnum_REQ) /* parm string */
+ +sizeof(RAP_USER_INFO_L0) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ int res = -1;
+
+ memset(param, '\0', sizeof(param));
+ p = make_header(param, RAP_WUserEnum,
+ RAP_NetUserEnum_REQ, RAP_USER_INFO_L0);
+ PUTWORD(p,0); /* 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)) {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam,endp);
+ cli->rap_error = res;
+ if (cli->rap_error != 0) {
+ DEBUG(1,("NetUserEnum gave error %d\n", cli->rap_error));
+ }
+ }
+
+ if (!rdata) {
+ DEBUG(4,("NetUserEnum no data returned\n"));
+ goto out;
+ }
+
+ if (res == 0 || res == ERRmoredata) {
+ int i, count = 0;
+ char *endp = rparam + rprcnt;
+ char username[RAP_USERNAME_LEN];
+
+ p = rparam + WORDSIZE + WORDSIZE; /* skip result and converter */
+ GETWORD(p, count, endp);
+
+ endp = rdata + rdrcnt;
+ for (i=0,p=rdata;i<count && p < endp;i++) {
+ p += rap_getstringf(p,
+ username,
+ RAP_USERNAME_LEN,
+ RAP_USERNAME_LEN,
+ endp);
+ if (username[0]) {
+ fn(username, cli);
+ }
+ }
+ } else {
+ DEBUG(4,("NetUserEnum res=%d\n", res));
+ }
+
+ out:
+
+ 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;
+ unsigned 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 */
+ {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+
+ 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;
+ unsigned int rdrcnt,rprcnt;
+ int res = -1;
+ 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 */
+ {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam,endp);
+ if (res == 0 || res == ERRmoredata) {
+ TALLOC_CTX *frame = talloc_stackframe();
+ int converter = 0,id = 0, perms = 0, locks = 0;
+ char *fpath, *fuser;
+
+ p = rparam + WORDSIZE; /* skip result */
+ GETWORD(p, converter, endp);
+
+ p = rdata;
+ endp = rdata + rdrcnt;
+
+ GETDWORD(p, id, endp);
+ GETWORD(p, perms, endp);
+ GETWORD(p, locks, endp);
+
+ p += rap_getstringp(frame,
+ p,
+ &fpath,
+ rdata,
+ converter,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &fuser,
+ rdata,
+ converter,
+ endp);
+
+ if (fpath && fuser) {
+ fn(fpath, fuser, perms, locks, id);
+ }
+
+ TALLOC_FREE(frame);
+ } 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, const char * user,
+ const char * base_path,
+ void (*fn)(const char *, const char *, uint16, uint16,
+ uint32))
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_WFileEnum2_REQ) /* req string */
+ +sizeof(RAP_FILE_INFO_L3) /* return string */
+ +1024 /* base path (opt) */
+ +RAP_USERNAME_LEN /* user name (opt) */
+ +WORDSIZE /* info level */
+ +WORDSIZE /* buffer size */
+ +DWORDSIZE /* resume key ? */
+ +DWORDSIZE]; /* resume key ? */
+ int count = -1;
+ int res = -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, 1024);
+ 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 */
+ {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+
+ if (res == 0 || res == ERRmoredata) {
+ TALLOC_CTX *frame = talloc_stackframe();
+ int converter = 0, i;
+
+ p = rparam + WORDSIZE; /* skip result */
+ GETWORD(p, converter, endp);
+ GETWORD(p, count, endp);
+
+ p = rdata;
+ endp = rdata + rdrcnt;
+ for (i=0; i<count && p < endp; i++) {
+ int id = 0, perms = 0, locks = 0;
+ char *fpath, *fuser;
+
+ GETDWORD(p, id, endp);
+ GETWORD(p, perms, endp);
+ GETWORD(p, locks, endp);
+ p += rap_getstringp(frame,
+ p,
+ &fpath,
+ rdata,
+ converter,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &fuser,
+ rdata,
+ converter,
+ endp);
+
+ if (fpath && fuser) {
+ fn(fpath, fuser, perms, locks, id);
+ }
+ } /* BB fix ERRmoredata case to send resume request */
+ TALLOC_FREE(frame);
+ } 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;
+ unsigned int rdrcnt,rprcnt;
+ int res = -1;
+ 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 */
+ {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+
+ if (res == 0) {
+ /* nothing to do */
+ } else {
+ DEBUG(4,("NetShareAdd res=%d\n", res));
+ }
+ } else {
+ 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;
+ unsigned int rdrcnt,rprcnt;
+ int res = -1;
+ 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 */
+ {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+
+ if (res == 0) {
+ /* nothing to do */
+ } else {
+ DEBUG(4,("NetShareDelete res=%d\n", res));
+ }
+ } else {
+ 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, const char *workgroup, char **pdc_name)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned 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;
+ int res = -1;
+
+ *pdc_name = NULL;
+
+ /* 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 */
+ )) {
+
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+ cli->rap_error = res;
+
+ /*
+ * 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, endp);
+ p = rdata;
+ endp = rdata + rdrcnt;
+
+ if (count > 0) {
+ TALLOC_CTX *frame = talloc_stackframe();
+ char *dcname;
+ p += rap_getstring(frame,
+ p,
+ &dcname,
+ endp);
+ if (dcname) {
+ *pdc_name = SMB_STRDUP(dcname);
+ }
+ TALLOC_FREE(frame);
+ }
+ } 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;
+ unsigned 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 */
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+
+ if (res == 0) {
+ TALLOC_CTX *frame = talloc_stackframe();
+ char *server_domain;
+ int converter = 0;
+
+ p = rparam + WORDSIZE;
+ GETWORD(p, converter, endp);
+
+ p = rdata + DWORDSIZE + DWORDSIZE; /* skip computer & user names */
+ endp = rdata + rdrcnt;
+ p += rap_getstringp(frame,
+ p,
+ &server_domain,
+ rdata,
+ converter,
+ endp);
+
+ if (server_domain) {
+ fstrcpy(cli->server_domain, server_domain);
+ }
+ TALLOC_FREE(frame);
+ }
+ }
+
+ 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;
+ unsigned 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 */
+ )) {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam,endp);
+
+ if (res == 0 || res == ERRmoredata) {
+ p = rdata;
+ endp = rparam + rprcnt;
+ p += 18;
+ GETDWORD(p,*pstype,endp);
+ *pstype &= ~SV_TYPE_LOCAL_LIST_ONLY;
+ }
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return(res == 0 || res == ERRmoredata);
+}
+
+bool cli_get_server_name(TALLOC_CTX *mem_ctx, struct cli_state *cli,
+ char **servername)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned 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 */
+ bool res = false;
+ char *endp;
+ fstring tmp;
+
+ /* 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 */
+ )) {
+ goto failed;
+ }
+
+ endp = rparam + rprcnt;
+ if (GETRES(rparam, endp) != 0) {
+ goto failed;
+ }
+
+ if (rdrcnt < 16) {
+ DEBUG(10, ("invalid data count %d, expected >= 16\n", rdrcnt));
+ goto failed;
+ }
+
+ if (pull_ascii(tmp, rdata, sizeof(tmp)-1, 16, STR_TERMINATE) == -1) {
+ DEBUG(10, ("pull_ascii failed\n"));
+ goto failed;
+ }
+
+ if (!(*servername = talloc_strdup(mem_ctx, tmp))) {
+ DEBUG(1, ("talloc_strdup failed\n"));
+ goto failed;
+ }
+
+ res = true;
+
+ failed:
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+ return res;
+}
+
+/*************************************************************************
+*
+* 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;
+ unsigned 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 */
+ )) {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam,endp);
+ cli->rap_error = res;
+
+ if (res == 0 || res == ERRmoredata) {
+ int i, count = 0;
+
+ p = rparam + WORDSIZE + WORDSIZE;
+ GETWORD(p, count,endp);
+
+ p = rdata;
+ endp = rdata + rdrcnt;
+ for (i = 0;i < count && p < endp;i++, p += 16) {
+ char ret_server[RAP_MACHNAME_LEN];
+
+ p += rap_getstringf(p,
+ ret_server,
+ RAP_MACHNAME_LEN,
+ RAP_MACHNAME_LEN,
+ endp);
+ 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, const char *user, const char *workstation)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned 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? */
+ char upperbuf[MAX(RAP_USERNAME_LEN,RAP_MACHNAME_LEN)];
+ int res = -1;
+ char *tmp = NULL;
+
+ 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 */
+ strlcpy(upperbuf, user, sizeof(upperbuf));
+ strupper_m(upperbuf);
+ tmp = upperbuf;
+ PUTSTRINGF(p, tmp, RAP_USERNAME_LEN);
+ p++; /* strange format, but ok */
+ strlcpy(upperbuf, workstation, sizeof(upperbuf));
+ strupper_m(upperbuf);
+ tmp = upperbuf;
+ PUTSTRINGF(p, tmp, 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 */
+ )) {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam,endp);
+ cli->rap_error = res;
+
+ 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;
+ unsigned 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)) {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+ cli->rap_error = res;
+ if (res != 0) {
+ DEBUG(1,("NetPrintQEnum gave error %d\n", res));
+ }
+ }
+
+ if (!rdata) {
+ DEBUG(4,("NetPrintQEnum no data returned\n"));
+ goto out;
+ }
+
+ if (res == 0 || res == ERRmoredata) {
+ TALLOC_CTX *frame = talloc_stackframe();
+ char *endp = rparam + rprcnt;
+ int i, converter = 0, count = 0;
+
+ p = rparam + WORDSIZE;
+ GETWORD(p, converter, endp);
+ GETWORD(p, count, endp);
+
+ p = rdata;
+ endp = rdata + rdrcnt;
+ for (i=0;i<count && p < endp;i++) {
+ char qname[RAP_SHARENAME_LEN];
+ char *sep_file, *print_proc, *dest, *parms, *comment;
+ uint16_t jobcount = 0, priority = 0;
+ uint16_t start_time = 0, until_time = 0, status = 0;
+
+ p += rap_getstringf(p,
+ qname,
+ RAP_SHARENAME_LEN,
+ RAP_SHARENAME_LEN,
+ endp);
+ p++; /* pad */
+ GETWORD(p, priority, endp);
+ GETWORD(p, start_time, endp);
+ GETWORD(p, until_time, endp);
+ p += rap_getstringp(frame,
+ p,
+ &sep_file,
+ rdata,
+ converter,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &print_proc,
+ rdata,
+ converter,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &dest,
+ rdata,
+ converter,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &parms,
+ rdata,
+ converter,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &comment,
+ rdata,
+ converter,
+ endp);
+ GETWORD(p, status, endp);
+ GETWORD(p, jobcount, endp);
+
+ if (sep_file && print_proc && dest && parms &&
+ comment) {
+ 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 = 0, pos = 0, fsstatus = 0;
+ char ownername[RAP_USERNAME_LEN];
+ char notifyname[RAP_MACHNAME_LEN];
+ char datatype[RAP_DATATYPE_LEN];
+ char *jparms, *jstatus, *jcomment;
+ unsigned int submitted = 0, jsize = 0;
+
+ GETWORD(p, jid, endp);
+ p += rap_getstringf(p,
+ ownername,
+ RAP_USERNAME_LEN,
+ RAP_USERNAME_LEN,
+ endp);
+ p++; /* pad byte */
+ p += rap_getstringf(p,
+ notifyname,
+ RAP_MACHNAME_LEN,
+ RAP_MACHNAME_LEN,
+ endp);
+ p += rap_getstringf(p,
+ datatype,
+ RAP_DATATYPE_LEN,
+ RAP_DATATYPE_LEN,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &jparms,
+ rdata,
+ converter,
+ endp);
+ GETWORD(p, pos, endp);
+ GETWORD(p, fsstatus, endp);
+ p += rap_getstringp(frame,
+ p,
+ &jstatus,
+ rdata,
+ converter,
+ endp);
+ GETDWORD(p, submitted, endp);
+ GETDWORD(p, jsize, endp);
+ p += rap_getstringp(frame,
+ p,
+ &jcomment,
+ rdata,
+ converter,
+ endp);
+
+ if (jparms && jstatus && jcomment) {
+ jfn(jid, ownername, notifyname, datatype, jparms, pos, fsstatus,
+ jstatus, submitted, jsize, jcomment);
+ }
+ }
+ }
+ }
+ TALLOC_FREE(frame);
+ } else {
+ DEBUG(4,("NetPrintQEnum res=%d\n", res));
+ }
+
+ out:
+
+ 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;
+ unsigned 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)) {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+ cli->rap_error = res;
+ if (res != 0) {
+ DEBUG(1,("NetPrintQGetInfo gave error %d\n", res));
+ }
+ }
+
+ if (!rdata) {
+ DEBUG(4,("NetPrintQGetInfo no data returned\n"));
+ goto out;
+ }
+
+ if (res == 0 || res == ERRmoredata) {
+ TALLOC_CTX *frame = talloc_stackframe();
+ char *endp = rparam + rprcnt;
+ int rsize = 0, converter = 0;
+ char qname[RAP_SHARENAME_LEN];
+ char *sep_file, *print_proc, *dest, *parms, *comment;
+ uint16_t jobcount = 0, priority = 0;
+ uint16_t start_time = 0, until_time = 0, status = 0;
+
+ p = rparam + WORDSIZE;
+ GETWORD(p, converter, endp);
+ GETWORD(p, rsize, endp);
+
+ p = rdata;
+ endp = rdata + rdrcnt;
+ p += rap_getstringf(p,
+ qname,
+ RAP_SHARENAME_LEN,
+ RAP_SHARENAME_LEN,
+ endp);
+ p++; /* pad */
+ GETWORD(p, priority, endp);
+ GETWORD(p, start_time, endp);
+ GETWORD(p, until_time, endp);
+ p += rap_getstringp(frame,
+ p,
+ &sep_file,
+ rdata,
+ converter,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &print_proc,
+ rdata,
+ converter,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &dest,
+ rdata,
+ converter,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &parms,
+ rdata,
+ converter,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &comment,
+ rdata,
+ converter,
+ endp);
+ GETWORD(p, status, endp);
+ GETWORD(p, jobcount, endp);
+
+ if (sep_file && print_proc && dest &&
+ parms && comment) {
+ 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)&&
+ p<endp;j++) {
+ uint16_t jid = 0, pos = 0, fsstatus = 0;
+ char ownername[RAP_USERNAME_LEN];
+ char notifyname[RAP_MACHNAME_LEN];
+ char datatype[RAP_DATATYPE_LEN];
+ char *jparms, *jstatus, *jcomment;
+ unsigned int submitted = 0, jsize = 0;
+
+ GETWORD(p, jid, endp);
+ p += rap_getstringf(p,
+ ownername,
+ RAP_USERNAME_LEN,
+ RAP_USERNAME_LEN,
+ endp);
+ p++; /* pad byte */
+ p += rap_getstringf(p,
+ notifyname,
+ RAP_MACHNAME_LEN,
+ RAP_MACHNAME_LEN,
+ endp);
+ p += rap_getstringf(p,
+ datatype,
+ RAP_DATATYPE_LEN,
+ RAP_DATATYPE_LEN,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &jparms,
+ rdata,
+ converter,
+ endp);
+ GETWORD(p, pos,endp);
+ GETWORD(p, fsstatus,endp);
+ p += rap_getstringp(frame,
+ p,
+ &jstatus,
+ rdata,
+ converter,
+ endp);
+ GETDWORD(p, submitted,endp);
+ GETDWORD(p, jsize,endp);
+ p += rap_getstringp(frame,
+ p,
+ &jcomment,
+ rdata,
+ converter,
+ endp);
+
+ if (jparms && jstatus && jcomment) {
+ jfn(jid, ownername, notifyname, datatype, jparms, pos, fsstatus,
+ jstatus, submitted, jsize, jcomment);
+ }
+ }
+ }
+ TALLOC_FREE(frame);
+ } else {
+ DEBUG(4,("NetPrintQGetInfo res=%d\n", res));
+ }
+
+ out:
+
+ 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;
+ unsigned 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)) {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+ 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) {
+ DEBUG(4,("NetServiceEnum no data returned\n"));
+ goto out;
+ }
+
+ if (res == 0 || res == ERRmoredata) {
+ char *endp = rparam + rprcnt;
+ int i, count = 0;
+
+ p = rparam + WORDSIZE + WORDSIZE; /* skip result and converter */
+ GETWORD(p, count,endp);
+
+ endp = rdata + rdrcnt;
+ for (i=0,p=rdata;i<count && p < endp;i++) {
+ char comment[RAP_SRVCCMNT_LEN];
+ char servicename[RAP_SRVCNAME_LEN];
+
+ p += rap_getstringf(p,
+ servicename,
+ RAP_SRVCNAME_LEN,
+ RAP_SRVCNAME_LEN,
+ endp);
+ p+=8; /* pass status words */
+ p += rap_getstringf(p,
+ comment,
+ RAP_SRVCCMNT_LEN,
+ RAP_SRVCCMNT_LEN,
+ endp);
+
+ if (servicename[0]) {
+ fn(servicename, comment, cli); /* BB add status too */
+ }
+ }
+ } else {
+ DEBUG(4,("NetServiceEnum res=%d\n", res));
+ }
+
+ out:
+
+ 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;
+ unsigned 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)) {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+ cli->rap_error = res;
+ if (res != 0) {
+ DEBUG(1,("NetSessionEnum gave error %d\n", res));
+ }
+ }
+
+ if (!rdata) {
+ DEBUG(4,("NetSesssionEnum no data returned\n"));
+ goto out;
+ }
+
+ if (res == 0 || res == ERRmoredata) {
+ TALLOC_CTX *frame = talloc_stackframe();
+ char *endp = rparam + rprcnt;
+ int i, converter = 0, count = 0;
+
+ p = rparam + WORDSIZE;
+ GETWORD(p, converter, endp);
+ GETWORD(p, count, endp);
+
+ endp = rdata + rdrcnt;
+ for (i=0,p=rdata;i<count && p < endp;i++) {
+ char *wsname, *username, *clitype_name;
+ uint16_t num_conns = 0, num_opens = 0, num_users = 0;
+ unsigned int sess_time = 0, idle_time = 0, user_flags = 0;
+
+ p += rap_getstringp(frame,
+ p,
+ &wsname,
+ rdata,
+ converter,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &username,
+ rdata,
+ converter,
+ endp);
+ GETWORD(p, num_conns, endp);
+ GETWORD(p, num_opens, endp);
+ GETWORD(p, num_users, endp);
+ GETDWORD(p, sess_time, endp);
+ GETDWORD(p, idle_time, endp);
+ GETDWORD(p, user_flags, endp);
+ p += rap_getstringp(frame,
+ p,
+ &clitype_name,
+ rdata,
+ converter,
+ endp);
+
+ if (wsname && username && clitype_name) {
+ fn(wsname, username, num_conns, num_opens, num_users, sess_time,
+ idle_time, user_flags, clitype_name);
+ }
+ }
+ TALLOC_FREE(frame);
+ } else {
+ DEBUG(4,("NetSessionEnum res=%d\n", res));
+ }
+
+ out:
+
+ 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;
+ unsigned int rprcnt, rdrcnt;
+ char *endp;
+ 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)) {
+ endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+ cli->rap_error = res;
+ if (cli->rap_error != 0) {
+ DEBUG(1,("NetSessionGetInfo gave error %d\n", cli->rap_error));
+ }
+ }
+
+ if (!rdata) {
+ DEBUG(4,("NetSessionGetInfo no data returned\n"));
+ goto out;
+ }
+
+ endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+
+ if (res == 0 || res == ERRmoredata) {
+ TALLOC_CTX *frame = talloc_stackframe();
+ int converter = 0;
+ char *wsname, *username, *clitype_name;
+ uint16_t num_conns = 0, num_opens = 0, num_users = 0;
+ unsigned int sess_time = 0, idle_time = 0, user_flags = 0;
+
+ p = rparam + WORDSIZE;
+ GETWORD(p, converter,endp);
+ p += WORDSIZE; /* skip rsize */
+
+ p = rdata;
+ endp = rdata + rdrcnt;
+ p += rap_getstringp(frame,
+ p,
+ &wsname,
+ rdata,
+ converter,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &username,
+ rdata,
+ converter,
+ endp);
+ GETWORD(p, num_conns, endp);
+ GETWORD(p, num_opens, endp);
+ GETWORD(p, num_users, endp);
+ GETDWORD(p, sess_time, endp);
+ GETDWORD(p, idle_time, endp);
+ GETDWORD(p, user_flags, endp);
+ p += rap_getstringp(frame,
+ p,
+ &clitype_name,
+ rdata,
+ converter,
+ endp);
+
+ if (wsname && username && clitype_name) {
+ fn(wsname, username, num_conns, num_opens, num_users, sess_time,
+ idle_time, user_flags, clitype_name);
+ }
+ TALLOC_FREE(frame);
+ } else {
+ DEBUG(4,("NetSessionGetInfo res=%d\n", res));
+ }
+
+ out:
+
+ 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;
+ unsigned int rprcnt, rdrcnt;
+ int res = -1;
+
+ 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 */
+ {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+ 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_t conid, uint16_t contype,
+ uint16_t numopens, uint16_t numusers,
+ uint32_t 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;
+ unsigned 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)) {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+ cli->rap_error = res;
+ if (res != 0) {
+ DEBUG(1,("NetConnectionEnum gave error %d\n", res));
+ }
+ }
+
+ if (!rdata) {
+ DEBUG(4,("NetConnectionEnum no data returned\n"));
+ goto out;
+ }
+
+ if (res == 0 || res == ERRmoredata) {
+ TALLOC_CTX *frame = talloc_stackframe();
+ char *endp = rparam + rprcnt;
+ int i, converter = 0, count = 0;
+
+ p = rparam + WORDSIZE;
+ GETWORD(p, converter, endp);
+ GETWORD(p, count, endp);
+
+ endp = rdata + rdrcnt;
+ for (i=0,p=rdata;i<count && p < endp;i++) {
+ char *netname, *username;
+ uint16_t conn_id = 0, conn_type = 0, num_opens = 0, num_users = 0;
+ unsigned int conn_time = 0;
+
+ GETWORD(p,conn_id, endp);
+ GETWORD(p,conn_type, endp);
+ GETWORD(p,num_opens, endp);
+ GETWORD(p,num_users, endp);
+ GETDWORD(p,conn_time, endp);
+ p += rap_getstringp(frame,
+ p,
+ &username,
+ rdata,
+ converter,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &netname,
+ rdata,
+ converter,
+ endp);
+
+ if (username && netname) {
+ fn(conn_id, conn_type, num_opens, num_users, conn_time,
+ username, netname);
+ }
+ }
+ TALLOC_FREE(frame);
+ } else {
+ DEBUG(4,("NetConnectionEnum res=%d\n", res));
+ }
+
+ out:
+
+ 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..ec63281630
--- /dev/null
+++ b/source3/libsmb/clireadwrite.c
@@ -0,0 +1,717 @@
+/*
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+ Calculate the recommended read buffer size
+****************************************************************************/
+static size_t cli_read_max_bufsize(struct cli_state *cli)
+{
+ if (!client_is_signing_on(cli) && !cli_encryption_on(cli)
+ && (cli->posix_capabilities & CIFS_UNIX_LARGE_READ_CAP)) {
+ return CLI_SAMBA_MAX_POSIX_LARGE_READX_SIZE;
+ }
+ if (cli->capabilities & CAP_LARGE_READX) {
+ return cli->is_samba
+ ? CLI_SAMBA_MAX_LARGE_READX_SIZE
+ : CLI_WINDOWS_MAX_LARGE_READX_SIZE;
+ }
+ return (cli->max_xmit - (smb_size+32)) & ~1023;
+}
+
+/*
+ * Send a read&x request
+ */
+
+struct async_req *cli_read_andx_send(TALLOC_CTX *mem_ctx,
+ struct event_context *ev,
+ struct cli_state *cli, int fnum,
+ off_t offset, size_t size)
+{
+ struct async_req *result;
+ struct cli_request *req;
+ bool bigoffset = False;
+
+ uint16_t vwv[12];
+ uint8_t wct = 10;
+
+ if (size > cli_read_max_bufsize(cli)) {
+ DEBUG(0, ("cli_read_andx_send got size=%d, can only handle "
+ "size=%d\n", (int)size,
+ (int)cli_read_max_bufsize(cli)));
+ return NULL;
+ }
+
+ SCVAL(vwv + 0, 0, 0xFF);
+ SCVAL(vwv + 0, 1, 0);
+ SSVAL(vwv + 1, 0, 0);
+ SSVAL(vwv + 2, 0, fnum);
+ SIVAL(vwv + 3, 0, offset);
+ SSVAL(vwv + 5, 0, size);
+ SSVAL(vwv + 6, 0, size);
+ SSVAL(vwv + 7, 0, (size >> 16));
+ SSVAL(vwv + 8, 0, 0);
+ SSVAL(vwv + 9, 0, 0);
+
+ if ((SMB_BIG_UINT)offset >> 32) {
+ bigoffset = True;
+ SIVAL(vwv + 10, 0,
+ (((SMB_BIG_UINT)offset)>>32) & 0xffffffff);
+ wct += 2;
+ }
+
+ result = cli_request_send(mem_ctx, ev, cli, SMBreadX, 0, wct, vwv,
+ 0, NULL);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ req = talloc_get_type_abort(result->private_data, struct cli_request);
+
+ req->data.read.ofs = offset;
+ req->data.read.size = size;
+ req->data.read.received = 0;
+ req->data.read.rcvbuf = NULL;
+
+ return result;
+}
+
+/*
+ * Pull the data out of a finished async read_and_x request. rcvbuf is
+ * talloced from the request, so better make sure that you copy it away before
+ * you talloc_free(req). "rcvbuf" is NOT a talloc_ctx of its own, so do not
+ * talloc_move it!
+ */
+
+NTSTATUS cli_read_andx_recv(struct async_req *req, ssize_t *received,
+ uint8_t **rcvbuf)
+{
+ struct cli_request *cli_req = talloc_get_type_abort(
+ req->private_data, struct cli_request);
+ uint8_t wct;
+ uint16_t *vwv;
+ uint16_t num_bytes;
+ uint8_t *bytes;
+ NTSTATUS status;
+ size_t size;
+
+ SMB_ASSERT(req->state >= ASYNC_REQ_DONE);
+ if (req->state == ASYNC_REQ_ERROR) {
+ return req->status;
+ }
+
+ status = cli_pull_reply(req, &wct, &vwv, &num_bytes, &bytes);
+
+ if (NT_STATUS_IS_ERR(status)) {
+ return status;
+ }
+
+ if (wct < 12) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ /* size is the number of bytes the server returned.
+ * Might be zero. */
+ size = SVAL(vwv + 5, 0);
+ size |= (((unsigned int)SVAL(vwv + 7, 0)) << 16);
+
+ if (size > cli_req->data.read.size) {
+ DEBUG(5,("server returned more than we wanted!\n"));
+ return NT_STATUS_UNEXPECTED_IO_ERROR;
+ }
+
+ *rcvbuf = (uint8_t *)(smb_base(cli_req->inbuf) + SVAL(vwv + 6, 0));
+ *received = size;
+ return NT_STATUS_OK;
+}
+
+/*
+ * Parallel read support.
+ *
+ * cli_pull sends as many read&x requests as the server would allow via
+ * max_mux at a time. When replies flow back in, the data is written into
+ * the callback function "sink" in the right order.
+ */
+
+struct cli_pull_state {
+ struct async_req *req;
+
+ struct event_context *ev;
+ struct cli_state *cli;
+ uint16_t fnum;
+ off_t start_offset;
+ SMB_OFF_T size;
+
+ NTSTATUS (*sink)(char *buf, size_t n, void *priv);
+ void *priv;
+
+ size_t chunk_size;
+
+ /*
+ * Outstanding requests
+ */
+ int num_reqs;
+ struct async_req **reqs;
+
+ /*
+ * For how many bytes did we send requests already?
+ */
+ SMB_OFF_T requested;
+
+ /*
+ * Next request index to push into "sink". This walks around the "req"
+ * array, taking care that the requests are pushed to "sink" in the
+ * right order. If necessary (i.e. replies don't come in in the right
+ * order), replies are held back in "reqs".
+ */
+ int top_req;
+
+ /*
+ * How many bytes did we push into "sink"?
+ */
+
+ SMB_OFF_T pushed;
+};
+
+static char *cli_pull_print(TALLOC_CTX *mem_ctx, struct async_req *req)
+{
+ struct cli_pull_state *state = talloc_get_type_abort(
+ req->private_data, struct cli_pull_state);
+ char *result;
+
+ result = async_req_print(mem_ctx, req);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ return talloc_asprintf_append_buffer(
+ result, "num_reqs=%d, top_req=%d",
+ state->num_reqs, state->top_req);
+}
+
+static void cli_pull_read_done(struct async_req *read_req);
+
+/*
+ * Prepare an async pull request
+ */
+
+struct async_req *cli_pull_send(TALLOC_CTX *mem_ctx,
+ struct event_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum, off_t start_offset,
+ SMB_OFF_T size, size_t window_size,
+ NTSTATUS (*sink)(char *buf, size_t n,
+ void *priv),
+ void *priv)
+{
+ struct async_req *result;
+ struct cli_pull_state *state;
+ int i;
+
+ result = async_req_new(mem_ctx, ev);
+ if (result == NULL) {
+ goto failed;
+ }
+ state = talloc(result, struct cli_pull_state);
+ if (state == NULL) {
+ goto failed;
+ }
+ result->private_data = state;
+ result->print = cli_pull_print;
+ state->req = result;
+
+ state->cli = cli;
+ state->ev = ev;
+ state->fnum = fnum;
+ state->start_offset = start_offset;
+ state->size = size;
+ state->sink = sink;
+ state->priv = priv;
+
+ state->pushed = 0;
+ state->top_req = 0;
+
+ if (size == 0) {
+ if (!async_post_status(result, NT_STATUS_OK)) {
+ goto failed;
+ }
+ return result;
+ }
+
+ state->chunk_size = cli_read_max_bufsize(cli);
+
+ state->num_reqs = MAX(window_size/state->chunk_size, 1);
+ state->num_reqs = MIN(state->num_reqs, cli->max_mux);
+
+ state->reqs = TALLOC_ZERO_ARRAY(state, struct async_req *,
+ state->num_reqs);
+ if (state->reqs == NULL) {
+ goto failed;
+ }
+
+ state->requested = 0;
+
+ for (i=0; i<state->num_reqs; i++) {
+ SMB_OFF_T size_left;
+ size_t request_thistime;
+
+ if (state->requested >= size) {
+ state->num_reqs = i;
+ break;
+ }
+
+ size_left = size - state->requested;
+ request_thistime = MIN(size_left, state->chunk_size);
+
+ state->reqs[i] = cli_read_andx_send(
+ state->reqs, ev, cli, fnum,
+ state->start_offset + state->requested,
+ request_thistime);
+
+ if (state->reqs[i] == NULL) {
+ goto failed;
+ }
+
+ state->reqs[i]->async.fn = cli_pull_read_done;
+ state->reqs[i]->async.priv = result;
+
+ state->requested += request_thistime;
+ }
+ return result;
+
+failed:
+ TALLOC_FREE(result);
+ return NULL;
+}
+
+/*
+ * Handle incoming read replies, push the data into sink and send out new
+ * requests if necessary.
+ */
+
+static void cli_pull_read_done(struct async_req *read_req)
+{
+ struct async_req *pull_req = talloc_get_type_abort(
+ read_req->async.priv, struct async_req);
+ struct cli_pull_state *state = talloc_get_type_abort(
+ pull_req->private_data, struct cli_pull_state);
+ struct cli_request *read_state = talloc_get_type_abort(
+ read_req->private_data, struct cli_request);
+ NTSTATUS status;
+
+ status = cli_read_andx_recv(read_req, &read_state->data.read.received,
+ &read_state->data.read.rcvbuf);
+ if (!NT_STATUS_IS_OK(status)) {
+ async_req_error(state->req, status);
+ return;
+ }
+
+ /*
+ * This loop is the one to take care of out-of-order replies. All
+ * pending requests are in state->reqs, state->reqs[top_req] is the
+ * one that is to be pushed next. If however a request later than
+ * top_req is replied to, then we can't push yet. If top_req is
+ * replied to at a later point then, we need to push all the finished
+ * requests.
+ */
+
+ while (state->reqs[state->top_req] != NULL) {
+ struct cli_request *top_read;
+
+ DEBUG(11, ("cli_pull_read_done: top_req = %d\n",
+ state->top_req));
+
+ if (state->reqs[state->top_req]->state < ASYNC_REQ_DONE) {
+ DEBUG(11, ("cli_pull_read_done: top request not yet "
+ "done\n"));
+ return;
+ }
+
+ top_read = talloc_get_type_abort(
+ state->reqs[state->top_req]->private_data,
+ struct cli_request);
+
+ DEBUG(10, ("cli_pull_read_done: Pushing %d bytes, %d already "
+ "pushed\n", (int)top_read->data.read.received,
+ (int)state->pushed));
+
+ status = state->sink((char *)top_read->data.read.rcvbuf,
+ top_read->data.read.received,
+ state->priv);
+ if (!NT_STATUS_IS_OK(status)) {
+ async_req_error(state->req, status);
+ return;
+ }
+ state->pushed += top_read->data.read.received;
+
+ TALLOC_FREE(state->reqs[state->top_req]);
+
+ if (state->requested < state->size) {
+ struct async_req *new_req;
+ SMB_OFF_T size_left;
+ size_t request_thistime;
+
+ size_left = state->size - state->requested;
+ request_thistime = MIN(size_left, state->chunk_size);
+
+ DEBUG(10, ("cli_pull_read_done: Requesting %d bytes "
+ "at %d, position %d\n",
+ (int)request_thistime,
+ (int)(state->start_offset
+ + state->requested),
+ state->top_req));
+
+ new_req = cli_read_andx_send(
+ state->reqs, state->ev, state->cli,
+ state->fnum,
+ state->start_offset + state->requested,
+ request_thistime);
+
+ if (async_req_nomem(new_req, state->req)) {
+ return;
+ }
+
+ new_req->async.fn = cli_pull_read_done;
+ new_req->async.priv = pull_req;
+
+ state->reqs[state->top_req] = new_req;
+ state->requested += request_thistime;
+ }
+
+ state->top_req = (state->top_req+1) % state->num_reqs;
+ }
+
+ async_req_done(pull_req);
+}
+
+NTSTATUS cli_pull_recv(struct async_req *req, SMB_OFF_T *received)
+{
+ struct cli_pull_state *state = talloc_get_type_abort(
+ req->private_data, struct cli_pull_state);
+
+ SMB_ASSERT(req->state >= ASYNC_REQ_DONE);
+ if (req->state == ASYNC_REQ_ERROR) {
+ return req->status;
+ }
+ *received = state->pushed;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_pull(struct cli_state *cli, uint16_t fnum,
+ off_t start_offset, SMB_OFF_T size, size_t window_size,
+ NTSTATUS (*sink)(char *buf, size_t n, void *priv),
+ void *priv, SMB_OFF_T *received)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct event_context *ev;
+ struct async_req *req;
+ NTSTATUS result = NT_STATUS_NO_MEMORY;
+
+ if (cli->fd_event != NULL) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ev = event_context_init(frame);
+ if (ev == NULL) {
+ goto nomem;
+ }
+
+ req = cli_pull_send(frame, ev, cli, fnum, start_offset, size,
+ window_size, sink, priv);
+ if (req == NULL) {
+ goto nomem;
+ }
+
+ while (req->state < ASYNC_REQ_DONE) {
+ event_loop_once(ev);
+ }
+
+ result = cli_pull_recv(req, received);
+ nomem:
+ TALLOC_FREE(frame);
+ return result;
+}
+
+static NTSTATUS cli_read_sink(char *buf, size_t n, void *priv)
+{
+ char **pbuf = (char **)priv;
+ memcpy(*pbuf, buf, n);
+ *pbuf += n;
+ return NT_STATUS_OK;
+}
+
+ssize_t cli_read(struct cli_state *cli, int fnum, char *buf,
+ off_t offset, size_t size)
+{
+ NTSTATUS status;
+ SMB_OFF_T ret;
+
+ status = cli_pull(cli, fnum, offset, size, size,
+ cli_read_sink, &buf, &ret);
+ if (!NT_STATUS_IS_OK(status)) {
+ cli_set_error(cli, status);
+ return -1;
+ }
+ return ret;
+}
+
+/****************************************************************************
+ 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,
+ const char *buf,
+ size_t size,
+ int i)
+{
+ char *p;
+ bool large_writex = false;
+ /* We can only do direct writes if not signing and not encrypting. */
+ bool direct_writes = !client_is_signing_on(cli) && !cli_encryption_on(cli);
+
+ if (!direct_writes && size + 1 > cli->bufsize) {
+ cli->outbuf = (char *)SMB_REALLOC(cli->outbuf, size + 1024);
+ if (!cli->outbuf) {
+ return False;
+ }
+ cli->inbuf = (char *)SMB_REALLOC(cli->inbuf, size + 1024);
+ if (cli->inbuf == NULL) {
+ SAFE_FREE(cli->outbuf);
+ return False;
+ }
+ cli->bufsize = size + 1024;
+ }
+
+ memset(cli->outbuf,'\0',smb_size);
+ memset(cli->inbuf,'\0',smb_size);
+
+ if (cli->capabilities & CAP_LARGE_FILES) {
+ large_writex = True;
+ }
+
+ if (large_writex) {
+ cli_set_message(cli->outbuf,14,0,True);
+ } else {
+ cli_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,0);
+ SSVAL(cli->outbuf,smb_vwv7,mode);
+
+ SSVAL(cli->outbuf,smb_vwv8,(mode & 0x0008) ? size : 0);
+ /*
+ * According to CIFS-TR-1p00, this following field should only
+ * be set if CAP_LARGE_WRITEX is set. We should check this
+ * locally. However, this check might already have been
+ * done by our callers.
+ */
+ SSVAL(cli->outbuf,smb_vwv9,(size>>16));
+ SSVAL(cli->outbuf,smb_vwv10,size);
+ /* +1 is pad byte. */
+ SSVAL(cli->outbuf,smb_vwv11,
+ smb_buf(cli->outbuf) - smb_base(cli->outbuf) + 1);
+
+ if (large_writex) {
+ SIVAL(cli->outbuf,smb_vwv12,(((SMB_BIG_UINT)offset)>>32) & 0xffffffff);
+ }
+
+ p = smb_base(cli->outbuf) + SVAL(cli->outbuf,smb_vwv11) -1;
+ *p++ = '\0'; /* pad byte. */
+ if (!direct_writes) {
+ memcpy(p, buf, size);
+ }
+ if (size > 0x1FFFF) {
+ /* This is a POSIX 14 word large write. */
+ set_message_bcc(cli->outbuf, 0); /* Set bcc to zero. */
+ _smb_setlen_large(cli->outbuf,smb_size + 28 + 1 /* pad */ + size - 4);
+ } else {
+ cli_setup_bcc(cli, p+size);
+ }
+
+ SSVAL(cli->outbuf,smb_mid,cli->mid + i);
+
+ show_msg(cli->outbuf);
+ if (direct_writes) {
+ /* For direct writes we now need to write the data
+ * directly out of buf. */
+ return cli_send_smb_direct_writeX(cli, buf, size);
+ } else {
+ 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,
+ const char *buf, off_t offset, size_t size)
+{
+ ssize_t bwritten = 0;
+ unsigned int issued = 0;
+ unsigned int received = 0;
+ int mpx = 1;
+ size_t writesize;
+ int blocks;
+
+ if(cli->max_mux > 1) {
+ mpx = cli->max_mux-1;
+ } else {
+ mpx = 1;
+ }
+
+ /* Default (small) writesize. */
+ writesize = (cli->max_xmit - (smb_size+32)) & ~1023;
+
+ if (write_mode == 0 &&
+ !client_is_signing_on(cli) &&
+ !cli_encryption_on(cli) &&
+ (cli->posix_capabilities & CIFS_UNIX_LARGE_WRITE_CAP) &&
+ (cli->capabilities & CAP_LARGE_FILES)) {
+ /* Only do massive writes if we can do them direct
+ * with no signing or encrypting - not on a pipe. */
+ writesize = CLI_SAMBA_MAX_POSIX_LARGE_WRITEX_SIZE;
+ } else if ((cli->capabilities & CAP_LARGE_WRITEX) &&
+ (strcmp(cli->dev, "LPT1:") != 0)) {
+
+ /* Printer devices are restricted to max_xmit
+ * writesize in Vista and XPSP3. */
+
+ if (cli->is_samba) {
+ writesize = CLI_SAMBA_MAX_LARGE_WRITEX_SIZE;
+ } else if (!client_is_signing_on(cli)) {
+ /* Windows restricts signed writes to max_xmit.
+ * Found by Volker. */
+ writesize = CLI_WINDOWS_MAX_LARGE_WRITEX_SIZE;
+ }
+ }
+
+ blocks = (size + (writesize-1)) / writesize;
+
+ while (received < blocks) {
+
+ while ((issued - received < mpx) && (issued < blocks)) {
+ ssize_t bsent = issued * writesize;
+ ssize_t size1 = MIN(writesize, 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);
+ if (writesize > 0xFFFF) {
+ 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);
+
+ cli_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 + total, 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..f0b786c899
--- /dev/null
+++ b/source3/libsmb/clisecdesc.c
@@ -0,0 +1,130 @@
+/*
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+ query the security descriptor for a open file
+ ****************************************************************************/
+SEC_DESC *cli_query_secdesc(struct cli_state *cli, int fnum,
+ TALLOC_CTX *mem_ctx)
+{
+ uint8_t param[8];
+ uint8_t *rparam=NULL, *rdata=NULL;
+ unsigned int rparam_count=0, rdata_count=0;
+ SEC_DESC *psd = NULL;
+ NTSTATUS status;
+
+ SIVAL(param, 0, fnum);
+ SIVAL(param, 4, 0x7);
+
+ status = cli_trans(talloc_tos(), cli, SMBnttrans,
+ NULL, -1, /* name, fid */
+ NT_TRANSACT_QUERY_SECURITY_DESC, 0, /* function, flags */
+ NULL, 0, 0, /* setup, length, max */
+ param, 8, 4, /* param, length, max */
+ NULL, 0, 0x10000, /* data, length, max */
+ NULL, NULL, /* rsetup, length */
+ &rparam, &rparam_count,
+ &rdata, &rdata_count);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("NT_TRANSACT_QUERY_SECURITY_DESC failed: %s\n",
+ nt_errstr(status)));
+ goto cleanup;
+ }
+
+ status = unmarshall_sec_desc(mem_ctx, (uint8 *)rdata, rdata_count,
+ &psd);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("unmarshall_sec_desc failed: %s\n",
+ nt_errstr(status)));
+ goto cleanup;
+ }
+
+ cleanup:
+
+ TALLOC_FREE(rparam);
+ TALLOC_FREE(rdata);
+
+ 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;
+ unsigned int rparam_count=0, rdata_count=0;
+ uint32 sec_info = 0;
+ TALLOC_CTX *frame = talloc_stackframe();
+ bool ret = False;
+ uint8 *data;
+ size_t len;
+ NTSTATUS status;
+
+ status = marshall_sec_desc(talloc_tos(), sd, &data, &len);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("marshall_sec_desc failed: %s\n",
+ nt_errstr(status)));
+ goto cleanup;
+ }
+
+ SIVAL(param, 0, fnum);
+
+ if (sd->dacl)
+ sec_info |= DACL_SECURITY_INFORMATION;
+ if (sd->owner_sid)
+ sec_info |= OWNER_SECURITY_INFORMATION;
+ if (sd->group_sid)
+ sec_info |= GROUP_SECURITY_INFORMATION;
+ SSVAL(param, 4, sec_info);
+
+ if (!cli_send_nt_trans(cli,
+ NT_TRANSACT_SET_SECURITY_DESC,
+ 0,
+ NULL, 0, 0,
+ param, 8, 0,
+ (char *)data, len, 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_FREE(frame);
+
+ return ret;
+}
diff --git a/source3/libsmb/clispnego.c b/source3/libsmb/clispnego.c
new file mode 100644
index 0000000000..fa9dba098f
--- /dev/null
+++ b/source3/libsmb/clispnego.c
@@ -0,0 +1,600 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple kerberos5/SPNEGO routines
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
+ Copyright (C) Luke Howard 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/*
+ generate a negTokenInit packet given a GUID, a list of supported
+ OIDs (the mechanisms) and a principal name string
+*/
+DATA_BLOB spnego_gen_negTokenInit(char 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;
+}
+
+/*
+ Generate a negTokenInit as used by the client side ... It has a mechType
+ (OID), and a mechToken (a security blob) ...
+
+ Really, we need to break out the NTLMSSP stuff as well, because it could be
+ raw in the packets!
+*/
+DATA_BLOB gen_negTokenInit(const char *OID, DATA_BLOB blob)
+{
+ 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));
+ asn1_write_OID(&data, OID);
+ 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 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,
+ char *OIDs[ASN1_MAX_OIDS],
+ char **principal)
+{
+ int i;
+ bool ret;
+ 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-1; i++) {
+ char *oid_str = NULL;
+ asn1_read_OID(&data,&oid_str);
+ OIDs[i] = oid_str;
+ }
+ OIDs[i] = NULL;
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+
+ *principal = NULL;
+ if (asn1_tag_remaining(&data) > 0) {
+ 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;
+ if (data.has_error) {
+ int j;
+ SAFE_FREE(*principal);
+ for(j = 0; j < i && j < ASN1_MAX_OIDS-1; j++) {
+ SAFE_FREE(OIDs[j]);
+ }
+ }
+
+ 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-1; i++) {
+ char *oid_str = NULL;
+ asn1_read_OID(&data,&oid_str);
+ OIDs[i] = oid_str;
+ }
+ OIDs[i] = NULL;
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+
+ /* Skip any optional req_flags that are sent per RFC 4178 */
+ if (asn1_check_tag(&data, ASN1_CONTEXT(1))) {
+ uint8 flags;
+
+ asn1_start_tag(&data, ASN1_CONTEXT(1));
+ asn1_start_tag(&data, ASN1_BITFIELD);
+ while (asn1_tag_remaining(&data) > 0)
+ asn1_read_uint8(&data, &flags);
+ 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) {
+ int j;
+ data_blob_free(secblob);
+ for(j = 0; j < i && j < ASN1_MAX_OIDS-1; j++) {
+ SAFE_FREE(OIDs[j]);
+ }
+ 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(const DATA_BLOB ticket, const uint8 tok_id[2])
+{
+ 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(&data, tok_id, 2);
+ 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, uint8 tok_id[2])
+{
+ 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);
+
+ data_remaining = asn1_tag_remaining(&data);
+
+ if (data_remaining < 3) {
+ data.has_error = True;
+ } else {
+ asn1_read(&data, tok_id, 2);
+ data_remaining -= 2;
+ *ticket = data_blob(NULL, data_remaining);
+ asn1_read(&data, ticket->data, ticket->length);
+ }
+
+ asn1_end_tag(&data);
+
+ ret = !data.has_error;
+
+ if (data.has_error) {
+ data_blob_free(ticket);
+ }
+
+ asn1_free(&data);
+
+ return ret;
+}
+
+
+/*
+ generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY
+ kerberos session setup
+*/
+int spnego_gen_negTokenTarg(const char *principal, int time_offset,
+ DATA_BLOB *targ,
+ DATA_BLOB *session_key_krb5, uint32 extra_ap_opts,
+ time_t *expire_time)
+{
+ int retval;
+ DATA_BLOB tkt, tkt_wrapped;
+ const char *krb_mechs[] = {OID_KERBEROS5_OLD, OID_KERBEROS5, OID_NTLMSSP, NULL};
+
+ /* get a kerberos ticket for the service and extract the session key */
+ retval = cli_krb5_get_ticket(principal, time_offset,
+ &tkt, session_key_krb5, extra_ap_opts, NULL,
+ expire_time);
+
+ if (retval)
+ return retval;
+
+ /* wrap that up in a nice GSS-API wrapping */
+ tkt_wrapped = spnego_gen_krb5_wrap(tkt, TOK_ID_KRB_AP_REQ);
+
+ /* 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 retval;
+}
+
+
+/*
+ parse a spnego NTLMSSP challenge packet giving two security blobs
+*/
+bool spnego_parse_challenge(const 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;
+
+ if (data.has_error) {
+ data_blob_free(chal1);
+ data_blob_free(chal2);
+ }
+
+ asn1_free(&data);
+ return ret;
+}
+
+
+/*
+ generate a SPNEGO 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 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));
+ data_blob_free(auth);
+ asn1_free(&data);
+ return False;
+ }
+
+ asn1_free(&data);
+ return True;
+}
+
+/*
+ generate a minimal SPNEGO response packet. Doesn't contain much.
+*/
+DATA_BLOB spnego_gen_auth_response(DATA_BLOB *reply, NTSTATUS nt_status,
+ const char *mechOID)
+{
+ ASN1_DATA data;
+ DATA_BLOB ret;
+ uint8 negResult;
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ negResult = SPNEGO_NEG_RESULT_ACCEPT;
+ } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ negResult = SPNEGO_NEG_RESULT_INCOMPLETE;
+ } else {
+ negResult = SPNEGO_NEG_RESULT_REJECT;
+ }
+
+ 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, negResult);
+ asn1_pop_tag(&data);
+
+ if (mechOID) {
+ asn1_push_tag(&data,ASN1_CONTEXT(1));
+ asn1_write_OID(&data, mechOID);
+ asn1_pop_tag(&data);
+ }
+
+ if (reply && reply->data != NULL) {
+ asn1_push_tag(&data,ASN1_CONTEXT(2));
+ asn1_write_OctetString(&data, reply->data, reply->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 auth packet. This contains the encrypted passwords
+*/
+bool spnego_parse_auth_response(DATA_BLOB blob, NTSTATUS nt_status,
+ const char *mechOID,
+ DATA_BLOB *auth)
+{
+ ASN1_DATA data;
+ uint8 negResult;
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ negResult = SPNEGO_NEG_RESULT_ACCEPT;
+ } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ negResult = SPNEGO_NEG_RESULT_INCOMPLETE;
+ } else {
+ negResult = SPNEGO_NEG_RESULT_REJECT;
+ }
+
+ 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, negResult);
+ asn1_end_tag(&data);
+
+ *auth = data_blob_null;
+
+ if (asn1_tag_remaining(&data)) {
+ asn1_start_tag(&data,ASN1_CONTEXT(1));
+ asn1_check_OID(&data, mechOID);
+ asn1_end_tag(&data);
+
+ if (asn1_tag_remaining(&data)) {
+ asn1_start_tag(&data,ASN1_CONTEXT(2));
+ asn1_read_OctetString(&data, auth);
+ asn1_end_tag(&data);
+ }
+ } else if (negResult == SPNEGO_NEG_RESULT_INCOMPLETE) {
+ data.has_error = 1;
+ }
+
+ /* Binding against Win2K DC returns a duplicate of the responseToken in
+ * the optional mechListMIC field. This is a bug in Win2K. We ignore
+ * this field if it exists. Win2K8 may return a proper mechListMIC at
+ * which point we need to implement the integrity checking. */
+ if (asn1_tag_remaining(&data)) {
+ DATA_BLOB mechList = data_blob_null;
+ asn1_start_tag(&data, ASN1_CONTEXT(3));
+ asn1_read_OctetString(&data, &mechList);
+ asn1_end_tag(&data);
+ data_blob_free(&mechList);
+ DEBUG(5,("spnego_parse_auth_response received mechListMIC, "
+ "ignoring.\n"));
+ }
+
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+
+ if (data.has_error) {
+ DEBUG(3,("spnego_parse_auth_response failed at %d\n", (int)data.ofs));
+ asn1_free(&data);
+ data_blob_free(auth);
+ return False;
+ }
+
+ asn1_free(&data);
+ return True;
+}
diff --git a/source3/libsmb/clistr.c b/source3/libsmb/clistr.c
new file mode 100644
index 0000000000..5d20d632aa
--- /dev/null
+++ b/source3/libsmb/clistr.c
@@ -0,0 +1,94 @@
+/*
+ Unix SMB/CIFS implementation.
+ client string routines
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+size_t clistr_push_fn(const char *function,
+ unsigned int line,
+ struct cli_state *cli,
+ void *dest,
+ const char *src,
+ int dest_len,
+ int flags)
+{
+ size_t buf_used = PTR_DIFF(dest, cli->outbuf);
+ if (dest_len == -1) {
+ if (((ptrdiff_t)dest < (ptrdiff_t)cli->outbuf) || (buf_used > cli->bufsize)) {
+ DEBUG(0, ("Pushing string of 'unlimited' length into non-SMB buffer!\n"));
+ return push_string_fn(function, line,
+ cli->outbuf,
+ SVAL(cli->outbuf, smb_flg2),
+ dest, src, -1, flags);
+ }
+ return push_string_fn(function, line, cli->outbuf,
+ SVAL(cli->outbuf, smb_flg2),
+ dest, src, cli->bufsize - buf_used,
+ flags);
+ }
+
+ /* 'normal' push into size-specified buffer */
+ return push_string_fn(function, line, cli->outbuf,
+ SVAL(cli->outbuf, smb_flg2),
+ dest, src, dest_len, flags);
+}
+
+size_t clistr_pull_fn(const char *function,
+ unsigned int line,
+ struct cli_state *cli,
+ char *dest,
+ const void *src,
+ int dest_len,
+ int src_len,
+ int flags)
+{
+ return pull_string_fn(function, line, cli->inbuf,
+ SVAL(cli->inbuf, smb_flg2), dest, src, dest_len,
+ src_len, flags);
+}
+
+size_t clistr_pull_talloc_fn(const char *function,
+ unsigned int line,
+ TALLOC_CTX *ctx,
+ struct cli_state *cli,
+ char **pp_dest,
+ const void *src,
+ int src_len,
+ int flags)
+{
+ return pull_string_talloc_fn(function,
+ line,
+ ctx,
+ cli->inbuf,
+ SVAL(cli->inbuf, smb_flg2),
+ pp_dest,
+ src,
+ src_len,
+ flags);
+}
+
+size_t clistr_align_out(struct cli_state *cli, const void *p, int flags)
+{
+ return align_string(cli->outbuf, (const char *)p, flags);
+}
+
+size_t clistr_align_in(struct cli_state *cli, const void *p, int flags)
+{
+ return align_string(cli->inbuf, (const char *)p, flags);
+}
diff --git a/source3/libsmb/clitrans.c b/source3/libsmb/clitrans.c
new file mode 100644
index 0000000000..c929f0b7a9
--- /dev/null
+++ b/source3/libsmb/clitrans.c
@@ -0,0 +1,1409 @@
+/*
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+
+/****************************************************************************
+ 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, unsigned int lsetup, unsigned int msetup,
+ const char *param, unsigned int lparam, unsigned int mparam,
+ const char *data, unsigned int ldata, unsigned int mdata)
+{
+ unsigned int i;
+ unsigned int this_ldata,this_lparam;
+ unsigned int tot_data=0,tot_param=0;
+ char *outdata,*outparam;
+ char *p;
+ int pipe_name_len=0;
+ uint16 mid;
+
+ 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);
+ cli_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);
+
+ /*
+ * Save the mid we're using. We need this for finding
+ * signing replies.
+ */
+
+ mid = cli->mid;
+
+ 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);
+
+ if (!cli_send_smb(cli)) {
+ return False;
+ }
+
+ /* Note we're in a trans state. Save the sequence
+ * numbers for replies. */
+ client_set_trans_sign_state_on(cli, mid);
+
+ if (this_ldata < ldata || this_lparam < lparam) {
+ /* receive interim response */
+ if (!cli_receive_smb(cli) || cli_is_error(cli)) {
+ client_set_trans_sign_state_off(cli, mid);
+ 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));
+
+ client_set_trans_sign_state_off(cli, mid);
+ client_set_trans_sign_state_on(cli, mid);
+
+ cli_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);
+
+ /*
+ * Save the mid we're using. We need this for finding
+ * signing replies.
+ */
+ mid = cli->mid;
+
+ show_msg(cli->outbuf);
+ if (!cli_send_smb(cli)) {
+ client_set_trans_sign_state_off(cli, mid);
+ return False;
+ }
+
+ /* Ensure we use the same mid for the secondaries. */
+ cli->mid = mid;
+
+ 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, unsigned int *param_len,
+ char **data, unsigned int *data_len)
+{
+ unsigned int total_data=0;
+ unsigned int total_param=0;
+ unsigned int this_data,this_param;
+ NTSTATUS status;
+ bool ret = False;
+
+ *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. Note that STATUS_NO_MORE_FILES is
+ * returned when a trans2 findfirst/next finishes.
+ * When setting up an encrypted transport we can also
+ * see NT_STATUS_MORE_PROCESSING_REQUIRED here.
+ *
+ * Vista returns NT_STATUS_INACCESSIBLE_SYSTEM_SHORTCUT if the folder
+ * "<share>/Users/All Users" is enumerated. This is a special pseudo
+ * folder, and the response does not have parameters (nor a parameter
+ * length).
+ */
+ status = cli_nt_error(cli);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ if (NT_STATUS_IS_ERR(status) ||
+ NT_STATUS_EQUAL(status,STATUS_NO_MORE_FILES) ||
+ NT_STATUS_EQUAL(status,NT_STATUS_INACCESSIBLE_SYSTEM_SHORTCUT)) {
+ goto out;
+ }
+ }
+
+ /* parse out the lengths */
+ total_data = SVAL(cli->inbuf,smb_tdrcnt);
+ total_param = SVAL(cli->inbuf,smb_tprcnt);
+
+ /* allocate it */
+ if (total_data!=0) {
+ /* We know adding 2 is safe as total_data is an
+ * SVAL <= 0xFFFF. */
+ *data = (char *)SMB_REALLOC(*data,total_data+2);
+ if (!(*data)) {
+ DEBUG(0,("cli_receive_trans: failed to enlarge data buffer\n"));
+ goto out;
+ }
+ }
+
+ if (total_param!=0) {
+ /* We know adding 2 is safe as total_param is an
+ * SVAL <= 0xFFFF. */
+ *param = (char *)SMB_REALLOC(*param,total_param+2);
+ if (!(*param)) {
+ DEBUG(0,("cli_receive_trans: failed to enlarge param buffer\n"));
+ goto out;
+ }
+ }
+
+ for (;;) {
+ 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"));
+ goto out;
+ }
+
+ if (this_data + *data_len < this_data ||
+ this_data + *data_len < *data_len ||
+ this_param + *param_len < this_param ||
+ this_param + *param_len < *param_len) {
+ DEBUG(1,("Data overflow in cli_receive_trans\n"));
+ goto out;
+ }
+
+ if (this_data) {
+ unsigned int data_offset_out = SVAL(cli->inbuf,smb_drdisp);
+ unsigned int data_offset_in = SVAL(cli->inbuf,smb_droff);
+
+ if (data_offset_out > total_data ||
+ data_offset_out + this_data > total_data ||
+ data_offset_out + this_data < data_offset_out ||
+ data_offset_out + this_data < this_data) {
+ DEBUG(1,("Data overflow in cli_receive_trans\n"));
+ goto out;
+ }
+ if (data_offset_in > cli->bufsize ||
+ data_offset_in + this_data > cli->bufsize ||
+ data_offset_in + this_data < data_offset_in ||
+ data_offset_in + this_data < this_data) {
+ DEBUG(1,("Data overflow in cli_receive_trans\n"));
+ goto out;
+ }
+
+ memcpy(*data + data_offset_out, smb_base(cli->inbuf) + data_offset_in, this_data);
+ }
+ if (this_param) {
+ unsigned int param_offset_out = SVAL(cli->inbuf,smb_prdisp);
+ unsigned int param_offset_in = SVAL(cli->inbuf,smb_proff);
+
+ if (param_offset_out > total_param ||
+ param_offset_out + this_param > total_param ||
+ param_offset_out + this_param < param_offset_out ||
+ param_offset_out + this_param < this_param) {
+ DEBUG(1,("Param overflow in cli_receive_trans\n"));
+ goto out;
+ }
+ if (param_offset_in > cli->bufsize ||
+ param_offset_in + this_param > cli->bufsize ||
+ param_offset_in + this_param < param_offset_in ||
+ param_offset_in + this_param < this_param) {
+ DEBUG(1,("Param overflow in cli_receive_trans\n"));
+ goto out;
+ }
+
+ memcpy(*param + param_offset_out, smb_base(cli->inbuf) + param_offset_in, this_param);
+ }
+ *data_len += this_data;
+ *param_len += this_param;
+
+ if (total_data <= *data_len && total_param <= *param_len) {
+ ret = True;
+ break;
+ }
+
+ if (!cli_receive_smb(cli)) {
+ goto out;
+ }
+
+ 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)));
+ goto out;
+ }
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ if (NT_STATUS_IS_ERR(cli_nt_error(cli))) {
+ goto out;
+ }
+ }
+
+ /* parse out the total lengths again - they can shrink! */
+ if (SVAL(cli->inbuf,smb_tdrcnt) < total_data)
+ total_data = SVAL(cli->inbuf,smb_tdrcnt);
+ if (SVAL(cli->inbuf,smb_tprcnt) < total_param)
+ total_param = SVAL(cli->inbuf,smb_tprcnt);
+
+ if (total_data <= *data_len && total_param <= *param_len) {
+ ret = True;
+ break;
+ }
+ }
+
+ out:
+
+ if (ret) {
+ /* Ensure the last 2 bytes of param and data are 2 null
+ * bytes. These are malloc'ed, but not included in any
+ * length counts. This allows cli_XX string reading functions
+ * to safely null terminate. */
+ if (total_data) {
+ SSVAL(*data,total_data,0);
+ }
+ if (total_param) {
+ SSVAL(*param,total_param,0);
+ }
+ }
+
+ client_set_trans_sign_state_off(cli, SVAL(cli->inbuf,smb_mid));
+ return ret;
+}
+
+/****************************************************************************
+ Send a SMB nttrans request.
+****************************************************************************/
+
+bool cli_send_nt_trans(struct cli_state *cli,
+ int function,
+ int flags,
+ uint16 *setup, unsigned int lsetup, unsigned int msetup,
+ char *param, unsigned int lparam, unsigned int mparam,
+ char *data, unsigned int ldata, unsigned int mdata)
+{
+ unsigned int i;
+ unsigned int this_ldata,this_lparam;
+ unsigned int tot_data=0,tot_param=0;
+ uint16 mid;
+ 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);
+ cli_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);
+
+ /*
+ * Save the mid we're using. We need this for finding
+ * signing replies.
+ */
+
+ mid = cli->mid;
+
+ 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);
+ if (!cli_send_smb(cli)) {
+ return False;
+ }
+
+ /* Note we're in a trans state. Save the sequence
+ * numbers for replies. */
+ client_set_trans_sign_state_on(cli, mid);
+
+ if (this_ldata < ldata || this_lparam < lparam) {
+ /* receive interim response */
+ if (!cli_receive_smb(cli) || cli_is_error(cli)) {
+ client_set_trans_sign_state_off(cli, mid);
+ 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));
+
+ cli_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);
+
+ /*
+ * Save the mid we're using. We need this for finding
+ * signing replies.
+ */
+ mid = cli->mid;
+
+ show_msg(cli->outbuf);
+
+ if (!cli_send_smb(cli)) {
+ client_set_trans_sign_state_off(cli, mid);
+ return False;
+ }
+
+ /* Ensure we use the same mid for the secondaries. */
+ cli->mid = mid;
+
+ 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, unsigned int *param_len,
+ char **data, unsigned int *data_len)
+{
+ unsigned int total_data=0;
+ unsigned int total_param=0;
+ unsigned int this_data,this_param;
+ uint8 eclass;
+ uint32 ecode;
+ bool ret = False;
+
+ *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 (!(eclass == ERRDOS && ecode == ERRmoredata)) {
+ goto out;
+ }
+ }
+
+ /*
+ * Likewise for NT_STATUS_BUFFER_TOO_SMALL
+ */
+ if (cli_is_nt_error(cli)) {
+ if (!NT_STATUS_EQUAL(cli_nt_error(cli),
+ NT_STATUS_BUFFER_TOO_SMALL)) {
+ goto out;
+ }
+ }
+
+ /* parse out the lengths */
+ total_data = IVAL(cli->inbuf,smb_ntr_TotalDataCount);
+ total_param = IVAL(cli->inbuf,smb_ntr_TotalParameterCount);
+ /* Only allow 16 megs. */
+ if (total_param > 16*1024*1024) {
+ DEBUG(0,("cli_receive_nt_trans: param buffer too large %d\n",
+ total_param));
+ goto out;
+ }
+ if (total_data > 16*1024*1024) {
+ DEBUG(0,("cli_receive_nt_trans: data buffer too large %d\n",
+ total_data));
+ goto out;
+ }
+
+ /* allocate it */
+ if (total_data) {
+ /* We know adding 2 is safe as total_data is less
+ * than 16mb (above). */
+ *data = (char *)SMB_REALLOC(*data,total_data+2);
+ if (!(*data)) {
+ DEBUG(0,("cli_receive_nt_trans: failed to enlarge data buffer to %d\n",total_data));
+ goto out;
+ }
+ }
+
+ if (total_param) {
+ /* We know adding 2 is safe as total_param is less
+ * than 16mb (above). */
+ *param = (char *)SMB_REALLOC(*param,total_param+2);
+ if (!(*param)) {
+ DEBUG(0,("cli_receive_nt_trans: failed to enlarge param buffer to %d\n", total_param));
+ goto out;
+ }
+ }
+
+ 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_nt_trans\n"));
+ goto out;
+ }
+
+ if (this_data + *data_len < this_data ||
+ this_data + *data_len < *data_len ||
+ this_param + *param_len < this_param ||
+ this_param + *param_len < *param_len) {
+ DEBUG(1,("Data overflow in cli_receive_nt_trans\n"));
+ goto out;
+ }
+
+ if (this_data) {
+ unsigned int data_offset_out = SVAL(cli->inbuf,smb_ntr_DataDisplacement);
+ unsigned int data_offset_in = SVAL(cli->inbuf,smb_ntr_DataOffset);
+
+ if (data_offset_out > total_data ||
+ data_offset_out + this_data > total_data ||
+ data_offset_out + this_data < data_offset_out ||
+ data_offset_out + this_data < this_data) {
+ DEBUG(1,("Data overflow in cli_receive_nt_trans\n"));
+ goto out;
+ }
+ if (data_offset_in > cli->bufsize ||
+ data_offset_in + this_data > cli->bufsize ||
+ data_offset_in + this_data < data_offset_in ||
+ data_offset_in + this_data < this_data) {
+ DEBUG(1,("Data overflow in cli_receive_nt_trans\n"));
+ goto out;
+ }
+
+ memcpy(*data + data_offset_out, smb_base(cli->inbuf) + data_offset_in, this_data);
+ }
+
+ if (this_param) {
+ unsigned int param_offset_out = SVAL(cli->inbuf,smb_ntr_ParameterDisplacement);
+ unsigned int param_offset_in = SVAL(cli->inbuf,smb_ntr_ParameterOffset);
+
+ if (param_offset_out > total_param ||
+ param_offset_out + this_param > total_param ||
+ param_offset_out + this_param < param_offset_out ||
+ param_offset_out + this_param < this_param) {
+ DEBUG(1,("Param overflow in cli_receive_nt_trans\n"));
+ goto out;
+ }
+ if (param_offset_in > cli->bufsize ||
+ param_offset_in + this_param > cli->bufsize ||
+ param_offset_in + this_param < param_offset_in ||
+ param_offset_in + this_param < this_param) {
+ DEBUG(1,("Param overflow in cli_receive_nt_trans\n"));
+ goto out;
+ }
+
+ memcpy(*param + param_offset_out, smb_base(cli->inbuf) + param_offset_in, this_param);
+ }
+
+ *data_len += this_data;
+ *param_len += this_param;
+
+ if (total_data <= *data_len && total_param <= *param_len) {
+ ret = True;
+ break;
+ }
+
+ if (!cli_receive_smb(cli)) {
+ goto out;
+ }
+
+ 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)));
+ goto out;
+ }
+ if (cli_is_dos_error(cli)) {
+ cli_dos_error(cli, &eclass, &ecode);
+ if(!(eclass == ERRDOS && ecode == ERRmoredata)) {
+ goto out;
+ }
+ }
+ /*
+ * Likewise for NT_STATUS_BUFFER_TOO_SMALL
+ */
+ if (cli_is_nt_error(cli)) {
+ if (!NT_STATUS_EQUAL(cli_nt_error(cli),
+ NT_STATUS_BUFFER_TOO_SMALL)) {
+ goto out;
+ }
+ }
+
+ /* parse out the total lengths again - they can shrink! */
+ if (IVAL(cli->inbuf,smb_ntr_TotalDataCount) < total_data)
+ total_data = IVAL(cli->inbuf,smb_ntr_TotalDataCount);
+ if (IVAL(cli->inbuf,smb_ntr_TotalParameterCount) < total_param)
+ total_param = IVAL(cli->inbuf,smb_ntr_TotalParameterCount);
+
+ if (total_data <= *data_len && total_param <= *param_len) {
+ ret = True;
+ break;
+ }
+ }
+
+ out:
+
+ if (ret) {
+ /* Ensure the last 2 bytes of param and data are 2 null
+ * bytes. These are malloc'ed, but not included in any
+ * length counts. This allows cli_XX string reading functions
+ * to safely null terminate. */
+ if (total_data) {
+ SSVAL(*data,total_data,0);
+ }
+ if (total_param) {
+ SSVAL(*param,total_param,0);
+ }
+ }
+
+ client_set_trans_sign_state_off(cli, SVAL(cli->inbuf,smb_mid));
+ return ret;
+}
+
+struct trans_recvblob {
+ uint8_t *data;
+ uint32_t max, total, received;
+};
+
+struct cli_trans_state {
+ struct cli_state *cli;
+ struct event_context *ev;
+ uint8_t cmd;
+ uint16_t mid;
+ const char *pipe_name;
+ uint16_t fid;
+ uint16_t function;
+ int flags;
+ uint16_t *setup;
+ uint8_t num_setup, max_setup;
+ uint8_t *param;
+ uint32_t num_param, param_sent;
+ uint8_t *data;
+ uint32_t num_data, data_sent;
+
+ uint8_t num_rsetup;
+ uint16_t *rsetup;
+ struct trans_recvblob rparam;
+ struct trans_recvblob rdata;
+
+ TALLOC_CTX *secondary_request_ctx;
+};
+
+static void cli_trans_recv_helper(struct async_req *req);
+
+static struct async_req *cli_ship_trans(TALLOC_CTX *mem_ctx,
+ struct cli_trans_state *state)
+{
+ TALLOC_CTX *frame;
+ struct async_req *result = NULL;
+ struct cli_request *cli_req;
+ uint8_t wct;
+ uint16_t *vwv;
+ uint8_t *bytes = NULL;
+ uint16_t param_offset;
+ uint16_t this_param = 0;
+ uint16_t this_data = 0;
+ uint32_t useable_space;
+ uint8_t cmd;
+
+ frame = talloc_stackframe();
+
+ cmd = state->cmd;
+
+ if ((state->param_sent != 0) || (state->data_sent != 0)) {
+ /* The secondary commands are one after the primary ones */
+ cmd += 1;
+ }
+
+ param_offset = smb_size - 4;
+
+ switch (cmd) {
+ case SMBtrans:
+ bytes = TALLOC_ZERO_P(talloc_tos(), uint8_t); /* padding */
+ if (bytes == NULL) {
+ goto fail;
+ }
+ bytes = smb_bytes_push_str(
+ bytes, (state->cli->capabilities & CAP_UNICODE) != 0,
+ state->pipe_name);
+ if (bytes == NULL) {
+ goto fail;
+ }
+ wct = 14 + state->num_setup;
+ param_offset += talloc_get_size(bytes);
+ break;
+ case SMBtrans2:
+ bytes = TALLOC_ARRAY(talloc_tos(), uint8_t, 3); /* padding */
+ if (bytes == NULL) {
+ goto fail;
+ }
+ bytes[0] = 0;
+ bytes[1] = 'D'; /* Copy this from "old" 3.0 behaviour */
+ bytes[2] = ' ';
+ wct = 14 + state->num_setup;
+ param_offset += talloc_get_size(bytes);
+ break;
+ case SMBtranss:
+ wct = 8;
+ break;
+ case SMBtranss2:
+ wct = 9;
+ break;
+ case SMBnttrans:
+ wct = 19 + state->num_setup;
+ break;
+ case SMBnttranss:
+ wct = 18;
+ break;
+ default:
+ goto fail;
+ }
+
+ useable_space = state->cli->max_xmit - smb_size - sizeof(uint16_t)*wct;
+
+ if (state->param_sent < state->num_param) {
+ this_param = MIN(state->num_param - state->param_sent,
+ useable_space);
+ }
+
+ if (state->data_sent < state->num_data) {
+ this_data = MIN(state->num_data - state->data_sent,
+ useable_space - this_param);
+ }
+
+ vwv = TALLOC_ARRAY(talloc_tos(), uint16_t, wct);
+ if (vwv == NULL) {
+ goto fail;
+ }
+ param_offset += wct * sizeof(uint16_t);
+
+ DEBUG(10, ("num_setup=%u, max_setup=%u, "
+ "param_total=%u, this_param=%u, max_param=%u, "
+ "data_total=%u, this_data=%u, max_data=%u, "
+ "param_offset=%u, param_disp=%u, data_disp=%u\n",
+ (unsigned)state->num_setup, (unsigned)state->max_setup,
+ (unsigned)state->num_param, (unsigned)this_param,
+ (unsigned)state->rparam.max,
+ (unsigned)state->num_data, (unsigned)this_data,
+ (unsigned)state->rdata.max,
+ (unsigned)param_offset,
+ (unsigned)state->param_sent, (unsigned)state->data_sent));
+
+ switch (cmd) {
+ case SMBtrans:
+ case SMBtrans2:
+ SSVAL(vwv + 0, 0, state->num_param);
+ SSVAL(vwv + 1, 0, state->num_data);
+ SSVAL(vwv + 2, 0, state->rparam.max);
+ SSVAL(vwv + 3, 0, state->rdata.max);
+ SCVAL(vwv + 4, 0, state->max_setup);
+ SCVAL(vwv + 4, 1, 0); /* reserved */
+ SSVAL(vwv + 5, 0, state->flags);
+ SIVAL(vwv + 6, 0, 0); /* timeout */
+ SSVAL(vwv + 8, 0, 0); /* reserved */
+ SSVAL(vwv + 9, 0, this_param);
+ SSVAL(vwv +10, 0, param_offset);
+ SSVAL(vwv +11, 0, this_data);
+ SSVAL(vwv +12, 0, param_offset + this_param);
+ SCVAL(vwv +13, 0, state->num_setup);
+ SCVAL(vwv +13, 1, 0); /* reserved */
+ memcpy(vwv + 14, state->setup,
+ sizeof(uint16_t) * state->num_setup);
+ break;
+ case SMBtranss:
+ case SMBtranss2:
+ SSVAL(vwv + 0, 0, state->num_param);
+ SSVAL(vwv + 1, 0, state->num_data);
+ SSVAL(vwv + 2, 0, this_param);
+ SSVAL(vwv + 3, 0, param_offset);
+ SSVAL(vwv + 4, 0, state->param_sent);
+ SSVAL(vwv + 5, 0, this_data);
+ SSVAL(vwv + 6, 0, param_offset + this_param);
+ SSVAL(vwv + 7, 0, state->data_sent);
+ if (cmd == SMBtranss2) {
+ SSVAL(vwv + 8, 0, state->fid);
+ }
+ break;
+ case SMBnttrans:
+ SCVAL(vwv, 0, state->max_setup);
+ SSVAL(vwv, 1, 0); /* reserved */
+ SIVAL(vwv, 3, state->num_param);
+ SIVAL(vwv, 7, state->num_data);
+ SIVAL(vwv, 11, state->rparam.max);
+ SIVAL(vwv, 15, state->rdata.max);
+ SIVAL(vwv, 19, this_param);
+ SIVAL(vwv, 23, param_offset);
+ SIVAL(vwv, 27, this_data);
+ SIVAL(vwv, 31, param_offset + this_param);
+ SCVAL(vwv, 35, state->num_setup);
+ SSVAL(vwv, 36, state->function);
+ memcpy(vwv + 19, state->setup,
+ sizeof(uint16_t) * state->num_setup);
+ break;
+ case SMBnttranss:
+ SSVAL(vwv, 0, 0); /* reserved */
+ SCVAL(vwv, 2, 0); /* reserved */
+ SIVAL(vwv, 3, state->num_param);
+ SIVAL(vwv, 7, state->num_data);
+ SIVAL(vwv, 11, this_param);
+ SIVAL(vwv, 15, param_offset);
+ SIVAL(vwv, 19, state->param_sent);
+ SIVAL(vwv, 23, this_data);
+ SIVAL(vwv, 27, param_offset + this_param);
+ SIVAL(vwv, 31, state->data_sent);
+ SCVAL(vwv, 35, 0); /* reserved */
+ break;
+ }
+
+ bytes = (uint8_t *)talloc_append_blob(
+ talloc_tos(), bytes,
+ data_blob_const(state->param + state->param_sent, this_param));
+ if (bytes == NULL) {
+ goto fail;
+ }
+ state->param_sent += this_param;
+
+ bytes = (uint8_t *)talloc_append_blob(
+ talloc_tos(), bytes,
+ data_blob_const(state->data + state->data_sent, this_data));
+ if (bytes == NULL) {
+ goto fail;
+ }
+ state->data_sent += this_data;
+
+ if ((cmd == SMBtrans) || (cmd == SMBtrans2) || (cmd == SMBnttrans)) {
+ /*
+ * Primary request, retrieve our mid
+ */
+ result = cli_request_send(mem_ctx, state->ev, state->cli,
+ cmd, 0, wct, vwv,
+ talloc_get_size(bytes), bytes);
+ if (result == NULL) {
+ goto fail;
+ }
+ cli_req = talloc_get_type_abort(result->private_data,
+ struct cli_request);
+ state->mid = cli_req->mid;
+ } else {
+ uint16_t num_bytes = talloc_get_size(bytes);
+ /*
+ * Secondary request, we have to fix up the mid. Thus we do
+ * the chain_cork/chain/uncork ourselves.
+ */
+ if (!cli_chain_cork(state->cli, state->ev,
+ wct * sizeof(uint16_t) + num_bytes + 3)) {
+ goto fail;
+ }
+ result = cli_request_send(mem_ctx, state->ev, state->cli,
+ cmd, 0, wct, vwv, num_bytes, bytes);
+ if (result == NULL) {
+ goto fail;
+ }
+ cli_req = talloc_get_type_abort(result->private_data,
+ struct cli_request);
+ cli_req->recv_helper.fn = cli_trans_recv_helper;
+ cli_req->recv_helper.priv = state;
+ cli_req->mid = state->mid;
+ client_set_trans_sign_state_off(state->cli, state->mid);
+ cli_chain_uncork(state->cli);
+ }
+
+ client_set_trans_sign_state_on(state->cli, state->mid);
+
+ fail:
+ TALLOC_FREE(frame);
+ return result;
+}
+
+static void cli_trans_ship_rest(struct async_req *req,
+ struct cli_trans_state *state)
+{
+ state->secondary_request_ctx = talloc_new(state);
+ if (state->secondary_request_ctx == NULL) {
+ async_req_error(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+
+ while ((state->param_sent < state->num_param)
+ || (state->data_sent < state->num_data)) {
+ struct async_req *cli_req;
+
+ cli_req = cli_ship_trans(state->secondary_request_ctx, state);
+ if (cli_req == NULL) {
+ async_req_error(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ }
+}
+
+static bool cli_trans_oob(uint32_t bufsize, uint32_t offset, uint32_t length)
+{
+ if ((offset + length < offset) || (offset + length < length)) {
+ /* wrap */
+ return true;
+ }
+ if ((offset > bufsize) || (offset + length > bufsize)) {
+ /* overflow */
+ return true;
+ }
+ return false;
+}
+
+static NTSTATUS cli_pull_trans(struct async_req *req,
+ struct cli_request *cli_req,
+ uint8_t smb_cmd, bool expect_first_reply,
+ uint8_t *pnum_setup, uint16_t **psetup,
+ uint32_t *ptotal_param, uint32_t *pnum_param,
+ uint32_t *pparam_disp, uint8_t **pparam,
+ uint32_t *ptotal_data, uint32_t *pnum_data,
+ uint32_t *pdata_disp, uint8_t **pdata)
+{
+ uint32_t param_ofs, data_ofs;
+ uint8_t wct;
+ uint16_t *vwv;
+ uint16_t num_bytes;
+ uint8_t *bytes;
+ NTSTATUS status;
+
+ status = cli_pull_reply(req, &wct, &vwv, &num_bytes, &bytes);
+
+ /*
+ * We can receive something like STATUS_MORE_ENTRIES, so don't use
+ * !NT_STATUS_IS_OK(status) here.
+ */
+
+ if (NT_STATUS_IS_ERR(status)) {
+ return status;
+ }
+
+ if (expect_first_reply) {
+ if ((wct != 0) || (num_bytes != 0)) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ return NT_STATUS_OK;
+ }
+
+ switch (smb_cmd) {
+ case SMBtrans:
+ case SMBtrans2:
+ if (wct < 10) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ *ptotal_param = SVAL(vwv + 0, 0);
+ *ptotal_data = SVAL(vwv + 1, 0);
+ *pnum_param = SVAL(vwv + 3, 0);
+ param_ofs = SVAL(vwv + 4, 0);
+ *pparam_disp = SVAL(vwv + 5, 0);
+ *pnum_data = SVAL(vwv + 6, 0);
+ data_ofs = SVAL(vwv + 7, 0);
+ *pdata_disp = SVAL(vwv + 8, 0);
+ *pnum_setup = CVAL(vwv + 9, 0);
+ if (wct < 10 + (*pnum_setup)) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ *psetup = vwv + 10;
+
+ break;
+ case SMBnttrans:
+ if (wct < 18) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ *ptotal_param = IVAL(vwv, 3);
+ *ptotal_data = IVAL(vwv, 7);
+ *pnum_param = IVAL(vwv, 11);
+ param_ofs = IVAL(vwv, 15);
+ *pparam_disp = IVAL(vwv, 19);
+ *pnum_data = IVAL(vwv, 23);
+ data_ofs = IVAL(vwv, 27);
+ *pdata_disp = IVAL(vwv, 31);
+ *pnum_setup = CVAL(vwv, 35);
+ *psetup = vwv + 18;
+ break;
+
+ default:
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ /*
+ * Check for buffer overflows. data_ofs needs to be checked against
+ * the incoming buffer length, data_disp against the total
+ * length. Likewise for param_ofs/param_disp.
+ */
+
+ if (cli_trans_oob(smb_len(cli_req->inbuf), param_ofs, *pnum_param)
+ || cli_trans_oob(*ptotal_param, *pparam_disp, *pnum_param)
+ || cli_trans_oob(smb_len(cli_req->inbuf), data_ofs, *pnum_data)
+ || cli_trans_oob(*ptotal_data, *pdata_disp, *pnum_data)) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ *pparam = (uint8_t *)cli_req->inbuf + 4 + param_ofs;
+ *pdata = (uint8_t *)cli_req->inbuf + 4 + data_ofs;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cli_trans_pull_blob(TALLOC_CTX *mem_ctx,
+ struct trans_recvblob *blob,
+ uint32_t total, uint32_t thistime,
+ uint8_t *buf, uint32_t displacement)
+{
+ if (blob->data == NULL) {
+ if (total > blob->max) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ blob->total = total;
+ blob->data = TALLOC_ARRAY(mem_ctx, uint8_t, total);
+ if (blob->data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ if (total > blob->total) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ if (thistime) {
+ memcpy(blob->data + displacement, buf, thistime);
+ blob->received += thistime;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static void cli_trans_recv_helper(struct async_req *req)
+{
+ struct cli_request *cli_req = talloc_get_type_abort(
+ req->private_data, struct cli_request);
+ struct cli_trans_state *state = talloc_get_type_abort(
+ cli_req->recv_helper.priv, struct cli_trans_state);
+ uint8_t num_setup = 0;
+ uint16_t *setup = NULL;
+ uint32_t total_param = 0;
+ uint32_t num_param = 0;
+ uint32_t param_disp = 0;
+ uint32_t total_data = 0;
+ uint32_t num_data = 0;
+ uint32_t data_disp = 0;
+ uint8_t *param = NULL;
+ uint8_t *data = NULL;
+ bool sent_all;
+ NTSTATUS status;
+
+ sent_all = (state->param_sent == state->num_param)
+ && (state->data_sent == state->num_data);
+
+ status = cli_pull_trans(
+ req, cli_req, state->cmd, !sent_all, &num_setup, &setup,
+ &total_param, &num_param, &param_disp, &param,
+ &total_data, &num_data, &data_disp, &data);
+
+ /*
+ * We can receive something like STATUS_MORE_ENTRIES, so don't use
+ * !NT_STATUS_IS_OK(status) here.
+ */
+
+ if (NT_STATUS_IS_ERR(status)) {
+ async_req_error(req, status);
+ return;
+ }
+
+ if (!sent_all) {
+ cli_trans_ship_rest(req, state);
+ return;
+ }
+
+ /*
+ * We've just received a real response. This means that we don't need
+ * the secondary cli_request structures anymore, they have all been
+ * shipped to the server.
+ */
+ TALLOC_FREE(state->secondary_request_ctx);
+
+ if (num_setup != 0) {
+ TALLOC_FREE(state->rsetup);
+ state->rsetup = (uint16_t *)TALLOC_MEMDUP(
+ state, setup, sizeof(uint16_t) * num_setup);
+ if (state->rsetup == NULL) {
+ async_req_error(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ }
+
+ status = cli_trans_pull_blob(
+ state, &state->rparam, total_param, num_param, param,
+ param_disp);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("Pulling params failed: %s\n", nt_errstr(status)));
+ async_req_error(req, status);
+ return;
+ }
+
+ status = cli_trans_pull_blob(
+ state, &state->rdata, total_data, num_data, data,
+ data_disp);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("Pulling data failed: %s\n", nt_errstr(status)));
+ async_req_error(req, status);
+ return;
+ }
+
+ if ((state->rparam.total == state->rparam.received)
+ && (state->rdata.total == state->rdata.received)) {
+ client_set_trans_sign_state_off(state->cli, state->mid);
+ async_req_done(req);
+ }
+}
+
+struct async_req *cli_trans_send(
+ TALLOC_CTX *mem_ctx, struct event_context *ev,
+ struct cli_state *cli, uint8_t trans_cmd,
+ const char *pipe_name, uint16_t fid, uint16_t function, int flags,
+ uint16_t *setup, uint8_t num_setup, uint8_t max_setup,
+ uint8_t *param, uint32_t num_param, uint32_t max_param,
+ uint8_t *data, uint32_t num_data, uint32_t max_data)
+{
+ struct async_req *req;
+ struct cli_request *cli_req;
+ struct cli_trans_state *state;
+
+ /*
+ * We can't use it in a chained request chain, we'd get the offset
+ * calculations wrong.
+ */
+
+ if (cli_in_chain(cli)) {
+ return NULL;
+ }
+
+ if ((trans_cmd == SMBtrans) || (trans_cmd == SMBtrans2)) {
+ if ((num_param > 0xffff) || (max_param > 0xffff)
+ || (num_data > 0xffff) || (max_data > 0xffff)) {
+ DEBUG(3, ("Attempt to send invalid trans2 request "
+ "(setup %u, params %u/%u, data %u/%u)\n",
+ (unsigned)num_setup,
+ (unsigned)num_param, (unsigned)max_param,
+ (unsigned)num_data, (unsigned)max_data));
+ return NULL;
+ }
+ }
+
+ state = talloc(mem_ctx, struct cli_trans_state);
+ if (state == NULL) {
+ goto nomem;
+ }
+
+ state->cli = cli;
+ state->ev = ev;
+ state->cmd = trans_cmd;
+ state->num_rsetup = 0;
+ state->rsetup = NULL;
+ ZERO_STRUCT(state->rparam);
+ ZERO_STRUCT(state->rdata);
+ state->secondary_request_ctx = NULL;
+
+ if (trans_cmd == SMBtrans) {
+ state->pipe_name = talloc_strdup(state, pipe_name);
+ if (state->pipe_name == NULL) {
+ goto nomem;
+ }
+ }
+ if (trans_cmd == SMBtrans2) {
+ state->fid = fid;
+ }
+ if (trans_cmd == SMBnttrans) {
+ state->function = function;
+ }
+
+ state->flags = flags;
+
+ if (setup != NULL) {
+ state->setup = (uint16_t *)TALLOC_MEMDUP(
+ state, setup, sizeof(*setup) * num_setup);
+ if (state->setup == NULL) {
+ goto nomem;
+ }
+ state->num_setup = num_setup;
+ } else {
+ state->setup = NULL;
+ state->num_setup = 0;
+ }
+
+ state->max_setup = max_setup;
+
+ if (param != NULL) {
+ state->param = (uint8_t *)TALLOC_MEMDUP(state, param,
+ num_param);
+ if (state->param == NULL) {
+ goto nomem;
+ }
+ state->num_param = num_param;
+ } else {
+ state->param = NULL;
+ state->num_param = 0;
+ }
+
+ state->param_sent = 0;
+ state->rparam.max = max_param;
+
+ if (data != NULL) {
+ state->data = (uint8_t *)TALLOC_MEMDUP(state, data, num_data);
+ if (state->data == NULL) {
+ goto nomem;
+ }
+ state->num_data = num_data;
+ } else {
+ state->data = NULL;
+ state->num_data = 0;
+ }
+
+ state->data_sent = 0;
+ state->rdata.max = max_data;
+
+ req = cli_ship_trans(state, state);
+ if (req == NULL) {
+ goto nomem;
+ }
+
+ cli_req = talloc_get_type_abort(req->private_data, struct cli_request);
+ cli_req->recv_helper.fn = cli_trans_recv_helper;
+ cli_req->recv_helper.priv = state;
+
+ return req;
+
+ nomem:
+ TALLOC_FREE(state);
+ return NULL;
+}
+
+NTSTATUS cli_trans_recv(struct async_req *req, TALLOC_CTX *mem_ctx,
+ uint16_t **setup, uint8_t *num_setup,
+ uint8_t **param, uint32_t *num_param,
+ uint8_t **data, uint32_t *num_data)
+{
+ struct cli_request *cli_req = talloc_get_type_abort(
+ req->private_data, struct cli_request);
+ struct cli_trans_state *state = talloc_get_type_abort(
+ cli_req->recv_helper.priv, struct cli_trans_state);
+
+ SMB_ASSERT(req->state >= ASYNC_REQ_DONE);
+ if (req->state == ASYNC_REQ_ERROR) {
+ return req->status;
+ }
+
+ if (setup != NULL) {
+ *setup = talloc_move(mem_ctx, &state->rsetup);
+ *num_setup = state->num_rsetup;
+ } else {
+ TALLOC_FREE(state->rsetup);
+ }
+
+ if (param != NULL) {
+ *param = talloc_move(mem_ctx, &state->rparam.data);
+ *num_param = state->rparam.total;
+ } else {
+ TALLOC_FREE(state->rparam.data);
+ }
+
+ if (data != NULL) {
+ *data = talloc_move(mem_ctx, &state->rdata.data);
+ *num_data = state->rdata.total;
+ } else {
+ TALLOC_FREE(state->rdata.data);
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_trans(TALLOC_CTX *mem_ctx, struct cli_state *cli,
+ uint8_t trans_cmd,
+ const char *pipe_name, uint16_t fid, uint16_t function,
+ int flags,
+ uint16_t *setup, uint8_t num_setup, uint8_t max_setup,
+ uint8_t *param, uint32_t num_param, uint32_t max_param,
+ uint8_t *data, uint32_t num_data, uint32_t max_data,
+ uint16_t **rsetup, uint8_t *num_rsetup,
+ uint8_t **rparam, uint32_t *num_rparam,
+ uint8_t **rdata, uint32_t *num_rdata)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct event_context *ev;
+ struct async_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (cli->fd_event != NULL) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ cli_set_error(cli, NT_STATUS_INVALID_PARAMETER);
+ goto fail;
+ }
+
+ ev = event_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+
+ req = cli_trans_send(frame, ev, cli, trans_cmd,
+ pipe_name, fid, function, flags,
+ setup, num_setup, max_setup,
+ param, num_param, max_param,
+ data, num_data, max_data);
+ if (req == NULL) {
+ goto fail;
+ }
+
+ while (req->state < ASYNC_REQ_DONE) {
+ event_loop_once(ev);
+ }
+
+ status = cli_trans_recv(req, mem_ctx, rsetup, num_rsetup,
+ rparam, num_rparam, rdata, num_rdata);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
diff --git a/source3/libsmb/conncache.c b/source3/libsmb/conncache.c
new file mode 100644
index 0000000000..b440d61048
--- /dev/null
+++ b/source3/libsmb/conncache.c
@@ -0,0 +1,256 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon connection manager
+
+ Copyright (C) Tim Potter 2001
+ Copyright (C) Andrew Bartlett 2002
+ Copyright (C) Gerald (Jerry) Carter 2003
+ Copyright (C) Marc VanHeyningen 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "includes.h"
+
+/**
+ * @file
+ * Negative connection cache implemented in terms of gencache API
+ *
+ * The negative connection cache stores names of servers which have
+ * been unresponsive so that we don't waste time repeatedly trying
+ * to contact them. It used to use an in-memory linked list, but
+ * this limited its utility to a single process
+ */
+
+
+/**
+ * prefix used for all entries put into the general cache
+ */
+static const char NEGATIVE_CONN_CACHE_PREFIX[] = "NEG_CONN_CACHE";
+
+/**
+ * Marshalls the domain and server name into the key for the gencache
+ * record
+ *
+ * @param[in] domain required
+ * @param[in] server may be a FQDN or an IP address
+ * @return the resulting string, which the caller is responsible for
+ * SAFE_FREE()ing
+ * @retval NULL returned on error
+ */
+static char *negative_conn_cache_keystr(const char *domain, const char *server)
+{
+ const char NEGATIVE_CONN_CACHE_KEY_FMT[] = "%s/%s,%s";
+ char *keystr = NULL;
+
+ SMB_ASSERT(domain != NULL);
+ if (server == NULL)
+ server = "";
+
+ keystr = talloc_asprintf(talloc_tos(),NEGATIVE_CONN_CACHE_KEY_FMT,
+ NEGATIVE_CONN_CACHE_PREFIX, domain, server);
+ if (keystr == NULL) {
+ DEBUG(0, ("negative_conn_cache_keystr: malloc error\n"));
+ }
+
+ return keystr;
+}
+
+/**
+ * Marshalls the NT status into a printable value field for the gencache
+ * record
+ *
+ * @param[in] status
+ * @return the resulting string, which the caller is responsible for
+ * SAFE_FREE()ing
+ * @retval NULL returned on error
+ */
+static char *negative_conn_cache_valuestr(NTSTATUS status)
+{
+ char *valuestr = NULL;
+
+ valuestr = talloc_asprintf(talloc_tos(), "%x", NT_STATUS_V(status));
+ if (valuestr == NULL) {
+ DEBUG(0, ("negative_conn_cache_valuestr: malloc error\n"));
+ }
+
+ return valuestr;
+}
+
+/**
+ * Un-marshalls the NT status from a printable field for the gencache
+ * record
+ *
+ * @param[in] value The value field from the record
+ * @return the decoded NT status
+ * @retval NT_STATUS_OK returned on error
+ */
+static NTSTATUS negative_conn_cache_valuedecode(const char *value)
+{
+ NTSTATUS result = NT_STATUS_OK;
+
+ SMB_ASSERT(value != NULL);
+ if (sscanf(value, "%x", &(NT_STATUS_V(result))) != 1)
+ DEBUG(0, ("negative_conn_cache_valuestr: unable to parse "
+ "value field '%s'\n", value));
+ return result;
+}
+
+/**
+ * Function passed to gencache_iterate to remove any matching items
+ * from the list
+ *
+ * @param[in] key Key to the record found and to be deleted
+ * @param[in] value Value to the record (ignored)
+ * @param[in] timeout Timeout remaining for the record (ignored)
+ * @param[in] dptr Handle for passing additional data (ignored)
+ */
+static void delete_matches(const char *key, const char *value,
+ time_t timeout, void *dptr)
+{
+ gencache_del(key);
+}
+
+
+/**
+ * Checks for a given domain/server record in the negative cache
+ *
+ * @param[in] domain
+ * @param[in] server may be either a FQDN or an IP address
+ * @return The cached failure status
+ * @retval NT_STATUS_OK returned if no record is found or an error occurs
+ */
+NTSTATUS check_negative_conn_cache( const char *domain, const char *server)
+{
+ NTSTATUS result = NT_STATUS_OK;
+ char *key = NULL;
+ char *value = NULL;
+
+ key = negative_conn_cache_keystr(domain, server);
+ if (key == NULL)
+ goto done;
+
+ if (gencache_get(key, &value, (time_t *) NULL))
+ result = negative_conn_cache_valuedecode(value);
+ done:
+ DEBUG(9,("check_negative_conn_cache returning result %d for domain %s "
+ "server %s\n", NT_STATUS_V(result), domain, server));
+ TALLOC_FREE(key);
+ SAFE_FREE(value);
+ return result;
+}
+
+/**
+ * Delete any negative cache entry for the given domain/server
+ *
+ * @param[in] domain
+ * @param[in] server may be either a FQDN or an IP address
+ */
+void delete_negative_conn_cache(const char *domain, const char *server)
+{
+ char *key = NULL;
+
+ key = negative_conn_cache_keystr(domain, server);
+ if (key == NULL)
+ goto done;
+
+ gencache_del(key);
+ DEBUG(9,("delete_negative_conn_cache removing domain %s server %s\n",
+ domain, server));
+ done:
+ TALLOC_FREE(key);
+ return;
+}
+
+
+/**
+ * Add an entry to the failed connection cache
+ *
+ * @param[in] domain
+ * @param[in] server may be a FQDN or an IP addr in printable form
+ * @param[in] result error to cache; must not be NT_STATUS_OK
+ */
+void add_failed_connection_entry(const char *domain, const char *server,
+ NTSTATUS result)
+{
+ char *key = NULL;
+ char *value = NULL;
+
+ SMB_ASSERT(!NT_STATUS_IS_OK(result));
+
+ key = negative_conn_cache_keystr(domain, server);
+ if (key == NULL) {
+ DEBUG(0, ("add_failed_connection_entry: key creation error\n"));
+ goto done;
+ }
+
+ value = negative_conn_cache_valuestr(result);
+ if (value == NULL) {
+ DEBUG(0, ("add_failed_connection_entry: value creation error\n"));
+ goto done;
+ }
+
+ if (gencache_set(key, value,
+ time((time_t *) NULL)
+ + FAILED_CONNECTION_CACHE_TIMEOUT))
+ DEBUG(9,("add_failed_connection_entry: added domain %s (%s) "
+ "to failed conn cache\n", domain, server ));
+ else
+ DEBUG(1,("add_failed_connection_entry: failed to add "
+ "domain %s (%s) to failed conn cache\n",
+ domain, server));
+
+ done:
+ TALLOC_FREE(key);
+ TALLOC_FREE(value);
+ return;
+}
+
+/**
+ * Deletes all records from the negative connection cache in all domains
+ */
+void flush_negative_conn_cache( void )
+{
+ flush_negative_conn_cache_for_domain("*");
+}
+
+
+/**
+ * Deletes all records for a specified domain from the negative connection
+ * cache
+ *
+ * @param[in] domain String to match against domain portion of keys, or "*"
+ * to match all domains
+ */
+void flush_negative_conn_cache_for_domain(const char *domain)
+{
+ char *key_pattern = NULL;
+
+ key_pattern = negative_conn_cache_keystr(domain,"*");
+ if (key_pattern == NULL) {
+ DEBUG(0, ("flush_negative_conn_cache_for_domain: "
+ "key creation error\n"));
+ goto done;
+ }
+
+ gencache_iterate(delete_matches, (void *) NULL, key_pattern);
+ DEBUG(8, ("flush_negative_conn_cache_for_domain: flushed domain %s\n",
+ domain));
+
+ done:
+ TALLOC_FREE(key_pattern);
+ return;
+}
diff --git a/source3/libsmb/credentials.c b/source3/libsmb/credentials.c
new file mode 100644
index 0000000000..9d33e6d93d
--- /dev/null
+++ b/source3/libsmb/credentials.c
@@ -0,0 +1,361 @@
+/*
+ Unix SMB/CIFS implementation.
+ code to manipulate domain credentials
+ Copyright (C) Andrew Tridgell 1997-1998
+ Largely rewritten by Jeremy Allison 2005.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+ Represent a credential as a string.
+****************************************************************************/
+
+char *credstr(const unsigned char *cred)
+{
+ char *result;
+ result = talloc_asprintf(talloc_tos(),
+ "%02X%02X%02X%02X%02X%02X%02X%02X",
+ cred[0], cred[1], cred[2], cred[3],
+ cred[4], cred[5], cred[6], cred[7]);
+ SMB_ASSERT(result != NULL);
+ return result;
+}
+
+/****************************************************************************
+ Setup the session key and the client and server creds in dc.
+ ADS-style 128 bit session keys.
+ Used by both client and server creds setup.
+****************************************************************************/
+
+static void creds_init_128(struct dcinfo *dc,
+ const struct netr_Credential *clnt_chal_in,
+ const struct netr_Credential *srv_chal_in,
+ const unsigned char mach_pw[16])
+{
+ unsigned char zero[4], tmp[16];
+ HMACMD5Context ctx;
+ struct MD5Context md5;
+
+ /* Just in case this isn't already there */
+ memcpy(dc->mach_pw, mach_pw, 16);
+
+ ZERO_STRUCT(dc->sess_key);
+
+ memset(zero, 0, sizeof(zero));
+
+ hmac_md5_init_rfc2104(mach_pw, 16, &ctx);
+ MD5Init(&md5);
+ MD5Update(&md5, zero, sizeof(zero));
+ MD5Update(&md5, clnt_chal_in->data, 8);
+ MD5Update(&md5, srv_chal_in->data, 8);
+ MD5Final(tmp, &md5);
+ hmac_md5_update(tmp, sizeof(tmp), &ctx);
+ hmac_md5_final(dc->sess_key, &ctx);
+
+ /* debug output */
+ DEBUG(5,("creds_init_128\n"));
+ DEBUG(5,("\tclnt_chal_in: %s\n", credstr(clnt_chal_in->data)));
+ DEBUG(5,("\tsrv_chal_in : %s\n", credstr(srv_chal_in->data)));
+ dump_data_pw("\tsession_key ", (const unsigned char *)dc->sess_key, 16);
+
+ /* Generate the next client and server creds. */
+
+ des_crypt112(dc->clnt_chal.data, /* output */
+ clnt_chal_in->data, /* input */
+ dc->sess_key, /* input */
+ 1);
+
+ des_crypt112(dc->srv_chal.data, /* output */
+ srv_chal_in->data, /* input */
+ dc->sess_key, /* input */
+ 1);
+
+ /* Seed is the client chal. */
+ memcpy(dc->seed_chal.data, dc->clnt_chal.data, 8);
+}
+
+/****************************************************************************
+ Setup the session key and the client and server creds in dc.
+ Used by both client and server creds setup.
+****************************************************************************/
+
+static void creds_init_64(struct dcinfo *dc,
+ const struct netr_Credential *clnt_chal_in,
+ const struct netr_Credential *srv_chal_in,
+ const unsigned char mach_pw[16])
+{
+ uint32 sum[2];
+ unsigned char sum2[8];
+
+ /* Just in case this isn't already there */
+ if (dc->mach_pw != mach_pw) {
+ memcpy(dc->mach_pw, mach_pw, 16);
+ }
+
+ sum[0] = IVAL(clnt_chal_in->data, 0) + IVAL(srv_chal_in->data, 0);
+ sum[1] = IVAL(clnt_chal_in->data, 4) + IVAL(srv_chal_in->data, 4);
+
+ SIVAL(sum2,0,sum[0]);
+ SIVAL(sum2,4,sum[1]);
+
+ ZERO_STRUCT(dc->sess_key);
+
+ des_crypt128(dc->sess_key, sum2, dc->mach_pw);
+
+ /* debug output */
+ DEBUG(5,("creds_init_64\n"));
+ DEBUG(5,("\tclnt_chal_in: %s\n", credstr(clnt_chal_in->data)));
+ DEBUG(5,("\tsrv_chal_in : %s\n", credstr(srv_chal_in->data)));
+ DEBUG(5,("\tclnt+srv : %s\n", credstr(sum2)));
+ DEBUG(5,("\tsess_key_out : %s\n", credstr(dc->sess_key)));
+
+ /* Generate the next client and server creds. */
+
+ des_crypt112(dc->clnt_chal.data, /* output */
+ clnt_chal_in->data, /* input */
+ dc->sess_key, /* input */
+ 1);
+
+ des_crypt112(dc->srv_chal.data, /* output */
+ srv_chal_in->data, /* input */
+ dc->sess_key, /* input */
+ 1);
+
+ /* Seed is the client chal. */
+ memcpy(dc->seed_chal.data, dc->clnt_chal.data, 8);
+}
+
+/****************************************************************************
+ Utility function to step credential chain one forward.
+ Deliberately doesn't update the seed. See reseed comment below.
+****************************************************************************/
+
+static void creds_step(struct dcinfo *dc)
+{
+ DOM_CHAL time_chal;
+
+ DEBUG(5,("\tsequence = 0x%x\n", (unsigned int)dc->sequence ));
+
+ DEBUG(5,("\tseed: %s\n", credstr(dc->seed_chal.data) ));
+
+ SIVAL(time_chal.data, 0, IVAL(dc->seed_chal.data, 0) + dc->sequence);
+ SIVAL(time_chal.data, 4, IVAL(dc->seed_chal.data, 4));
+
+ DEBUG(5,("\tseed+seq %s\n", credstr(time_chal.data) ));
+
+ des_crypt112(dc->clnt_chal.data, time_chal.data, dc->sess_key, 1);
+
+ DEBUG(5,("\tCLIENT %s\n", credstr(dc->clnt_chal.data) ));
+
+ SIVAL(time_chal.data, 0, IVAL(dc->seed_chal.data, 0) + dc->sequence + 1);
+ SIVAL(time_chal.data, 4, IVAL(dc->seed_chal.data, 4));
+
+ DEBUG(5,("\tseed+seq+1 %s\n", credstr(time_chal.data) ));
+
+ des_crypt112(dc->srv_chal.data, time_chal.data, dc->sess_key, 1);
+
+ DEBUG(5,("\tSERVER %s\n", credstr(dc->srv_chal.data) ));
+}
+
+/****************************************************************************
+ Create a server credential struct.
+****************************************************************************/
+
+void creds_server_init(uint32 neg_flags,
+ struct dcinfo *dc,
+ struct netr_Credential *clnt_chal,
+ struct netr_Credential *srv_chal,
+ const unsigned char mach_pw[16],
+ struct netr_Credential *init_chal_out)
+{
+ DEBUG(10,("creds_server_init: neg_flags : %x\n", (unsigned int)neg_flags));
+ DEBUG(10,("creds_server_init: client chal : %s\n", credstr(clnt_chal->data) ));
+ DEBUG(10,("creds_server_init: server chal : %s\n", credstr(srv_chal->data) ));
+ dump_data_pw("creds_server_init: machine pass", mach_pw, 16);
+
+ /* Generate the session key and the next client and server creds. */
+ if (neg_flags & NETLOGON_NEG_128BIT) {
+ creds_init_128(dc,
+ clnt_chal,
+ srv_chal,
+ mach_pw);
+ } else {
+ creds_init_64(dc,
+ clnt_chal,
+ srv_chal,
+ mach_pw);
+ }
+
+ dump_data_pw("creds_server_init: session key", dc->sess_key, 16);
+
+ DEBUG(10,("creds_server_init: clnt : %s\n", credstr(dc->clnt_chal.data) ));
+ DEBUG(10,("creds_server_init: server : %s\n", credstr(dc->srv_chal.data) ));
+ DEBUG(10,("creds_server_init: seed : %s\n", credstr(dc->seed_chal.data) ));
+
+ memcpy(init_chal_out->data, dc->srv_chal.data, 8);
+}
+
+/****************************************************************************
+ Check a credential sent by the client.
+****************************************************************************/
+
+bool netlogon_creds_server_check(const struct dcinfo *dc,
+ const struct netr_Credential *rcv_cli_chal_in)
+{
+ if (memcmp(dc->clnt_chal.data, rcv_cli_chal_in->data, 8)) {
+ DEBUG(5,("netlogon_creds_server_check: challenge : %s\n",
+ credstr(rcv_cli_chal_in->data)));
+ DEBUG(5,("calculated: %s\n", credstr(dc->clnt_chal.data)));
+ DEBUG(2,("netlogon_creds_server_check: credentials check failed.\n"));
+ return false;
+ }
+
+ DEBUG(10,("netlogon_creds_server_check: credentials check OK.\n"));
+
+ return true;
+}
+/****************************************************************************
+ Replace current seed chal. Internal function - due to split server step below.
+****************************************************************************/
+
+static void creds_reseed(struct dcinfo *dc)
+{
+ struct netr_Credential time_chal;
+
+ SIVAL(time_chal.data, 0, IVAL(dc->seed_chal.data, 0) + dc->sequence + 1);
+ SIVAL(time_chal.data, 4, IVAL(dc->seed_chal.data, 4));
+
+ dc->seed_chal = time_chal;
+
+ DEBUG(5,("cred_reseed: seed %s\n", credstr(dc->seed_chal.data) ));
+}
+
+/****************************************************************************
+ Step the server credential chain one forward.
+****************************************************************************/
+
+bool netlogon_creds_server_step(struct dcinfo *dc,
+ const struct netr_Authenticator *received_cred,
+ struct netr_Authenticator *cred_out)
+{
+ bool ret;
+ struct dcinfo tmp_dc = *dc;
+
+ /* Do all operations on a temporary copy of the dc,
+ which we throw away if the checks fail. */
+
+ tmp_dc.sequence = received_cred->timestamp;
+
+ creds_step(&tmp_dc);
+
+ /* Create the outgoing credentials */
+ cred_out->timestamp = tmp_dc.sequence + 1;
+ memcpy(&cred_out->cred, &tmp_dc.srv_chal, sizeof(cred_out->cred));
+
+ creds_reseed(&tmp_dc);
+
+ ret = netlogon_creds_server_check(&tmp_dc, &received_cred->cred);
+ if (!ret) {
+ return false;
+ }
+
+ /* creds step succeeded - replace the current creds. */
+ *dc = tmp_dc;
+ return true;
+}
+
+/****************************************************************************
+ Create a client credential struct.
+****************************************************************************/
+
+void creds_client_init(uint32 neg_flags,
+ struct dcinfo *dc,
+ struct netr_Credential *clnt_chal,
+ struct netr_Credential *srv_chal,
+ const unsigned char mach_pw[16],
+ struct netr_Credential *init_chal_out)
+{
+ dc->sequence = time(NULL);
+
+ DEBUG(10,("creds_client_init: neg_flags : %x\n", (unsigned int)neg_flags));
+ DEBUG(10,("creds_client_init: client chal : %s\n", credstr(clnt_chal->data) ));
+ DEBUG(10,("creds_client_init: server chal : %s\n", credstr(srv_chal->data) ));
+ dump_data_pw("creds_client_init: machine pass", (const unsigned char *)mach_pw, 16);
+
+ /* Generate the session key and the next client and server creds. */
+ if (neg_flags & NETLOGON_NEG_128BIT) {
+ creds_init_128(dc,
+ clnt_chal,
+ srv_chal,
+ mach_pw);
+ } else {
+ creds_init_64(dc,
+ clnt_chal,
+ srv_chal,
+ mach_pw);
+ }
+
+ dump_data_pw("creds_client_init: session key", dc->sess_key, 16);
+
+ DEBUG(10,("creds_client_init: clnt : %s\n", credstr(dc->clnt_chal.data) ));
+ DEBUG(10,("creds_client_init: server : %s\n", credstr(dc->srv_chal.data) ));
+ DEBUG(10,("creds_client_init: seed : %s\n", credstr(dc->seed_chal.data) ));
+
+ memcpy(init_chal_out->data, dc->clnt_chal.data, 8);
+}
+
+/****************************************************************************
+ Check a credential returned by the server.
+****************************************************************************/
+
+bool netlogon_creds_client_check(const struct dcinfo *dc,
+ const struct netr_Credential *rcv_srv_chal_in)
+{
+ if (memcmp(dc->srv_chal.data, rcv_srv_chal_in->data,
+ sizeof(dc->srv_chal.data))) {
+
+ DEBUG(0,("netlogon_creds_client_check: credentials check failed.\n"));
+ DEBUGADD(5,("netlogon_creds_client_check: challenge : %s\n",
+ credstr(rcv_srv_chal_in->data)));
+ DEBUGADD(5,("calculated: %s\n", credstr(dc->srv_chal.data)));
+ return false;
+ }
+
+ DEBUG(10,("netlogon_creds_client_check: credentials check OK.\n"));
+
+ return true;
+}
+
+
+/****************************************************************************
+ Step the client credentials to the next element in the chain, updating the
+ current client and server credentials and the seed
+ produce the next authenticator in the sequence ready to send to
+ the server
+****************************************************************************/
+
+void netlogon_creds_client_step(struct dcinfo *dc,
+ struct netr_Authenticator *next_cred_out)
+{
+ dc->sequence += 2;
+ creds_step(dc);
+ creds_reseed(dc);
+
+ memcpy(&next_cred_out->cred.data, &dc->clnt_chal.data,
+ sizeof(next_cred_out->cred.data));
+ next_cred_out->timestamp = dc->sequence;
+}
diff --git a/source3/libsmb/dcerpc_err.c b/source3/libsmb/dcerpc_err.c
new file mode 100644
index 0000000000..900b8d769f
--- /dev/null
+++ b/source3/libsmb/dcerpc_err.c
@@ -0,0 +1,55 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Copyright (C) Stefan Metzmacher 2004
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+
+struct dcerpc_fault_table {
+ const char *errstr;
+ uint32_t faultcode;
+};
+
+static const struct dcerpc_fault_table dcerpc_faults[] =
+{
+ { "DCERPC_FAULT_OP_RNG_ERROR", DCERPC_FAULT_OP_RNG_ERROR },
+ { "DCERPC_FAULT_UNK_IF", DCERPC_FAULT_UNK_IF },
+ { "DCERPC_FAULT_NDR", DCERPC_FAULT_NDR },
+ { "DCERPC_FAULT_INVALID_TAG", DCERPC_FAULT_INVALID_TAG },
+ { "DCERPC_FAULT_CONTEXT_MISMATCH", DCERPC_FAULT_CONTEXT_MISMATCH },
+ { "DCERPC_FAULT_OTHER", DCERPC_FAULT_OTHER },
+ { "DCERPC_FAULT_ACCESS_DENIED", DCERPC_FAULT_ACCESS_DENIED },
+
+ { NULL, 0}
+};
+
+const char *dcerpc_errstr(uint32 fault_code)
+{
+ char *result;
+ int idx = 0;
+
+ while (dcerpc_faults[idx].errstr != NULL) {
+ if (dcerpc_faults[idx].faultcode == fault_code) {
+ return dcerpc_faults[idx].errstr;
+ }
+ idx++;
+ }
+
+ result = talloc_asprintf(talloc_tos(), "DCERPC fault 0x%08x",
+ fault_code);
+ SMB_ASSERT(result != NULL);
+ return result;
+}
diff --git a/source3/libsmb/doserr.c b/source3/libsmb/doserr.c
new file mode 100644
index 0000000000..2716e04d91
--- /dev/null
+++ b/source3/libsmb/doserr.c
@@ -0,0 +1,202 @@
+/*
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* DOS error codes. please read doserr.h */
+
+#include "includes.h"
+
+typedef const struct {
+ const char *dos_errstr;
+ WERROR werror;
+} werror_code_struct;
+
+typedef const struct {
+ WERROR werror;
+ const char *friendly_errstr;
+} werror_str_struct;
+
+werror_code_struct dos_errs[] =
+{
+ { "WERR_OK", WERR_OK },
+ { "WERR_GENERAL_FAILURE", WERR_GENERAL_FAILURE },
+ { "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_SEM_TIMEOUT", WERR_SEM_TIMEOUT },
+ { "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_GROUP_NOT_FOUND", WERR_GROUP_NOT_FOUND },
+ { "WERR_USER_NOT_FOUND", WERR_USER_NOT_FOUND },
+ { "WERR_NOT_LOCAL_DOMAIN", WERR_NOT_LOCAL_DOMAIN },
+ { "WERR_USER_EXISTS", WERR_USER_EXISTS },
+ { "WERR_REVISION_MISMATCH", WERR_REVISION_MISMATCH },
+ { "WERR_NO_LOGON_SERVERS", WERR_NO_LOGON_SERVERS },
+ { "WERR_NO_SUCH_LOGON_SESSION", WERR_NO_SUCH_LOGON_SESSION },
+ { "WERR_USER_ALREADY_EXISTS", WERR_USER_ALREADY_EXISTS },
+ { "WERR_NO_SUCH_USER", WERR_NO_SUCH_USER },
+ { "WERR_GROUP_EXISTS", WERR_GROUP_EXISTS },
+ { "WERR_MEMBER_IN_GROUP", WERR_MEMBER_IN_GROUP },
+ { "WERR_USER_NOT_IN_GROUP", WERR_USER_NOT_IN_GROUP },
+ { "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 },
+ { "WERR_INVALID_COMPUTER_NAME", WERR_INVALID_COMPUTER_NAME },
+ { "WERR_INVALID_DOMAINNAME", WERR_INVALID_DOMAINNAME },
+ { "WERR_MACHINE_LOCKED", WERR_MACHINE_LOCKED },
+ { "WERR_DC_NOT_FOUND", WERR_DC_NOT_FOUND },
+ { "WERR_SETUP_NOT_JOINED", WERR_SETUP_NOT_JOINED },
+ { "WERR_SETUP_ALREADY_JOINED", WERR_SETUP_ALREADY_JOINED },
+ { "WERR_SETUP_DOMAIN_CONTROLLER", WERR_SETUP_DOMAIN_CONTROLLER },
+ { "WERR_DEFAULT_JOIN_REQUIRED", WERR_DEFAULT_JOIN_REQUIRED },
+ { "WERR_DEVICE_NOT_AVAILABLE", WERR_DEVICE_NOT_AVAILABLE },
+ { "WERR_LOGON_FAILURE", WERR_LOGON_FAILURE },
+ { "WERR_WRONG_PASSWORD", WERR_WRONG_PASSWORD },
+ { "WERR_PASSWORD_RESTRICTION", WERR_PASSWORD_RESTRICTION },
+ { "WERR_NO_SUCH_DOMAIN", WERR_NO_SUCH_DOMAIN },
+ { "WERR_NONE_MAPPED", WERR_NONE_MAPPED },
+ { "WERR_INVALID_SECURITY_DESCRIPTOR", WERR_INVALID_SECURITY_DESCRIPTOR },
+ { "WERR_INVALID_DOMAIN_STATE", WERR_INVALID_DOMAIN_STATE },
+ { "WERR_INVALID_DOMAIN_ROLE", WERR_INVALID_DOMAIN_ROLE },
+ { "WERR_SPECIAL_ACCOUNT", WERR_SPECIAL_ACCOUNT },
+ { "WERR_ALIAS_EXISTS", WERR_ALIAS_EXISTS },
+ { "WERR_NO_SUCH_ALIAS", WERR_NO_SUCH_ALIAS },
+ { "WERR_MEMBER_IN_ALIAS", WERR_MEMBER_IN_ALIAS },
+ { "WERR_TIME_SKEW", WERR_TIME_SKEW },
+ { "WERR_INVALID_OWNER", WERR_INVALID_OWNER },
+ { "WERR_SERVER_UNAVAILABLE", WERR_SERVER_UNAVAILABLE },
+ { "WERR_IO_PENDING", WERR_IO_PENDING },
+ { "WERR_INVALID_SERVICE_CONTROL", WERR_INVALID_SERVICE_CONTROL },
+ { "WERR_SERVICE_ALREADY_RUNNING", WERR_SERVICE_ALREADY_RUNNING },
+ { "WERR_NET_NAME_NOT_FOUND", WERR_NET_NAME_NOT_FOUND },
+ { "WERR_REG_CORRUPT", WERR_REG_CORRUPT },
+ { "WERR_REG_IO_FAILURE", WERR_REG_IO_FAILURE },
+ { "WERR_REG_FILE_INVALID", WERR_REG_FILE_INVALID },
+ { "WERR_NO_SUCH_SERVICE", WERR_NO_SUCH_SERVICE },
+ { "WERR_SERVICE_DISABLED", WERR_SERVICE_DISABLED },
+ { "WERR_SERVICE_NEVER_STARTED", WERR_SERVICE_NEVER_STARTED },
+ { "WERR_NOT_FOUND", WERR_NOT_FOUND },
+ { "WERR_CAN_NOT_COMPLETE", WERR_CAN_NOT_COMPLETE},
+ { "WERR_INVALID_FLAGS", WERR_INVALID_FLAGS},
+ { "WERR_PASSWORD_MUST_CHANGE", WERR_PASSWORD_MUST_CHANGE },
+ { "WERR_DOMAIN_CONTROLLER_NOT_FOUND", WERR_DOMAIN_CONTROLLER_NOT_FOUND },
+ { "WERR_ACCOUNT_LOCKED_OUT", WERR_ACCOUNT_LOCKED_OUT },
+ { "WERR_DS_DRA_BAD_DN", WERR_DS_DRA_BAD_DN },
+ { "WERR_DS_DRA_BAD_NC", WERR_DS_DRA_BAD_NC },
+ { NULL, W_ERROR(0) }
+};
+
+werror_str_struct dos_err_strs[] = {
+ { WERR_OK, "Success" },
+ { WERR_ACCESS_DENIED, "Access is denied" },
+ { WERR_INVALID_PARAM, "Invalid parameter" },
+ { WERR_NOT_SUPPORTED, "Not supported" },
+ { WERR_BAD_PASSWORD, "A bad password was supplied" },
+ { WERR_NOMEM, "Out of memory" },
+ { WERR_NO_LOGON_SERVERS, "No logon servers found" },
+ { WERR_NO_SUCH_LOGON_SESSION, "No such logon session" },
+ { WERR_DOMAIN_CONTROLLER_NOT_FOUND, "A domain controller could not be found" },
+ { WERR_DC_NOT_FOUND, "A domain controller could not be found" },
+ { WERR_SETUP_NOT_JOINED, "Join failed" },
+ { WERR_SETUP_ALREADY_JOINED, "Machine is already joined" },
+ { WERR_SETUP_DOMAIN_CONTROLLER, "Machine is a Domain Controller" },
+ { WERR_LOGON_FAILURE, "Invalid logon credentials" },
+ { WERR_USER_EXISTS, "User account already exists" },
+ { WERR_PASSWORD_MUST_CHANGE, "The password must be changed" },
+ { WERR_ACCOUNT_LOCKED_OUT, "Account locked out" },
+ { WERR_TIME_SKEW, "Time difference between client and server" },
+ { WERR_USER_ALREADY_EXISTS, "User already exists" },
+ { WERR_PASSWORD_RESTRICTION, "Password does not meet restrictions" },
+ { WERR_NONE_MAPPED, "Could not map names to SIDs" },
+ { WERR_NO_SUCH_USER, "No such User" },
+ { WERR_GROUP_EXISTS, "Group already exists" },
+ { WERR_DS_DRA_BAD_DN, "An invalid distinguished name was specified for this replication" },
+ { WERR_DS_DRA_BAD_NC, "An invalid naming context was specified for this replication operation" },
+ { WERR_WRONG_PASSWORD, "The current password is incorrect" }
+};
+
+/*****************************************************************************
+ Returns a DOS error message. not amazingly helpful, but better than a number.
+ *****************************************************************************/
+
+const char *dos_errstr(WERROR werror)
+{
+ char *result;
+ int idx = 0;
+
+ 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++;
+ }
+
+ result = talloc_asprintf(talloc_tos(), "DOS code 0x%08x",
+ W_ERROR_V(werror));
+ SMB_ASSERT(result != NULL);
+ return result;
+}
+
+/*****************************************************************************
+ Get friendly error string for WERRORs
+ *****************************************************************************/
+
+const char *get_friendly_werror_msg(WERROR werror)
+{
+ int i = 0;
+
+ for (i = 0; i < ARRAY_SIZE(dos_err_strs); i++) {
+ if (W_ERROR_V(dos_err_strs[i].werror) ==
+ W_ERROR_V(werror)) {
+ return dos_err_strs[i].friendly_errstr;
+ }
+ }
+
+ return dos_errstr(werror);
+}
+
+/* compat function for samba4 */
+const char *win_errstr(WERROR werror)
+{
+ return dos_errstr(werror);
+}
diff --git a/source3/libsmb/dsgetdcname.c b/source3/libsmb/dsgetdcname.c
new file mode 100644
index 0000000000..2a445cbd5a
--- /dev/null
+++ b/source3/libsmb/dsgetdcname.c
@@ -0,0 +1,1449 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ dsgetdcname
+
+ Copyright (C) Gerald Carter 2006
+ Copyright (C) Guenther Deschner 2007-2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+#define DSGETDCNAME_FMT "DSGETDCNAME/DOMAIN/%s"
+/* 15 minutes */
+#define DSGETDCNAME_CACHE_TTL 60*15
+
+struct ip_service_name {
+ struct sockaddr_storage ss;
+ unsigned port;
+ const char *hostname;
+};
+
+static NTSTATUS make_dc_info_from_cldap_reply(TALLOC_CTX *mem_ctx,
+ uint32_t flags,
+ struct sockaddr_storage *ss,
+ uint32_t nt_version,
+ union nbt_cldap_netlogon *r,
+ struct netr_DsRGetDCNameInfo **info);
+
+/****************************************************************
+****************************************************************/
+
+void debug_dsdcinfo_flags(int lvl, uint32_t flags)
+{
+ DEBUG(lvl,("debug_dsdcinfo_flags: 0x%08x\n\t", flags));
+
+ if (flags & DS_FORCE_REDISCOVERY)
+ DEBUGADD(lvl,("DS_FORCE_REDISCOVERY "));
+ if (flags & 0x000000002)
+ DEBUGADD(lvl,("0x00000002 "));
+ if (flags & 0x000000004)
+ DEBUGADD(lvl,("0x00000004 "));
+ if (flags & 0x000000008)
+ DEBUGADD(lvl,("0x00000008 "));
+ if (flags & DS_DIRECTORY_SERVICE_REQUIRED)
+ DEBUGADD(lvl,("DS_DIRECTORY_SERVICE_REQUIRED "));
+ if (flags & DS_DIRECTORY_SERVICE_PREFERRED)
+ DEBUGADD(lvl,("DS_DIRECTORY_SERVICE_PREFERRED "));
+ if (flags & DS_GC_SERVER_REQUIRED)
+ DEBUGADD(lvl,("DS_GC_SERVER_REQUIRED "));
+ if (flags & DS_PDC_REQUIRED)
+ DEBUGADD(lvl,("DS_PDC_REQUIRED "));
+ if (flags & DS_BACKGROUND_ONLY)
+ DEBUGADD(lvl,("DS_BACKGROUND_ONLY "));
+ if (flags & DS_IP_REQUIRED)
+ DEBUGADD(lvl,("DS_IP_REQUIRED "));
+ if (flags & DS_KDC_REQUIRED)
+ DEBUGADD(lvl,("DS_KDC_REQUIRED "));
+ if (flags & DS_TIMESERV_REQUIRED)
+ DEBUGADD(lvl,("DS_TIMESERV_REQUIRED "));
+ if (flags & DS_WRITABLE_REQUIRED)
+ DEBUGADD(lvl,("DS_WRITABLE_REQUIRED "));
+ if (flags & DS_GOOD_TIMESERV_PREFERRED)
+ DEBUGADD(lvl,("DS_GOOD_TIMESERV_PREFERRED "));
+ if (flags & DS_AVOID_SELF)
+ DEBUGADD(lvl,("DS_AVOID_SELF "));
+ if (flags & DS_ONLY_LDAP_NEEDED)
+ DEBUGADD(lvl,("DS_ONLY_LDAP_NEEDED "));
+ if (flags & DS_IS_FLAT_NAME)
+ DEBUGADD(lvl,("DS_IS_FLAT_NAME "));
+ if (flags & DS_IS_DNS_NAME)
+ DEBUGADD(lvl,("DS_IS_DNS_NAME "));
+ if (flags & 0x00040000)
+ DEBUGADD(lvl,("0x00040000 "));
+ if (flags & 0x00080000)
+ DEBUGADD(lvl,("0x00080000 "));
+ if (flags & 0x00100000)
+ DEBUGADD(lvl,("0x00100000 "));
+ if (flags & 0x00200000)
+ DEBUGADD(lvl,("0x00200000 "));
+ if (flags & 0x00400000)
+ DEBUGADD(lvl,("0x00400000 "));
+ if (flags & 0x00800000)
+ DEBUGADD(lvl,("0x00800000 "));
+ if (flags & 0x01000000)
+ DEBUGADD(lvl,("0x01000000 "));
+ if (flags & 0x02000000)
+ DEBUGADD(lvl,("0x02000000 "));
+ if (flags & 0x04000000)
+ DEBUGADD(lvl,("0x04000000 "));
+ if (flags & 0x08000000)
+ DEBUGADD(lvl,("0x08000000 "));
+ if (flags & 0x10000000)
+ DEBUGADD(lvl,("0x10000000 "));
+ if (flags & 0x20000000)
+ DEBUGADD(lvl,("0x20000000 "));
+ if (flags & DS_RETURN_DNS_NAME)
+ DEBUGADD(lvl,("DS_RETURN_DNS_NAME "));
+ if (flags & DS_RETURN_FLAT_NAME)
+ DEBUGADD(lvl,("DS_RETURN_FLAT_NAME "));
+ if (flags)
+ DEBUGADD(lvl,("\n"));
+}
+
+/****************************************************************
+****************************************************************/
+
+static char *dsgetdcname_cache_key(TALLOC_CTX *mem_ctx, const char *domain)
+{
+ if (!domain) {
+ return NULL;
+ }
+
+ return talloc_asprintf_strupper_m(mem_ctx, DSGETDCNAME_FMT, domain);
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS dsgetdcname_cache_delete(TALLOC_CTX *mem_ctx,
+ const char *domain_name)
+{
+ char *key;
+
+ if (!gencache_init()) {
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ key = dsgetdcname_cache_key(mem_ctx, domain_name);
+ if (!key) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!gencache_del(key)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS dsgetdcname_cache_store(TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ const DATA_BLOB *blob)
+{
+ time_t expire_time;
+ char *key;
+ bool ret = false;
+
+ if (!gencache_init()) {
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ key = dsgetdcname_cache_key(mem_ctx, domain_name);
+ if (!key) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ expire_time = time(NULL) + DSGETDCNAME_CACHE_TTL;
+
+ if (gencache_lock_entry(key) != 0) {
+ return NT_STATUS_LOCK_NOT_GRANTED;
+ }
+
+ ret = gencache_set_data_blob(key, blob, expire_time);
+
+ gencache_unlock_entry(key);
+
+ return ret ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+/****************************************************************
+****************************************************************/
+
+#define SET_STRING(x) \
+ talloc_strdup(mem_ctx, x); \
+ NT_STATUS_HAVE_NO_MEMORY(x);
+
+static NTSTATUS map_logon29_from_cldap_reply(TALLOC_CTX *mem_ctx,
+ uint32_t flags,
+ struct sockaddr_storage *ss,
+ uint32_t nt_version,
+ union nbt_cldap_netlogon *r,
+ struct nbt_cldap_netlogon_29 *p)
+{
+ char addr[INET6_ADDRSTRLEN];
+
+ ZERO_STRUCTP(p);
+
+ print_sockaddr(addr, sizeof(addr), ss);
+
+ /* FIXME */
+ p->dc_sock_addr_size = 0x10; /* the w32 winsock addr size */
+ p->dc_sock_addr.family = 2; /* AF_INET */
+ p->dc_sock_addr.pdc_ip = talloc_strdup(mem_ctx, addr);
+
+ switch (nt_version & 0x0000001f) {
+ case 0:
+ return NT_STATUS_INVALID_PARAMETER;
+ case 1:
+ case 16:
+ case 17:
+ p->pdc_name = SET_STRING(r->logon1.pdc_name);
+ p->domain = SET_STRING(r->logon1.domain_name);
+
+ if (flags & DS_PDC_REQUIRED) {
+ p->server_type = NBT_SERVER_WRITABLE |
+ NBT_SERVER_PDC;
+ }
+ break;
+ case 2:
+ case 3:
+ case 18:
+ case 19:
+ p->pdc_name = SET_STRING(r->logon3.pdc_name);
+ p->domain = SET_STRING(r->logon3.domain_name);
+ p->pdc_dns_name = SET_STRING(r->logon3.pdc_dns_name);
+ p->dns_domain = SET_STRING(r->logon3.dns_domain);
+ p->server_type = r->logon3.server_type;
+ p->forest = SET_STRING(r->logon3.forest);
+ p->domain_uuid = r->logon3.domain_uuid;
+
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ p->pdc_name = SET_STRING(r->logon5.pdc_name);
+ p->domain = SET_STRING(r->logon5.domain);
+ p->pdc_dns_name = SET_STRING(r->logon5.pdc_dns_name);
+ p->dns_domain = SET_STRING(r->logon5.dns_domain);
+ p->server_type = r->logon5.server_type;
+ p->forest = SET_STRING(r->logon5.forest);
+ p->domain_uuid = r->logon5.domain_uuid;
+ p->server_site = SET_STRING(r->logon5.server_site);
+ p->client_site = SET_STRING(r->logon5.client_site);
+
+ break;
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ p->pdc_name = SET_STRING(r->logon13.pdc_name);
+ p->domain = SET_STRING(r->logon13.domain);
+ p->pdc_dns_name = SET_STRING(r->logon13.pdc_dns_name);
+ p->dns_domain = SET_STRING(r->logon13.dns_domain);
+ p->server_type = r->logon13.server_type;
+ p->forest = SET_STRING(r->logon13.forest);
+ p->domain_uuid = r->logon13.domain_uuid;
+ p->server_site = SET_STRING(r->logon13.server_site);
+ p->client_site = SET_STRING(r->logon13.client_site);
+
+ break;
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ p->pdc_name = SET_STRING(r->logon15.pdc_name);
+ p->domain = SET_STRING(r->logon15.domain);
+ p->pdc_dns_name = SET_STRING(r->logon15.pdc_dns_name);
+ p->dns_domain = SET_STRING(r->logon15.dns_domain);
+ p->server_type = r->logon15.server_type;
+ p->forest = SET_STRING(r->logon15.forest);
+ p->domain_uuid = r->logon15.domain_uuid;
+ p->server_site = SET_STRING(r->logon15.server_site);
+ p->client_site = SET_STRING(r->logon15.client_site);
+
+ break;
+ case 29:
+ case 30:
+ case 31:
+ p->pdc_name = SET_STRING(r->logon29.pdc_name);
+ p->domain = SET_STRING(r->logon29.domain);
+ p->pdc_dns_name = SET_STRING(r->logon29.pdc_dns_name);
+ p->dns_domain = SET_STRING(r->logon29.dns_domain);
+ p->server_type = r->logon29.server_type;
+ p->forest = SET_STRING(r->logon29.forest);
+ p->domain_uuid = r->logon29.domain_uuid;
+ p->server_site = SET_STRING(r->logon29.server_site);
+ p->client_site = SET_STRING(r->logon29.client_site);
+ p->next_closest_site = SET_STRING(r->logon29.next_closest_site);
+
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS store_cldap_reply(TALLOC_CTX *mem_ctx,
+ uint32_t flags,
+ struct sockaddr_storage *ss,
+ uint32_t nt_version,
+ union nbt_cldap_netlogon *r)
+{
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+ NTSTATUS status;
+ struct nbt_cldap_netlogon_29 logon29;
+
+ status = map_logon29_from_cldap_reply(mem_ctx, flags, ss,
+ nt_version, r, &logon29);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ndr_err = ndr_push_struct_blob(&blob, mem_ctx, &logon29,
+ (ndr_push_flags_fn_t)ndr_push_nbt_cldap_netlogon_29);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ if (logon29.domain) {
+ status = dsgetdcname_cache_store(mem_ctx, logon29.domain, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (logon29.client_site) {
+ sitename_store(logon29.domain, logon29.client_site);
+ }
+ }
+ if (logon29.dns_domain) {
+ status = dsgetdcname_cache_store(mem_ctx, logon29.dns_domain, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (logon29.client_site) {
+ sitename_store(logon29.dns_domain, logon29.client_site);
+ }
+ }
+
+ done:
+ data_blob_free(&blob);
+
+ return status;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS dsgetdcname_cache_refresh(TALLOC_CTX *mem_ctx,
+ struct messaging_context *msg_ctx,
+ const char *domain_name,
+ struct GUID *domain_guid,
+ uint32_t flags,
+ const char *site_name,
+ struct netr_DsRGetDCNameInfo *info)
+{
+ struct netr_DsRGetDCNameInfo *dc_info;
+
+ return dsgetdcname(mem_ctx,
+ msg_ctx,
+ domain_name,
+ domain_guid,
+ site_name,
+ flags | DS_FORCE_REDISCOVERY,
+ &dc_info);
+}
+
+/****************************************************************
+****************************************************************/
+
+static uint32_t get_cldap_reply_server_flags(union nbt_cldap_netlogon *r,
+ uint32_t nt_version)
+{
+ switch (nt_version & 0x0000001f) {
+ case 0:
+ case 1:
+ case 16:
+ case 17:
+ return 0;
+ case 2:
+ case 3:
+ case 18:
+ case 19:
+ return r->logon3.server_type;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ return r->logon5.server_type;
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ return r->logon13.server_type;
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ return r->logon15.server_type;
+ case 29:
+ case 30:
+ case 31:
+ return r->logon29.server_type;
+ default:
+ return 0;
+ }
+}
+
+/****************************************************************
+****************************************************************/
+
+#define RETURN_ON_FALSE(x) if (!x) return false;
+
+static bool check_cldap_reply_required_flags(uint32_t ret_flags,
+ uint32_t req_flags)
+{
+ if (ret_flags == 0) {
+ return true;
+ }
+
+ if (req_flags & DS_PDC_REQUIRED)
+ RETURN_ON_FALSE(ret_flags & NBT_SERVER_PDC);
+
+ if (req_flags & DS_GC_SERVER_REQUIRED)
+ RETURN_ON_FALSE(ret_flags & NBT_SERVER_GC);
+
+ if (req_flags & DS_ONLY_LDAP_NEEDED)
+ RETURN_ON_FALSE(ret_flags & NBT_SERVER_LDAP);
+
+ if ((req_flags & DS_DIRECTORY_SERVICE_REQUIRED) ||
+ (req_flags & DS_DIRECTORY_SERVICE_PREFERRED))
+ RETURN_ON_FALSE(ret_flags & NBT_SERVER_DS);
+
+ if (req_flags & DS_KDC_REQUIRED)
+ RETURN_ON_FALSE(ret_flags & NBT_SERVER_KDC);
+
+ if (req_flags & DS_TIMESERV_REQUIRED)
+ RETURN_ON_FALSE(ret_flags & NBT_SERVER_TIMESERV);
+
+ if (req_flags & DS_WRITABLE_REQUIRED)
+ RETURN_ON_FALSE(ret_flags & NBT_SERVER_WRITABLE);
+
+ return true;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS dsgetdcname_cache_fetch(TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ struct GUID *domain_guid,
+ uint32_t flags,
+ const char *site_name,
+ struct netr_DsRGetDCNameInfo **info_p,
+ bool *expired)
+{
+ char *key;
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+ struct netr_DsRGetDCNameInfo *info;
+ union nbt_cldap_netlogon p;
+ struct nbt_cldap_netlogon_29 r;
+ NTSTATUS status;
+
+ if (!gencache_init()) {
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ key = dsgetdcname_cache_key(mem_ctx, domain_name);
+ if (!key) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!gencache_get_data_blob(key, &blob, expired)) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ info = TALLOC_ZERO_P(mem_ctx, struct netr_DsRGetDCNameInfo);
+ if (!info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, &r,
+ (ndr_pull_flags_fn_t)ndr_pull_nbt_cldap_netlogon_29);
+
+ data_blob_free(&blob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ dsgetdcname_cache_delete(mem_ctx, domain_name);
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ p.logon29 = r;
+
+ status = make_dc_info_from_cldap_reply(mem_ctx, flags, NULL,
+ 29,
+ &p, &info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_DEBUG(netr_DsRGetDCNameInfo, info);
+ }
+
+ /* check flags */
+ if (!check_cldap_reply_required_flags(info->dc_flags, flags)) {
+ DEBUG(10,("invalid flags\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if ((flags & DS_IP_REQUIRED) &&
+ (info->dc_address_type != DS_ADDRESS_TYPE_INET)) {
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+
+ *info_p = info;
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS dsgetdcname_cached(TALLOC_CTX *mem_ctx,
+ struct messaging_context *msg_ctx,
+ const char *domain_name,
+ struct GUID *domain_guid,
+ uint32_t flags,
+ const char *site_name,
+ struct netr_DsRGetDCNameInfo **info)
+{
+ NTSTATUS status;
+ bool expired = false;
+
+ status = dsgetdcname_cache_fetch(mem_ctx, domain_name, domain_guid,
+ flags, site_name, info, &expired);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10,("dsgetdcname_cached: cache fetch failed with: %s\n",
+ nt_errstr(status)));
+ return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+ }
+
+ if (flags & DS_BACKGROUND_ONLY) {
+ return status;
+ }
+
+ if (expired) {
+ status = dsgetdcname_cache_refresh(mem_ctx, msg_ctx,
+ domain_name,
+ domain_guid, flags,
+ site_name, *info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ return status;
+}
+
+/****************************************************************
+****************************************************************/
+
+static bool check_allowed_required_flags(uint32_t flags,
+ const char *site_name)
+{
+ uint32_t return_type = flags & (DS_RETURN_FLAT_NAME|DS_RETURN_DNS_NAME);
+ uint32_t offered_type = flags & (DS_IS_FLAT_NAME|DS_IS_DNS_NAME);
+ uint32_t query_type = flags & (DS_BACKGROUND_ONLY|DS_FORCE_REDISCOVERY);
+
+ /* FIXME: check for DSGETDC_VALID_FLAGS and check for excluse bits
+ * (DS_PDC_REQUIRED, DS_KDC_REQUIRED, DS_GC_SERVER_REQUIRED) */
+
+ debug_dsdcinfo_flags(10, flags);
+
+ if ((flags & DS_TRY_NEXTCLOSEST_SITE) && site_name) {
+ return false;
+ }
+
+ if (return_type == (DS_RETURN_FLAT_NAME|DS_RETURN_DNS_NAME)) {
+ return false;
+ }
+
+ if (offered_type == (DS_IS_DNS_NAME|DS_IS_FLAT_NAME)) {
+ return false;
+ }
+
+ if (query_type == (DS_BACKGROUND_ONLY|DS_FORCE_REDISCOVERY)) {
+ return false;
+ }
+
+#if 0
+ if ((flags & DS_RETURN_DNS_NAME) && (!(flags & DS_IP_REQUIRED))) {
+ printf("gd: here5 \n");
+ return false;
+ }
+#endif
+ return true;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS discover_dc_netbios(TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ uint32_t flags,
+ struct ip_service_name **returned_dclist,
+ int *returned_count)
+{
+ NTSTATUS status;
+ enum nbt_name_type name_type = NBT_NAME_LOGON;
+ struct ip_service *iplist;
+ int i;
+ struct ip_service_name *dclist = NULL;
+ int count;
+
+ *returned_dclist = NULL;
+ *returned_count = 0;
+
+ if (lp_disable_netbios()) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ if (flags & DS_PDC_REQUIRED) {
+ name_type = NBT_NAME_PDC;
+ }
+
+ status = internal_resolve_name(domain_name, name_type, NULL,
+ &iplist, &count,
+ "lmhosts wins bcast");
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10,("discover_dc_netbios: failed to find DC\n"));
+ return status;
+ }
+
+ dclist = TALLOC_ZERO_ARRAY(mem_ctx, struct ip_service_name, count);
+ if (!dclist) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<count; i++) {
+
+ char addr[INET6_ADDRSTRLEN];
+ struct ip_service_name *r = &dclist[i];
+
+ print_sockaddr(addr, sizeof(addr),
+ &iplist[i].ss);
+
+ r->ss = iplist[i].ss;
+ r->port = iplist[i].port;
+ r->hostname = talloc_strdup(mem_ctx, addr);
+ if (!r->hostname) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ }
+
+ *returned_dclist = dclist;
+ *returned_count = count;
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS discover_dc_dns(TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ struct GUID *domain_guid,
+ uint32_t flags,
+ const char *site_name,
+ struct ip_service_name **returned_dclist,
+ int *return_count)
+{
+ int i, j;
+ NTSTATUS status;
+ struct dns_rr_srv *dcs = NULL;
+ int numdcs = 0;
+ int numaddrs = 0;
+ struct ip_service_name *dclist = NULL;
+ int count = 0;
+
+ if (flags & DS_PDC_REQUIRED) {
+ status = ads_dns_query_pdc(mem_ctx, domain_name,
+ &dcs, &numdcs);
+ } else if (flags & DS_GC_SERVER_REQUIRED) {
+ status = ads_dns_query_gcs(mem_ctx, domain_name, site_name,
+ &dcs, &numdcs);
+ } else if (flags & DS_KDC_REQUIRED) {
+ status = ads_dns_query_kdcs(mem_ctx, domain_name, site_name,
+ &dcs, &numdcs);
+ } else if (flags & DS_DIRECTORY_SERVICE_REQUIRED) {
+ status = ads_dns_query_dcs(mem_ctx, domain_name, site_name,
+ &dcs, &numdcs);
+ } else if (domain_guid) {
+ status = ads_dns_query_dcs_guid(mem_ctx, domain_name,
+ domain_guid, &dcs, &numdcs);
+ } else {
+ status = ads_dns_query_dcs(mem_ctx, domain_name, site_name,
+ &dcs, &numdcs);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (numdcs == 0) {
+ return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+ }
+
+ for (i=0;i<numdcs;i++) {
+ numaddrs += MAX(dcs[i].num_ips,1);
+ }
+
+ dclist = TALLOC_ZERO_ARRAY(mem_ctx,
+ struct ip_service_name,
+ numaddrs);
+ if (!dclist) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* now unroll the list of IP addresses */
+
+ *return_count = 0;
+ i = 0;
+ j = 0;
+
+ while ((i < numdcs) && (count < numaddrs)) {
+
+ struct ip_service_name *r = &dclist[count];
+
+ r->port = dcs[count].port;
+ r->hostname = dcs[count].hostname;
+
+ /* If we don't have an IP list for a name, lookup it up */
+
+ if (!dcs[i].ss_s) {
+ interpret_string_addr(&r->ss, dcs[i].hostname, 0);
+ i++;
+ j = 0;
+ } else {
+ /* use the IP addresses from the SRV sresponse */
+
+ if (j >= dcs[i].num_ips) {
+ i++;
+ j = 0;
+ continue;
+ }
+
+ r->ss = dcs[i].ss_s[j];
+ j++;
+ }
+
+ /* make sure it is a valid IP. I considered checking the
+ * negative connection cache, but this is the wrong place for
+ * it. Maybe only as a hac. After think about it, if all of
+ * the IP addresses retuend from DNS are dead, what hope does a
+ * netbios name lookup have? The standard reason for falling
+ * back to netbios lookups is that our DNS server doesn't know
+ * anything about the DC's -- jerry */
+
+ if (!is_zero_addr(&r->ss)) {
+ count++;
+ continue;
+ }
+ }
+
+ *returned_dclist = dclist;
+ *return_count = count;
+
+ if (count > 0) {
+ return NT_STATUS_OK;
+ }
+
+ return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS make_domain_controller_info(TALLOC_CTX *mem_ctx,
+ const char *dc_unc,
+ const char *dc_address,
+ uint32_t dc_address_type,
+ const struct GUID *domain_guid,
+ const char *domain_name,
+ const char *forest_name,
+ uint32_t flags,
+ const char *dc_site_name,
+ const char *client_site_name,
+ struct netr_DsRGetDCNameInfo **info_out)
+{
+ struct netr_DsRGetDCNameInfo *info;
+
+ info = TALLOC_ZERO_P(mem_ctx, struct netr_DsRGetDCNameInfo);
+ NT_STATUS_HAVE_NO_MEMORY(info);
+
+ if (dc_unc) {
+ info->dc_unc = talloc_strdup(mem_ctx, dc_unc);
+ NT_STATUS_HAVE_NO_MEMORY(info->dc_unc);
+ }
+
+ if (dc_address) {
+ if (!(dc_address[0] == '\\' && dc_address[1] == '\\')) {
+ info->dc_address = talloc_asprintf(mem_ctx, "\\\\%s",
+ dc_address);
+ } else {
+ info->dc_address = talloc_strdup(mem_ctx, dc_address);
+ }
+ NT_STATUS_HAVE_NO_MEMORY(info->dc_address);
+ }
+
+ info->dc_address_type = dc_address_type;
+
+ if (domain_guid) {
+ info->domain_guid = *domain_guid;
+ }
+
+ if (domain_name) {
+ info->domain_name = talloc_strdup(mem_ctx, domain_name);
+ NT_STATUS_HAVE_NO_MEMORY(info->domain_name);
+ }
+
+ if (forest_name && *forest_name) {
+ info->forest_name = talloc_strdup(mem_ctx, forest_name);
+ NT_STATUS_HAVE_NO_MEMORY(info->forest_name);
+ flags |= DS_DNS_FOREST;
+ }
+
+ info->dc_flags = flags;
+
+ if (dc_site_name) {
+ info->dc_site_name = talloc_strdup(mem_ctx, dc_site_name);
+ NT_STATUS_HAVE_NO_MEMORY(info->dc_site_name);
+ }
+
+ if (client_site_name) {
+ info->client_site_name = talloc_strdup(mem_ctx,
+ client_site_name);
+ NT_STATUS_HAVE_NO_MEMORY(info->client_site_name);
+ }
+
+ *info_out = info;
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static void map_dc_and_domain_names(uint32_t flags,
+ const char *dc_name,
+ const char *domain_name,
+ const char *dns_dc_name,
+ const char *dns_domain_name,
+ uint32_t *dc_flags,
+ const char **hostname_p,
+ const char **domain_p)
+{
+ switch (flags & 0xf0000000) {
+ case DS_RETURN_FLAT_NAME:
+ if (dc_name && domain_name &&
+ *dc_name && *domain_name) {
+ *hostname_p = dc_name;
+ *domain_p = domain_name;
+ break;
+ }
+ case DS_RETURN_DNS_NAME:
+ default:
+ if (dns_dc_name && dns_domain_name &&
+ *dns_dc_name && *dns_domain_name) {
+ *hostname_p = dns_dc_name;
+ *domain_p = dns_domain_name;
+ *dc_flags |= DS_DNS_DOMAIN | DS_DNS_CONTROLLER;
+ break;
+ }
+ if (dc_name && domain_name &&
+ *dc_name && *domain_name) {
+ *hostname_p = dc_name;
+ *domain_p = domain_name;
+ break;
+ }
+ }
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS make_dc_info_from_cldap_reply(TALLOC_CTX *mem_ctx,
+ uint32_t flags,
+ struct sockaddr_storage *ss,
+ uint32_t nt_version,
+ union nbt_cldap_netlogon *r,
+ struct netr_DsRGetDCNameInfo **info)
+{
+ const char *dc_hostname = NULL;
+ const char *dc_domain_name = NULL;
+ const char *dc_address = NULL;
+ const char *dc_forest = NULL;
+ uint32_t dc_address_type = 0;
+ uint32_t dc_flags = 0;
+ struct GUID *dc_domain_guid = NULL;
+ const char *dc_server_site = NULL;
+ const char *dc_client_site = NULL;
+
+ char addr[INET6_ADDRSTRLEN];
+
+ if (ss) {
+ print_sockaddr(addr, sizeof(addr), ss);
+ dc_address = addr;
+ dc_address_type = DS_ADDRESS_TYPE_INET;
+ }
+
+ switch (nt_version & 0x0000001f) {
+ case 0:
+ case 1:
+ case 16:
+ case 17:
+ if (!ss) {
+ dc_address = r->logon1.pdc_name;
+ dc_address_type = DS_ADDRESS_TYPE_NETBIOS;
+ }
+
+ map_dc_and_domain_names(flags,
+ r->logon1.pdc_name,
+ r->logon1.domain_name,
+ NULL,
+ NULL,
+ &dc_flags,
+ &dc_hostname,
+ &dc_domain_name);
+
+ if (flags & DS_PDC_REQUIRED) {
+ dc_flags = NBT_SERVER_WRITABLE | NBT_SERVER_PDC;
+ }
+ break;
+ case 2:
+ case 3:
+ case 18:
+ case 19:
+ if (!ss) {
+ dc_address = r->logon3.pdc_ip;
+ dc_address_type = DS_ADDRESS_TYPE_INET;
+ }
+
+ map_dc_and_domain_names(flags,
+ r->logon3.pdc_name,
+ r->logon3.domain_name,
+ r->logon3.pdc_dns_name,
+ r->logon3.dns_domain,
+ &dc_flags,
+ &dc_hostname,
+ &dc_domain_name);
+
+ dc_flags |= r->logon3.server_type;
+ dc_forest = r->logon3.forest;
+ dc_domain_guid = &r->logon3.domain_uuid;
+
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ if (!ss) {
+ dc_address = r->logon5.pdc_name;
+ dc_address_type = DS_ADDRESS_TYPE_NETBIOS;
+ }
+
+ map_dc_and_domain_names(flags,
+ r->logon5.pdc_name,
+ r->logon5.domain,
+ r->logon5.pdc_dns_name,
+ r->logon5.dns_domain,
+ &dc_flags,
+ &dc_hostname,
+ &dc_domain_name);
+
+ dc_flags |= r->logon5.server_type;
+ dc_forest = r->logon5.forest;
+ dc_domain_guid = &r->logon5.domain_uuid;
+ dc_server_site = r->logon5.server_site;
+ dc_client_site = r->logon5.client_site;
+
+ break;
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ if (!ss) {
+ dc_address = r->logon13.dc_sock_addr.pdc_ip;
+ dc_address_type = DS_ADDRESS_TYPE_INET;
+ }
+
+ map_dc_and_domain_names(flags,
+ r->logon13.pdc_name,
+ r->logon13.domain,
+ r->logon13.pdc_dns_name,
+ r->logon13.dns_domain,
+ &dc_flags,
+ &dc_hostname,
+ &dc_domain_name);
+
+ dc_flags |= r->logon13.server_type;
+ dc_forest = r->logon13.forest;
+ dc_domain_guid = &r->logon13.domain_uuid;
+ dc_server_site = r->logon13.server_site;
+ dc_client_site = r->logon13.client_site;
+
+ break;
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ if (!ss) {
+ dc_address = r->logon15.pdc_name;
+ dc_address_type = DS_ADDRESS_TYPE_NETBIOS;
+ }
+
+ map_dc_and_domain_names(flags,
+ r->logon15.pdc_name,
+ r->logon15.domain,
+ r->logon15.pdc_dns_name,
+ r->logon15.dns_domain,
+ &dc_flags,
+ &dc_hostname,
+ &dc_domain_name);
+
+ dc_flags |= r->logon15.server_type;
+ dc_forest = r->logon15.forest;
+ dc_domain_guid = &r->logon15.domain_uuid;
+ dc_server_site = r->logon15.server_site;
+ dc_client_site = r->logon15.client_site;
+
+ break;
+ case 29:
+ case 30:
+ case 31:
+ if (!ss) {
+ dc_address = r->logon29.dc_sock_addr.pdc_ip;
+ dc_address_type = DS_ADDRESS_TYPE_INET;
+ }
+
+ map_dc_and_domain_names(flags,
+ r->logon29.pdc_name,
+ r->logon29.domain,
+ r->logon29.pdc_dns_name,
+ r->logon29.dns_domain,
+ &dc_flags,
+ &dc_hostname,
+ &dc_domain_name);
+
+ dc_flags |= r->logon29.server_type;
+ dc_forest = r->logon29.forest;
+ dc_domain_guid = &r->logon29.domain_uuid;
+ dc_server_site = r->logon29.server_site;
+ dc_client_site = r->logon29.client_site;
+
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return make_domain_controller_info(mem_ctx,
+ dc_hostname,
+ dc_address,
+ dc_address_type,
+ dc_domain_guid,
+ dc_domain_name,
+ dc_forest,
+ dc_flags,
+ dc_server_site,
+ dc_client_site,
+ info);
+}
+
+/****************************************************************
+****************************************************************/
+
+static uint32_t map_ds_flags_to_nt_version(uint32_t flags)
+{
+ uint32_t nt_version = 0;
+
+ if (flags & DS_PDC_REQUIRED) {
+ nt_version |= NETLOGON_VERSION_PDC;
+ }
+
+ if (flags & DS_GC_SERVER_REQUIRED) {
+ nt_version |= NETLOGON_VERSION_GC;
+ }
+
+ if (flags & DS_TRY_NEXTCLOSEST_SITE) {
+ nt_version |= NETLOGON_VERSION_WITH_CLOSEST_SITE;
+ }
+
+ if (flags & DS_IP_REQUIRED) {
+ nt_version |= NETLOGON_VERSION_IP;
+ }
+
+ return nt_version;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS process_dc_dns(TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ uint32_t flags,
+ struct ip_service_name *dclist,
+ int num_dcs,
+ struct netr_DsRGetDCNameInfo **info)
+{
+ int i = 0;
+ bool valid_dc = false;
+ union nbt_cldap_netlogon *r = NULL;
+ uint32_t nt_version = NETLOGON_VERSION_5 |
+ NETLOGON_VERSION_5EX;
+ uint32_t ret_flags = 0;
+ NTSTATUS status;
+
+ nt_version |= map_ds_flags_to_nt_version(flags);
+
+ for (i=0; i<num_dcs; i++) {
+
+ DEBUG(10,("LDAP ping to %s\n", dclist[i].hostname));
+
+ if (ads_cldap_netlogon(mem_ctx, dclist[i].hostname,
+ domain_name,
+ &nt_version,
+ &r))
+ {
+ ret_flags = get_cldap_reply_server_flags(r, nt_version);
+
+ if (check_cldap_reply_required_flags(ret_flags, flags)) {
+ valid_dc = true;
+ break;
+ }
+ }
+
+ continue;
+ }
+
+ if (!valid_dc) {
+ return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+ }
+
+ status = make_dc_info_from_cldap_reply(mem_ctx, flags, &dclist[i].ss,
+ nt_version, r, info);
+ if (NT_STATUS_IS_OK(status)) {
+ return store_cldap_reply(mem_ctx, flags, &dclist[i].ss,
+ nt_version, r);
+ }
+
+ return status;
+}
+
+/****************************************************************
+****************************************************************/
+
+static struct event_context *ev_context(void)
+{
+ static struct event_context *ctx;
+
+ if (!ctx && !(ctx = event_context_init(NULL))) {
+ smb_panic("Could not init event context");
+ }
+ return ctx;
+}
+
+/****************************************************************
+****************************************************************/
+
+static struct messaging_context *msg_context(TALLOC_CTX *mem_ctx)
+{
+ static struct messaging_context *ctx;
+
+ if (!ctx && !(ctx = messaging_init(mem_ctx, server_id_self(),
+ ev_context()))) {
+ smb_panic("Could not init messaging context");
+ }
+ return ctx;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS process_dc_netbios(TALLOC_CTX *mem_ctx,
+ struct messaging_context *msg_ctx,
+ const char *domain_name,
+ uint32_t flags,
+ struct ip_service_name *dclist,
+ int num_dcs,
+ struct netr_DsRGetDCNameInfo **info)
+{
+ struct sockaddr_storage ss;
+ struct ip_service ip_list;
+ enum nbt_name_type name_type = NBT_NAME_LOGON;
+ NTSTATUS status;
+ int i;
+ const char *dc_name = NULL;
+ fstring tmp_dc_name;
+ union nbt_cldap_netlogon *r = NULL;
+ bool store_cache = false;
+ uint32_t nt_version = NETLOGON_VERSION_1 |
+ NETLOGON_VERSION_5 |
+ NETLOGON_VERSION_5EX_WITH_IP;
+
+ if (!msg_ctx) {
+ msg_ctx = msg_context(mem_ctx);
+ }
+
+ if (flags & DS_PDC_REQUIRED) {
+ name_type = NBT_NAME_PDC;
+ }
+
+ nt_version |= map_ds_flags_to_nt_version(flags);
+
+ DEBUG(10,("process_dc_netbios\n"));
+
+ for (i=0; i<num_dcs; i++) {
+
+ ip_list.ss = dclist[i].ss;
+ ip_list.port = 0;
+
+ if (!interpret_string_addr(&ss, dclist[i].hostname, AI_NUMERICHOST)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (send_getdc_request(mem_ctx, msg_ctx,
+ &dclist[i].ss, domain_name,
+ NULL, nt_version))
+ {
+ int k;
+ smb_msleep(300);
+ for (k=0; k<5; k++) {
+ if (receive_getdc_response(mem_ctx,
+ &dclist[i].ss,
+ domain_name,
+ &nt_version,
+ &dc_name,
+ &r)) {
+ store_cache = true;
+ namecache_store(dc_name, NBT_NAME_SERVER, 1, &ip_list);
+ goto make_reply;
+ }
+ smb_msleep(1500);
+ }
+ }
+
+ if (name_status_find(domain_name,
+ name_type,
+ NBT_NAME_SERVER,
+ &dclist[i].ss,
+ tmp_dc_name))
+ {
+ struct nbt_cldap_netlogon_1 logon1;
+
+ r = TALLOC_ZERO_P(mem_ctx, union nbt_cldap_netlogon);
+ NT_STATUS_HAVE_NO_MEMORY(r);
+
+ ZERO_STRUCT(logon1);
+
+ nt_version = NETLOGON_VERSION_1;
+
+ logon1.nt_version = nt_version;
+ logon1.pdc_name = tmp_dc_name;
+ logon1.domain_name = talloc_strdup_upper(mem_ctx, domain_name);
+ NT_STATUS_HAVE_NO_MEMORY(logon1.domain_name);
+
+ r->logon1 = logon1;
+
+ namecache_store(tmp_dc_name, NBT_NAME_SERVER, 1, &ip_list);
+
+ goto make_reply;
+ }
+ }
+
+ return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+
+ make_reply:
+
+ status = make_dc_info_from_cldap_reply(mem_ctx, flags, &dclist[i].ss,
+ nt_version, r, info);
+ if (NT_STATUS_IS_OK(status) && store_cache) {
+ return store_cldap_reply(mem_ctx, flags, &dclist[i].ss,
+ nt_version, r);
+ }
+
+ return status;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS dsgetdcname_rediscover(TALLOC_CTX *mem_ctx,
+ struct messaging_context *msg_ctx,
+ const char *domain_name,
+ struct GUID *domain_guid,
+ uint32_t flags,
+ const char *site_name,
+ struct netr_DsRGetDCNameInfo **info)
+{
+ NTSTATUS status = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+ struct ip_service_name *dclist = NULL;
+ int num_dcs;
+
+ DEBUG(10,("dsgetdcname_rediscover\n"));
+
+ if (flags & DS_IS_FLAT_NAME) {
+
+ status = discover_dc_netbios(mem_ctx, domain_name, flags,
+ &dclist, &num_dcs);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return process_dc_netbios(mem_ctx, msg_ctx, domain_name, flags,
+ dclist, num_dcs, info);
+ }
+
+ if (flags & DS_IS_DNS_NAME) {
+
+ status = discover_dc_dns(mem_ctx, domain_name, domain_guid,
+ flags, site_name, &dclist, &num_dcs);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return process_dc_dns(mem_ctx, domain_name, flags,
+ dclist, num_dcs, info);
+ }
+
+ status = discover_dc_dns(mem_ctx, domain_name, domain_guid, flags,
+ site_name, &dclist, &num_dcs);
+
+ if (NT_STATUS_IS_OK(status) && num_dcs != 0) {
+
+ status = process_dc_dns(mem_ctx, domain_name, flags, dclist,
+ num_dcs, info);
+ if (NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ status = discover_dc_netbios(mem_ctx, domain_name, flags, &dclist,
+ &num_dcs);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return process_dc_netbios(mem_ctx, msg_ctx, domain_name, flags, dclist,
+ num_dcs, info);
+}
+
+/********************************************************************
+ dsgetdcname.
+
+ This will be the only public function here.
+********************************************************************/
+
+NTSTATUS dsgetdcname(TALLOC_CTX *mem_ctx,
+ struct messaging_context *msg_ctx,
+ const char *domain_name,
+ struct GUID *domain_guid,
+ const char *site_name,
+ uint32_t flags,
+ struct netr_DsRGetDCNameInfo **info)
+{
+ NTSTATUS status = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+ struct netr_DsRGetDCNameInfo *myinfo = NULL;
+ char *query_site = NULL;
+
+ DEBUG(10,("dsgetdcname: domain_name: %s, "
+ "domain_guid: %s, site_name: %s, flags: 0x%08x\n",
+ domain_name,
+ domain_guid ? GUID_string(mem_ctx, domain_guid) : "(null)",
+ site_name, flags));
+
+ *info = NULL;
+
+ if (!check_allowed_required_flags(flags, site_name)) {
+ DEBUG(0,("invalid flags specified\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!site_name) {
+ query_site = sitename_fetch(domain_name);
+ } else {
+ query_site = SMB_STRDUP(site_name);
+ }
+
+ if (flags & DS_FORCE_REDISCOVERY) {
+ goto rediscover;
+ }
+
+ status = dsgetdcname_cached(mem_ctx, msg_ctx, domain_name, domain_guid,
+ flags, query_site, &myinfo);
+ if (NT_STATUS_IS_OK(status)) {
+ *info = myinfo;
+ goto done;
+ }
+
+ if (flags & DS_BACKGROUND_ONLY) {
+ goto done;
+ }
+
+ rediscover:
+ status = dsgetdcname_rediscover(mem_ctx, msg_ctx, domain_name,
+ domain_guid, flags, query_site,
+ &myinfo);
+
+ if (NT_STATUS_IS_OK(status)) {
+ *info = myinfo;
+ }
+
+ done:
+ SAFE_FREE(query_site);
+
+ return status;
+}
diff --git a/source3/libsmb/errormap.c b/source3/libsmb/errormap.c
new file mode 100644
index 0000000000..4ec30f7e17
--- /dev/null
+++ b/source3/libsmb/errormap.c
@@ -0,0 +1,1609 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * error mapping functions
+ * Copyright (C) Andrew Tridgell 2001
+ * Copyright (C) Andrew Bartlett 2001
+ * Copyright (C) Tim Potter 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+
+/* 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 */
+static const 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, ERRinvalidparam, 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},
+/*
+ * Not an official error, as only bit 0x80000000, not bits 0xC0000000 are set.
+ */
+ {ERRDOS, ERRmoredata, STATUS_BUFFER_OVERFLOW},
+ {ERRDOS, ERRnofiles, STATUS_NO_MORE_FILES},
+ {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, ERRinvalidpath, NT_STATUS_OBJECT_PATH_INVALID},
+ {ERRDOS, ERRbadpath, NT_STATUS_OBJECT_PATH_NOT_FOUND},
+ {ERRDOS, ERRinvalidpath, 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, ERRbaddirectory, 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, ERRunknownlevel, 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 */
+static const 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_ACCESS_DENIED},
+ {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, STATUS_NO_MORE_FILES},
+ {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, ERRunknownlevel, 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 */
+static const 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}};
+
+static const struct {
+ WERROR werror;
+ NTSTATUS ntstatus;
+} werror_to_ntstatus_map[] = {
+ { W_ERROR(0x5), NT_STATUS_ACCESS_DENIED },
+ { WERR_OK, NT_STATUS_OK }
+};
+
+/*****************************************************************************
+convert a dos eclas/ecode to a NT status32 code
+ *****************************************************************************/
+NTSTATUS dos_to_ntstatus(uint8 eclass, uint32 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; !W_ERROR_IS_OK(werror_to_ntstatus_map[i].werror); i++) {
+ if (W_ERROR_V(error) ==
+ W_ERROR_V(werror_to_ntstatus_map[i].werror)) {
+ return werror_to_ntstatus_map[i].ntstatus;
+ }
+ }
+
+ 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);
+}
+
+#if defined(HAVE_GSSAPI)
+/*******************************************************************************
+ Map between gssapi errors and NT status. I made these up :-(. JRA.
+*******************************************************************************/
+
+static const struct {
+ unsigned long gss_err;
+ NTSTATUS ntstatus;
+} gss_to_ntstatus_errormap[] = {
+#if defined(GSS_S_CALL_INACCESSIBLE_READ)
+ {GSS_S_CALL_INACCESSIBLE_READ, NT_STATUS_INVALID_PARAMETER},
+#endif
+#if defined(GSS_S_CALL_INACCESSIBLE_WRITE)
+ {GSS_S_CALL_INACCESSIBLE_WRITE, NT_STATUS_INVALID_PARAMETER},
+#endif
+#if defined(GSS_S_CALL_BAD_STRUCTURE)
+ {GSS_S_CALL_BAD_STRUCTURE, NT_STATUS_INVALID_PARAMETER},
+#endif
+#if defined(GSS_S_BAD_MECH)
+ {GSS_S_BAD_MECH, NT_STATUS_INVALID_PARAMETER},
+#endif
+#if defined(GSS_S_BAD_NAME)
+ {GSS_S_BAD_NAME, NT_STATUS_INVALID_ACCOUNT_NAME},
+#endif
+#if defined(GSS_S_BAD_NAMETYPE)
+ {GSS_S_BAD_NAMETYPE, NT_STATUS_INVALID_PARAMETER},
+#endif
+#if defined(GSS_S_BAD_BINDINGS)
+ {GSS_S_BAD_BINDINGS, NT_STATUS_INVALID_PARAMETER},
+#endif
+#if defined(GSS_S_BAD_STATUS)
+ {GSS_S_BAD_STATUS, NT_STATUS_UNSUCCESSFUL},
+#endif
+#if defined(GSS_S_BAD_SIG)
+ {GSS_S_BAD_SIG, NT_STATUS_ACCESS_DENIED},
+#endif
+#if defined(GSS_S_NO_CRED)
+ {GSS_S_NO_CRED, NT_STATUS_ACCESS_DENIED},
+#endif
+#if defined(GSS_S_NO_CONTEXT)
+ {GSS_S_NO_CONTEXT, NT_STATUS_ACCESS_DENIED},
+#endif
+#if defined(GSS_S_DEFECTIVE_TOKEN)
+ {GSS_S_DEFECTIVE_TOKEN, NT_STATUS_ACCESS_DENIED},
+#endif
+#if defined(GSS_S_DEFECTIVE_CREDENTIAL)
+ {GSS_S_DEFECTIVE_CREDENTIAL, NT_STATUS_ACCESS_DENIED},
+#endif
+#if defined(GSS_S_CREDENTIALS_EXPIRED)
+ {GSS_S_CREDENTIALS_EXPIRED, NT_STATUS_PASSWORD_EXPIRED},
+#endif
+#if defined(GSS_S_CONTEXT_EXPIRED)
+ {GSS_S_CONTEXT_EXPIRED, NT_STATUS_PASSWORD_EXPIRED},
+#endif
+#if defined(GSS_S_BAD_QOP)
+ {GSS_S_BAD_QOP, NT_STATUS_ACCESS_DENIED},
+#endif
+#if defined(GSS_S_UNAUTHORIZED)
+ {GSS_S_UNAUTHORIZED, NT_STATUS_ACCESS_DENIED},
+#endif
+#if defined(GSS_S_UNAVAILABLE)
+ {GSS_S_UNAVAILABLE, NT_STATUS_UNSUCCESSFUL},
+#endif
+#if defined(GSS_S_DUPLICATE_ELEMENT)
+ {GSS_S_DUPLICATE_ELEMENT, NT_STATUS_INVALID_PARAMETER},
+#endif
+#if defined(GSS_S_NAME_NOT_MN)
+ {GSS_S_NAME_NOT_MN, NT_STATUS_INVALID_PARAMETER},
+#endif
+ { 0, NT_STATUS_OK }
+};
+
+/*********************************************************************
+ Map an NT error code from a gssapi error code.
+*********************************************************************/
+
+NTSTATUS map_nt_error_from_gss(uint32 gss_maj, uint32 minor)
+{
+ int i = 0;
+
+ if (gss_maj == GSS_S_COMPLETE) {
+ return NT_STATUS_OK;
+ }
+
+ if (gss_maj == GSS_S_CONTINUE_NEEDED) {
+ return NT_STATUS_MORE_PROCESSING_REQUIRED;
+ }
+
+ if (gss_maj == GSS_S_FAILURE) {
+ return map_nt_error_from_unix((int)minor);
+ }
+
+ /* Look through list */
+ while(gss_to_ntstatus_errormap[i].gss_err != 0) {
+ if (gss_to_ntstatus_errormap[i].gss_err == gss_maj) {
+ return gss_to_ntstatus_errormap[i].ntstatus;
+ }
+ i++;
+ }
+
+ /* Default return */
+ return NT_STATUS_ACCESS_DENIED;
+}
+#endif
diff --git a/source3/libsmb/libsmb_cache.c b/source3/libsmb/libsmb_cache.c
new file mode 100644
index 0000000000..bfacea368d
--- /dev/null
+++ b/source3/libsmb/libsmb_cache.c
@@ -0,0 +1,244 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client library implementation (server cache)
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmbclient.h"
+#include "libsmb_internal.h"
+
+/*
+ * Structure we use if internal caching mechanism is used
+ * nothing fancy here.
+ */
+struct smbc_server_cache {
+ char *server_name;
+ char *share_name;
+ char *workgroup;
+ char *username;
+ SMBCSRV *server;
+
+ struct smbc_server_cache *next, *prev;
+};
+
+
+
+/*
+ * Add a new connection to the server cache.
+ * This function is only used if the external cache is not enabled
+ */
+int
+SMBC_add_cached_server(SMBCCTX * context,
+ SMBCSRV * newsrv,
+ const char * server,
+ const char * share,
+ const char * workgroup,
+ const char * username)
+{
+ struct smbc_server_cache * srvcache = NULL;
+
+ if (!(srvcache = SMB_MALLOC_P(struct smbc_server_cache))) {
+ errno = ENOMEM;
+ DEBUG(3, ("Not enough space for server cache allocation\n"));
+ return 1;
+ }
+
+ ZERO_STRUCTP(srvcache);
+
+ srvcache->server = newsrv;
+
+ srvcache->server_name = SMB_STRDUP(server);
+ if (!srvcache->server_name) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ srvcache->share_name = SMB_STRDUP(share);
+ if (!srvcache->share_name) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ srvcache->workgroup = SMB_STRDUP(workgroup);
+ if (!srvcache->workgroup) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ srvcache->username = SMB_STRDUP(username);
+ if (!srvcache->username) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ DLIST_ADD(context->internal->server_cache, srvcache);
+ return 0;
+
+failed:
+ SAFE_FREE(srvcache->server_name);
+ SAFE_FREE(srvcache->share_name);
+ SAFE_FREE(srvcache->workgroup);
+ SAFE_FREE(srvcache->username);
+ SAFE_FREE(srvcache);
+
+ return 1;
+}
+
+
+
+/*
+ * Search the server cache for a server
+ * returns server handle on success, NULL on error (not found)
+ * This function is only used if the external cache is not enabled
+ */
+SMBCSRV *
+SMBC_get_cached_server(SMBCCTX * context,
+ const char * server,
+ const char * share,
+ const char * workgroup,
+ const char * user)
+{
+ struct smbc_server_cache * srv = NULL;
+
+ /* Search the cache lines */
+ for (srv = context->internal->server_cache; srv; srv = srv->next) {
+
+ if (strcmp(server,srv->server_name) == 0 &&
+ strcmp(workgroup,srv->workgroup) == 0 &&
+ strcmp(user, srv->username) == 0) {
+
+ /* If the share name matches, we're cool */
+ if (strcmp(share, srv->share_name) == 0) {
+ return srv->server;
+ }
+
+ /*
+ * We only return an empty share name or the attribute
+ * server on an exact match (which would have been
+ * caught above).
+ */
+ if (*share == '\0' || strcmp(share, "*IPC$") == 0)
+ continue;
+
+ /*
+ * Never return an empty share name or the attribute
+ * server if it wasn't what was requested.
+ */
+ if (*srv->share_name == '\0' ||
+ strcmp(srv->share_name, "*IPC$") == 0)
+ continue;
+
+ /*
+ * If we're only allowing one share per server, then
+ * a connection to the server (other than the
+ * attribute server connection) is cool.
+ */
+ if (smbc_getOptionOneSharePerServer(context)) {
+ /*
+ * The currently connected share name
+ * doesn't match the requested share, so
+ * disconnect from the current share.
+ */
+ if (! cli_tdis(srv->server->cli)) {
+ /* Sigh. Couldn't disconnect. */
+ cli_shutdown(srv->server->cli);
+ srv->server->cli = NULL;
+ smbc_getFunctionRemoveCachedServer(context)(context, srv->server);
+ continue;
+ }
+
+ /*
+ * Save the new share name. We've
+ * disconnected from the old share, and are
+ * about to connect to the new one.
+ */
+ SAFE_FREE(srv->share_name);
+ srv->share_name = SMB_STRDUP(share);
+ if (!srv->share_name) {
+ /* Out of memory. */
+ cli_shutdown(srv->server->cli);
+ srv->server->cli = NULL;
+ smbc_getFunctionRemoveCachedServer(context)(context, srv->server);
+ continue;
+ }
+
+
+ return srv->server;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+
+/*
+ * Search the server cache for a server and remove it
+ * returns 0 on success
+ * This function is only used if the external cache is not enabled
+ */
+int
+SMBC_remove_cached_server(SMBCCTX * context,
+ SMBCSRV * server)
+{
+ struct smbc_server_cache * srv = NULL;
+
+ for (srv = context->internal->server_cache; srv; srv = srv->next) {
+ if (server == srv->server) {
+
+ /* remove this sucker */
+ DLIST_REMOVE(context->internal->server_cache, srv);
+ SAFE_FREE(srv->server_name);
+ SAFE_FREE(srv->share_name);
+ SAFE_FREE(srv->workgroup);
+ SAFE_FREE(srv->username);
+ SAFE_FREE(srv);
+ return 0;
+ }
+ }
+ /* server not found */
+ return 1;
+}
+
+
+/*
+ * Try to remove all the servers in cache
+ * returns 1 on failure and 0 if all servers could be removed.
+ */
+int
+SMBC_purge_cached_servers(SMBCCTX * context)
+{
+ struct smbc_server_cache * srv;
+ struct smbc_server_cache * next;
+ int could_not_purge_all = 0;
+
+ for (srv = context->internal->server_cache,
+ next = (srv ? srv->next :NULL);
+ srv;
+ srv = next,
+ next = (srv ? srv->next : NULL)) {
+
+ if (SMBC_remove_unused_server(context, srv->server)) {
+ /* could not be removed */
+ could_not_purge_all = 1;
+ }
+ }
+ return could_not_purge_all;
+}
diff --git a/source3/libsmb/libsmb_compat.c b/source3/libsmb/libsmb_compat.c
new file mode 100644
index 0000000000..ad8fd922db
--- /dev/null
+++ b/source3/libsmb/libsmb_compat.c
@@ -0,0 +1,542 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client library implementation (Old interface compatibility)
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+ Copyright (C) Derrell Lipman 2003, 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "includes.h"
+#include "libsmb_internal.h"
+
+struct smbc_compat_fdlist {
+ SMBCFILE * file;
+ int fd;
+ struct smbc_compat_fdlist *next, *prev;
+};
+
+static SMBCCTX * statcont = NULL;
+static int smbc_compat_initialized = 0;
+static int smbc_compat_nextfd = 0;
+static struct smbc_compat_fdlist * smbc_compat_fd_in_use = NULL;
+static struct smbc_compat_fdlist * smbc_compat_fd_avail = NULL;
+
+/* Find an fd and return the SMBCFILE * or NULL on failure */
+static SMBCFILE *
+find_fd(int fd)
+{
+ struct smbc_compat_fdlist * f = smbc_compat_fd_in_use;
+ while (f) {
+ if (f->fd == fd)
+ return f->file;
+ f = f->next;
+ }
+ return NULL;
+}
+
+/* Add an fd, returns 0 on success, -1 on error with errno set */
+static int
+add_fd(SMBCFILE * file)
+{
+ struct smbc_compat_fdlist * f = smbc_compat_fd_avail;
+
+ if (f) {
+ /* We found one that's available */
+ DLIST_REMOVE(smbc_compat_fd_avail, f);
+
+ } else {
+ /*
+ * None were available, so allocate one. Keep the number of
+ * file descriptors determinate. This allows the application
+ * to allocate bitmaps or mapping of file descriptors based on
+ * a known maximum number of file descriptors that will ever
+ * be returned.
+ */
+ if (smbc_compat_nextfd >= FD_SETSIZE) {
+ errno = EMFILE;
+ return -1;
+ }
+
+ f = SMB_MALLOC_P(struct smbc_compat_fdlist);
+ if (!f) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ f->fd = SMBC_BASE_FD + smbc_compat_nextfd++;
+ }
+
+ f->file = file;
+ DLIST_ADD(smbc_compat_fd_in_use, f);
+
+ return f->fd;
+}
+
+
+
+/* Delete an fd, returns 0 on success */
+static int
+del_fd(int fd)
+{
+ struct smbc_compat_fdlist * f = smbc_compat_fd_in_use;
+
+ while (f) {
+ if (f->fd == fd)
+ break;
+ f = f->next;
+ }
+
+ if (f) {
+ /* found */
+ DLIST_REMOVE(smbc_compat_fd_in_use, f);
+ f->file = NULL;
+ DLIST_ADD(smbc_compat_fd_avail, f);
+ return 0;
+ }
+ return 1;
+}
+
+
+
+int
+smbc_init(smbc_get_auth_data_fn fn,
+ int debug)
+{
+ if (!smbc_compat_initialized) {
+ statcont = smbc_new_context();
+ if (!statcont)
+ return -1;
+
+ smbc_setDebug(statcont, debug);
+ smbc_setFunctionAuthData(statcont, fn);
+
+ if (!smbc_init_context(statcont)) {
+ smbc_free_context(statcont, False);
+ return -1;
+ }
+
+ smbc_compat_initialized = 1;
+
+ return 0;
+ }
+ return 0;
+}
+
+
+SMBCCTX *
+smbc_set_context(SMBCCTX * context)
+{
+ SMBCCTX *old_context = statcont;
+
+ if (context) {
+ /* Save provided context. It must have been initialized! */
+ statcont = context;
+
+ /* You'd better know what you're doing. We won't help you. */
+ smbc_compat_initialized = 1;
+ }
+
+ return old_context;
+}
+
+
+int
+smbc_open(const char *furl,
+ int flags,
+ mode_t mode)
+{
+ SMBCFILE * file;
+ int fd;
+
+ file = smbc_getFunctionOpen(statcont)(statcont, furl, flags, mode);
+ if (!file)
+ return -1;
+
+ fd = add_fd(file);
+ if (fd == -1)
+ smbc_getFunctionClose(statcont)(statcont, file);
+ return fd;
+}
+
+
+int
+smbc_creat(const char *furl,
+ mode_t mode)
+{
+ SMBCFILE * file;
+ int fd;
+
+ file = smbc_getFunctionCreat(statcont)(statcont, furl, mode);
+ if (!file)
+ return -1;
+
+ fd = add_fd(file);
+ if (fd == -1) {
+ /* Hmm... should we delete the file too ? I guess we could try */
+ smbc_getFunctionClose(statcont)(statcont, file);
+ smbc_getFunctionUnlink(statcont)(statcont, furl);
+ }
+ return fd;
+}
+
+
+ssize_t
+smbc_read(int fd,
+ void *buf,
+ size_t bufsize)
+{
+ SMBCFILE * file = find_fd(fd);
+ return smbc_getFunctionRead(statcont)(statcont, file, buf, bufsize);
+}
+
+ssize_t
+smbc_write(int fd,
+ const void *buf,
+ size_t bufsize)
+{
+ SMBCFILE * file = find_fd(fd);
+ return smbc_getFunctionWrite(statcont)(statcont, file, buf, bufsize);
+}
+
+off_t
+smbc_lseek(int fd,
+ off_t offset,
+ int whence)
+{
+ SMBCFILE * file = find_fd(fd);
+ return smbc_getFunctionLseek(statcont)(statcont, file, offset, whence);
+}
+
+int
+smbc_close(int fd)
+{
+ SMBCFILE * file = find_fd(fd);
+ del_fd(fd);
+ return smbc_getFunctionClose(statcont)(statcont, file);
+}
+
+int
+smbc_unlink(const char *fname)
+{
+ return smbc_getFunctionUnlink(statcont)(statcont, fname);
+}
+
+int
+smbc_rename(const char *ourl,
+ const char *nurl)
+{
+ return smbc_getFunctionRename(statcont)(statcont, ourl,
+ statcont, nurl);
+}
+
+int
+smbc_opendir(const char *durl)
+{
+ SMBCFILE * file;
+ int fd;
+
+ file = smbc_getFunctionOpendir(statcont)(statcont, durl);
+ if (!file)
+ return -1;
+
+ fd = add_fd(file);
+ if (fd == -1)
+ smbc_getFunctionClosedir(statcont)(statcont, file);
+
+ return fd;
+}
+
+int
+smbc_closedir(int dh)
+{
+ SMBCFILE * file = find_fd(dh);
+ del_fd(dh);
+ return smbc_getFunctionClosedir(statcont)(statcont, file);
+}
+
+int
+smbc_getdents(unsigned int dh,
+ struct smbc_dirent *dirp,
+ int count)
+{
+ SMBCFILE * file = find_fd(dh);
+ return smbc_getFunctionGetdents(statcont)(statcont, file, dirp, count);
+}
+
+struct smbc_dirent *
+smbc_readdir(unsigned int dh)
+{
+ SMBCFILE * file = find_fd(dh);
+ return smbc_getFunctionReaddir(statcont)(statcont, file);
+}
+
+off_t
+smbc_telldir(int dh)
+{
+ SMBCFILE * file = find_fd(dh);
+ return smbc_getFunctionTelldir(statcont)(statcont, file);
+}
+
+int
+smbc_lseekdir(int fd,
+ off_t offset)
+{
+ SMBCFILE * file = find_fd(fd);
+ return smbc_getFunctionLseekdir(statcont)(statcont, file, offset);
+}
+
+int
+smbc_mkdir(const char *durl,
+ mode_t mode)
+{
+ return smbc_getFunctionMkdir(statcont)(statcont, durl, mode);
+}
+
+int
+smbc_rmdir(const char *durl)
+{
+ return smbc_getFunctionRmdir(statcont)(statcont, durl);
+}
+
+int
+smbc_stat(const char *url,
+ struct stat *st)
+{
+ return smbc_getFunctionStat(statcont)(statcont, url, st);
+}
+
+int
+smbc_fstat(int fd,
+ struct stat *st)
+{
+ SMBCFILE * file = find_fd(fd);
+ return smbc_getFunctionFstat(statcont)(statcont, file, st);
+}
+
+int
+smbc_ftruncate(int fd,
+ off_t size)
+{
+ SMBCFILE * file = find_fd(fd);
+ return smbc_getFunctionFtruncate(statcont)(statcont, file, size);
+}
+
+int
+smbc_chmod(const char *url,
+ mode_t mode)
+{
+ return smbc_getFunctionChmod(statcont)(statcont, url, mode);
+}
+
+int
+smbc_utimes(const char *fname,
+ struct timeval *tbuf)
+{
+ return smbc_getFunctionUtimes(statcont)(statcont, fname, tbuf);
+}
+
+#ifdef HAVE_UTIME_H
+int
+smbc_utime(const char *fname,
+ struct utimbuf *utbuf)
+{
+ struct timeval tv[2];
+
+ if (utbuf == NULL)
+ return smbc_getFunctionUtimes(statcont)(statcont, fname, NULL);
+
+ tv[0].tv_sec = utbuf->actime;
+ tv[1].tv_sec = utbuf->modtime;
+ tv[0].tv_usec = tv[1].tv_usec = 0;
+
+ return smbc_getFunctionUtimes(statcont)(statcont, fname, tv);
+}
+#endif
+
+int
+smbc_setxattr(const char *fname,
+ const char *name,
+ const void *value,
+ size_t size,
+ int flags)
+{
+ return smbc_getFunctionSetxattr(statcont)(statcont,
+ fname, name,
+ value, size, flags);
+}
+
+int
+smbc_lsetxattr(const char *fname,
+ const char *name,
+ const void *value,
+ size_t size,
+ int flags)
+{
+ return smbc_getFunctionSetxattr(statcont)(statcont,
+ fname, name,
+ value, size, flags);
+}
+
+int
+smbc_fsetxattr(int fd,
+ const char *name,
+ const void *value,
+ size_t size,
+ int flags)
+{
+ SMBCFILE * file = find_fd(fd);
+ if (file == NULL) {
+ errno = EBADF;
+ return -1;
+ }
+ return smbc_getFunctionSetxattr(statcont)(statcont,
+ file->fname, name,
+ value, size, flags);
+}
+
+int
+smbc_getxattr(const char *fname,
+ const char *name,
+ const void *value,
+ size_t size)
+{
+ return smbc_getFunctionGetxattr(statcont)(statcont,
+ fname, name,
+ value, size);
+}
+
+int
+smbc_lgetxattr(const char *fname,
+ const char *name,
+ const void *value,
+ size_t size)
+{
+ return smbc_getFunctionGetxattr(statcont)(statcont,
+ fname, name,
+ value, size);
+}
+
+int
+smbc_fgetxattr(int fd,
+ const char *name,
+ const void *value,
+ size_t size)
+{
+ SMBCFILE * file = find_fd(fd);
+ if (file == NULL) {
+ errno = EBADF;
+ return -1;
+ }
+ return smbc_getFunctionGetxattr(statcont)(statcont,
+ file->fname, name,
+ value, size);
+}
+
+int
+smbc_removexattr(const char *fname,
+ const char *name)
+{
+ return smbc_getFunctionRemovexattr(statcont)(statcont, fname, name);
+}
+
+int
+smbc_lremovexattr(const char *fname,
+ const char *name)
+{
+ return smbc_getFunctionRemovexattr(statcont)(statcont, fname, name);
+}
+
+int
+smbc_fremovexattr(int fd,
+ const char *name)
+{
+ SMBCFILE * file = find_fd(fd);
+ if (file == NULL) {
+ errno = EBADF;
+ return -1;
+ }
+ return smbc_getFunctionRemovexattr(statcont)(statcont,
+ file->fname, name);
+}
+
+int
+smbc_listxattr(const char *fname,
+ char *list,
+ size_t size)
+{
+ return smbc_getFunctionListxattr(statcont)(statcont,
+ fname, list, size);
+}
+
+int
+smbc_llistxattr(const char *fname,
+ char *list,
+ size_t size)
+{
+ return smbc_getFunctionListxattr(statcont)(statcont,
+ fname, list, size);
+}
+
+int
+smbc_flistxattr(int fd,
+ char *list,
+ size_t size)
+{
+ SMBCFILE * file = find_fd(fd);
+ if (file == NULL) {
+ errno = EBADF;
+ return -1;
+ }
+ return smbc_getFunctionListxattr(statcont)(statcont,
+ file->fname, list, size);
+}
+
+int
+smbc_print_file(const char *fname,
+ const char *printq)
+{
+ return smbc_getFunctionPrintFile(statcont)(statcont, fname,
+ statcont, printq);
+}
+
+int
+smbc_open_print_job(const char *fname)
+{
+ SMBCFILE * file;
+
+ file = smbc_getFunctionOpenPrintJob(statcont)(statcont, fname);
+ if (!file) return -1;
+ return file->cli_fd;
+}
+
+int
+smbc_list_print_jobs(const char *purl,
+ smbc_list_print_job_fn fn)
+{
+ return smbc_getFunctionListPrintJobs(statcont)(statcont, purl, fn);
+}
+
+int
+smbc_unlink_print_job(const char *purl,
+ int id)
+{
+ return smbc_getFunctionUnlinkPrintJob(statcont)(statcont, purl, id);
+}
+
+
diff --git a/source3/libsmb/libsmb_context.c b/source3/libsmb/libsmb_context.c
new file mode 100644
index 0000000000..19843383de
--- /dev/null
+++ b/source3/libsmb/libsmb_context.c
@@ -0,0 +1,645 @@
+/*
+ Unix SMB/Netbios implementation.
+ SMB client library implementation
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000, 2002
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+ Copyright (C) Derrell Lipman 2003-2008
+ Copyright (C) Jeremy Allison 2007, 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmbclient.h"
+#include "libsmb_internal.h"
+
+
+/*
+ * Is the logging working / configfile read ?
+ */
+static bool SMBC_initialized;
+static unsigned int initialized_ctx_count;
+
+/*
+ * Get a new empty handle to fill in with your own info
+ */
+SMBCCTX *
+smbc_new_context(void)
+{
+ SMBCCTX *context;
+
+ /*
+ * All newly added context fields should be placed in
+ * SMBC_internal_data, not directly in SMBCCTX.
+ */
+ context = SMB_MALLOC_P(SMBCCTX);
+ if (!context) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ ZERO_STRUCTP(context);
+
+ context->internal = SMB_MALLOC_P(struct SMBC_internal_data);
+ if (!context->internal) {
+ SAFE_FREE(context);
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ /* Initialize the context and establish reasonable defaults */
+ ZERO_STRUCTP(context->internal);
+
+ smbc_setDebug(context, 0);
+ smbc_setTimeout(context, 20000);
+
+ smbc_setOptionFullTimeNames(context, False);
+ smbc_setOptionOpenShareMode(context, SMBC_SHAREMODE_DENY_NONE);
+ smbc_setOptionSmbEncryptionLevel(context, SMBC_ENCRYPTLEVEL_NONE);
+ smbc_setOptionBrowseMaxLmbCount(context, 3); /* # LMBs to query */
+ smbc_setOptionUrlEncodeReaddirEntries(context, False);
+ smbc_setOptionOneSharePerServer(context, False);
+
+ smbc_setFunctionAuthData(context, SMBC_get_auth_data);
+ smbc_setFunctionCheckServer(context, SMBC_check_server);
+ smbc_setFunctionRemoveUnusedServer(context, SMBC_remove_unused_server);
+
+ smbc_setOptionUserData(context, NULL);
+ smbc_setFunctionAddCachedServer(context, SMBC_add_cached_server);
+ smbc_setFunctionGetCachedServer(context, SMBC_get_cached_server);
+ smbc_setFunctionRemoveCachedServer(context, SMBC_remove_cached_server);
+ smbc_setFunctionPurgeCachedServers(context, SMBC_purge_cached_servers);
+
+ smbc_setFunctionOpen(context, SMBC_open_ctx);
+ smbc_setFunctionCreat(context, SMBC_creat_ctx);
+ smbc_setFunctionRead(context, SMBC_read_ctx);
+ smbc_setFunctionWrite(context, SMBC_write_ctx);
+ smbc_setFunctionClose(context, SMBC_close_ctx);
+ smbc_setFunctionUnlink(context, SMBC_unlink_ctx);
+ smbc_setFunctionRename(context, SMBC_rename_ctx);
+ smbc_setFunctionLseek(context, SMBC_lseek_ctx);
+ smbc_setFunctionFtruncate(context, SMBC_ftruncate_ctx);
+ smbc_setFunctionStat(context, SMBC_stat_ctx);
+ smbc_setFunctionFstat(context, SMBC_fstat_ctx);
+ smbc_setFunctionOpendir(context, SMBC_opendir_ctx);
+ smbc_setFunctionClosedir(context, SMBC_closedir_ctx);
+ smbc_setFunctionReaddir(context, SMBC_readdir_ctx);
+ smbc_setFunctionGetdents(context, SMBC_getdents_ctx);
+ smbc_setFunctionMkdir(context, SMBC_mkdir_ctx);
+ smbc_setFunctionRmdir(context, SMBC_rmdir_ctx);
+ smbc_setFunctionTelldir(context, SMBC_telldir_ctx);
+ smbc_setFunctionLseekdir(context, SMBC_lseekdir_ctx);
+ smbc_setFunctionFstatdir(context, SMBC_fstatdir_ctx);
+ smbc_setFunctionChmod(context, SMBC_chmod_ctx);
+ smbc_setFunctionUtimes(context, SMBC_utimes_ctx);
+ smbc_setFunctionSetxattr(context, SMBC_setxattr_ctx);
+ smbc_setFunctionGetxattr(context, SMBC_getxattr_ctx);
+ smbc_setFunctionRemovexattr(context, SMBC_removexattr_ctx);
+ smbc_setFunctionListxattr(context, SMBC_listxattr_ctx);
+
+ smbc_setFunctionOpenPrintJob(context, SMBC_open_print_job_ctx);
+ smbc_setFunctionPrintFile(context, SMBC_print_file_ctx);
+ smbc_setFunctionListPrintJobs(context, SMBC_list_print_jobs_ctx);
+ smbc_setFunctionUnlinkPrintJob(context, SMBC_unlink_print_job_ctx);
+
+ return context;
+}
+
+/*
+ * Free a context
+ *
+ * Returns 0 on success. Otherwise returns 1, the SMBCCTX is _not_ freed
+ * and thus you'll be leaking memory if not handled properly.
+ *
+ */
+int
+smbc_free_context(SMBCCTX *context,
+ int shutdown_ctx)
+{
+ if (!context) {
+ errno = EBADF;
+ return 1;
+ }
+
+ if (shutdown_ctx) {
+ SMBCFILE * f;
+ DEBUG(1,("Performing aggressive shutdown.\n"));
+
+ f = context->internal->files;
+ while (f) {
+ smbc_getFunctionClose(context)(context, f);
+ f = f->next;
+ }
+ context->internal->files = NULL;
+
+ /* First try to remove the servers the nice way. */
+ if (smbc_getFunctionPurgeCachedServers(context)(context)) {
+ SMBCSRV * s;
+ SMBCSRV * next;
+ DEBUG(1, ("Could not purge all servers, "
+ "Nice way shutdown failed.\n"));
+ s = context->internal->servers;
+ while (s) {
+ DEBUG(1, ("Forced shutdown: %p (fd=%d)\n",
+ s, s->cli->fd));
+ cli_shutdown(s->cli);
+ smbc_getFunctionRemoveCachedServer(context)(context,
+ s);
+ next = s->next;
+ DLIST_REMOVE(context->internal->servers, s);
+ SAFE_FREE(s);
+ s = next;
+ }
+ context->internal->servers = NULL;
+ }
+ }
+ else {
+ /* This is the polite way */
+ if (smbc_getFunctionPurgeCachedServers(context)(context)) {
+ DEBUG(1, ("Could not purge all servers, "
+ "free_context failed.\n"));
+ errno = EBUSY;
+ return 1;
+ }
+ if (context->internal->servers) {
+ DEBUG(1, ("Active servers in context, "
+ "free_context failed.\n"));
+ errno = EBUSY;
+ return 1;
+ }
+ if (context->internal->files) {
+ DEBUG(1, ("Active files in context, "
+ "free_context failed.\n"));
+ errno = EBUSY;
+ return 1;
+ }
+ }
+
+ /* Things we have to clean up */
+ free(smbc_getWorkgroup(context));
+ smbc_setWorkgroup(context, NULL);
+
+ free(smbc_getNetbiosName(context));
+ smbc_setNetbiosName(context, NULL);
+
+ free(smbc_getUser(context));
+ smbc_setUser(context, NULL);
+
+ DEBUG(3, ("Context %p successfully freed\n", context));
+
+ SAFE_FREE(context->internal);
+ SAFE_FREE(context);
+
+ if (initialized_ctx_count) {
+ initialized_ctx_count--;
+ }
+
+ if (initialized_ctx_count == 0 && SMBC_initialized) {
+ gencache_shutdown();
+ secrets_shutdown();
+ gfree_all();
+ SMBC_initialized = false;
+ }
+ return 0;
+}
+
+
+/**
+ * Deprecated interface. Do not use. Instead, use the various
+ * smbc_setOption*() functions or smbc_setFunctionAuthDataWithContext().
+ */
+void
+smbc_option_set(SMBCCTX *context,
+ char *option_name,
+ ... /* option_value */)
+{
+ va_list ap;
+ union {
+ int i;
+ bool b;
+ smbc_get_auth_data_with_context_fn auth_fn;
+ void *v;
+ const char *s;
+ } option_value;
+
+ va_start(ap, option_name);
+
+ if (strcmp(option_name, "debug_to_stderr") == 0) {
+ option_value.b = (bool) va_arg(ap, int);
+ smbc_setOptionDebugToStderr(context, option_value.b);
+
+ } else if (strcmp(option_name, "full_time_names") == 0) {
+ option_value.b = (bool) va_arg(ap, int);
+ smbc_setOptionFullTimeNames(context, option_value.b);
+
+ } else if (strcmp(option_name, "open_share_mode") == 0) {
+ option_value.i = va_arg(ap, int);
+ smbc_setOptionOpenShareMode(context, option_value.i);
+
+ } else if (strcmp(option_name, "auth_function") == 0) {
+ option_value.auth_fn =
+ va_arg(ap, smbc_get_auth_data_with_context_fn);
+ smbc_setFunctionAuthDataWithContext(context, option_value.auth_fn);
+
+ } else if (strcmp(option_name, "user_data") == 0) {
+ option_value.v = va_arg(ap, void *);
+ smbc_setOptionUserData(context, option_value.v);
+
+ } else if (strcmp(option_name, "smb_encrypt_level") == 0) {
+ option_value.s = va_arg(ap, const char *);
+ if (strcmp(option_value.s, "none") == 0) {
+ smbc_setOptionSmbEncryptionLevel(context,
+ SMBC_ENCRYPTLEVEL_NONE);
+ } else if (strcmp(option_value.s, "request") == 0) {
+ smbc_setOptionSmbEncryptionLevel(context,
+ SMBC_ENCRYPTLEVEL_REQUEST);
+ } else if (strcmp(option_value.s, "require") == 0) {
+ smbc_setOptionSmbEncryptionLevel(context,
+ SMBC_ENCRYPTLEVEL_REQUIRE);
+ }
+
+ } else if (strcmp(option_name, "browse_max_lmb_count") == 0) {
+ option_value.i = va_arg(ap, int);
+ smbc_setOptionBrowseMaxLmbCount(context, option_value.i);
+
+ } else if (strcmp(option_name, "urlencode_readdir_entries") == 0) {
+ option_value.b = (bool) va_arg(ap, int);
+ smbc_setOptionUrlEncodeReaddirEntries(context, option_value.b);
+
+ } else if (strcmp(option_name, "one_share_per_server") == 0) {
+ option_value.b = (bool) va_arg(ap, int);
+ smbc_setOptionOneSharePerServer(context, option_value.b);
+
+ } else if (strcmp(option_name, "use_kerberos") == 0) {
+ option_value.b = (bool) va_arg(ap, int);
+ smbc_setOptionUseKerberos(context, option_value.b);
+
+ } else if (strcmp(option_name, "fallback_after_kerberos") == 0) {
+ option_value.b = (bool) va_arg(ap, int);
+ smbc_setOptionFallbackAfterKerberos(context, option_value.b);
+
+ } else if (strcmp(option_name, "no_auto_anonymous_login") == 0) {
+ option_value.b = (bool) va_arg(ap, int);
+ smbc_setOptionNoAutoAnonymousLogin(context, option_value.b);
+ }
+
+ va_end(ap);
+}
+
+
+/*
+ * Deprecated interface. Do not use. Instead, use the various
+ * smbc_getOption*() functions.
+ */
+void *
+smbc_option_get(SMBCCTX *context,
+ char *option_name)
+{
+ if (strcmp(option_name, "debug_stderr") == 0) {
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+ return (void *) (intptr_t) smbc_getOptionDebugToStderr(context);
+#else
+ return (void *) smbc_getOptionDebugToStderr(context);
+#endif
+
+ } else if (strcmp(option_name, "full_time_names") == 0) {
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+ return (void *) (intptr_t) smbc_getOptionFullTimeNames(context);
+#else
+ return (void *) smbc_getOptionFullTimeNames(context);
+#endif
+
+ } else if (strcmp(option_name, "open_share_mode") == 0) {
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+ return (void *) (intptr_t) smbc_getOptionOpenShareMode(context);
+#else
+ return (void *) smbc_getOptionOpenShareMode(context);
+#endif
+
+ } else if (strcmp(option_name, "auth_function") == 0) {
+ return (void *) smbc_getFunctionAuthDataWithContext(context);
+
+ } else if (strcmp(option_name, "user_data") == 0) {
+ return smbc_getOptionUserData(context);
+
+ } else if (strcmp(option_name, "smb_encrypt_level") == 0) {
+ switch(smbc_getOptionSmbEncryptionLevel(context))
+ {
+ case 0:
+ return (void *) "none";
+ case 1:
+ return (void *) "request";
+ case 2:
+ return (void *) "require";
+ }
+
+ } else if (strcmp(option_name, "smb_encrypt_on") == 0) {
+ SMBCSRV *s;
+ unsigned int num_servers = 0;
+
+ for (s = context->internal->servers; s; s = s->next) {
+ num_servers++;
+ if (s->cli->trans_enc_state == NULL) {
+ return (void *)false;
+ }
+ }
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+ return (void *) (intptr_t) (bool) (num_servers > 0);
+#else
+ return (void *) (bool) (num_servers > 0);
+#endif
+
+ } else if (strcmp(option_name, "browse_max_lmb_count") == 0) {
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+ return (void *) (intptr_t) smbc_getOptionBrowseMaxLmbCount(context);
+#else
+ return (void *) smbc_getOptionBrowseMaxLmbCount(context);
+#endif
+
+ } else if (strcmp(option_name, "urlencode_readdir_entries") == 0) {
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+ return (void *)(intptr_t) smbc_getOptionUrlEncodeReaddirEntries(context);
+#else
+ return (void *) (bool) smbc_getOptionUrlEncodeReaddirEntries(context);
+#endif
+
+ } else if (strcmp(option_name, "one_share_per_server") == 0) {
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+ return (void *) (intptr_t) smbc_getOptionOneSharePerServer(context);
+#else
+ return (void *) (bool) smbc_getOptionOneSharePerServer(context);
+#endif
+
+ } else if (strcmp(option_name, "use_kerberos") == 0) {
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+ return (void *) (intptr_t) smbc_getOptionUseKerberos(context);
+#else
+ return (void *) (bool) smbc_getOptionUseKerberos(context);
+#endif
+
+ } else if (strcmp(option_name, "fallback_after_kerberos") == 0) {
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+ return (void *)(intptr_t) smbc_getOptionFallbackAfterKerberos(context);
+#else
+ return (void *) (bool) smbc_getOptionFallbackAfterKerberos(context);
+#endif
+
+ } else if (strcmp(option_name, "no_auto_anonymous_login") == 0) {
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+ return (void *) (intptr_t) smbc_getOptionNoAutoAnonymousLogin(context);
+#else
+ return (void *) (bool) smbc_getOptionNoAutoAnonymousLogin(context);
+#endif
+ }
+
+ return NULL;
+}
+
+
+/*
+ * Initialize the library, etc.
+ *
+ * We accept a struct containing handle information.
+ * valid values for info->debug from 0 to 100,
+ * and insist that info->fn must be non-null.
+ */
+SMBCCTX *
+smbc_init_context(SMBCCTX *context)
+{
+ int pid;
+ char *user = NULL;
+ char *home = NULL;
+
+ if (!context) {
+ errno = EBADF;
+ return NULL;
+ }
+
+ /* Do not initialise the same client twice */
+ if (context->internal->initialized) {
+ return NULL;
+ }
+
+ if ((!smbc_getFunctionAuthData(context) &&
+ !smbc_getFunctionAuthDataWithContext(context)) ||
+ smbc_getDebug(context) < 0 ||
+ smbc_getDebug(context) > 100) {
+
+ errno = EINVAL;
+ return NULL;
+
+ }
+
+ if (!SMBC_initialized) {
+ /*
+ * Do some library-wide intializations the first time we get
+ * called
+ */
+ bool conf_loaded = False;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ load_case_tables();
+
+ setup_logging("libsmbclient", True);
+ if (context->internal->debug_stderr) {
+ dbf = x_stderr;
+ x_setbuf(x_stderr, NULL);
+ }
+
+ /* Here we would open the smb.conf file if needed ... */
+
+ lp_set_in_client(True);
+
+ home = getenv("HOME");
+ if (home) {
+ char *conf = NULL;
+ if (asprintf(&conf, "%s/.smb/smb.conf", home) > 0) {
+ if (lp_load(conf, True, False, False, True)) {
+ conf_loaded = True;
+ } else {
+ DEBUG(5, ("Could not load config file: %s\n",
+ conf));
+ }
+ SAFE_FREE(conf);
+ }
+ }
+
+ if (!conf_loaded) {
+ /*
+ * Well, if that failed, try the get_dyn_CONFIGFILE
+ * Which points to the standard locn, and if that
+ * fails, silently ignore it and use the internal
+ * defaults ...
+ */
+
+ if (!lp_load(get_dyn_CONFIGFILE(), True, False, False, False)) {
+ DEBUG(5, ("Could not load config file: %s\n",
+ get_dyn_CONFIGFILE()));
+ } else if (home) {
+ char *conf;
+ /*
+ * We loaded the global config file. Now lets
+ * load user-specific modifications to the
+ * global config.
+ */
+ if (asprintf(&conf,
+ "%s/.smb/smb.conf.append",
+ home) > 0) {
+ if (!lp_load(conf, True, False, False, False)) {
+ DEBUG(10,
+ ("Could not append config file: "
+ "%s\n",
+ conf));
+ }
+ SAFE_FREE(conf);
+ }
+ }
+ }
+
+ load_interfaces(); /* Load the list of interfaces ... */
+
+ reopen_logs(); /* Get logging working ... */
+
+ /*
+ * Block SIGPIPE (from lib/util_sock.c: write())
+ * It is not needed and should not stop execution
+ */
+ BlockSignals(True, SIGPIPE);
+
+ /* Done with one-time initialisation */
+ SMBC_initialized = true;
+
+ TALLOC_FREE(frame);
+ }
+
+ if (!smbc_getUser(context)) {
+ /*
+ * 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 = SMB_STRDUP("guest");
+ } else {
+ user = SMB_STRDUP(user);
+ }
+
+ if (!user) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ smbc_setUser(context, user);
+ }
+
+ if (!smbc_getNetbiosName(context)) {
+ /*
+ * 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
+ */
+ char *netbios_name;
+ if (global_myname()) {
+ netbios_name = SMB_STRDUP(global_myname());
+ } else {
+ /*
+ * Hmmm, I want to get hostname as well, but I am too
+ * lazy for the moment
+ */
+ pid = sys_getpid();
+ netbios_name = (char *)SMB_MALLOC(17);
+ if (!netbios_name) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ slprintf(netbios_name, 16,
+ "smbc%s%d", smbc_getUser(context), pid);
+ }
+
+ if (!netbios_name) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ smbc_setNetbiosName(context, netbios_name);
+ }
+
+ DEBUG(1, ("Using netbios name %s.\n", smbc_getNetbiosName(context)));
+
+ if (!smbc_getWorkgroup(context)) {
+ char *workgroup;
+
+ if (lp_workgroup()) {
+ workgroup = SMB_STRDUP(lp_workgroup());
+ }
+ else {
+ /* TODO: Think about a decent default workgroup */
+ workgroup = SMB_STRDUP("samba");
+ }
+
+ if (!workgroup) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ smbc_setWorkgroup(context, workgroup);
+ }
+
+ DEBUG(1, ("Using workgroup %s.\n", smbc_getWorkgroup(context)));
+
+ /* shortest timeout is 1 second */
+ if (smbc_getTimeout(context) > 0 && smbc_getTimeout(context) < 1000)
+ smbc_setTimeout(context, 1000);
+
+ /*
+ * FIXME: Should we check the function pointers here?
+ */
+
+ context->internal->initialized = True;
+ initialized_ctx_count++;
+
+ return context;
+}
+
+
+/* Return the verion of samba, and thus libsmbclient */
+const char *
+smbc_version(void)
+{
+ return SAMBA_VERSION_STRING;
+}
+
+
+/*
+ * Set the credentials so DFS will work when following referrals.
+ */
+void
+smbc_set_credentials(char *workgroup,
+ char *user,
+ char *password,
+ smbc_bool use_kerberos,
+ char *signing_state)
+{
+
+ set_cmdline_auth_info_username(user);
+ set_cmdline_auth_info_password(password);
+ set_cmdline_auth_info_use_kerberos(use_kerberos);
+ if (! set_cmdline_auth_info_signing_state(signing_state)) {
+ DEBUG(0, ("Invalid signing state: %s", signing_state));
+ }
+ set_global_myworkgroup(workgroup);
+ cli_cm_set_credentials();
+}
diff --git a/source3/libsmb/libsmb_dir.c b/source3/libsmb/libsmb_dir.c
new file mode 100644
index 0000000000..aa313f2c05
--- /dev/null
+++ b/source3/libsmb/libsmb_dir.c
@@ -0,0 +1,1949 @@
+/*
+ Unix SMB/Netbios implementation.
+ SMB client library implementation
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000, 2002
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+ Copyright (C) Derrell Lipman 2003-2008
+ Copyright (C) Jeremy Allison 2007, 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmbclient.h"
+#include "libsmb_internal.h"
+
+
+/*
+ * Routine to open a directory
+ * We accept the URL syntax explained in SMBC_parse_path(), above.
+ */
+
+static void
+remove_dir(SMBCFILE *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(SMBCFILE *dir,
+ const char *name,
+ const char *comment,
+ uint32 type)
+{
+ struct smbc_dirent *dirent;
+ int size;
+ int name_length = (name == NULL ? 0 : strlen(name));
+ int comment_len = (comment == NULL ? 0 : strlen(comment));
+
+ /*
+ * Allocate space for the dirent, which must be increased by the
+ * size of the name and the comment and 1 each for the null terminator.
+ */
+
+ size = sizeof(struct smbc_dirent) + name_length + comment_len + 2;
+
+ dirent = (struct smbc_dirent *)SMB_MALLOC(size);
+
+ if (!dirent) {
+
+ dir->dir_error = ENOMEM;
+ return -1;
+
+ }
+
+ ZERO_STRUCTP(dirent);
+
+ if (dir->dir_list == NULL) {
+
+ dir->dir_list = SMB_MALLOC_P(struct smbc_dir_list);
+ if (!dir->dir_list) {
+
+ SAFE_FREE(dirent);
+ dir->dir_error = ENOMEM;
+ return -1;
+
+ }
+ ZERO_STRUCTP(dir->dir_list);
+
+ dir->dir_end = dir->dir_next = dir->dir_list;
+ }
+ else {
+
+ dir->dir_end->next = SMB_MALLOC_P(struct smbc_dir_list);
+
+ if (!dir->dir_end->next) {
+
+ SAFE_FREE(dirent);
+ dir->dir_error = ENOMEM;
+ return -1;
+
+ }
+ ZERO_STRUCTP(dir->dir_end->next);
+
+ dir->dir_end = dir->dir_end->next;
+ }
+
+ dir->dir_end->next = NULL;
+ dir->dir_end->dirent = dirent;
+
+ dirent->smbc_type = type;
+ dirent->namelen = name_length;
+ dirent->commentlen = comment_len;
+ dirent->dirlen = size;
+
+ /*
+ * dirent->namelen + 1 includes the null (no null termination needed)
+ * Ditto for dirent->commentlen.
+ * The space for the two null bytes was allocated.
+ */
+ 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_unique_wg_fn(const char *name,
+ uint32 type,
+ const char *comment,
+ void *state)
+{
+ SMBCFILE *dir = (SMBCFILE *)state;
+ struct smbc_dir_list *dir_list;
+ struct smbc_dirent *dirent;
+ int dirent_type;
+ int do_remove = 0;
+
+ 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 */
+ }
+
+ /* Point to the one just added */
+ dirent = dir->dir_end->dirent;
+
+ /* See if this was a duplicate */
+ for (dir_list = dir->dir_list;
+ dir_list != dir->dir_end;
+ dir_list = dir_list->next) {
+ if (! do_remove &&
+ strcmp(dir_list->dirent->name, dirent->name) == 0) {
+ /* Duplicate. End end of list need to be removed. */
+ do_remove = 1;
+ }
+
+ if (do_remove && dir_list->next == dir->dir_end) {
+ /* Found the end of the list. Remove it. */
+ dir->dir_end = dir_list;
+ free(dir_list->next);
+ free(dirent);
+ dir_list->next = NULL;
+ break;
+ }
+ }
+}
+
+static void
+list_fn(const char *name,
+ uint32 type,
+ const char *comment,
+ void *state)
+{
+ SMBCFILE *dir = (SMBCFILE *)state;
+ int dirent_type;
+
+ /*
+ * We need to process the type a little ...
+ *
+ * Disk share = 0x00000000
+ * Print share = 0x00000001
+ * Comms share = 0x00000002 (obsolete?)
+ * IPC$ share = 0x00000003
+ *
+ * administrative shares:
+ * ADMIN$, IPC$, C$, D$, E$ ... are type |= 0x80000000
+ */
+
+ if (dir->dir_type == SMBC_FILE_SHARE) {
+ switch (type) {
+ case 0 | 0x80000000:
+ case 0:
+ dirent_type = SMBC_FILE_SHARE;
+ break;
+
+ case 1:
+ dirent_type = SMBC_PRINTER_SHARE;
+ break;
+
+ case 2:
+ dirent_type = SMBC_COMMS_SHARE;
+ break;
+
+ case 3 | 0x80000000:
+ 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(const char *mnt,
+ file_info *finfo,
+ const char *mask,
+ void *state)
+{
+
+ if (add_dirent((SMBCFILE *)state, finfo->name, "",
+ (finfo->mode&aDIR?SMBC_DIR:SMBC_FILE)) < 0) {
+
+ /* Handle an error ... */
+
+ /* FIXME: Add some code ... */
+
+ }
+
+}
+
+static int
+net_share_enum_rpc(struct cli_state *cli,
+ void (*fn)(const char *name,
+ uint32 type,
+ const char *comment,
+ void *state),
+ void *state)
+{
+ int i;
+ WERROR result;
+ uint32 preferred_len = 0xffffffff;
+ uint32 type;
+ struct srvsvc_NetShareInfoCtr info_ctr;
+ struct srvsvc_NetShareCtr1 ctr1;
+ fstring name = "";
+ fstring comment = "";
+ struct rpc_pipe_client *pipe_hnd;
+ NTSTATUS nt_status;
+ uint32_t resume_handle = 0;
+ uint32_t total_entries = 0;
+
+ /* Open the server service pipe */
+ nt_status = cli_rpc_pipe_open_noauth(cli, &ndr_table_srvsvc.syntax_id,
+ &pipe_hnd);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1, ("net_share_enum_rpc pipe open fail!\n"));
+ return -1;
+ }
+
+ ZERO_STRUCT(info_ctr);
+ ZERO_STRUCT(ctr1);
+
+ info_ctr.level = 1;
+ info_ctr.ctr.ctr1 = &ctr1;
+
+ /* Issue the NetShareEnum RPC call and retrieve the response */
+ nt_status = rpccli_srvsvc_NetShareEnumAll(pipe_hnd, talloc_tos(),
+ pipe_hnd->desthost,
+ &info_ctr,
+ preferred_len,
+ &total_entries,
+ &resume_handle,
+ &result);
+
+ /* Was it successful? */
+ if (!NT_STATUS_IS_OK(nt_status) || !W_ERROR_IS_OK(result) ||
+ total_entries == 0) {
+ /* Nope. Go clean up. */
+ goto done;
+ }
+
+ /* For each returned entry... */
+ for (i = 0; i < total_entries; i++) {
+
+ /* pull out the share name */
+ fstrcpy(name, info_ctr.ctr.ctr1->array[i].name);
+
+ /* pull out the share's comment */
+ fstrcpy(comment, info_ctr.ctr.ctr1->array[i].comment);
+
+ /* Get the type value */
+ type = info_ctr.ctr.ctr1->array[i].type;
+
+ /* Add this share to the list */
+ (*fn)(name, type, comment, state);
+ }
+
+done:
+ /* Close the server service pipe */
+ TALLOC_FREE(pipe_hnd);
+
+ /* Tell 'em if it worked */
+ return W_ERROR_IS_OK(result) ? 0 : -1;
+}
+
+
+/*
+ * Verify that the options specified in a URL are valid
+ */
+int
+SMBC_check_options(char *server,
+ char *share,
+ char *path,
+ char *options)
+{
+ DEBUG(4, ("SMBC_check_options(): server='%s' share='%s' "
+ "path='%s' options='%s'\n",
+ server, share, path, options));
+
+ /* No options at all is always ok */
+ if (! *options) return 0;
+
+ /* Currently, we don't support any options. */
+ return -1;
+}
+
+
+SMBCFILE *
+SMBC_opendir_ctx(SMBCCTX *context,
+ const char *fname)
+{
+ int saved_errno;
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *options = NULL;
+ char *workgroup = NULL;
+ char *path = NULL;
+ uint16 mode;
+ char *p = NULL;
+ SMBCSRV *srv = NULL;
+ SMBCFILE *dir = NULL;
+ struct sockaddr_storage rem_ss;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+ DEBUG(4, ("no valid context\n"));
+ errno = EINVAL + 8192;
+ TALLOC_FREE(frame);
+ return NULL;
+
+ }
+
+ if (!fname) {
+ DEBUG(4, ("no valid fname\n"));
+ errno = EINVAL + 8193;
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ if (SMBC_parse_path(frame,
+ context,
+ fname,
+ &workgroup,
+ &server,
+ &share,
+ &path,
+ &user,
+ &password,
+ &options)) {
+ DEBUG(4, ("no valid path\n"));
+ errno = EINVAL + 8194;
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ DEBUG(4, ("parsed path: fname='%s' server='%s' share='%s' "
+ "path='%s' options='%s'\n",
+ fname, server, share, path, options));
+
+ /* Ensure the options are valid */
+ if (SMBC_check_options(server, share, path, options)) {
+ DEBUG(4, ("unacceptable options (%s)\n", options));
+ errno = EINVAL + 8195;
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ if (!user || user[0] == (char)0) {
+ user = talloc_strdup(frame, smbc_getUser(context));
+ if (!user) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+ }
+
+ dir = SMB_MALLOC_P(SMBCFILE);
+
+ if (!dir) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ ZERO_STRUCTP(dir);
+
+ dir->cli_fd = 0;
+ dir->fname = SMB_STRDUP(fname);
+ dir->srv = NULL;
+ dir->offset = 0;
+ dir->file = False;
+ dir->dir_list = dir->dir_next = dir->dir_end = NULL;
+
+ if (server[0] == (char)0) {
+
+ int i;
+ int count;
+ int max_lmb_count;
+ struct ip_service *ip_list;
+ struct ip_service server_addr;
+ struct user_auth_info u_info;
+
+ if (share[0] != (char)0 || path[0] != (char)0) {
+
+ errno = EINVAL + 8196;
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ /* Determine how many local master browsers to query */
+ max_lmb_count = (smbc_getOptionBrowseMaxLmbCount(context) == 0
+ ? INT_MAX
+ : smbc_getOptionBrowseMaxLmbCount(context));
+
+ memset(&u_info, '\0', sizeof(u_info));
+ u_info.username = talloc_strdup(frame,user);
+ u_info.password = talloc_strdup(frame,password);
+ if (!u_info.username || !u_info.password) {
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ /*
+ * We have server and share and path empty but options
+ * requesting that we scan all master browsers for their list
+ * of workgroups/domains. This implies that we must first try
+ * broadcast queries to find all master browsers, and if that
+ * doesn't work, then try our other methods which return only
+ * a single master browser.
+ */
+
+ ip_list = NULL;
+ if (!NT_STATUS_IS_OK(name_resolve_bcast(MSBROWSE, 1, &ip_list,
+ &count)))
+ {
+
+ SAFE_FREE(ip_list);
+
+ if (!find_master_ip(workgroup, &server_addr.ss)) {
+
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ errno = ENOENT;
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ ip_list = (struct ip_service *)memdup(
+ &server_addr, sizeof(server_addr));
+ if (ip_list == NULL) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+ count = 1;
+ }
+
+ for (i = 0; i < count && i < max_lmb_count; i++) {
+ char addr[INET6_ADDRSTRLEN];
+ char *wg_ptr = NULL;
+ struct cli_state *cli = NULL;
+
+ print_sockaddr(addr, sizeof(addr), &ip_list[i].ss);
+ DEBUG(99, ("Found master browser %d of %d: %s\n",
+ i+1, MAX(count, max_lmb_count),
+ addr));
+
+ cli = get_ipc_connect_master_ip(talloc_tos(),
+ &ip_list[i],
+ &u_info,
+ &wg_ptr);
+ /* cli == NULL is the master browser refused to talk or
+ could not be found */
+ if (!cli) {
+ continue;
+ }
+
+ workgroup = talloc_strdup(frame, wg_ptr);
+ server = talloc_strdup(frame, cli->desthost);
+
+ cli_shutdown(cli);
+
+ if (!workgroup || !server) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ DEBUG(4, ("using workgroup %s %s\n",
+ workgroup, server));
+
+ /*
+ * For each returned master browser IP address, get a
+ * connection to IPC$ on the server if we do not
+ * already have one, and determine the
+ * workgroups/domains that it knows about.
+ */
+
+ srv = SMBC_server(frame, context, True, server, "IPC$",
+ &workgroup, &user, &password);
+ if (!srv) {
+ continue;
+ }
+
+ dir->srv = srv;
+ dir->dir_type = SMBC_WORKGROUP;
+
+ /* Now, list the stuff ... */
+
+ if (!cli_NetServerEnum(srv->cli,
+ workgroup,
+ SV_TYPE_DOMAIN_ENUM,
+ list_unique_wg_fn,
+ (void *)dir)) {
+ continue;
+ }
+ }
+
+ SAFE_FREE(ip_list);
+ } else {
+ /*
+ * Server not an empty string ... Check the rest and see what
+ * gives
+ */
+ if (*share == '\0') {
+ if (*path != '\0') {
+
+ /* Should not have empty share with path */
+ errno = EINVAL + 8197;
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ TALLOC_FREE(frame);
+ return NULL;
+
+ }
+
+ /*
+ * We don't know if <server> is really a server name
+ * or is a workgroup/domain name. If we already have
+ * a server structure for it, we'll use it.
+ * Otherwise, check to see if <server><1D>,
+ * <server><1B>, or <server><20> translates. We check
+ * to see if <server> is an IP address first.
+ */
+
+ /*
+ * See if we have an existing server. Do not
+ * establish a connection if one does not already
+ * exist.
+ */
+ srv = SMBC_server(frame, context, False,
+ server, "IPC$",
+ &workgroup, &user, &password);
+
+ /*
+ * If no existing server and not an IP addr, look for
+ * LMB or DMB
+ */
+ if (!srv &&
+ !is_ipaddress(server) &&
+ (resolve_name(server, &rem_ss, 0x1d) || /* LMB */
+ resolve_name(server, &rem_ss, 0x1b) )) { /* DMB */
+
+ fstring buserver;
+
+ dir->dir_type = SMBC_SERVER;
+
+ /*
+ * Get the backup list ...
+ */
+ if (!name_status_find(server, 0, 0,
+ &rem_ss, buserver)) {
+
+ DEBUG(0,("Could not get name of "
+ "local/domain master browser "
+ "for server %s\n", server));
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ errno = EPERM;
+ TALLOC_FREE(frame);
+ return NULL;
+
+ }
+
+ /*
+ * Get a connection to IPC$ on the server if
+ * we do not already have one
+ */
+ srv = SMBC_server(frame, context, True,
+ buserver, "IPC$",
+ &workgroup,
+ &user, &password);
+ if (!srv) {
+ DEBUG(0, ("got no contact to IPC$\n"));
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ TALLOC_FREE(frame);
+ return NULL;
+
+ }
+
+ dir->srv = srv;
+
+ /* Now, list the servers ... */
+ if (!cli_NetServerEnum(srv->cli, server,
+ 0x0000FFFE, list_fn,
+ (void *)dir)) {
+
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+ } else if (srv ||
+ (resolve_name(server, &rem_ss, 0x20))) {
+
+ /*
+ * If we hadn't found the server, get one now
+ */
+ if (!srv) {
+ srv = SMBC_server(frame, context, True,
+ server, "IPC$",
+ &workgroup,
+ &user, &password);
+ }
+
+ if (!srv) {
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ TALLOC_FREE(frame);
+ return NULL;
+
+ }
+
+ dir->dir_type = SMBC_FILE_SHARE;
+ dir->srv = srv;
+
+ /* List the shares ... */
+
+ if (net_share_enum_rpc(
+ srv->cli,
+ list_fn,
+ (void *) dir) < 0 &&
+ cli_RNetShareEnum(
+ srv->cli,
+ list_fn,
+ (void *)dir) < 0) {
+
+ errno = cli_errno(srv->cli);
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ TALLOC_FREE(frame);
+ return NULL;
+
+ }
+ } else {
+ /* Neither the workgroup nor server exists */
+ errno = ECONNREFUSED;
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ }
+ else {
+ /*
+ * The server and share are specified ... work from
+ * there ...
+ */
+ char *targetpath;
+ struct cli_state *targetcli;
+
+ /* We connect to the server and list the directory */
+ dir->dir_type = SMBC_FILE_SHARE;
+
+ srv = SMBC_server(frame, context, True, server, share,
+ &workgroup, &user, &password);
+
+ if (!srv) {
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ dir->srv = srv;
+
+ /* Now, list the files ... */
+
+ p = path + strlen(path);
+ path = talloc_asprintf_append(path, "\\*");
+ if (!path) {
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ if (!cli_resolve_path(frame, "", srv->cli, path,
+ &targetcli, &targetpath)) {
+ d_printf("Could not resolve %s\n", path);
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ if (cli_list(targetcli, targetpath,
+ aDIR | aSYSTEM | aHIDDEN,
+ dir_list_fn, (void *)dir) < 0) {
+
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ saved_errno = SMBC_errno(context, targetcli);
+
+ if (saved_errno == EINVAL) {
+ /*
+ * See if they asked to opendir
+ * something other than a directory.
+ * If so, the converted error value we
+ * got would have been EINVAL rather
+ * than ENOTDIR.
+ */
+ *p = '\0'; /* restore original path */
+
+ if (SMBC_getatr(context, srv, path,
+ &mode, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL) &&
+ ! IS_DOS_DIR(mode)) {
+
+ /* It is. Correct the error value */
+ saved_errno = ENOTDIR;
+ }
+ }
+
+ /*
+ * If there was an error and the server is no
+ * good any more...
+ */
+ if (cli_is_error(targetcli) &&
+ smbc_getFunctionCheckServer(context)(context, srv)) {
+
+ /* ... then remove it. */
+ if (smbc_getFunctionRemoveUnusedServer(context)(context,
+ srv)) {
+ /*
+ * We could not remove the
+ * server completely, remove
+ * it from the cache so we
+ * will not get it again. It
+ * will be removed when the
+ * last file/dir is closed.
+ */
+ smbc_getFunctionRemoveCachedServer(context)(context, srv);
+ }
+ }
+
+ errno = saved_errno;
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+ }
+
+ }
+
+ DLIST_ADD(context->internal->files, dir);
+ TALLOC_FREE(frame);
+ return dir;
+
+}
+
+/*
+ * Routine to close a directory
+ */
+
+int
+SMBC_closedir_ctx(SMBCCTX *context,
+ SMBCFILE *dir)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!dir || !SMBC_dlist_contains(context->internal->files, dir)) {
+ errno = EBADF;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ remove_dir(dir); /* Clean it up */
+
+ DLIST_REMOVE(context->internal->files, dir);
+
+ if (dir) {
+
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir); /* Free the space too */
+ }
+
+ TALLOC_FREE(frame);
+ return 0;
+
+}
+
+static void
+smbc_readdir_internal(SMBCCTX * context,
+ struct smbc_dirent *dest,
+ struct smbc_dirent *src,
+ int max_namebuf_len)
+{
+ if (smbc_getOptionUrlEncodeReaddirEntries(context)) {
+
+ /* url-encode the name. get back remaining buffer space */
+ max_namebuf_len =
+ SMBC_urlencode(dest->name, src->name, max_namebuf_len);
+
+ /* We now know the name length */
+ dest->namelen = strlen(dest->name);
+
+ /* Save the pointer to the beginning of the comment */
+ dest->comment = dest->name + dest->namelen + 1;
+
+ /* Copy the comment */
+ strncpy(dest->comment, src->comment, max_namebuf_len - 1);
+ dest->comment[max_namebuf_len - 1] = '\0';
+
+ /* Save other fields */
+ dest->smbc_type = src->smbc_type;
+ dest->commentlen = strlen(dest->comment);
+ dest->dirlen = ((dest->comment + dest->commentlen + 1) -
+ (char *) dest);
+ } else {
+
+ /* No encoding. Just copy the entry as is. */
+ memcpy(dest, src, src->dirlen);
+ dest->comment = (char *)(&dest->name + src->namelen + 1);
+ }
+
+}
+
+/*
+ * Routine to get a directory entry
+ */
+
+struct smbc_dirent *
+SMBC_readdir_ctx(SMBCCTX *context,
+ SMBCFILE *dir)
+{
+ int maxlen;
+ struct smbc_dirent *dirp, *dirent;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ /* Check that all is ok first ... */
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL;
+ DEBUG(0, ("Invalid context in SMBC_readdir_ctx()\n"));
+ TALLOC_FREE(frame);
+ return NULL;
+
+ }
+
+ if (!dir || !SMBC_dlist_contains(context->internal->files, dir)) {
+
+ errno = EBADF;
+ DEBUG(0, ("Invalid dir in SMBC_readdir_ctx()\n"));
+ TALLOC_FREE(frame);
+ return NULL;
+
+ }
+
+ if (dir->file != False) { /* FIXME, should be dir, perhaps */
+
+ errno = ENOTDIR;
+ DEBUG(0, ("Found file vs directory in SMBC_readdir_ctx()\n"));
+ TALLOC_FREE(frame);
+ return NULL;
+
+ }
+
+ if (!dir->dir_next) {
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ dirent = dir->dir_next->dirent;
+ if (!dirent) {
+
+ errno = ENOENT;
+ TALLOC_FREE(frame);
+ return NULL;
+
+ }
+
+ dirp = (struct smbc_dirent *)context->internal->dirent;
+ maxlen = (sizeof(context->internal->dirent) -
+ sizeof(struct smbc_dirent));
+
+ smbc_readdir_internal(context, dirp, dirent, maxlen);
+
+ dir->dir_next = dir->dir_next->next;
+
+ TALLOC_FREE(frame);
+ return dirp;
+}
+
+/*
+ * Routine to get directory entries
+ */
+
+int
+SMBC_getdents_ctx(SMBCCTX *context,
+ SMBCFILE *dir,
+ struct smbc_dirent *dirp,
+ int count)
+{
+ int rem = count;
+ int reqd;
+ int maxlen;
+ char *ndir = (char *)dirp;
+ struct smbc_dir_list *dirlist;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ /* Check that all is ok first ... */
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ if (!dir || !SMBC_dlist_contains(context->internal->files, dir)) {
+
+ errno = EBADF;
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ if (dir->file != False) { /* FIXME, should be dir, perhaps */
+
+ errno = ENOTDIR;
+ TALLOC_FREE(frame);
+ 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 ((dirlist = dir->dir_next)) {
+ struct smbc_dirent *dirent;
+
+ if (!dirlist->dirent) {
+
+ errno = ENOENT; /* Bad error */
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ /* Do urlencoding of next entry, if so selected */
+ dirent = (struct smbc_dirent *)context->internal->dirent;
+ maxlen = (sizeof(context->internal->dirent) -
+ sizeof(struct smbc_dirent));
+ smbc_readdir_internal(context, dirent,
+ dirlist->dirent, maxlen);
+
+ reqd = dirent->dirlen;
+
+ if (rem < reqd) {
+
+ if (rem < count) { /* We managed to copy something */
+
+ errno = 0;
+ TALLOC_FREE(frame);
+ return count - rem;
+
+ }
+ else { /* Nothing copied ... */
+
+ errno = EINVAL; /* Not enough space ... */
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ }
+
+ 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;
+
+ dir->dir_next = dirlist = dirlist -> next;
+ }
+
+ TALLOC_FREE(frame);
+
+ if (rem == count)
+ return 0;
+ else
+ return count - rem;
+
+}
+
+/*
+ * Routine to create a directory ...
+ */
+
+int
+SMBC_mkdir_ctx(SMBCCTX *context,
+ const char *fname,
+ mode_t mode)
+{
+ SMBCSRV *srv = NULL;
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *workgroup = NULL;
+ char *path = NULL;
+ char *targetpath = NULL;
+ struct cli_state *targetcli = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!fname) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ DEBUG(4, ("smbc_mkdir(%s)\n", fname));
+
+ if (SMBC_parse_path(frame,
+ context,
+ fname,
+ &workgroup,
+ &server,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!user || user[0] == (char)0) {
+ user = talloc_strdup(frame, smbc_getUser(context));
+ if (!user) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ }
+
+ srv = SMBC_server(frame, context, True,
+ server, share, &workgroup, &user, &password);
+
+ if (!srv) {
+
+ TALLOC_FREE(frame);
+ return -1; /* errno set by SMBC_server */
+
+ }
+
+ /*d_printf(">>>mkdir: resolving %s\n", path);*/
+ if (!cli_resolve_path(frame, "", srv->cli, path,
+ &targetcli, &targetpath)) {
+ d_printf("Could not resolve %s\n", path);
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ /*d_printf(">>>mkdir: resolved path as %s\n", targetpath);*/
+
+ if (!cli_mkdir(targetcli, targetpath)) {
+
+ errno = SMBC_errno(context, targetcli);
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ TALLOC_FREE(frame);
+ 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(const char *mnt,
+ 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_ctx(SMBCCTX *context,
+ const char *fname)
+{
+ SMBCSRV *srv = NULL;
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *workgroup = NULL;
+ char *path = NULL;
+ char *targetpath = NULL;
+ struct cli_state *targetcli = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!fname) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ DEBUG(4, ("smbc_rmdir(%s)\n", fname));
+
+ if (SMBC_parse_path(frame,
+ context,
+ fname,
+ &workgroup,
+ &server,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!user || user[0] == (char)0) {
+ user = talloc_strdup(frame, smbc_getUser(context));
+ if (!user) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ }
+
+ srv = SMBC_server(frame, context, True,
+ server, share, &workgroup, &user, &password);
+
+ if (!srv) {
+
+ TALLOC_FREE(frame);
+ return -1; /* errno set by SMBC_server */
+
+ }
+
+ /*d_printf(">>>rmdir: resolving %s\n", path);*/
+ if (!cli_resolve_path(frame, "", srv->cli, path,
+ &targetcli, &targetpath)) {
+ d_printf("Could not resolve %s\n", path);
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ /*d_printf(">>>rmdir: resolved path as %s\n", targetpath);*/
+
+
+ if (!cli_rmdir(targetcli, targetpath)) {
+
+ errno = SMBC_errno(context, targetcli);
+
+ if (errno == EACCES) { /* Check if the dir empty or not */
+
+ /* Local storage to avoid buffer overflows */
+ char *lpath;
+
+ smbc_rmdir_dirempty = True; /* Make this so ... */
+
+ lpath = talloc_asprintf(frame, "%s\\*",
+ targetpath);
+ if (!lpath) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (cli_list(targetcli, 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(context, targetcli)));
+ errno = EACCES;
+
+ }
+
+ if (smbc_rmdir_dirempty)
+ errno = EACCES;
+ else
+ errno = ENOTEMPTY;
+
+ }
+
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ TALLOC_FREE(frame);
+ return 0;
+
+}
+
+/*
+ * Routine to return the current directory position
+ */
+
+off_t
+SMBC_telldir_ctx(SMBCCTX *context,
+ SMBCFILE *dir)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ if (!dir || !SMBC_dlist_contains(context->internal->files, dir)) {
+
+ errno = EBADF;
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ if (dir->file != False) { /* FIXME, should be dir, perhaps */
+
+ errno = ENOTDIR;
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ /* See if we're already at the end. */
+ if (dir->dir_next == NULL) {
+ /* We are. */
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ /*
+ * We return the pointer here as the offset
+ */
+ TALLOC_FREE(frame);
+ return (off_t)(long)dir->dir_next->dirent;
+}
+
+/*
+ * A routine to run down the list and see if the entry is OK
+ */
+
+static struct smbc_dir_list *
+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_ctx(SMBCCTX *context,
+ SMBCFILE *dir,
+ off_t offset)
+{
+ long int l_offset = offset; /* Handle problems of size */
+ struct smbc_dirent *dirent = (struct smbc_dirent *)l_offset;
+ struct smbc_dir_list *list_ent = (struct smbc_dir_list *)NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ if (dir->file != False) { /* FIXME, should be dir, perhaps */
+
+ errno = ENOTDIR;
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ /* Now, check what we were passed and see if it is OK ... */
+
+ if (dirent == NULL) { /* Seek to the begining of the list */
+
+ dir->dir_next = dir->dir_list;
+ TALLOC_FREE(frame);
+ return 0;
+
+ }
+
+ if (offset == -1) { /* Seek to the end of the list */
+ dir->dir_next = NULL;
+ TALLOC_FREE(frame);
+ 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 = check_dir_ent(dir->dir_list, dirent)) == NULL) {
+ errno = EINVAL; /* Bad entry */
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ dir->dir_next = list_ent;
+
+ TALLOC_FREE(frame);
+ return 0;
+}
+
+/*
+ * Routine to fstat a dir
+ */
+
+int
+SMBC_fstatdir_ctx(SMBCCTX *context,
+ SMBCFILE *dir,
+ struct stat *st)
+{
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* No code yet ... */
+ return 0;
+}
+
+int
+SMBC_chmod_ctx(SMBCCTX *context,
+ const char *fname,
+ mode_t newmode)
+{
+ SMBCSRV *srv = NULL;
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *workgroup = NULL;
+ char *path = NULL;
+ uint16 mode;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!fname) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ DEBUG(4, ("smbc_chmod(%s, 0%3o)\n", fname, newmode));
+
+ if (SMBC_parse_path(frame,
+ context,
+ fname,
+ &workgroup,
+ &server,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!user || user[0] == (char)0) {
+ user = talloc_strdup(frame, smbc_getUser(context));
+ if (!user) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ }
+
+ srv = SMBC_server(frame, context, True,
+ server, share, &workgroup, &user, &password);
+
+ if (!srv) {
+ TALLOC_FREE(frame);
+ return -1; /* errno set by SMBC_server */
+ }
+
+ mode = 0;
+
+ if (!(newmode & (S_IWUSR | S_IWGRP | S_IWOTH))) mode |= aRONLY;
+ if ((newmode & S_IXUSR) && lp_map_archive(-1)) mode |= aARCH;
+ if ((newmode & S_IXGRP) && lp_map_system(-1)) mode |= aSYSTEM;
+ if ((newmode & S_IXOTH) && lp_map_hidden(-1)) mode |= aHIDDEN;
+
+ if (!cli_setatr(srv->cli, path, mode, 0)) {
+ errno = SMBC_errno(context, srv->cli);
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ TALLOC_FREE(frame);
+ return 0;
+}
+
+int
+SMBC_utimes_ctx(SMBCCTX *context,
+ const char *fname,
+ struct timeval *tbuf)
+{
+ SMBCSRV *srv = NULL;
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *workgroup = NULL;
+ char *path = NULL;
+ time_t access_time;
+ time_t write_time;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!fname) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (tbuf == NULL) {
+ access_time = write_time = time(NULL);
+ } else {
+ access_time = tbuf[0].tv_sec;
+ write_time = tbuf[1].tv_sec;
+ }
+
+ if (DEBUGLVL(4)) {
+ char *p;
+ char atimebuf[32];
+ char mtimebuf[32];
+
+ strncpy(atimebuf, ctime(&access_time), sizeof(atimebuf) - 1);
+ atimebuf[sizeof(atimebuf) - 1] = '\0';
+ if ((p = strchr(atimebuf, '\n')) != NULL) {
+ *p = '\0';
+ }
+
+ strncpy(mtimebuf, ctime(&write_time), sizeof(mtimebuf) - 1);
+ mtimebuf[sizeof(mtimebuf) - 1] = '\0';
+ if ((p = strchr(mtimebuf, '\n')) != NULL) {
+ *p = '\0';
+ }
+
+ dbgtext("smbc_utimes(%s, atime = %s mtime = %s)\n",
+ fname, atimebuf, mtimebuf);
+ }
+
+ if (SMBC_parse_path(frame,
+ context,
+ fname,
+ &workgroup,
+ &server,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!user || user[0] == (char)0) {
+ user = talloc_strdup(frame, smbc_getUser(context));
+ if (!user) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ }
+
+ srv = SMBC_server(frame, context, True,
+ server, share, &workgroup, &user, &password);
+
+ if (!srv) {
+ TALLOC_FREE(frame);
+ return -1; /* errno set by SMBC_server */
+ }
+
+ if (!SMBC_setatr(context, srv, path,
+ 0, access_time, write_time, 0, 0)) {
+ TALLOC_FREE(frame);
+ return -1; /* errno set by SMBC_setatr */
+ }
+
+ TALLOC_FREE(frame);
+ return 0;
+}
+
+/*
+ * Routine to unlink() a file
+ */
+
+int
+SMBC_unlink_ctx(SMBCCTX *context,
+ const char *fname)
+{
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *workgroup = NULL;
+ char *path = NULL;
+ char *targetpath = NULL;
+ struct cli_state *targetcli = NULL;
+ SMBCSRV *srv = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ if (!fname) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ if (SMBC_parse_path(frame,
+ context,
+ fname,
+ &workgroup,
+ &server,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!user || user[0] == (char)0) {
+ user = talloc_strdup(frame, smbc_getUser(context));
+ if (!user) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ }
+
+ srv = SMBC_server(frame, context, True,
+ server, share, &workgroup, &user, &password);
+
+ if (!srv) {
+ TALLOC_FREE(frame);
+ return -1; /* SMBC_server sets errno */
+
+ }
+
+ /*d_printf(">>>unlink: resolving %s\n", path);*/
+ if (!cli_resolve_path(frame, "", srv->cli, path,
+ &targetcli, &targetpath)) {
+ d_printf("Could not resolve %s\n", path);
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ /*d_printf(">>>unlink: resolved path as %s\n", targetpath);*/
+
+ if (!cli_unlink(targetcli, targetpath)) {
+
+ errno = SMBC_errno(context, targetcli);
+
+ if (errno == EACCES) { /* Check if the file is a directory */
+
+ int saverr = errno;
+ SMB_OFF_T size = 0;
+ uint16 mode = 0;
+ struct timespec write_time_ts;
+ struct timespec access_time_ts;
+ struct timespec change_time_ts;
+ SMB_INO_T ino = 0;
+
+ if (!SMBC_getatr(context, srv, path, &mode, &size,
+ NULL,
+ &access_time_ts,
+ &write_time_ts,
+ &change_time_ts,
+ &ino)) {
+
+ /* Hmmm, bad error ... What? */
+
+ errno = SMBC_errno(context, targetcli);
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+ else {
+
+ if (IS_DOS_DIR(mode))
+ errno = EISDIR;
+ else
+ errno = saverr; /* Restore this */
+
+ }
+ }
+
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ TALLOC_FREE(frame);
+ return 0; /* Success ... */
+
+}
+
+/*
+ * Routine to rename() a file
+ */
+
+int
+SMBC_rename_ctx(SMBCCTX *ocontext,
+ const char *oname,
+ SMBCCTX *ncontext,
+ const char *nname)
+{
+ char *server1 = NULL;
+ char *share1 = NULL;
+ char *server2 = NULL;
+ char *share2 = NULL;
+ char *user1 = NULL;
+ char *user2 = NULL;
+ char *password1 = NULL;
+ char *password2 = NULL;
+ char *workgroup = NULL;
+ char *path1 = NULL;
+ char *path2 = NULL;
+ char *targetpath1 = NULL;
+ char *targetpath2 = NULL;
+ struct cli_state *targetcli1 = NULL;
+ struct cli_state *targetcli2 = NULL;
+ SMBCSRV *srv = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!ocontext || !ncontext ||
+ !ocontext->internal->initialized ||
+ !ncontext->internal->initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!oname || !nname) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ DEBUG(4, ("smbc_rename(%s,%s)\n", oname, nname));
+
+ if (SMBC_parse_path(frame,
+ ocontext,
+ oname,
+ &workgroup,
+ &server1,
+ &share1,
+ &path1,
+ &user1,
+ &password1,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!user1 || user1[0] == (char)0) {
+ user1 = talloc_strdup(frame, smbc_getUser(ocontext));
+ if (!user1) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ }
+
+ if (SMBC_parse_path(frame,
+ ncontext,
+ nname,
+ NULL,
+ &server2,
+ &share2,
+ &path2,
+ &user2,
+ &password2,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!user2 || user2[0] == (char)0) {
+ user2 = talloc_strdup(frame, smbc_getUser(ncontext));
+ if (!user2) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ }
+
+ if (strcmp(server1, server2) || strcmp(share1, share2) ||
+ strcmp(user1, user2)) {
+ /* Can't rename across file systems, or users?? */
+ errno = EXDEV;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ srv = SMBC_server(frame, ocontext, True,
+ server1, share1, &workgroup, &user1, &password1);
+ if (!srv) {
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ /*d_printf(">>>rename: resolving %s\n", path1);*/
+ if (!cli_resolve_path(frame, "", srv->cli, path1,
+ &targetcli1, &targetpath1)) {
+ d_printf("Could not resolve %s\n", path1);
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ /*d_printf(">>>rename: resolved path as %s\n", targetpath1);*/
+ /*d_printf(">>>rename: resolving %s\n", path2);*/
+ if (!cli_resolve_path(frame, "", srv->cli, path2,
+ &targetcli2, &targetpath2)) {
+ d_printf("Could not resolve %s\n", path2);
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ /*d_printf(">>>rename: resolved path as %s\n", targetpath2);*/
+
+ if (strcmp(targetcli1->desthost, targetcli2->desthost) ||
+ strcmp(targetcli1->share, targetcli2->share))
+ {
+ /* can't rename across file systems */
+ errno = EXDEV;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!cli_rename(targetcli1, targetpath1, targetpath2)) {
+ int eno = SMBC_errno(ocontext, targetcli1);
+
+ if (eno != EEXIST ||
+ !cli_unlink(targetcli1, targetpath2) ||
+ !cli_rename(targetcli1, targetpath1, targetpath2)) {
+
+ errno = eno;
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+ }
+
+ TALLOC_FREE(frame);
+ return 0; /* Success */
+}
+
diff --git a/source3/libsmb/libsmb_file.c b/source3/libsmb/libsmb_file.c
new file mode 100644
index 0000000000..7b287096c2
--- /dev/null
+++ b/source3/libsmb/libsmb_file.c
@@ -0,0 +1,864 @@
+/*
+ Unix SMB/Netbios implementation.
+ SMB client library implementation
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000, 2002
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+ Copyright (C) Derrell Lipman 2003-2008
+ Copyright (C) Jeremy Allison 2007, 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmbclient.h"
+#include "libsmb_internal.h"
+
+
+/*
+ * Routine to open() a file ...
+ */
+
+SMBCFILE *
+SMBC_open_ctx(SMBCCTX *context,
+ const char *fname,
+ int flags,
+ mode_t mode)
+{
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *workgroup = NULL;
+ char *path = NULL;
+ char *targetpath = NULL;
+ struct cli_state *targetcli = NULL;
+ SMBCSRV *srv = NULL;
+ SMBCFILE *file = NULL;
+ int fd;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ TALLOC_FREE(frame);
+ return NULL;
+
+ }
+
+ if (!fname) {
+
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return NULL;
+
+ }
+
+ if (SMBC_parse_path(frame,
+ context,
+ fname,
+ &workgroup,
+ &server,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ if (!user || user[0] == (char)0) {
+ user = talloc_strdup(frame, smbc_getUser(context));
+ if (!user) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+ }
+
+ srv = SMBC_server(frame, context, True,
+ server, share, &workgroup, &user, &password);
+
+ if (!srv) {
+ if (errno == EPERM) errno = EACCES;
+ TALLOC_FREE(frame);
+ return NULL; /* 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 {
+ file = SMB_MALLOC_P(SMBCFILE);
+
+ if (!file) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ ZERO_STRUCTP(file);
+
+ /*d_printf(">>>open: resolving %s\n", path);*/
+ if (!cli_resolve_path(frame, "", srv->cli, path,
+ &targetcli, &targetpath)) {
+ d_printf("Could not resolve %s\n", path);
+ SAFE_FREE(file);
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+ /*d_printf(">>>open: resolved %s as %s\n", path, targetpath);*/
+
+ if ((fd = cli_open(targetcli, targetpath, flags,
+ context->internal->share_mode)) < 0) {
+
+ /* Handle the error ... */
+
+ SAFE_FREE(file);
+ errno = SMBC_errno(context, targetcli);
+ TALLOC_FREE(frame);
+ return NULL;
+
+ }
+
+ /* Fill in file struct */
+
+ file->cli_fd = fd;
+ file->fname = SMB_STRDUP(fname);
+ file->srv = srv;
+ file->offset = 0;
+ file->file = True;
+
+ DLIST_ADD(context->internal->files, file);
+
+ /*
+ * If the file was opened in O_APPEND mode, all write
+ * operations should be appended to the file. To do that,
+ * though, using this protocol, would require a getattrE()
+ * call for each and every write, to determine where the end
+ * of the file is. (There does not appear to be an append flag
+ * in the protocol.) Rather than add all of that overhead of
+ * retrieving the current end-of-file offset prior to each
+ * write operation, we'll assume that most append operations
+ * will continuously write, so we'll just set the offset to
+ * the end of the file now and hope that's adequate.
+ *
+ * Note to self: If this proves inadequate, and O_APPEND
+ * should, in some cases, be forced for each write, add a
+ * field in the context options structure, for
+ * "strict_append_mode" which would select between the current
+ * behavior (if FALSE) or issuing a getattrE() prior to each
+ * write and forcing the write to the end of the file (if
+ * TRUE). Adding that capability will likely require adding
+ * an "append" flag into the _SMBCFILE structure to track
+ * whether a file was opened in O_APPEND mode. -- djl
+ */
+ if (flags & O_APPEND) {
+ if (SMBC_lseek_ctx(context, file, 0, SEEK_END) < 0) {
+ (void) SMBC_close_ctx(context, file);
+ errno = ENXIO;
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+ }
+
+ TALLOC_FREE(frame);
+ return file;
+
+ }
+
+ /* Check if opendir needed ... */
+
+ if (fd == -1) {
+ int eno = 0;
+
+ eno = SMBC_errno(context, srv->cli);
+ file = smbc_getFunctionOpendir(context)(context, fname);
+ if (!file) errno = eno;
+ TALLOC_FREE(frame);
+ return file;
+
+ }
+
+ errno = EINVAL; /* FIXME, correct errno ? */
+ TALLOC_FREE(frame);
+ return NULL;
+
+}
+
+/*
+ * Routine to create a file
+ */
+
+SMBCFILE *
+SMBC_creat_ctx(SMBCCTX *context,
+ const char *path,
+ mode_t mode)
+{
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL;
+ return NULL;
+
+ }
+
+ return SMBC_open_ctx(context, path,
+ O_WRONLY | O_CREAT | O_TRUNC, mode);
+}
+
+/*
+ * Routine to read() a file ...
+ */
+
+ssize_t
+SMBC_read_ctx(SMBCCTX *context,
+ SMBCFILE *file,
+ void *buf,
+ size_t count)
+{
+ int ret;
+ char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
+ char *path = NULL;
+ char *targetpath = NULL;
+ struct cli_state *targetcli = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ /*
+ * offset:
+ *
+ * Compiler bug (possibly) -- gcc (GCC) 3.3.5 (Debian 1:3.3.5-2) --
+ * appears to pass file->offset (which is type off_t) differently than
+ * a local variable of type off_t. Using local variable "offset" in
+ * the call to cli_read() instead of file->offset fixes a problem
+ * retrieving data at an offset greater than 4GB.
+ */
+ off_t offset;
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ DEBUG(4, ("smbc_read(%p, %d)\n", file, (int)count));
+
+ if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
+ errno = EBADF;
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ offset = file->offset;
+
+ /* Check that the buffer exists ... */
+
+ if (buf == NULL) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ /*d_printf(">>>read: parsing %s\n", file->fname);*/
+ if (SMBC_parse_path(frame,
+ context,
+ file->fname,
+ NULL,
+ &server,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ /*d_printf(">>>read: resolving %s\n", path);*/
+ if (!cli_resolve_path(frame, "", file->srv->cli, path,
+ &targetcli, &targetpath)) {
+ d_printf("Could not resolve %s\n", path);
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
+
+ ret = cli_read(targetcli, file->cli_fd, (char *)buf, offset, count);
+
+ if (ret < 0) {
+
+ errno = SMBC_errno(context, targetcli);
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ file->offset += ret;
+
+ DEBUG(4, (" --> %d\n", ret));
+
+ TALLOC_FREE(frame);
+ return ret; /* Success, ret bytes of data ... */
+
+}
+
+/*
+ * Routine to write() a file ...
+ */
+
+ssize_t
+SMBC_write_ctx(SMBCCTX *context,
+ SMBCFILE *file,
+ const void *buf,
+ size_t count)
+{
+ int ret;
+ off_t offset;
+ char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
+ char *path = NULL;
+ char *targetpath = NULL;
+ struct cli_state *targetcli = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ /* First check all pointers before dereferencing them */
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
+ errno = EBADF;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ /* Check that the buffer exists ... */
+
+ if (buf == NULL) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ offset = file->offset; /* See "offset" comment in SMBC_read_ctx() */
+
+ /*d_printf(">>>write: parsing %s\n", file->fname);*/
+ if (SMBC_parse_path(frame,
+ context,
+ file->fname,
+ NULL,
+ &server,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ /*d_printf(">>>write: resolving %s\n", path);*/
+ if (!cli_resolve_path(frame, "", file->srv->cli, path,
+ &targetcli, &targetpath)) {
+ d_printf("Could not resolve %s\n", path);
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ /*d_printf(">>>write: resolved path as %s\n", targetpath);*/
+
+ ret = cli_write(targetcli, file->cli_fd,
+ 0, (char *)buf, offset, count);
+
+ if (ret <= 0) {
+ errno = SMBC_errno(context, targetcli);
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ file->offset += ret;
+
+ TALLOC_FREE(frame);
+ return ret; /* Success, 0 bytes of data ... */
+}
+
+/*
+ * Routine to close() a file ...
+ */
+
+int
+SMBC_close_ctx(SMBCCTX *context,
+ SMBCFILE *file)
+{
+ SMBCSRV *srv;
+ char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
+ char *path = NULL;
+ char *targetpath = NULL;
+ struct cli_state *targetcli = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
+ errno = EBADF;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ /* IS a dir ... */
+ if (!file->file) {
+ TALLOC_FREE(frame);
+ return smbc_getFunctionClosedir(context)(context, file);
+ }
+
+ /*d_printf(">>>close: parsing %s\n", file->fname);*/
+ if (SMBC_parse_path(frame,
+ context,
+ file->fname,
+ NULL,
+ &server,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ /*d_printf(">>>close: resolving %s\n", path);*/
+ if (!cli_resolve_path(frame, "", file->srv->cli, path,
+ &targetcli, &targetpath)) {
+ d_printf("Could not resolve %s\n", path);
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ /*d_printf(">>>close: resolved path as %s\n", targetpath);*/
+
+ if (!cli_close(targetcli, file->cli_fd)) {
+
+ DEBUG(3, ("cli_close failed on %s. purging server.\n",
+ file->fname));
+ /* Deallocate slot and remove the server
+ * from the server cache if unused */
+ errno = SMBC_errno(context, targetcli);
+ srv = file->srv;
+ DLIST_REMOVE(context->internal->files, file);
+ SAFE_FREE(file->fname);
+ SAFE_FREE(file);
+ smbc_getFunctionRemoveUnusedServer(context)(context, srv);
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ DLIST_REMOVE(context->internal->files, file);
+ SAFE_FREE(file->fname);
+ SAFE_FREE(file);
+ TALLOC_FREE(frame);
+
+ return 0;
+}
+
+/*
+ * 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(SMBCCTX * context,
+ SMBCSRV *srv,
+ char *path,
+ uint16 *mode,
+ SMB_OFF_T *size,
+ struct timespec *create_time_ts,
+ struct timespec *access_time_ts,
+ struct timespec *write_time_ts,
+ struct timespec *change_time_ts,
+ SMB_INO_T *ino)
+{
+ char *fixedpath = NULL;
+ char *targetpath = NULL;
+ struct cli_state *targetcli = NULL;
+ time_t write_time;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ /* path fixup for . and .. */
+ if (strequal(path, ".") || strequal(path, "..")) {
+ fixedpath = talloc_strdup(frame, "\\");
+ if (!fixedpath) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ } else {
+ fixedpath = talloc_strdup(frame, path);
+ if (!fixedpath) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ trim_string(fixedpath, NULL, "\\..");
+ trim_string(fixedpath, NULL, "\\.");
+ }
+ DEBUG(4,("SMBC_getatr: sending qpathinfo\n"));
+
+ if (!cli_resolve_path(frame, "", srv->cli, fixedpath,
+ &targetcli, &targetpath)) {
+ d_printf("Couldn't resolve %s\n", path);
+ TALLOC_FREE(frame);
+ return False;
+ }
+
+ if (!srv->no_pathinfo2 &&
+ cli_qpathinfo2(targetcli, targetpath,
+ create_time_ts,
+ access_time_ts,
+ write_time_ts,
+ change_time_ts,
+ size, mode, ino)) {
+ TALLOC_FREE(frame);
+ return True;
+ }
+
+ /* if this is NT then don't bother with the getatr */
+ if (targetcli->capabilities & CAP_NT_SMBS) {
+ errno = EPERM;
+ TALLOC_FREE(frame);
+ return False;
+ }
+
+ if (cli_getatr(targetcli, targetpath, mode, size, &write_time)) {
+
+ struct timespec w_time_ts;
+
+ w_time_ts = convert_time_t_to_timespec(write_time);
+
+ if (write_time_ts != NULL) {
+ *write_time_ts = w_time_ts;
+ }
+
+ if (create_time_ts != NULL) {
+ *create_time_ts = w_time_ts;
+ }
+
+ if (access_time_ts != NULL) {
+ *access_time_ts = w_time_ts;
+ }
+
+ if (change_time_ts != NULL) {
+ *change_time_ts = w_time_ts;
+ }
+
+ srv->no_pathinfo2 = True;
+ TALLOC_FREE(frame);
+ return True;
+ }
+
+ errno = EPERM;
+ TALLOC_FREE(frame);
+ return False;
+
+}
+
+/*
+ * Set file info on an SMB server. Use setpathinfo call first. If that
+ * fails, use setattrE..
+ *
+ * Access and modification time parameters are always used and must be
+ * provided. Create time, if zero, will be determined from the actual create
+ * time of the file. If non-zero, the create time will be set as well.
+ *
+ * "mode" (attributes) parameter may be set to -1 if it is not to be set.
+ */
+bool
+SMBC_setatr(SMBCCTX * context, SMBCSRV *srv, char *path,
+ time_t create_time,
+ time_t access_time,
+ time_t write_time,
+ time_t change_time,
+ uint16 mode)
+{
+ int fd;
+ int ret;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ /*
+ * First, try setpathinfo (if qpathinfo succeeded), for it is the
+ * modern function for "new code" to be using, and it works given a
+ * filename rather than requiring that the file be opened to have its
+ * attributes manipulated.
+ */
+ if (srv->no_pathinfo ||
+ ! cli_setpathinfo(srv->cli, path,
+ create_time,
+ access_time,
+ write_time,
+ change_time,
+ mode)) {
+
+ /*
+ * setpathinfo is not supported; go to plan B.
+ *
+ * cli_setatr() does not work on win98, and it also doesn't
+ * support setting the access time (only the modification
+ * time), so in all cases, we open the specified file and use
+ * cli_setattrE() which should work on all OS versions, and
+ * supports both times.
+ */
+
+ /* Don't try {q,set}pathinfo() again, with this server */
+ srv->no_pathinfo = True;
+
+ /* Open the file */
+ if ((fd = cli_open(srv->cli, path, O_RDWR, DENY_NONE)) < 0) {
+
+ errno = SMBC_errno(context, srv->cli);
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ /* Set the new attributes */
+ ret = cli_setattrE(srv->cli, fd,
+ change_time,
+ access_time,
+ write_time);
+
+ /* Close the file */
+ cli_close(srv->cli, fd);
+
+ /*
+ * Unfortunately, setattrE() doesn't have a provision for
+ * setting the access mode (attributes). We'll have to try
+ * cli_setatr() for that, and with only this parameter, it
+ * seems to work on win98.
+ */
+ if (ret && mode != (uint16) -1) {
+ ret = cli_setatr(srv->cli, path, mode, 0);
+ }
+
+ if (! ret) {
+ errno = SMBC_errno(context, srv->cli);
+ TALLOC_FREE(frame);
+ return False;
+ }
+ }
+
+ TALLOC_FREE(frame);
+ return True;
+}
+
+/*
+ * A routine to lseek() a file
+ */
+
+off_t
+SMBC_lseek_ctx(SMBCCTX *context,
+ SMBCFILE *file,
+ off_t offset,
+ int whence)
+{
+ SMB_OFF_T size;
+ char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
+ char *path = NULL;
+ char *targetpath = NULL;
+ struct cli_state *targetcli = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
+
+ errno = EBADF;
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ if (!file->file) {
+
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1; /* Can't lseek a dir ... */
+
+ }
+
+ switch (whence) {
+ case SEEK_SET:
+ file->offset = offset;
+ break;
+
+ case SEEK_CUR:
+ file->offset += offset;
+ break;
+
+ case SEEK_END:
+ /*d_printf(">>>lseek: parsing %s\n", file->fname);*/
+ if (SMBC_parse_path(frame,
+ context,
+ file->fname,
+ NULL,
+ &server,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ /*d_printf(">>>lseek: resolving %s\n", path);*/
+ if (!cli_resolve_path(frame, "", file->srv->cli, path,
+ &targetcli, &targetpath)) {
+ d_printf("Could not resolve %s\n", path);
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ /*d_printf(">>>lseek: resolved path as %s\n", targetpath);*/
+
+ if (!cli_qfileinfo(targetcli, file->cli_fd, NULL,
+ &size, NULL, NULL, NULL, NULL, NULL))
+ {
+ SMB_OFF_T b_size = size;
+ if (!cli_getattrE(targetcli, file->cli_fd,
+ NULL, &b_size, NULL, NULL, NULL))
+ {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ } else
+ size = b_size;
+ }
+ file->offset = size + offset;
+ break;
+
+ default:
+ errno = EINVAL;
+ break;
+
+ }
+
+ TALLOC_FREE(frame);
+ return file->offset;
+
+}
+
+
+/*
+ * Routine to truncate a file given by its file descriptor, to a specified size
+ */
+
+int
+SMBC_ftruncate_ctx(SMBCCTX *context,
+ SMBCFILE *file,
+ off_t length)
+{
+ SMB_OFF_T size = length;
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *path = NULL;
+ char *targetpath = NULL;
+ struct cli_state *targetcli = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
+ errno = EBADF;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!file->file) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ /*d_printf(">>>fstat: parsing %s\n", file->fname);*/
+ if (SMBC_parse_path(frame,
+ context,
+ file->fname,
+ NULL,
+ &server,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ /*d_printf(">>>fstat: resolving %s\n", path);*/
+ if (!cli_resolve_path(frame, "", file->srv->cli, path,
+ &targetcli, &targetpath)) {
+ d_printf("Could not resolve %s\n", path);
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
+
+ if (!cli_ftruncate(targetcli, file->cli_fd, size)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ TALLOC_FREE(frame);
+ return 0;
+
+}
diff --git a/source3/libsmb/libsmb_misc.c b/source3/libsmb/libsmb_misc.c
new file mode 100644
index 0000000000..dd7add5a61
--- /dev/null
+++ b/source3/libsmb/libsmb_misc.c
@@ -0,0 +1,73 @@
+/*
+ Unix SMB/Netbios implementation.
+ SMB client library implementation
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000, 2002
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+ Copyright (C) Derrell Lipman 2003-2008
+ Copyright (C) Jeremy Allison 2007, 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmbclient.h"
+#include "libsmb_internal.h"
+
+
+/*
+ * check if an element is part of the list.
+ */
+int
+SMBC_dlist_contains(SMBCFILE * list, SMBCFILE *p)
+{
+ if (!p || !list) return False;
+ do {
+ if (p == list) return True;
+ list = list->next;
+ } while (list);
+ return False;
+}
+
+
+/*
+ * Convert an SMB error into a UNIX error ...
+ */
+int
+SMBC_errno(SMBCCTX *context,
+ struct cli_state *c)
+{
+ int ret = cli_errno(c);
+
+ if (cli_is_dos_error(c)) {
+ uint8 eclass;
+ uint32 ecode;
+
+ cli_dos_error(c, &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);
+
+ DEBUG(3,("smbc errno %s -> %d\n",
+ nt_errstr(status), ret));
+ }
+
+ return ret;
+}
+
diff --git a/source3/libsmb/libsmb_path.c b/source3/libsmb/libsmb_path.c
new file mode 100644
index 0000000000..2c3a5f8866
--- /dev/null
+++ b/source3/libsmb/libsmb_path.c
@@ -0,0 +1,400 @@
+/*
+ Unix SMB/Netbios implementation.
+ SMB client library implementation
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000, 2002
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+ Copyright (C) Derrell Lipman 2003-2008
+ Copyright (C) Jeremy Allison 2007, 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmbclient.h"
+#include "libsmb_internal.h"
+
+
+/* Used by urldecode_talloc() */
+static int
+hex2int( unsigned int _char )
+{
+ if ( _char >= 'A' && _char <='F')
+ return _char - 'A' + 10;
+ if ( _char >= 'a' && _char <='f')
+ return _char - 'a' + 10;
+ if ( _char >= '0' && _char <='9')
+ return _char - '0';
+ return -1;
+}
+
+/*
+ * SMBC_urldecode()
+ * and urldecode_talloc() (internal fn.)
+ *
+ * Convert strings of %xx to their single character equivalent. Each 'x' must
+ * be a valid hexadecimal digit, or that % sequence is left undecoded.
+ *
+ * dest may, but need not be, the same pointer as src.
+ *
+ * Returns the number of % sequences which could not be converted due to lack
+ * of two following hexadecimal digits.
+ */
+static int
+urldecode_talloc(TALLOC_CTX *ctx, char **pp_dest, const char *src)
+{
+ int old_length = strlen(src);
+ int i = 0;
+ int err_count = 0;
+ size_t newlen = 1;
+ char *p, *dest;
+
+ if (old_length == 0) {
+ return 0;
+ }
+
+ *pp_dest = NULL;
+ for (i = 0; i < old_length; ) {
+ unsigned char character = src[i++];
+
+ if (character == '%') {
+ int a = i+1 < old_length ? hex2int(src[i]) : -1;
+ int b = i+1 < old_length ? hex2int(src[i+1]) : -1;
+
+ /* Replace valid sequence */
+ if (a != -1 && b != -1) {
+ /* Replace valid %xx sequence with %dd */
+ character = (a * 16) + b;
+ if (character == '\0') {
+ break; /* Stop at %00 */
+ }
+ i += 2;
+ } else {
+ err_count++;
+ }
+ }
+ newlen++;
+ }
+
+ dest = TALLOC_ARRAY(ctx, char, newlen);
+ if (!dest) {
+ return err_count;
+ }
+
+ err_count = 0;
+ for (p = dest, i = 0; i < old_length; ) {
+ unsigned char character = src[i++];
+
+ if (character == '%') {
+ int a = i+1 < old_length ? hex2int(src[i]) : -1;
+ int b = i+1 < old_length ? hex2int(src[i+1]) : -1;
+
+ /* Replace valid sequence */
+ if (a != -1 && b != -1) {
+ /* Replace valid %xx sequence with %dd */
+ character = (a * 16) + b;
+ if (character == '\0') {
+ break; /* Stop at %00 */
+ }
+ i += 2;
+ } else {
+ err_count++;
+ }
+ }
+ *p++ = character;
+ }
+
+ *p = '\0';
+ *pp_dest = dest;
+ return err_count;
+}
+
+int
+SMBC_urldecode(char *dest,
+ char *src,
+ size_t max_dest_len)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ char *pdest;
+ int ret = urldecode_talloc(frame, &pdest, src);
+
+ if (pdest) {
+ strlcpy(dest, pdest, max_dest_len);
+ }
+ TALLOC_FREE(frame);
+ return ret;
+}
+
+/*
+ * SMBC_urlencode()
+ *
+ * Convert any characters not specifically allowed in a URL into their %xx
+ * equivalent.
+ *
+ * Returns the remaining buffer length.
+ */
+int
+SMBC_urlencode(char *dest,
+ char *src,
+ int max_dest_len)
+{
+ char hex[] = "0123456789ABCDEF";
+
+ for (; *src != '\0' && max_dest_len >= 3; src++) {
+
+ if ((*src < '0' &&
+ *src != '-' &&
+ *src != '.') ||
+ (*src > '9' &&
+ *src < 'A') ||
+ (*src > 'Z' &&
+ *src < 'a' &&
+ *src != '_') ||
+ (*src > 'z')) {
+ *dest++ = '%';
+ *dest++ = hex[(*src >> 4) & 0x0f];
+ *dest++ = hex[*src & 0x0f];
+ max_dest_len -= 3;
+ } else {
+ *dest++ = *src;
+ max_dest_len--;
+ }
+ }
+
+ *dest++ = '\0';
+ max_dest_len--;
+
+ return max_dest_len;
+}
+
+/*
+ * Function to parse a path and turn it into components
+ *
+ * The general format of an SMB URI is explain in Christopher Hertel's CIFS
+ * book, at http://ubiqx.org/cifs/Appendix-D.html. We accept a subset of the
+ * general format ("smb:" only; we do not look for "cifs:").
+ *
+ *
+ * We accept:
+ * smb://[[[domain;]user[:password]@]server[/share[/path[/file]]]][?options]
+ *
+ * Meaning of URLs:
+ *
+ * smb:// Show all workgroups.
+ *
+ * The method of locating the list of workgroups varies
+ * depending upon the setting of the context variable
+ * context->options.browse_max_lmb_count. This value
+ * determines the maximum number of local master browsers to
+ * query for the list of workgroups. In order to ensure that
+ * a complete list of workgroups is obtained, all master
+ * browsers must be queried, but if there are many
+ * workgroups, the time spent querying can begin to add up.
+ * For small networks (not many workgroups), it is suggested
+ * that this variable be set to 0, indicating query all local
+ * master browsers. When the network has many workgroups, a
+ * reasonable setting for this variable might be around 3.
+ *
+ * smb://name/ if name<1D> or name<1B> exists, list servers in
+ * workgroup, else, if name<20> exists, list all shares
+ * for server ...
+ *
+ * If "options" are provided, this function returns the entire option list as a
+ * string, for later parsing by the caller. Note that currently, no options
+ * are supported.
+ */
+
+static const char *smbc_prefix = "smb:";
+
+int
+SMBC_parse_path(TALLOC_CTX *ctx,
+ SMBCCTX *context,
+ const char *fname,
+ char **pp_workgroup,
+ char **pp_server,
+ char **pp_share,
+ char **pp_path,
+ char **pp_user,
+ char **pp_password,
+ char **pp_options)
+{
+ char *s;
+ const char *p;
+ char *q, *r;
+ int len;
+
+ /* Ensure these returns are at least valid pointers. */
+ *pp_server = talloc_strdup(ctx, "");
+ *pp_share = talloc_strdup(ctx, "");
+ *pp_path = talloc_strdup(ctx, "");
+ *pp_user = talloc_strdup(ctx, "");
+ *pp_password = talloc_strdup(ctx, "");
+
+ if (!*pp_server || !*pp_share || !*pp_path ||
+ !*pp_user || !*pp_password) {
+ return -1;
+ }
+
+ /*
+ * Assume we wont find an authentication domain to parse, so default
+ * to the workgroup in the provided context.
+ */
+ if (pp_workgroup != NULL) {
+ *pp_workgroup =
+ talloc_strdup(ctx, smbc_getWorkgroup(context));
+ }
+
+ if (pp_options) {
+ *pp_options = talloc_strdup(ctx, "");
+ }
+ s = talloc_strdup(ctx, fname);
+
+ /* 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)) {
+ DEBUG(1, ("Invalid path (does not begin with smb://"));
+ return -1;
+ }
+
+ p += 2; /* Skip the double slash */
+
+ /* See if any options were specified */
+ if ((q = strrchr(p, '?')) != NULL ) {
+ /* There are options. Null terminate here and point to them */
+ *q++ = '\0';
+
+ DEBUG(4, ("Found options '%s'", q));
+
+ /* Copy the options */
+ if (*pp_options != NULL) {
+ TALLOC_FREE(*pp_options);
+ *pp_options = talloc_strdup(ctx, q);
+ }
+ }
+
+ if (*p == '\0') {
+ goto decoding;
+ }
+
+ if (*p == '/') {
+ int wl = strlen(smbc_getWorkgroup(context));
+
+ if (wl > 16) {
+ wl = 16;
+ }
+
+ *pp_server = talloc_strdup(ctx, smbc_getWorkgroup(context));
+ if (!*pp_server) {
+ return -1;
+ }
+ *pp_server[wl] = '\0';
+ 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)) {
+ char *userinfo = NULL;
+ const char *u;
+
+ next_token_no_ltrim_talloc(ctx, &p, &userinfo, "@");
+ if (!userinfo) {
+ return -1;
+ }
+ u = userinfo;
+
+ if (strchr_m(u, ';')) {
+ char *workgroup;
+ next_token_no_ltrim_talloc(ctx, &u, &workgroup, ";");
+ if (!workgroup) {
+ return -1;
+ }
+ if (pp_workgroup) {
+ *pp_workgroup = workgroup;
+ }
+ }
+
+ if (strchr_m(u, ':')) {
+ next_token_no_ltrim_talloc(ctx, &u, pp_user, ":");
+ if (!*pp_user) {
+ return -1;
+ }
+ *pp_password = talloc_strdup(ctx, u);
+ if (!*pp_password) {
+ return -1;
+ }
+ } else {
+ *pp_user = talloc_strdup(ctx, u);
+ if (!*pp_user) {
+ return -1;
+ }
+ }
+ }
+
+ if (!next_token_talloc(ctx, &p, pp_server, "/")) {
+ return -1;
+ }
+
+ if (*p == (char)0) {
+ goto decoding; /* That's it ... */
+ }
+
+ if (!next_token_talloc(ctx, &p, pp_share, "/")) {
+ return -1;
+ }
+
+ /*
+ * Prepend a leading slash if there's a file path, as required by
+ * NetApp filers.
+ */
+ if (*p != '\0') {
+ *pp_path = talloc_asprintf(ctx,
+ "\\%s",
+ p);
+ } else {
+ *pp_path = talloc_strdup(ctx, "");
+ }
+ if (!*pp_path) {
+ return -1;
+ }
+ string_replace(*pp_path, '/', '\\');
+
+decoding:
+
+ (void) urldecode_talloc(ctx, pp_path, *pp_path);
+ (void) urldecode_talloc(ctx, pp_server, *pp_server);
+ (void) urldecode_talloc(ctx, pp_share, *pp_share);
+ (void) urldecode_talloc(ctx, pp_user, *pp_user);
+ (void) urldecode_talloc(ctx, pp_password, *pp_password);
+
+ return 0;
+}
+
diff --git a/source3/libsmb/libsmb_printjob.c b/source3/libsmb/libsmb_printjob.c
new file mode 100644
index 0000000000..c8d7ad039d
--- /dev/null
+++ b/source3/libsmb/libsmb_printjob.c
@@ -0,0 +1,336 @@
+/*
+ Unix SMB/Netbios implementation.
+ SMB client library implementation
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000, 2002
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+ Copyright (C) Derrell Lipman 2003-2008
+ Copyright (C) Jeremy Allison 2007, 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmbclient.h"
+#include "libsmb_internal.h"
+
+
+/*
+ * Open a print file to be written to by other calls
+ */
+
+SMBCFILE *
+SMBC_open_print_job_ctx(SMBCCTX *context,
+ const char *fname)
+{
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *path = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ if (!fname) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ DEBUG(4, ("SMBC_open_print_job_ctx(%s)\n", fname));
+
+ if (SMBC_parse_path(frame,
+ context,
+ fname,
+ NULL,
+ &server,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ /* What if the path is empty, or the file exists? */
+
+ TALLOC_FREE(frame);
+ return smbc_getFunctionOpen(context)(context, fname, O_WRONLY, 666);
+}
+
+/*
+ * 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_ctx(SMBCCTX *c_file,
+ const char *fname,
+ SMBCCTX *c_print,
+ const char *printq)
+{
+ SMBCFILE *fid1;
+ SMBCFILE *fid2;
+ int bytes;
+ int saverr;
+ int tot_bytes = 0;
+ char buf[4096];
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!c_file || !c_file->internal->initialized ||
+ !c_print || !c_print->internal->initialized) {
+
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ if (!fname && !printq) {
+
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ /* Try to open the file for reading ... */
+
+ if ((long)(fid1 = smbc_getFunctionOpen(c_file)(c_file, fname,
+ O_RDONLY, 0666)) < 0) {
+ DEBUG(3, ("Error, fname=%s, errno=%i\n", fname, errno));
+ TALLOC_FREE(frame);
+ return -1; /* smbc_open sets errno */
+ }
+
+ /* Now, try to open the printer file for writing */
+
+ if ((long)(fid2 = smbc_getFunctionOpenPrintJob(c_print)(c_print,
+ printq)) < 0) {
+
+ saverr = errno; /* Save errno */
+ smbc_getFunctionClose(c_file)(c_file, fid1);
+ errno = saverr;
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ while ((bytes = smbc_getFunctionRead(c_file)(c_file, fid1,
+ buf, sizeof(buf))) > 0) {
+
+ tot_bytes += bytes;
+
+ if ((smbc_getFunctionWrite(c_print)(c_print, fid2,
+ buf, bytes)) < 0) {
+
+ saverr = errno;
+ smbc_getFunctionClose(c_file)(c_file, fid1);
+ smbc_getFunctionClose(c_print)(c_print, fid2);
+ errno = saverr;
+
+ }
+
+ }
+
+ saverr = errno;
+
+ smbc_getFunctionClose(c_file)(c_file, fid1);
+ smbc_getFunctionClose(c_print)(c_print, fid2);
+
+ if (bytes < 0) {
+
+ errno = saverr;
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ TALLOC_FREE(frame);
+ return tot_bytes;
+
+}
+
+/*
+ * Routine to list print jobs on a printer share ...
+ */
+
+int
+SMBC_list_print_jobs_ctx(SMBCCTX *context,
+ const char *fname,
+ smbc_list_print_job_fn fn)
+{
+ SMBCSRV *srv = NULL;
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *workgroup = NULL;
+ char *path = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!fname) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ DEBUG(4, ("smbc_list_print_jobs(%s)\n", fname));
+
+ if (SMBC_parse_path(frame,
+ context,
+ fname,
+ &workgroup,
+ &server,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!user || user[0] == (char)0) {
+ user = talloc_strdup(frame, smbc_getUser(context));
+ if (!user) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ }
+
+ srv = SMBC_server(frame, context, True,
+ server, share, &workgroup, &user, &password);
+
+ if (!srv) {
+ TALLOC_FREE(frame);
+ return -1; /* errno set by SMBC_server */
+ }
+
+ if (cli_print_queue(srv->cli,
+ (void (*)(struct print_job_info *))fn) < 0) {
+ errno = SMBC_errno(context, srv->cli);
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ TALLOC_FREE(frame);
+ return 0;
+
+}
+
+/*
+ * Delete a print job from a remote printer share
+ */
+
+int
+SMBC_unlink_print_job_ctx(SMBCCTX *context,
+ const char *fname,
+ int id)
+{
+ SMBCSRV *srv = NULL;
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *workgroup = NULL;
+ char *path = NULL;
+ int err;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!fname) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ DEBUG(4, ("smbc_unlink_print_job(%s)\n", fname));
+
+ if (SMBC_parse_path(frame,
+ context,
+ fname,
+ &workgroup,
+ &server,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!user || user[0] == (char)0) {
+ user = talloc_strdup(frame, smbc_getUser(context));
+ if (!user) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ }
+
+ srv = SMBC_server(frame, context, True,
+ server, share, &workgroup, &user, &password);
+
+ if (!srv) {
+
+ TALLOC_FREE(frame);
+ return -1; /* errno set by SMBC_server */
+
+ }
+
+ if ((err = cli_printjob_del(srv->cli, id)) != 0) {
+
+ if (err < 0)
+ errno = SMBC_errno(context, srv->cli);
+ else if (err == ERRnosuchprintjob)
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ TALLOC_FREE(frame);
+ return 0;
+
+}
+
diff --git a/source3/libsmb/libsmb_server.c b/source3/libsmb/libsmb_server.c
new file mode 100644
index 0000000000..aeec255350
--- /dev/null
+++ b/source3/libsmb/libsmb_server.c
@@ -0,0 +1,699 @@
+/*
+ Unix SMB/Netbios implementation.
+ SMB client library implementation
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000, 2002
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+ Copyright (C) Derrell Lipman 2003-2008
+ Copyright (C) Jeremy Allison 2007, 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmbclient.h"
+#include "libsmb_internal.h"
+
+
+/*
+ * Check a server for being alive and well.
+ * returns 0 if the server is in shape. Returns 1 on error
+ *
+ * Also useable outside libsmbclient to enable external cache
+ * to do some checks too.
+ */
+int
+SMBC_check_server(SMBCCTX * context,
+ SMBCSRV * server)
+{
+ socklen_t size;
+ struct sockaddr addr;
+
+ size = sizeof(addr);
+ return (getpeername(server->cli->fd, &addr, &size) == -1);
+}
+
+/*
+ * Remove a server from the cached server list it's unused.
+ * On success, 0 is returned. 1 is returned if the server could not be removed.
+ *
+ * Also useable outside libsmbclient
+ */
+int
+SMBC_remove_unused_server(SMBCCTX * context,
+ SMBCSRV * srv)
+{
+ SMBCFILE * file;
+
+ /* are we being fooled ? */
+ if (!context || !context->internal->initialized || !srv) {
+ return 1;
+ }
+
+ /* Check all open files/directories for a relation with this server */
+ for (file = context->internal->files; file; file = file->next) {
+ if (file->srv == srv) {
+ /* Still used */
+ DEBUG(3, ("smbc_remove_usused_server: "
+ "%p still used by %p.\n",
+ srv, file));
+ return 1;
+ }
+ }
+
+ DLIST_REMOVE(context->internal->servers, srv);
+
+ cli_shutdown(srv->cli);
+ srv->cli = NULL;
+
+ DEBUG(3, ("smbc_remove_usused_server: %p removed.\n", srv));
+
+ smbc_getFunctionRemoveCachedServer(context)(context, srv);
+
+ SAFE_FREE(srv);
+ return 0;
+}
+
+/****************************************************************
+ * Call the auth_fn with fixed size (fstring) buffers.
+ ***************************************************************/
+void
+SMBC_call_auth_fn(TALLOC_CTX *ctx,
+ SMBCCTX *context,
+ const char *server,
+ const char *share,
+ char **pp_workgroup,
+ char **pp_username,
+ char **pp_password)
+{
+ fstring workgroup;
+ fstring username;
+ fstring password;
+ smbc_get_auth_data_with_context_fn auth_with_context_fn;
+
+ strlcpy(workgroup, *pp_workgroup, sizeof(workgroup));
+ strlcpy(username, *pp_username, sizeof(username));
+ strlcpy(password, *pp_password, sizeof(password));
+
+ /* See if there's an authentication with context function provided */
+ auth_with_context_fn = smbc_getFunctionAuthDataWithContext(context);
+ if (auth_with_context_fn)
+ {
+ (* auth_with_context_fn)(context,
+ server, share,
+ workgroup, sizeof(workgroup),
+ username, sizeof(username),
+ password, sizeof(password));
+ }
+ else
+ {
+ smbc_getFunctionAuthData(context)(server, share,
+ workgroup, sizeof(workgroup),
+ username, sizeof(username),
+ password, sizeof(password));
+ }
+
+ TALLOC_FREE(*pp_workgroup);
+ TALLOC_FREE(*pp_username);
+ TALLOC_FREE(*pp_password);
+
+ *pp_workgroup = talloc_strdup(ctx, workgroup);
+ *pp_username = talloc_strdup(ctx, username);
+ *pp_password = talloc_strdup(ctx, password);
+}
+
+
+void
+SMBC_get_auth_data(const char *server, const char *share,
+ char *workgroup_buf, int workgroup_buf_len,
+ char *username_buf, int username_buf_len,
+ char *password_buf, int password_buf_len)
+{
+ /* Default function just uses provided data. Nothing to do. */
+}
+
+
+
+SMBCSRV *
+SMBC_find_server(TALLOC_CTX *ctx,
+ SMBCCTX *context,
+ const char *server,
+ const char *share,
+ char **pp_workgroup,
+ char **pp_username,
+ char **pp_password)
+{
+ SMBCSRV *srv;
+ int auth_called = 0;
+
+ if (!pp_workgroup || !pp_username || !pp_password) {
+ return NULL;
+ }
+
+check_server_cache:
+
+ srv = smbc_getFunctionGetCachedServer(context)(context,
+ server, share,
+ *pp_workgroup,
+ *pp_username);
+
+ if (!auth_called && !srv && (!*pp_username || !(*pp_username)[0] ||
+ !*pp_password || !(*pp_password)[0])) {
+ SMBC_call_auth_fn(ctx, context, server, share,
+ pp_workgroup, pp_username, pp_password);
+
+ /*
+ * However, smbc_auth_fn may have picked up info relating to
+ * an existing connection, so try for an existing connection
+ * again ...
+ */
+ auth_called = 1;
+ goto check_server_cache;
+
+ }
+
+ if (srv) {
+ if (smbc_getFunctionCheckServer(context)(context, srv)) {
+ /*
+ * This server is no good anymore
+ * Try to remove it and check for more possible
+ * servers in the cache
+ */
+ if (smbc_getFunctionRemoveUnusedServer(context)(context,
+ srv)) {
+ /*
+ * We could not remove the server completely,
+ * remove it from the cache so we will not get
+ * it again. It will be removed when the last
+ * file/dir is closed.
+ */
+ smbc_getFunctionRemoveCachedServer(context)(context,
+ srv);
+ }
+
+ /*
+ * Maybe there are more cached connections to this
+ * server
+ */
+ goto check_server_cache;
+ }
+
+ return srv;
+ }
+
+ return NULL;
+}
+
+/*
+ * 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.
+ */
+
+SMBCSRV *
+SMBC_server(TALLOC_CTX *ctx,
+ SMBCCTX *context,
+ bool connect_if_not_found,
+ const char *server,
+ const char *share,
+ char **pp_workgroup,
+ char **pp_username,
+ char **pp_password)
+{
+ SMBCSRV *srv=NULL;
+ struct cli_state *c;
+ struct nmb_name called, calling;
+ const char *server_n = server;
+ struct sockaddr_storage ss;
+ int tried_reverse = 0;
+ int port_try_first;
+ int port_try_next;
+ const char *username_used;
+ NTSTATUS status;
+
+ zero_addr(&ss);
+ ZERO_STRUCT(c);
+
+ if (server[0] == 0) {
+ errno = EPERM;
+ return NULL;
+ }
+
+ /* Look for a cached connection */
+ srv = SMBC_find_server(ctx, context, server, share,
+ pp_workgroup, pp_username, pp_password);
+
+ /*
+ * If we found a connection and we're only allowed one share per
+ * server...
+ */
+ if (srv &&
+ *share != '\0' &&
+ smbc_getOptionOneSharePerServer(context)) {
+
+ /*
+ * ... then if there's no current connection to the share,
+ * connect to it. SMBC_find_server(), or rather the function
+ * pointed to by context->get_cached_srv_fn which
+ * was called by SMBC_find_server(), will have issued a tree
+ * disconnect if the requested share is not the same as the
+ * one that was already connected.
+ */
+ if (srv->cli->cnum == (uint16) -1) {
+ /* Ensure we have accurate auth info */
+ SMBC_call_auth_fn(ctx, context, server, share,
+ pp_workgroup,
+ pp_username,
+ pp_password);
+
+ if (!*pp_workgroup || !*pp_username || !*pp_password) {
+ errno = ENOMEM;
+ cli_shutdown(srv->cli);
+ srv->cli = NULL;
+ smbc_getFunctionRemoveCachedServer(context)(context,
+ srv);
+ return NULL;
+ }
+
+ /*
+ * We don't need to renegotiate encryption
+ * here as the encryption context is not per
+ * tid.
+ */
+
+ if (!cli_send_tconX(srv->cli, share, "?????",
+ *pp_password,
+ strlen(*pp_password)+1)) {
+
+ errno = SMBC_errno(context, srv->cli);
+ cli_shutdown(srv->cli);
+ srv->cli = NULL;
+ smbc_getFunctionRemoveCachedServer(context)(context,
+ srv);
+ srv = NULL;
+ }
+
+ /*
+ * Regenerate the dev value since it's based on both
+ * server and share
+ */
+ if (srv) {
+ srv->dev = (dev_t)(str_checksum(server) ^
+ str_checksum(share));
+ }
+ }
+ }
+
+ /* If we have a connection... */
+ if (srv) {
+
+ /* ... then we're done here. Give 'em what they came for. */
+ return srv;
+ }
+
+ /* If we're not asked to connect when a connection doesn't exist... */
+ if (! connect_if_not_found) {
+ /* ... then we're done here. */
+ return NULL;
+ }
+
+ if (!*pp_workgroup || !*pp_username || !*pp_password) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ make_nmb_name(&calling, smbc_getNetbiosName(context), 0x0);
+ make_nmb_name(&called , server, 0x20);
+
+ DEBUG(4,("SMBC_server: server_n=[%s] server=[%s]\n", server_n, server));
+
+ DEBUG(4,(" -> server_n=[%s] server=[%s]\n", server_n, server));
+
+again:
+
+ zero_addr(&ss);
+
+ /* have to open a new connection */
+ if ((c = cli_initialise()) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ if (smbc_getOptionUseKerberos(context)) {
+ c->use_kerberos = True;
+ }
+
+ if (smbc_getOptionFallbackAfterKerberos(context)) {
+ c->fallback_after_kerberos = True;
+ }
+
+ c->timeout = smbc_getTimeout(context);
+
+ /*
+ * Force use of port 139 for first try if share is $IPC, empty, or
+ * null, so browse lists can work
+ */
+ if (share == NULL || *share == '\0' || strcmp(share, "IPC$") == 0) {
+ port_try_first = 139;
+ port_try_next = 445;
+ } else {
+ port_try_first = 445;
+ port_try_next = 139;
+ }
+
+ c->port = port_try_first;
+
+ status = cli_connect(c, server_n, &ss);
+ if (!NT_STATUS_IS_OK(status)) {
+
+ /* First connection attempt failed. Try alternate port. */
+ c->port = port_try_next;
+
+ status = cli_connect(c, server_n, &ss);
+ if (!NT_STATUS_IS_OK(status)) {
+ cli_shutdown(c);
+ errno = ETIMEDOUT;
+ 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;
+ } else { /* Try one more time, but ensure we don't loop */
+
+ /* Only try this if server is an IP address ... */
+
+ if (is_ipaddress(server) && !tried_reverse) {
+ fstring remote_name;
+ struct sockaddr_storage rem_ss;
+
+ if (!interpret_string_addr(&rem_ss, server,
+ NI_NUMERICHOST)) {
+ DEBUG(4, ("Could not convert IP address "
+ "%s to struct sockaddr_storage\n",
+ server));
+ errno = ETIMEDOUT;
+ return NULL;
+ }
+
+ tried_reverse++; /* Yuck */
+
+ if (name_status_find("*", 0, 0,
+ &rem_ss, remote_name)) {
+ make_nmb_name(&called,
+ remote_name,
+ 0x20);
+ goto again;
+ }
+ }
+ }
+ errno = ETIMEDOUT;
+ return NULL;
+ }
+
+ DEBUG(4,(" session request ok\n"));
+
+ if (!cli_negprot(c)) {
+ cli_shutdown(c);
+ errno = ETIMEDOUT;
+ return NULL;
+ }
+
+ username_used = *pp_username;
+
+ if (!NT_STATUS_IS_OK(cli_session_setup(c, username_used,
+ *pp_password,
+ strlen(*pp_password),
+ *pp_password,
+ strlen(*pp_password),
+ *pp_workgroup))) {
+
+ /* Failed. Try an anonymous login, if allowed by flags. */
+ username_used = "";
+
+ if (smbc_getOptionNoAutoAnonymousLogin(context) ||
+ !NT_STATUS_IS_OK(cli_session_setup(c, username_used,
+ *pp_password, 1,
+ *pp_password, 0,
+ *pp_workgroup))) {
+
+ cli_shutdown(c);
+ errno = EPERM;
+ return NULL;
+ }
+ }
+
+ DEBUG(4,(" session setup ok\n"));
+
+ if (!cli_send_tconX(c, share, "?????",
+ *pp_password, strlen(*pp_password)+1)) {
+ errno = SMBC_errno(context, c);
+ cli_shutdown(c);
+ return NULL;
+ }
+
+ DEBUG(4,(" tconx ok\n"));
+
+ if (context->internal->smb_encryption_level) {
+ /* Attempt UNIX smb encryption. */
+ if (!NT_STATUS_IS_OK(cli_force_encryption(c,
+ username_used,
+ *pp_password,
+ *pp_workgroup))) {
+
+ /*
+ * context->smb_encryption_level == 1
+ * means don't fail if encryption can't be negotiated,
+ * == 2 means fail if encryption can't be negotiated.
+ */
+
+ DEBUG(4,(" SMB encrypt failed\n"));
+
+ if (context->internal->smb_encryption_level == 2) {
+ cli_shutdown(c);
+ errno = EPERM;
+ return NULL;
+ }
+ }
+ DEBUG(4,(" SMB encrypt ok\n"));
+ }
+
+ /*
+ * Ok, we have got a nice connection
+ * Let's allocate a server structure.
+ */
+
+ srv = SMB_MALLOC_P(SMBCSRV);
+ if (!srv) {
+ errno = ENOMEM;
+ goto failed;
+ }
+
+ ZERO_STRUCTP(srv);
+ srv->cli = c;
+ srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share));
+ srv->no_pathinfo = False;
+ srv->no_pathinfo2 = False;
+ srv->no_nt_session = False;
+
+ /* now add it to the cache (internal or external) */
+ /* Let the cache function set errno if it wants to */
+ errno = 0;
+ if (smbc_getFunctionAddCachedServer(context)(context, srv,
+ server, share,
+ *pp_workgroup,
+ *pp_username)) {
+ int saved_errno = errno;
+ DEBUG(3, (" Failed to add server to cache\n"));
+ errno = saved_errno;
+ if (errno == 0) {
+ errno = ENOMEM;
+ }
+ goto failed;
+ }
+
+ DEBUG(2, ("Server connect ok: //%s/%s: %p\n",
+ server, share, srv));
+
+ DLIST_ADD(context->internal->servers, srv);
+ return srv;
+
+failed:
+ cli_shutdown(c);
+ if (!srv) {
+ return NULL;
+ }
+
+ SAFE_FREE(srv);
+ return NULL;
+}
+
+/*
+ * Connect to a server for getting/setting attributes, possibly on an existing
+ * connection. This works similarly to SMBC_server().
+ */
+SMBCSRV *
+SMBC_attr_server(TALLOC_CTX *ctx,
+ SMBCCTX *context,
+ const char *server,
+ const char *share,
+ char **pp_workgroup,
+ char **pp_username,
+ char **pp_password)
+{
+ int flags;
+ struct sockaddr_storage ss;
+ struct cli_state *ipc_cli;
+ struct rpc_pipe_client *pipe_hnd;
+ NTSTATUS nt_status;
+ SMBCSRV *ipc_srv=NULL;
+
+ /*
+ * See if we've already created this special connection. Reference
+ * our "special" share name '*IPC$', which is an impossible real share
+ * name due to the leading asterisk.
+ */
+ ipc_srv = SMBC_find_server(ctx, context, server, "*IPC$",
+ pp_workgroup, pp_username, pp_password);
+ if (!ipc_srv) {
+
+ /* We didn't find a cached connection. Get the password */
+ if (!*pp_password || (*pp_password)[0] == '\0') {
+ /* ... then retrieve it now. */
+ SMBC_call_auth_fn(ctx, context, server, share,
+ pp_workgroup,
+ pp_username,
+ pp_password);
+ if (!*pp_workgroup || !*pp_username || !*pp_password) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ }
+
+ flags = 0;
+ if (smbc_getOptionUseKerberos(context)) {
+ flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
+ }
+
+ zero_addr(&ss);
+ nt_status = cli_full_connection(&ipc_cli,
+ global_myname(), server,
+ &ss, 0, "IPC$", "?????",
+ *pp_username,
+ *pp_workgroup,
+ *pp_password,
+ flags,
+ Undefined, NULL);
+ if (! NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1,("cli_full_connection failed! (%s)\n",
+ nt_errstr(nt_status)));
+ errno = ENOTSUP;
+ return NULL;
+ }
+
+ if (context->internal->smb_encryption_level) {
+ /* Attempt UNIX smb encryption. */
+ if (!NT_STATUS_IS_OK(cli_force_encryption(ipc_cli,
+ *pp_username,
+ *pp_password,
+ *pp_workgroup))) {
+
+ /*
+ * context->smb_encryption_level ==
+ * 1 means don't fail if encryption can't be
+ * negotiated, == 2 means fail if encryption
+ * can't be negotiated.
+ */
+
+ DEBUG(4,(" SMB encrypt failed on IPC$\n"));
+
+ if (context->internal->smb_encryption_level == 2) {
+ cli_shutdown(ipc_cli);
+ errno = EPERM;
+ return NULL;
+ }
+ }
+ DEBUG(4,(" SMB encrypt ok on IPC$\n"));
+ }
+
+ ipc_srv = SMB_MALLOC_P(SMBCSRV);
+ if (!ipc_srv) {
+ errno = ENOMEM;
+ cli_shutdown(ipc_cli);
+ return NULL;
+ }
+
+ ZERO_STRUCTP(ipc_srv);
+ ipc_srv->cli = ipc_cli;
+
+ nt_status = cli_rpc_pipe_open_noauth(
+ ipc_srv->cli, &ndr_table_lsarpc.syntax_id, &pipe_hnd);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1, ("cli_nt_session_open fail!\n"));
+ errno = ENOTSUP;
+ cli_shutdown(ipc_srv->cli);
+ free(ipc_srv);
+ return NULL;
+ }
+
+ /*
+ * Some systems don't support
+ * SEC_RIGHTS_MAXIMUM_ALLOWED, but NT sends 0x2000000
+ * so we might as well do it too.
+ */
+
+ nt_status = rpccli_lsa_open_policy(
+ pipe_hnd,
+ talloc_tos(),
+ True,
+ GENERIC_EXECUTE_ACCESS,
+ &ipc_srv->pol);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ errno = SMBC_errno(context, ipc_srv->cli);
+ cli_shutdown(ipc_srv->cli);
+ return NULL;
+ }
+
+ /* now add it to the cache (internal or external) */
+
+ errno = 0; /* let cache function set errno if it likes */
+ if (smbc_getFunctionAddCachedServer(context)(context, ipc_srv,
+ server,
+ "*IPC$",
+ *pp_workgroup,
+ *pp_username)) {
+ DEBUG(3, (" Failed to add server to cache\n"));
+ if (errno == 0) {
+ errno = ENOMEM;
+ }
+ cli_shutdown(ipc_srv->cli);
+ free(ipc_srv);
+ return NULL;
+ }
+
+ DLIST_ADD(context->internal->servers, ipc_srv);
+ }
+
+ return ipc_srv;
+}
diff --git a/source3/libsmb/libsmb_setget.c b/source3/libsmb/libsmb_setget.c
new file mode 100644
index 0000000000..d0823bd77e
--- /dev/null
+++ b/source3/libsmb/libsmb_setget.c
@@ -0,0 +1,905 @@
+/*
+ Unix SMB/Netbios implementation.
+ SMB client library implementation
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000, 2002
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+ Copyright (C) Derrell Lipman 2003-2008
+ Copyright (C) Jeremy Allison 2007, 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#define __LIBSMBCLIENT_INTERNAL__
+#include "libsmbclient.h"
+#include "libsmb_internal.h"
+
+
+/** Get the netbios name used for making connections */
+char *
+smbc_getNetbiosName(SMBCCTX *c)
+{
+ return c->netbios_name;
+}
+
+/** Set the netbios name used for making connections */
+void
+smbc_setNetbiosName(SMBCCTX *c, char * netbios_name)
+{
+ c->netbios_name = netbios_name;
+}
+
+/** Get the workgroup used for making connections */
+char *
+smbc_getWorkgroup(SMBCCTX *c)
+{
+ return c->workgroup;
+}
+
+/** Set the workgroup used for making connections */
+void
+smbc_setWorkgroup(SMBCCTX *c, char * workgroup)
+{
+ c->workgroup = workgroup;
+}
+
+/** Get the username used for making connections */
+char *
+smbc_getUser(SMBCCTX *c)
+{
+ return c->user;
+}
+
+/** Set the username used for making connections */
+void
+smbc_setUser(SMBCCTX *c, char * user)
+{
+ c->user = user;
+}
+
+/** Get the debug level */
+int
+smbc_getDebug(SMBCCTX *c)
+{
+ return c->debug;
+}
+
+/** Set the debug level */
+void
+smbc_setDebug(SMBCCTX *c, int debug)
+{
+ c->debug = debug;
+ DEBUGLEVEL = debug;
+}
+
+/**
+ * Get the timeout used for waiting on connections and response data
+ * (in milliseconds)
+ */
+int
+smbc_getTimeout(SMBCCTX *c)
+{
+ return c->timeout;
+}
+
+/**
+ * Set the timeout used for waiting on connections and response data
+ * (in milliseconds)
+ */
+void
+smbc_setTimeout(SMBCCTX *c, int timeout)
+{
+ c->timeout = timeout;
+}
+
+/** Get whether to log to standard error instead of standard output */
+smbc_bool
+smbc_getOptionDebugToStderr(SMBCCTX *c)
+{
+ return c->internal->debug_stderr;
+}
+
+/** Set whether to log to standard error instead of standard output */
+void
+smbc_setOptionDebugToStderr(SMBCCTX *c, smbc_bool b)
+{
+ c->internal->debug_stderr = b;
+}
+
+/**
+ * Get whether to use new-style time attribute names, e.g. WRITE_TIME rather
+ * than the old-style names such as M_TIME. This allows also setting/getting
+ * CREATE_TIME which was previously unimplemented. (Note that the old C_TIME
+ * was supposed to be CHANGE_TIME but was confused and sometimes referred to
+ * CREATE_TIME.)
+ */
+smbc_bool
+smbc_getOptionFullTimeNames(SMBCCTX *c)
+{
+ return c->internal->full_time_names;
+}
+
+/**
+ * Set whether to use new-style time attribute names, e.g. WRITE_TIME rather
+ * than the old-style names such as M_TIME. This allows also setting/getting
+ * CREATE_TIME which was previously unimplemented. (Note that the old C_TIME
+ * was supposed to be CHANGE_TIME but was confused and sometimes referred to
+ * CREATE_TIME.)
+ */
+void
+smbc_setOptionFullTimeNames(SMBCCTX *c, smbc_bool b)
+{
+ c->internal->full_time_names = b;
+}
+
+/**
+ * Get the share mode to use for files opened with SMBC_open_ctx(). The
+ * default is SMBC_SHAREMODE_DENY_NONE.
+ */
+smbc_share_mode
+smbc_getOptionOpenShareMode(SMBCCTX *c)
+{
+ return c->internal->share_mode;
+}
+
+/**
+ * Set the share mode to use for files opened with SMBC_open_ctx(). The
+ * default is SMBC_SHAREMODE_DENY_NONE.
+ */
+void
+smbc_setOptionOpenShareMode(SMBCCTX *c, smbc_share_mode share_mode)
+{
+ c->internal->share_mode = share_mode;
+}
+
+/** Retrieve a previously set user data handle */
+void *
+smbc_getOptionUserData(SMBCCTX *c)
+{
+ return c->internal->user_data;
+}
+
+/** Save a user data handle */
+void
+smbc_setOptionUserData(SMBCCTX *c, void *user_data)
+{
+ c->internal->user_data = user_data;
+}
+
+/** Get the encoded value for encryption level. */
+smbc_smb_encrypt_level
+smbc_getOptionSmbEncryptionLevel(SMBCCTX *c)
+{
+ return c->internal->smb_encryption_level;
+}
+
+/** Set the encoded value for encryption level. */
+void
+smbc_setOptionSmbEncryptionLevel(SMBCCTX *c, smbc_smb_encrypt_level level)
+{
+ c->internal->smb_encryption_level = level;
+}
+
+/**
+ * Get from how many local master browsers should the list of workgroups be
+ * retrieved. It can take up to 12 minutes or longer after a server becomes a
+ * local master browser, for it to have the entire browse list (the list of
+ * workgroups/domains) from an entire network. Since a client never knows
+ * which local master browser will be found first, the one which is found
+ * first and used to retrieve a browse list may have an incomplete or empty
+ * browse list. By requesting the browse list from multiple local master
+ * browsers, a more complete list can be generated. For small networks (few
+ * workgroups), it is recommended that this value be set to 0, causing the
+ * browse lists from all found local master browsers to be retrieved and
+ * merged. For networks with many workgroups, a suitable value for this
+ * variable is probably somewhere around 3. (Default: 3).
+ */
+int
+smbc_getOptionBrowseMaxLmbCount(SMBCCTX *c)
+{
+ return c->options.browse_max_lmb_count;
+}
+
+/**
+ * Set from how many local master browsers should the list of workgroups be
+ * retrieved. It can take up to 12 minutes or longer after a server becomes a
+ * local master browser, for it to have the entire browse list (the list of
+ * workgroups/domains) from an entire network. Since a client never knows
+ * which local master browser will be found first, the one which is found
+ * first and used to retrieve a browse list may have an incomplete or empty
+ * browse list. By requesting the browse list from multiple local master
+ * browsers, a more complete list can be generated. For small networks (few
+ * workgroups), it is recommended that this value be set to 0, causing the
+ * browse lists from all found local master browsers to be retrieved and
+ * merged. For networks with many workgroups, a suitable value for this
+ * variable is probably somewhere around 3. (Default: 3).
+ */
+void
+smbc_setOptionBrowseMaxLmbCount(SMBCCTX *c, int count)
+{
+ c->options.browse_max_lmb_count = count;
+}
+
+/**
+ * Get whether to url-encode readdir entries.
+ *
+ * There is a difference in the desired return strings from
+ * smbc_readdir() depending upon whether the filenames are to
+ * be displayed to the user, or whether they are to be
+ * appended to the path name passed to smbc_opendir() to call
+ * a further smbc_ function (e.g. open the file with
+ * smbc_open()). In the former case, the filename should be
+ * in "human readable" form. In the latter case, the smbc_
+ * functions expect a URL which must be url-encoded. Those
+ * functions decode the URL. If, for example, smbc_readdir()
+ * returned a file name of "abc%20def.txt", passing a path
+ * with this file name attached to smbc_open() would cause
+ * smbc_open to attempt to open the file "abc def.txt" since
+ * the %20 is decoded into a space.
+ *
+ * Set this option to True if the names returned by
+ * smbc_readdir() should be url-encoded such that they can be
+ * passed back to another smbc_ call. Set it to False if the
+ * names returned by smbc_readdir() are to be presented to the
+ * user.
+ *
+ * For backwards compatibility, this option defaults to False.
+ */
+smbc_bool
+smbc_getOptionUrlEncodeReaddirEntries(SMBCCTX *c)
+{
+ return c->options.urlencode_readdir_entries;
+}
+
+/**
+ * Set whether to url-encode readdir entries.
+ *
+ * There is a difference in the desired return strings from
+ * smbc_readdir() depending upon whether the filenames are to
+ * be displayed to the user, or whether they are to be
+ * appended to the path name passed to smbc_opendir() to call
+ * a further smbc_ function (e.g. open the file with
+ * smbc_open()). In the former case, the filename should be
+ * in "human readable" form. In the latter case, the smbc_
+ * functions expect a URL which must be url-encoded. Those
+ * functions decode the URL. If, for example, smbc_readdir()
+ * returned a file name of "abc%20def.txt", passing a path
+ * with this file name attached to smbc_open() would cause
+ * smbc_open to attempt to open the file "abc def.txt" since
+ * the %20 is decoded into a space.
+ *
+ * Set this option to True if the names returned by
+ * smbc_readdir() should be url-encoded such that they can be
+ * passed back to another smbc_ call. Set it to False if the
+ * names returned by smbc_readdir() are to be presented to the
+ * user.
+ *
+ * For backwards compatibility, this option defaults to False.
+ */
+void
+smbc_setOptionUrlEncodeReaddirEntries(SMBCCTX *c, smbc_bool b)
+{
+ c->options.urlencode_readdir_entries = b;
+}
+
+/**
+ * Get whether to use the same connection for all shares on a server.
+ *
+ * Some Windows versions appear to have a limit to the number
+ * of concurrent SESSIONs and/or TREE CONNECTions. In
+ * one-shot programs (i.e. the program runs and then quickly
+ * ends, thereby shutting down all connections), it is
+ * probably reasonable to establish a new connection for each
+ * share. In long-running applications, the limitation can be
+ * avoided by using only a single connection to each server,
+ * and issuing a new TREE CONNECT when the share is accessed.
+ */
+smbc_bool
+smbc_getOptionOneSharePerServer(SMBCCTX *c)
+{
+ return c->options.one_share_per_server;
+}
+
+/**
+ * Set whether to use the same connection for all shares on a server.
+ *
+ * Some Windows versions appear to have a limit to the number
+ * of concurrent SESSIONs and/or TREE CONNECTions. In
+ * one-shot programs (i.e. the program runs and then quickly
+ * ends, thereby shutting down all connections), it is
+ * probably reasonable to establish a new connection for each
+ * share. In long-running applications, the limitation can be
+ * avoided by using only a single connection to each server,
+ * and issuing a new TREE CONNECT when the share is accessed.
+ */
+void
+smbc_setOptionOneSharePerServer(SMBCCTX *c, smbc_bool b)
+{
+ c->options.one_share_per_server = b;
+}
+
+/** Get whether to enable use of kerberos */
+smbc_bool
+smbc_getOptionUseKerberos(SMBCCTX *c)
+{
+ return c->flags & SMB_CTX_FLAG_USE_KERBEROS ? True : False;
+}
+
+/** Set whether to enable use of kerberos */
+void
+smbc_setOptionUseKerberos(SMBCCTX *c, smbc_bool b)
+{
+ if (b) {
+ c->flags |= SMB_CTX_FLAG_USE_KERBEROS;
+ } else {
+ c->flags &= ~SMB_CTX_FLAG_USE_KERBEROS;
+ }
+}
+
+/** Get whether to fallback after kerberos */
+smbc_bool
+smbc_getOptionFallbackAfterKerberos(SMBCCTX *c)
+{
+ return c->flags & SMB_CTX_FLAG_FALLBACK_AFTER_KERBEROS ? True : False;
+}
+
+/** Set whether to fallback after kerberos */
+void
+smbc_setOptionFallbackAfterKerberos(SMBCCTX *c, smbc_bool b)
+{
+ if (b) {
+ c->flags |= SMB_CTX_FLAG_FALLBACK_AFTER_KERBEROS;
+ } else {
+ c->flags &= ~SMB_CTX_FLAG_FALLBACK_AFTER_KERBEROS;
+ }
+}
+
+/** Get whether to automatically select anonymous login */
+smbc_bool
+smbc_getOptionNoAutoAnonymousLogin(SMBCCTX *c)
+{
+ return c->flags & SMBCCTX_FLAG_NO_AUTO_ANONYMOUS_LOGON ? True : False;
+}
+
+/** Set whether to automatically select anonymous login */
+void
+smbc_setOptionNoAutoAnonymousLogin(SMBCCTX *c, smbc_bool b)
+{
+ if (b) {
+ c->flags |= SMBCCTX_FLAG_NO_AUTO_ANONYMOUS_LOGON;
+ } else {
+ c->flags &= ~SMBCCTX_FLAG_NO_AUTO_ANONYMOUS_LOGON;
+ }
+}
+
+/** Get the function for obtaining authentication data */
+smbc_get_auth_data_fn
+smbc_getFunctionAuthData(SMBCCTX *c)
+{
+ return c->callbacks.auth_fn;
+}
+
+/** Set the function for obtaining authentication data */
+void
+smbc_setFunctionAuthData(SMBCCTX *c, smbc_get_auth_data_fn fn)
+{
+ c->internal->auth_fn_with_context = NULL;
+ c->callbacks.auth_fn = fn;
+}
+
+/** Get the new-style authentication function which includes the context. */
+smbc_get_auth_data_with_context_fn
+smbc_getFunctionAuthDataWithContext(SMBCCTX *c)
+{
+ return c->internal->auth_fn_with_context;
+}
+
+/** Set the new-style authentication function which includes the context. */
+void
+smbc_setFunctionAuthDataWithContext(SMBCCTX *c,
+ smbc_get_auth_data_with_context_fn fn)
+{
+ c->callbacks.auth_fn = NULL;
+ c->internal->auth_fn_with_context = fn;
+}
+
+/** Get the function for checking if a server is still good */
+smbc_check_server_fn
+smbc_getFunctionCheckServer(SMBCCTX *c)
+{
+ return c->callbacks.check_server_fn;
+}
+
+/** Set the function for checking if a server is still good */
+void
+smbc_setFunctionCheckServer(SMBCCTX *c, smbc_check_server_fn fn)
+{
+ c->callbacks.check_server_fn = fn;
+}
+
+/** Get the function for removing a server if unused */
+smbc_remove_unused_server_fn
+smbc_getFunctionRemoveUnusedServer(SMBCCTX *c)
+{
+ return c->callbacks.remove_unused_server_fn;
+}
+
+/** Set the function for removing a server if unused */
+void
+smbc_setFunctionRemoveUnusedServer(SMBCCTX *c,
+ smbc_remove_unused_server_fn fn)
+{
+ c->callbacks.remove_unused_server_fn = fn;
+}
+
+/** Get the function for adding a cached server */
+smbc_add_cached_srv_fn
+smbc_getFunctionAddCachedServer(SMBCCTX *c)
+{
+ return c->callbacks.add_cached_srv_fn;
+}
+
+/** Set the function for adding a cached server */
+void
+smbc_setFunctionAddCachedServer(SMBCCTX *c, smbc_add_cached_srv_fn fn)
+{
+ c->callbacks.add_cached_srv_fn = fn;
+}
+
+/** Get the function for server cache lookup */
+smbc_get_cached_srv_fn
+smbc_getFunctionGetCachedServer(SMBCCTX *c)
+{
+ return c->callbacks.get_cached_srv_fn;
+}
+
+/** Set the function for server cache lookup */
+void
+smbc_setFunctionGetCachedServer(SMBCCTX *c, smbc_get_cached_srv_fn fn)
+{
+ c->callbacks.get_cached_srv_fn = fn;
+}
+
+/** Get the function for server cache removal */
+smbc_remove_cached_srv_fn
+smbc_getFunctionRemoveCachedServer(SMBCCTX *c)
+{
+ return c->callbacks.remove_cached_srv_fn;
+}
+
+/** Set the function for server cache removal */
+void
+smbc_setFunctionRemoveCachedServer(SMBCCTX *c,
+ smbc_remove_cached_srv_fn fn)
+{
+ c->callbacks.remove_cached_srv_fn = fn;
+}
+
+/**
+ * Get the function for server cache purging. This function tries to
+ * remove all cached servers (e.g. on disconnect)
+ */
+smbc_purge_cached_fn
+smbc_getFunctionPurgeCachedServers(SMBCCTX *c)
+{
+ return c->callbacks.purge_cached_fn;
+}
+
+/** Set the function to store private data of the server cache */
+void smbc_setServerCacheData(SMBCCTX *c, struct smbc_server_cache * cache)
+{
+ c->internal->server_cache = cache;
+}
+
+/** Get the function to store private data of the server cache */
+struct smbc_server_cache * smbc_getServerCacheData(SMBCCTX *c)
+{
+ return c->internal->server_cache;
+}
+
+
+/**
+ * Set the function for server cache purging. This function tries to
+ * remove all cached servers (e.g. on disconnect)
+ */
+void
+smbc_setFunctionPurgeCachedServers(SMBCCTX *c, smbc_purge_cached_fn fn)
+{
+ c->callbacks.purge_cached_fn = fn;
+}
+
+/**
+ * Callable functions for files.
+ */
+
+smbc_open_fn
+smbc_getFunctionOpen(SMBCCTX *c)
+{
+ return c->open;
+}
+
+void
+smbc_setFunctionOpen(SMBCCTX *c, smbc_open_fn fn)
+{
+ c->open = fn;
+}
+
+smbc_creat_fn
+smbc_getFunctionCreat(SMBCCTX *c)
+{
+ return c->creat;
+}
+
+void
+smbc_setFunctionCreat(SMBCCTX *c, smbc_creat_fn fn)
+{
+ c->creat = fn;
+}
+
+smbc_read_fn
+smbc_getFunctionRead(SMBCCTX *c)
+{
+ return c->read;
+}
+
+void
+smbc_setFunctionRead(SMBCCTX *c, smbc_read_fn fn)
+{
+ c->read = fn;
+}
+
+smbc_write_fn
+smbc_getFunctionWrite(SMBCCTX *c)
+{
+ return c->write;
+}
+
+void
+smbc_setFunctionWrite(SMBCCTX *c, smbc_write_fn fn)
+{
+ c->write = fn;
+}
+
+smbc_unlink_fn
+smbc_getFunctionUnlink(SMBCCTX *c)
+{
+ return c->unlink;
+}
+
+void
+smbc_setFunctionUnlink(SMBCCTX *c, smbc_unlink_fn fn)
+{
+ c->unlink = fn;
+}
+
+smbc_rename_fn
+smbc_getFunctionRename(SMBCCTX *c)
+{
+ return c->rename;
+}
+
+void
+smbc_setFunctionRename(SMBCCTX *c, smbc_rename_fn fn)
+{
+ c->rename = fn;
+}
+
+smbc_lseek_fn
+smbc_getFunctionLseek(SMBCCTX *c)
+{
+ return c->lseek;
+}
+
+void
+smbc_setFunctionLseek(SMBCCTX *c, smbc_lseek_fn fn)
+{
+ c->lseek = fn;
+}
+
+smbc_stat_fn
+smbc_getFunctionStat(SMBCCTX *c)
+{
+ return c->stat;
+}
+
+void
+smbc_setFunctionStat(SMBCCTX *c, smbc_stat_fn fn)
+{
+ c->stat = fn;
+}
+
+smbc_fstat_fn
+smbc_getFunctionFstat(SMBCCTX *c)
+{
+ return c->fstat;
+}
+
+void
+smbc_setFunctionFstat(SMBCCTX *c, smbc_fstat_fn fn)
+{
+ c->fstat = fn;
+}
+
+smbc_ftruncate_fn
+smbc_getFunctionFtruncate(SMBCCTX *c)
+{
+ return c->internal->posix_emu.ftruncate_fn;
+}
+
+void
+smbc_setFunctionFtruncate(SMBCCTX *c, smbc_ftruncate_fn fn)
+{
+ c->internal->posix_emu.ftruncate_fn = fn;
+}
+
+smbc_close_fn
+smbc_getFunctionClose(SMBCCTX *c)
+{
+ return c->close_fn;
+}
+
+void
+smbc_setFunctionClose(SMBCCTX *c, smbc_close_fn fn)
+{
+ c->close_fn = fn;
+}
+
+
+/**
+ * Callable functions for directories.
+ */
+
+smbc_opendir_fn
+smbc_getFunctionOpendir(SMBCCTX *c)
+{
+ return c->opendir;
+}
+
+void
+smbc_setFunctionOpendir(SMBCCTX *c, smbc_opendir_fn fn)
+{
+ c->opendir = fn;
+}
+
+smbc_closedir_fn
+smbc_getFunctionClosedir(SMBCCTX *c)
+{
+ return c->closedir;
+}
+
+void
+smbc_setFunctionClosedir(SMBCCTX *c, smbc_closedir_fn fn)
+{
+ c->closedir = fn;
+}
+
+smbc_readdir_fn
+smbc_getFunctionReaddir(SMBCCTX *c)
+{
+ return c->readdir;
+}
+
+void
+smbc_setFunctionReaddir(SMBCCTX *c, smbc_readdir_fn fn)
+{
+ c->readdir = fn;
+}
+
+smbc_getdents_fn
+smbc_getFunctionGetdents(SMBCCTX *c)
+{
+ return c->getdents;
+}
+
+void
+smbc_setFunctionGetdents(SMBCCTX *c, smbc_getdents_fn fn)
+{
+ c->getdents = fn;
+}
+
+smbc_mkdir_fn
+smbc_getFunctionMkdir(SMBCCTX *c)
+{
+ return c->mkdir;
+}
+
+void
+smbc_setFunctionMkdir(SMBCCTX *c, smbc_mkdir_fn fn)
+{
+ c->mkdir = fn;
+}
+
+smbc_rmdir_fn
+smbc_getFunctionRmdir(SMBCCTX *c)
+{
+ return c->rmdir;
+}
+
+void
+smbc_setFunctionRmdir(SMBCCTX *c, smbc_rmdir_fn fn)
+{
+ c->rmdir = fn;
+}
+
+smbc_telldir_fn
+smbc_getFunctionTelldir(SMBCCTX *c)
+{
+ return c->telldir;
+}
+
+void
+smbc_setFunctionTelldir(SMBCCTX *c, smbc_telldir_fn fn)
+{
+ c->telldir = fn;
+}
+
+smbc_lseekdir_fn
+smbc_getFunctionLseekdir(SMBCCTX *c)
+{
+ return c->lseekdir;
+}
+
+void
+smbc_setFunctionLseekdir(SMBCCTX *c, smbc_lseekdir_fn fn)
+{
+ c->lseekdir = fn;
+}
+
+smbc_fstatdir_fn
+smbc_getFunctionFstatdir(SMBCCTX *c)
+{
+ return c->fstatdir;
+}
+
+void
+smbc_setFunctionFstatdir(SMBCCTX *c, smbc_fstatdir_fn fn)
+{
+ c->fstatdir = fn;
+}
+
+
+/**
+ * Callable functions applicable to both files and directories.
+ */
+
+smbc_chmod_fn
+smbc_getFunctionChmod(SMBCCTX *c)
+{
+ return c->chmod;
+}
+
+void
+smbc_setFunctionChmod(SMBCCTX *c, smbc_chmod_fn fn)
+{
+ c->chmod = fn;
+}
+
+smbc_utimes_fn
+smbc_getFunctionUtimes(SMBCCTX *c)
+{
+ return c->utimes;
+}
+
+void
+smbc_setFunctionUtimes(SMBCCTX *c, smbc_utimes_fn fn)
+{
+ c->utimes = fn;
+}
+
+smbc_setxattr_fn
+smbc_getFunctionSetxattr(SMBCCTX *c)
+{
+ return c->setxattr;
+}
+
+void
+smbc_setFunctionSetxattr(SMBCCTX *c, smbc_setxattr_fn fn)
+{
+ c->setxattr = fn;
+}
+
+smbc_getxattr_fn
+smbc_getFunctionGetxattr(SMBCCTX *c)
+{
+ return c->getxattr;
+}
+
+void
+smbc_setFunctionGetxattr(SMBCCTX *c, smbc_getxattr_fn fn)
+{
+ c->getxattr = fn;
+}
+
+smbc_removexattr_fn
+smbc_getFunctionRemovexattr(SMBCCTX *c)
+{
+ return c->removexattr;
+}
+
+void
+smbc_setFunctionRemovexattr(SMBCCTX *c, smbc_removexattr_fn fn)
+{
+ c->removexattr = fn;
+}
+
+smbc_listxattr_fn
+smbc_getFunctionListxattr(SMBCCTX *c)
+{
+ return c->listxattr;
+}
+
+void
+smbc_setFunctionListxattr(SMBCCTX *c, smbc_listxattr_fn fn)
+{
+ c->listxattr = fn;
+}
+
+
+/**
+ * Callable functions related to printing
+ */
+
+smbc_print_file_fn
+smbc_getFunctionPrintFile(SMBCCTX *c)
+{
+ return c->print_file;
+}
+
+void
+smbc_setFunctionPrintFile(SMBCCTX *c, smbc_print_file_fn fn)
+{
+ c->print_file = fn;
+}
+
+smbc_open_print_job_fn
+smbc_getFunctionOpenPrintJob(SMBCCTX *c)
+{
+ return c->open_print_job;
+}
+
+void
+smbc_setFunctionOpenPrintJob(SMBCCTX *c,
+ smbc_open_print_job_fn fn)
+{
+ c->open_print_job = fn;
+}
+
+smbc_list_print_jobs_fn
+smbc_getFunctionListPrintJobs(SMBCCTX *c)
+{
+ return c->list_print_jobs;
+}
+
+void
+smbc_setFunctionListPrintJobs(SMBCCTX *c,
+ smbc_list_print_jobs_fn fn)
+{
+ c->list_print_jobs = fn;
+}
+
+smbc_unlink_print_job_fn
+smbc_getFunctionUnlinkPrintJob(SMBCCTX *c)
+{
+ return c->unlink_print_job;
+}
+
+void
+smbc_setFunctionUnlinkPrintJob(SMBCCTX *c,
+ smbc_unlink_print_job_fn fn)
+{
+ c->unlink_print_job = fn;
+}
+
diff --git a/source3/libsmb/libsmb_stat.c b/source3/libsmb/libsmb_stat.c
new file mode 100644
index 0000000000..27546f687e
--- /dev/null
+++ b/source3/libsmb/libsmb_stat.c
@@ -0,0 +1,302 @@
+/*
+ Unix SMB/Netbios implementation.
+ SMB client library implementation
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000, 2002
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+ Copyright (C) Derrell Lipman 2003-2008
+ Copyright (C) Jeremy Allison 2007, 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmbclient.h"
+#include "libsmb_internal.h"
+
+
+/*
+ * Generate an inode number from file name for those things that need it
+ */
+
+static ino_t
+generate_inode(SMBCCTX *context,
+ const char *name)
+{
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL;
+ return -1;
+
+ }
+
+ 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
+setup_stat(SMBCCTX *context,
+ struct stat *st,
+ char *fname,
+ SMB_OFF_T size,
+ int mode)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ 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;
+#ifdef HAVE_STAT_ST_BLKSIZE
+ st->st_blksize = 512;
+#endif
+#ifdef HAVE_STAT_ST_BLOCKS
+ st->st_blocks = (size+511)/512;
+#endif
+#ifdef HAVE_STRUCT_STAT_ST_RDEV
+ st->st_rdev = 0;
+#endif
+ 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 = generate_inode(context, fname);
+ }
+
+ TALLOC_FREE(frame);
+ return True; /* FIXME: Is this needed ? */
+
+}
+
+/*
+ * Routine to stat a file given a name
+ */
+
+int
+SMBC_stat_ctx(SMBCCTX *context,
+ const char *fname,
+ struct stat *st)
+{
+ SMBCSRV *srv = NULL;
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *workgroup = NULL;
+ char *path = NULL;
+ struct timespec write_time_ts;
+ struct timespec access_time_ts;
+ struct timespec change_time_ts;
+ SMB_OFF_T size = 0;
+ uint16 mode = 0;
+ SMB_INO_T ino = 0;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!fname) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ DEBUG(4, ("smbc_stat(%s)\n", fname));
+
+ if (SMBC_parse_path(frame,
+ context,
+ fname,
+ &workgroup,
+ &server,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!user || user[0] == (char)0) {
+ user = talloc_strdup(frame, smbc_getUser(context));
+ if (!user) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ }
+
+ srv = SMBC_server(frame, context, True,
+ server, share, &workgroup, &user, &password);
+
+ if (!srv) {
+ TALLOC_FREE(frame);
+ return -1; /* errno set by SMBC_server */
+ }
+
+ if (!SMBC_getatr(context, srv, path, &mode, &size,
+ NULL,
+ &access_time_ts,
+ &write_time_ts,
+ &change_time_ts,
+ &ino)) {
+ errno = SMBC_errno(context, srv->cli);
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ st->st_ino = ino;
+
+ setup_stat(context, st, (char *) fname, size, mode);
+
+ set_atimespec(st, access_time_ts);
+ set_ctimespec(st, change_time_ts);
+ set_mtimespec(st, write_time_ts);
+ st->st_dev = srv->dev;
+
+ TALLOC_FREE(frame);
+ return 0;
+
+}
+
+/*
+ * Routine to stat a file given an fd
+ */
+
+int
+SMBC_fstat_ctx(SMBCCTX *context,
+ SMBCFILE *file,
+ struct stat *st)
+{
+ struct timespec change_time_ts;
+ struct timespec access_time_ts;
+ struct timespec write_time_ts;
+ SMB_OFF_T size;
+ uint16 mode;
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *path = NULL;
+ char *targetpath = NULL;
+ struct cli_state *targetcli = NULL;
+ SMB_INO_T ino = 0;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!file || !SMBC_dlist_contains(context->internal->files, file)) {
+ errno = EBADF;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!file->file) {
+ TALLOC_FREE(frame);
+ return smbc_getFunctionFstatdir(context)(context, file, st);
+ }
+
+ /*d_printf(">>>fstat: parsing %s\n", file->fname);*/
+ if (SMBC_parse_path(frame,
+ context,
+ file->fname,
+ NULL,
+ &server,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ /*d_printf(">>>fstat: resolving %s\n", path);*/
+ if (!cli_resolve_path(frame, "", file->srv->cli, path,
+ &targetcli, &targetpath)) {
+ d_printf("Could not resolve %s\n", path);
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
+
+ if (!cli_qfileinfo(targetcli, file->cli_fd, &mode, &size,
+ NULL,
+ &access_time_ts,
+ &write_time_ts,
+ &change_time_ts,
+ &ino)) {
+
+ time_t change_time, access_time, write_time;
+
+ if (!cli_getattrE(targetcli, file->cli_fd, &mode, &size,
+ &change_time, &access_time, &write_time)) {
+
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ change_time_ts = convert_time_t_to_timespec(change_time);
+ access_time_ts = convert_time_t_to_timespec(access_time);
+ write_time_ts = convert_time_t_to_timespec(write_time);
+ }
+
+ st->st_ino = ino;
+
+ setup_stat(context, st, file->fname, size, mode);
+
+ set_atimespec(st, access_time_ts);
+ set_ctimespec(st, change_time_ts);
+ set_mtimespec(st, write_time_ts);
+ st->st_dev = file->srv->dev;
+
+ TALLOC_FREE(frame);
+ return 0;
+
+}
diff --git a/source3/libsmb/libsmb_xattr.c b/source3/libsmb/libsmb_xattr.c
new file mode 100644
index 0000000000..f1b3d1415e
--- /dev/null
+++ b/source3/libsmb/libsmb_xattr.c
@@ -0,0 +1,2323 @@
+/*
+ Unix SMB/Netbios implementation.
+ SMB client library implementation
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000, 2002
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+ Copyright (C) Derrell Lipman 2003-2008
+ Copyright (C) Jeremy Allison 2007, 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmbclient.h"
+#include "libsmb_internal.h"
+
+
+/*
+ * Find an lsa pipe handle associated with a cli struct.
+ */
+static struct rpc_pipe_client *
+find_lsa_pipe_hnd(struct cli_state *ipc_cli)
+{
+ struct rpc_pipe_client *pipe_hnd;
+
+ for (pipe_hnd = ipc_cli->pipe_list;
+ pipe_hnd;
+ pipe_hnd = pipe_hnd->next) {
+
+ if (ndr_syntax_id_equal(&pipe_hnd->abstract_syntax,
+ &ndr_table_lsarpc.syntax_id)) {
+ return pipe_hnd;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Sort ACEs according to the documentation at
+ * http://support.microsoft.com/kb/269175, at least as far as it defines the
+ * order.
+ */
+
+static int
+ace_compare(SEC_ACE *ace1,
+ SEC_ACE *ace2)
+{
+ bool b1;
+ bool b2;
+
+ /* If the ACEs are equal, we have nothing more to do. */
+ if (sec_ace_equal(ace1, ace2)) {
+ return 0;
+ }
+
+ /* Inherited follow non-inherited */
+ b1 = ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) != 0);
+ b2 = ((ace2->flags & SEC_ACE_FLAG_INHERITED_ACE) != 0);
+ if (b1 != b2) {
+ return (b1 ? 1 : -1);
+ }
+
+ /*
+ * What shall we do with AUDITs and ALARMs? It's undefined. We'll
+ * sort them after DENY and ALLOW.
+ */
+ b1 = (ace1->type != SEC_ACE_TYPE_ACCESS_ALLOWED &&
+ ace1->type != SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT &&
+ ace1->type != SEC_ACE_TYPE_ACCESS_DENIED &&
+ ace1->type != SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
+ b2 = (ace2->type != SEC_ACE_TYPE_ACCESS_ALLOWED &&
+ ace2->type != SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT &&
+ ace2->type != SEC_ACE_TYPE_ACCESS_DENIED &&
+ ace2->type != SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
+ if (b1 != b2) {
+ return (b1 ? 1 : -1);
+ }
+
+ /* Allowed ACEs follow denied ACEs */
+ b1 = (ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED ||
+ ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT);
+ b2 = (ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED ||
+ ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT);
+ if (b1 != b2) {
+ return (b1 ? 1 : -1);
+ }
+
+ /*
+ * ACEs applying to an entity's object follow those applying to the
+ * entity itself
+ */
+ b1 = (ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
+ ace1->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
+ b2 = (ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
+ ace2->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
+ if (b1 != b2) {
+ return (b1 ? 1 : -1);
+ }
+
+ /*
+ * If we get this far, the ACEs are similar as far as the
+ * characteristics we typically care about (those defined by the
+ * referenced MS document). We'll now sort by characteristics that
+ * just seems reasonable.
+ */
+
+ if (ace1->type != ace2->type) {
+ return ace2->type - ace1->type;
+ }
+
+ if (sid_compare(&ace1->trustee, &ace2->trustee)) {
+ return sid_compare(&ace1->trustee, &ace2->trustee);
+ }
+
+ if (ace1->flags != ace2->flags) {
+ return ace1->flags - ace2->flags;
+ }
+
+ if (ace1->access_mask != ace2->access_mask) {
+ return ace1->access_mask - ace2->access_mask;
+ }
+
+ if (ace1->size != ace2->size) {
+ return ace1->size - ace2->size;
+ }
+
+ return memcmp(ace1, ace2, sizeof(SEC_ACE));
+}
+
+
+static void
+sort_acl(SEC_ACL *the_acl)
+{
+ uint32 i;
+ if (!the_acl) return;
+
+ qsort(the_acl->aces, the_acl->num_aces, sizeof(the_acl->aces[0]),
+ QSORT_CAST ace_compare);
+
+ for (i=1;i<the_acl->num_aces;) {
+ if (sec_ace_equal(&the_acl->aces[i-1], &the_acl->aces[i])) {
+ int j;
+ for (j=i; j<the_acl->num_aces-1; j++) {
+ the_acl->aces[j] = the_acl->aces[j+1];
+ }
+ the_acl->num_aces--;
+ } else {
+ i++;
+ }
+ }
+}
+
+/* convert a SID to a string, either numeric or username/group */
+static void
+convert_sid_to_string(struct cli_state *ipc_cli,
+ POLICY_HND *pol,
+ fstring str,
+ bool numeric,
+ DOM_SID *sid)
+{
+ char **domains = NULL;
+ char **names = NULL;
+ enum lsa_SidType *types = NULL;
+ struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
+ TALLOC_CTX *ctx;
+
+ sid_to_fstring(str, sid);
+
+ if (numeric) {
+ return; /* no lookup desired */
+ }
+
+ if (!pipe_hnd) {
+ return;
+ }
+
+ /* Ask LSA to convert the sid to a name */
+
+ ctx = talloc_stackframe();
+
+ if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_sids(pipe_hnd, ctx,
+ pol, 1, sid, &domains,
+ &names, &types)) ||
+ !domains || !domains[0] || !names || !names[0]) {
+ TALLOC_FREE(ctx);
+ return;
+ }
+
+ TALLOC_FREE(ctx);
+ /* Converted OK */
+
+ slprintf(str, sizeof(fstring) - 1, "%s%s%s",
+ domains[0], lp_winbind_separator(),
+ names[0]);
+}
+
+/* convert a string to a SID, either numeric or username/group */
+static bool
+convert_string_to_sid(struct cli_state *ipc_cli,
+ POLICY_HND *pol,
+ bool numeric,
+ DOM_SID *sid,
+ const char *str)
+{
+ enum lsa_SidType *types = NULL;
+ DOM_SID *sids = NULL;
+ bool result = True;
+ TALLOC_CTX *ctx = NULL;
+ struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
+
+ if (!pipe_hnd) {
+ return False;
+ }
+
+ if (numeric) {
+ if (strncmp(str, "S-", 2) == 0) {
+ return string_to_sid(sid, str);
+ }
+
+ result = False;
+ goto done;
+ }
+
+ ctx = talloc_stackframe();
+ if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_names(pipe_hnd, ctx,
+ pol, 1, &str,
+ NULL, 1, &sids,
+ &types))) {
+ result = False;
+ goto done;
+ }
+
+ sid_copy(sid, &sids[0]);
+done:
+
+ TALLOC_FREE(ctx);
+ return result;
+}
+
+
+/* parse an ACE in the same format as print_ace() */
+static bool
+parse_ace(struct cli_state *ipc_cli,
+ POLICY_HND *pol,
+ SEC_ACE *ace,
+ bool numeric,
+ char *str)
+{
+ char *p;
+ const char *cp;
+ char *tok;
+ unsigned int atype;
+ unsigned int aflags;
+ unsigned int amask;
+ DOM_SID sid;
+ SEC_ACCESS mask;
+ const struct perm_value *v;
+ struct perm_value {
+ const char *perm;
+ uint32 mask;
+ };
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ /* These values discovered by inspection */
+ static const struct perm_value special_values[] = {
+ { "R", 0x00120089 },
+ { "W", 0x00120116 },
+ { "X", 0x001200a0 },
+ { "D", 0x00010000 },
+ { "P", 0x00040000 },
+ { "O", 0x00080000 },
+ { NULL, 0 },
+ };
+
+ static const struct perm_value standard_values[] = {
+ { "READ", 0x001200a9 },
+ { "CHANGE", 0x001301bf },
+ { "FULL", 0x001f01ff },
+ { NULL, 0 },
+ };
+
+
+ ZERO_STRUCTP(ace);
+ p = strchr_m(str,':');
+ if (!p) {
+ TALLOC_FREE(frame);
+ return False;
+ }
+ *p = '\0';
+ p++;
+ /* Try to parse numeric form */
+
+ if (sscanf(p, "%i/%i/%i", &atype, &aflags, &amask) == 3 &&
+ convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
+ goto done;
+ }
+
+ /* Try to parse text form */
+
+ if (!convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ cp = p;
+ if (!next_token_talloc(frame, &cp, &tok, "/")) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ if (StrnCaseCmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
+ atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
+ } else if (StrnCaseCmp(tok, "DENIED", strlen("DENIED")) == 0) {
+ atype = SEC_ACE_TYPE_ACCESS_DENIED;
+ } else {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ /* Only numeric form accepted for flags at present */
+
+ if (!(next_token_talloc(frame, &cp, &tok, "/") &&
+ sscanf(tok, "%i", &aflags))) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ if (!next_token_talloc(frame, &cp, &tok, "/")) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ if (strncmp(tok, "0x", 2) == 0) {
+ if (sscanf(tok, "%i", &amask) != 1) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+ goto done;
+ }
+
+ for (v = standard_values; v->perm; v++) {
+ if (strcmp(tok, v->perm) == 0) {
+ amask = v->mask;
+ goto done;
+ }
+ }
+
+ p = tok;
+
+ while(*p) {
+ bool found = False;
+
+ for (v = special_values; v->perm; v++) {
+ if (v->perm[0] == *p) {
+ amask |= v->mask;
+ found = True;
+ }
+ }
+
+ if (!found) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+ p++;
+ }
+
+ if (*p) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+done:
+ mask = amask;
+ init_sec_ace(ace, &sid, atype, mask, aflags);
+ TALLOC_FREE(frame);
+ return true;
+}
+
+/* add an ACE to a list of ACEs in a SEC_ACL */
+static bool
+add_ace(SEC_ACL **the_acl,
+ SEC_ACE *ace,
+ TALLOC_CTX *ctx)
+{
+ SEC_ACL *newacl;
+ SEC_ACE *aces;
+
+ if (! *the_acl) {
+ (*the_acl) = make_sec_acl(ctx, 3, 1, ace);
+ return True;
+ }
+
+ if ((aces = SMB_CALLOC_ARRAY(SEC_ACE,
+ 1+(*the_acl)->num_aces)) == NULL) {
+ return False;
+ }
+ memcpy(aces, (*the_acl)->aces, (*the_acl)->num_aces * sizeof(SEC_ACE));
+ memcpy(aces+(*the_acl)->num_aces, ace, sizeof(SEC_ACE));
+ newacl = make_sec_acl(ctx, (*the_acl)->revision,
+ 1+(*the_acl)->num_aces, aces);
+ SAFE_FREE(aces);
+ (*the_acl) = newacl;
+ return True;
+}
+
+
+/* parse a ascii version of a security descriptor */
+static SEC_DESC *
+sec_desc_parse(TALLOC_CTX *ctx,
+ struct cli_state *ipc_cli,
+ POLICY_HND *pol,
+ bool numeric,
+ char *str)
+{
+ const char *p = str;
+ char *tok;
+ SEC_DESC *ret = NULL;
+ size_t sd_size;
+ DOM_SID *group_sid=NULL;
+ DOM_SID *owner_sid=NULL;
+ SEC_ACL *dacl=NULL;
+ int revision=1;
+
+ while (next_token_talloc(ctx, &p, &tok, "\t,\r\n")) {
+
+ if (StrnCaseCmp(tok,"REVISION:", 9) == 0) {
+ revision = strtol(tok+9, NULL, 16);
+ continue;
+ }
+
+ if (StrnCaseCmp(tok,"OWNER:", 6) == 0) {
+ if (owner_sid) {
+ DEBUG(5,("OWNER specified more than once!\n"));
+ goto done;
+ }
+ owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
+ if (!owner_sid ||
+ !convert_string_to_sid(ipc_cli, pol,
+ numeric,
+ owner_sid, tok+6)) {
+ DEBUG(5, ("Failed to parse owner sid\n"));
+ goto done;
+ }
+ continue;
+ }
+
+ if (StrnCaseCmp(tok,"OWNER+:", 7) == 0) {
+ if (owner_sid) {
+ DEBUG(5,("OWNER specified more than once!\n"));
+ goto done;
+ }
+ owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
+ if (!owner_sid ||
+ !convert_string_to_sid(ipc_cli, pol,
+ False,
+ owner_sid, tok+7)) {
+ DEBUG(5, ("Failed to parse owner sid\n"));
+ goto done;
+ }
+ continue;
+ }
+
+ if (StrnCaseCmp(tok,"GROUP:", 6) == 0) {
+ if (group_sid) {
+ DEBUG(5,("GROUP specified more than once!\n"));
+ goto done;
+ }
+ group_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
+ if (!group_sid ||
+ !convert_string_to_sid(ipc_cli, pol,
+ numeric,
+ group_sid, tok+6)) {
+ DEBUG(5, ("Failed to parse group sid\n"));
+ goto done;
+ }
+ continue;
+ }
+
+ if (StrnCaseCmp(tok,"GROUP+:", 7) == 0) {
+ if (group_sid) {
+ DEBUG(5,("GROUP specified more than once!\n"));
+ goto done;
+ }
+ group_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
+ if (!group_sid ||
+ !convert_string_to_sid(ipc_cli, pol,
+ False,
+ group_sid, tok+6)) {
+ DEBUG(5, ("Failed to parse group sid\n"));
+ goto done;
+ }
+ continue;
+ }
+
+ if (StrnCaseCmp(tok,"ACL:", 4) == 0) {
+ SEC_ACE ace;
+ if (!parse_ace(ipc_cli, pol, &ace, numeric, tok+4)) {
+ DEBUG(5, ("Failed to parse ACL %s\n", tok));
+ goto done;
+ }
+ if(!add_ace(&dacl, &ace, ctx)) {
+ DEBUG(5, ("Failed to add ACL %s\n", tok));
+ goto done;
+ }
+ continue;
+ }
+
+ if (StrnCaseCmp(tok,"ACL+:", 5) == 0) {
+ SEC_ACE ace;
+ if (!parse_ace(ipc_cli, pol, &ace, False, tok+5)) {
+ DEBUG(5, ("Failed to parse ACL %s\n", tok));
+ goto done;
+ }
+ if(!add_ace(&dacl, &ace, ctx)) {
+ DEBUG(5, ("Failed to add ACL %s\n", tok));
+ goto done;
+ }
+ continue;
+ }
+
+ DEBUG(5, ("Failed to parse security descriptor\n"));
+ goto done;
+ }
+
+ ret = make_sec_desc(ctx, revision, SEC_DESC_SELF_RELATIVE,
+ owner_sid, group_sid, NULL, dacl, &sd_size);
+
+done:
+ SAFE_FREE(group_sid);
+ SAFE_FREE(owner_sid);
+
+ return ret;
+}
+
+
+/* Obtain the current dos attributes */
+static DOS_ATTR_DESC *
+dos_attr_query(SMBCCTX *context,
+ TALLOC_CTX *ctx,
+ const char *filename,
+ SMBCSRV *srv)
+{
+ struct timespec create_time_ts;
+ struct timespec write_time_ts;
+ struct timespec access_time_ts;
+ struct timespec change_time_ts;
+ SMB_OFF_T size = 0;
+ uint16 mode = 0;
+ SMB_INO_T inode = 0;
+ DOS_ATTR_DESC *ret;
+
+ ret = TALLOC_P(ctx, DOS_ATTR_DESC);
+ if (!ret) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ /* Obtain the DOS attributes */
+ if (!SMBC_getatr(context, srv, CONST_DISCARD(char *, filename),
+ &mode, &size,
+ &create_time_ts,
+ &access_time_ts,
+ &write_time_ts,
+ &change_time_ts,
+ &inode)) {
+ errno = SMBC_errno(context, srv->cli);
+ DEBUG(5, ("dos_attr_query Failed to query old attributes\n"));
+ return NULL;
+ }
+
+ ret->mode = mode;
+ ret->size = size;
+ ret->create_time = convert_timespec_to_time_t(create_time_ts);
+ ret->access_time = convert_timespec_to_time_t(access_time_ts);
+ ret->write_time = convert_timespec_to_time_t(write_time_ts);
+ ret->change_time = convert_timespec_to_time_t(change_time_ts);
+ ret->inode = inode;
+
+ return ret;
+}
+
+
+/* parse a ascii version of a security descriptor */
+static void
+dos_attr_parse(SMBCCTX *context,
+ DOS_ATTR_DESC *dad,
+ SMBCSRV *srv,
+ char *str)
+{
+ int n;
+ const char *p = str;
+ char *tok = NULL;
+ TALLOC_CTX *frame = NULL;
+ struct {
+ const char * create_time_attr;
+ const char * access_time_attr;
+ const char * write_time_attr;
+ const char * change_time_attr;
+ } attr_strings;
+
+ /* Determine whether to use old-style or new-style attribute names */
+ if (context->internal->full_time_names) {
+ /* new-style names */
+ attr_strings.create_time_attr = "CREATE_TIME";
+ attr_strings.access_time_attr = "ACCESS_TIME";
+ attr_strings.write_time_attr = "WRITE_TIME";
+ attr_strings.change_time_attr = "CHANGE_TIME";
+ } else {
+ /* old-style names */
+ attr_strings.create_time_attr = NULL;
+ attr_strings.access_time_attr = "A_TIME";
+ attr_strings.write_time_attr = "M_TIME";
+ attr_strings.change_time_attr = "C_TIME";
+ }
+
+ /* if this is to set the entire ACL... */
+ if (*str == '*') {
+ /* ... then increment past the first colon if there is one */
+ if ((p = strchr(str, ':')) != NULL) {
+ ++p;
+ } else {
+ p = str;
+ }
+ }
+
+ frame = talloc_stackframe();
+ while (next_token_talloc(frame, &p, &tok, "\t,\r\n")) {
+ if (StrnCaseCmp(tok, "MODE:", 5) == 0) {
+ long request = strtol(tok+5, NULL, 16);
+ if (request == 0) {
+ dad->mode = (request |
+ (IS_DOS_DIR(dad->mode)
+ ? FILE_ATTRIBUTE_DIRECTORY
+ : FILE_ATTRIBUTE_NORMAL));
+ } else {
+ dad->mode = request;
+ }
+ continue;
+ }
+
+ if (StrnCaseCmp(tok, "SIZE:", 5) == 0) {
+ dad->size = (SMB_OFF_T)atof(tok+5);
+ continue;
+ }
+
+ n = strlen(attr_strings.access_time_attr);
+ if (StrnCaseCmp(tok, attr_strings.access_time_attr, n) == 0) {
+ dad->access_time = (time_t)strtol(tok+n+1, NULL, 10);
+ continue;
+ }
+
+ n = strlen(attr_strings.change_time_attr);
+ if (StrnCaseCmp(tok, attr_strings.change_time_attr, n) == 0) {
+ dad->change_time = (time_t)strtol(tok+n+1, NULL, 10);
+ continue;
+ }
+
+ n = strlen(attr_strings.write_time_attr);
+ if (StrnCaseCmp(tok, attr_strings.write_time_attr, n) == 0) {
+ dad->write_time = (time_t)strtol(tok+n+1, NULL, 10);
+ continue;
+ }
+
+ if (attr_strings.create_time_attr != NULL) {
+ n = strlen(attr_strings.create_time_attr);
+ if (StrnCaseCmp(tok, attr_strings.create_time_attr,
+ n) == 0) {
+ dad->create_time = (time_t)strtol(tok+n+1,
+ NULL, 10);
+ continue;
+ }
+ }
+
+ if (StrnCaseCmp(tok, "INODE:", 6) == 0) {
+ dad->inode = (SMB_INO_T)atof(tok+6);
+ continue;
+ }
+ }
+ TALLOC_FREE(frame);
+}
+
+/*****************************************************
+ Retrieve the acls for a file.
+*******************************************************/
+
+static int
+cacl_get(SMBCCTX *context,
+ TALLOC_CTX *ctx,
+ SMBCSRV *srv,
+ struct cli_state *ipc_cli,
+ POLICY_HND *pol,
+ char *filename,
+ char *attr_name,
+ char *buf,
+ int bufsize)
+{
+ uint32 i;
+ int n = 0;
+ int n_used;
+ bool all;
+ bool all_nt;
+ bool all_nt_acls;
+ bool all_dos;
+ bool some_nt;
+ bool some_dos;
+ bool exclude_nt_revision = False;
+ bool exclude_nt_owner = False;
+ bool exclude_nt_group = False;
+ bool exclude_nt_acl = False;
+ bool exclude_dos_mode = False;
+ bool exclude_dos_size = False;
+ bool exclude_dos_create_time = False;
+ bool exclude_dos_access_time = False;
+ bool exclude_dos_write_time = False;
+ bool exclude_dos_change_time = False;
+ bool exclude_dos_inode = False;
+ bool numeric = True;
+ bool determine_size = (bufsize == 0);
+ int fnum = -1;
+ SEC_DESC *sd;
+ fstring sidstr;
+ fstring name_sandbox;
+ char *name;
+ char *pExclude;
+ char *p;
+ struct timespec create_time_ts;
+ struct timespec write_time_ts;
+ struct timespec access_time_ts;
+ struct timespec change_time_ts;
+ time_t create_time = (time_t)0;
+ time_t write_time = (time_t)0;
+ time_t access_time = (time_t)0;
+ time_t change_time = (time_t)0;
+ SMB_OFF_T size = 0;
+ uint16 mode = 0;
+ SMB_INO_T ino = 0;
+ struct cli_state *cli = srv->cli;
+ struct {
+ const char * create_time_attr;
+ const char * access_time_attr;
+ const char * write_time_attr;
+ const char * change_time_attr;
+ } attr_strings;
+ struct {
+ const char * create_time_attr;
+ const char * access_time_attr;
+ const char * write_time_attr;
+ const char * change_time_attr;
+ } excl_attr_strings;
+
+ /* Determine whether to use old-style or new-style attribute names */
+ if (context->internal->full_time_names) {
+ /* new-style names */
+ attr_strings.create_time_attr = "CREATE_TIME";
+ attr_strings.access_time_attr = "ACCESS_TIME";
+ attr_strings.write_time_attr = "WRITE_TIME";
+ attr_strings.change_time_attr = "CHANGE_TIME";
+
+ excl_attr_strings.create_time_attr = "CREATE_TIME";
+ excl_attr_strings.access_time_attr = "ACCESS_TIME";
+ excl_attr_strings.write_time_attr = "WRITE_TIME";
+ excl_attr_strings.change_time_attr = "CHANGE_TIME";
+ } else {
+ /* old-style names */
+ attr_strings.create_time_attr = NULL;
+ attr_strings.access_time_attr = "A_TIME";
+ attr_strings.write_time_attr = "M_TIME";
+ attr_strings.change_time_attr = "C_TIME";
+
+ excl_attr_strings.create_time_attr = NULL;
+ excl_attr_strings.access_time_attr = "dos_attr.A_TIME";
+ excl_attr_strings.write_time_attr = "dos_attr.M_TIME";
+ excl_attr_strings.change_time_attr = "dos_attr.C_TIME";
+ }
+
+ /* Copy name so we can strip off exclusions (if any are specified) */
+ strncpy(name_sandbox, attr_name, sizeof(name_sandbox) - 1);
+
+ /* Ensure name is null terminated */
+ name_sandbox[sizeof(name_sandbox) - 1] = '\0';
+
+ /* Play in the sandbox */
+ name = name_sandbox;
+
+ /* If there are any exclusions, point to them and mask them from name */
+ if ((pExclude = strchr(name, '!')) != NULL)
+ {
+ *pExclude++ = '\0';
+ }
+
+ all = (StrnCaseCmp(name, "system.*", 8) == 0);
+ all_nt = (StrnCaseCmp(name, "system.nt_sec_desc.*", 20) == 0);
+ all_nt_acls = (StrnCaseCmp(name, "system.nt_sec_desc.acl.*", 24) == 0);
+ all_dos = (StrnCaseCmp(name, "system.dos_attr.*", 17) == 0);
+ some_nt = (StrnCaseCmp(name, "system.nt_sec_desc.", 19) == 0);
+ some_dos = (StrnCaseCmp(name, "system.dos_attr.", 16) == 0);
+ numeric = (* (name + strlen(name) - 1) != '+');
+
+ /* Look for exclusions from "all" requests */
+ if (all || all_nt || all_dos) {
+
+ /* Exclusions are delimited by '!' */
+ for (;
+ pExclude != NULL;
+ pExclude = (p == NULL ? NULL : p + 1)) {
+
+ /* Find end of this exclusion name */
+ if ((p = strchr(pExclude, '!')) != NULL)
+ {
+ *p = '\0';
+ }
+
+ /* Which exclusion name is this? */
+ if (StrCaseCmp(pExclude,
+ "nt_sec_desc.revision") == 0) {
+ exclude_nt_revision = True;
+ }
+ else if (StrCaseCmp(pExclude,
+ "nt_sec_desc.owner") == 0) {
+ exclude_nt_owner = True;
+ }
+ else if (StrCaseCmp(pExclude,
+ "nt_sec_desc.group") == 0) {
+ exclude_nt_group = True;
+ }
+ else if (StrCaseCmp(pExclude,
+ "nt_sec_desc.acl") == 0) {
+ exclude_nt_acl = True;
+ }
+ else if (StrCaseCmp(pExclude,
+ "dos_attr.mode") == 0) {
+ exclude_dos_mode = True;
+ }
+ else if (StrCaseCmp(pExclude,
+ "dos_attr.size") == 0) {
+ exclude_dos_size = True;
+ }
+ else if (excl_attr_strings.create_time_attr != NULL &&
+ StrCaseCmp(pExclude,
+ excl_attr_strings.change_time_attr) == 0) {
+ exclude_dos_create_time = True;
+ }
+ else if (StrCaseCmp(pExclude,
+ excl_attr_strings.access_time_attr) == 0) {
+ exclude_dos_access_time = True;
+ }
+ else if (StrCaseCmp(pExclude,
+ excl_attr_strings.write_time_attr) == 0) {
+ exclude_dos_write_time = True;
+ }
+ else if (StrCaseCmp(pExclude,
+ excl_attr_strings.change_time_attr) == 0) {
+ exclude_dos_change_time = True;
+ }
+ else if (StrCaseCmp(pExclude, "dos_attr.inode") == 0) {
+ exclude_dos_inode = True;
+ }
+ else {
+ DEBUG(5, ("cacl_get received unknown exclusion: %s\n",
+ pExclude));
+ errno = ENOATTR;
+ return -1;
+ }
+ }
+ }
+
+ n_used = 0;
+
+ /*
+ * If we are (possibly) talking to an NT or new system and some NT
+ * attributes have been requested...
+ */
+ if (ipc_cli && (all || some_nt || all_nt_acls)) {
+ char *targetpath = NULL;
+ struct cli_state *targetcli = NULL;
+
+ /* Point to the portion after "system.nt_sec_desc." */
+ name += 19; /* if (all) this will be invalid but unused */
+
+ if (!cli_resolve_path(ctx, "", cli, filename,
+ &targetcli, &targetpath)) {
+ DEBUG(5, ("cacl_get Could not resolve %s\n",
+ filename));
+ errno = ENOENT;
+ return -1;
+ }
+
+ /* ... then obtain any NT attributes which were requested */
+ fnum = cli_nt_create(targetcli, targetpath, CREATE_ACCESS_READ);
+
+ if (fnum == -1) {
+ DEBUG(5, ("cacl_get failed to open %s: %s\n",
+ targetpath, cli_errstr(targetcli)));
+ errno = 0;
+ return -1;
+ }
+
+ sd = cli_query_secdesc(targetcli, fnum, ctx);
+
+ if (!sd) {
+ DEBUG(5,
+ ("cacl_get Failed to query old descriptor\n"));
+ errno = 0;
+ return -1;
+ }
+
+ cli_close(targetcli, fnum);
+
+ if (! exclude_nt_revision) {
+ if (all || all_nt) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx,
+ "REVISION:%d",
+ sd->revision);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ "REVISION:%d",
+ sd->revision);
+ }
+ } else if (StrCaseCmp(name, "revision") == 0) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx, "%d",
+ sd->revision);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize, "%d",
+ sd->revision);
+ }
+ }
+
+ if (!determine_size && n > bufsize) {
+ errno = ERANGE;
+ return -1;
+ }
+ buf += n;
+ n_used += n;
+ bufsize -= n;
+ n = 0;
+ }
+
+ if (! exclude_nt_owner) {
+ /* Get owner and group sid */
+ if (sd->owner_sid) {
+ convert_sid_to_string(ipc_cli, pol,
+ sidstr,
+ numeric,
+ sd->owner_sid);
+ } else {
+ fstrcpy(sidstr, "");
+ }
+
+ if (all || all_nt) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx, ",OWNER:%s",
+ sidstr);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else if (sidstr[0] != '\0') {
+ n = snprintf(buf, bufsize,
+ ",OWNER:%s", sidstr);
+ }
+ } else if (StrnCaseCmp(name, "owner", 5) == 0) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx, "%s", sidstr);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize, "%s",
+ sidstr);
+ }
+ }
+
+ if (!determine_size && n > bufsize) {
+ errno = ERANGE;
+ return -1;
+ }
+ buf += n;
+ n_used += n;
+ bufsize -= n;
+ n = 0;
+ }
+
+ if (! exclude_nt_group) {
+ if (sd->group_sid) {
+ convert_sid_to_string(ipc_cli, pol,
+ sidstr, numeric,
+ sd->group_sid);
+ } else {
+ fstrcpy(sidstr, "");
+ }
+
+ if (all || all_nt) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx, ",GROUP:%s",
+ sidstr);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else if (sidstr[0] != '\0') {
+ n = snprintf(buf, bufsize,
+ ",GROUP:%s", sidstr);
+ }
+ } else if (StrnCaseCmp(name, "group", 5) == 0) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx, "%s", sidstr);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ "%s", sidstr);
+ }
+ }
+
+ if (!determine_size && n > bufsize) {
+ errno = ERANGE;
+ return -1;
+ }
+ buf += n;
+ n_used += n;
+ bufsize -= n;
+ n = 0;
+ }
+
+ if (! exclude_nt_acl) {
+ /* Add aces to value buffer */
+ for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
+
+ SEC_ACE *ace = &sd->dacl->aces[i];
+ convert_sid_to_string(ipc_cli, pol,
+ sidstr, numeric,
+ &ace->trustee);
+
+ if (all || all_nt) {
+ if (determine_size) {
+ p = talloc_asprintf(
+ ctx,
+ ",ACL:"
+ "%s:%d/%d/0x%08x",
+ sidstr,
+ ace->type,
+ ace->flags,
+ ace->access_mask);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(
+ buf, bufsize,
+ ",ACL:%s:%d/%d/0x%08x",
+ sidstr,
+ ace->type,
+ ace->flags,
+ ace->access_mask);
+ }
+ } else if ((StrnCaseCmp(name, "acl", 3) == 0 &&
+ StrCaseCmp(name+3, sidstr) == 0) ||
+ (StrnCaseCmp(name, "acl+", 4) == 0 &&
+ StrCaseCmp(name+4, sidstr) == 0)) {
+ if (determine_size) {
+ p = talloc_asprintf(
+ ctx,
+ "%d/%d/0x%08x",
+ ace->type,
+ ace->flags,
+ ace->access_mask);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ "%d/%d/0x%08x",
+ ace->type,
+ ace->flags,
+ ace->access_mask);
+ }
+ } else if (all_nt_acls) {
+ if (determine_size) {
+ p = talloc_asprintf(
+ ctx,
+ "%s%s:%d/%d/0x%08x",
+ i ? "," : "",
+ sidstr,
+ ace->type,
+ ace->flags,
+ ace->access_mask);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ "%s%s:%d/%d/0x%08x",
+ i ? "," : "",
+ sidstr,
+ ace->type,
+ ace->flags,
+ ace->access_mask);
+ }
+ }
+ if (!determine_size && n > bufsize) {
+ errno = ERANGE;
+ return -1;
+ }
+ buf += n;
+ n_used += n;
+ bufsize -= n;
+ n = 0;
+ }
+ }
+
+ /* Restore name pointer to its original value */
+ name -= 19;
+ }
+
+ if (all || some_dos) {
+ /* Point to the portion after "system.dos_attr." */
+ name += 16; /* if (all) this will be invalid but unused */
+
+ /* Obtain the DOS attributes */
+ if (!SMBC_getatr(context, srv, filename, &mode, &size,
+ &create_time_ts,
+ &access_time_ts,
+ &write_time_ts,
+ &change_time_ts,
+ &ino)) {
+
+ errno = SMBC_errno(context, srv->cli);
+ return -1;
+
+ }
+
+ create_time = convert_timespec_to_time_t(create_time_ts);
+ access_time = convert_timespec_to_time_t(access_time_ts);
+ write_time = convert_timespec_to_time_t(write_time_ts);
+ change_time = convert_timespec_to_time_t(change_time_ts);
+
+ if (! exclude_dos_mode) {
+ if (all || all_dos) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx,
+ "%sMODE:0x%x",
+ (ipc_cli &&
+ (all || some_nt)
+ ? ","
+ : ""),
+ mode);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ "%sMODE:0x%x",
+ (ipc_cli &&
+ (all || some_nt)
+ ? ","
+ : ""),
+ mode);
+ }
+ } else if (StrCaseCmp(name, "mode") == 0) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx, "0x%x", mode);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ "0x%x", mode);
+ }
+ }
+
+ if (!determine_size && n > bufsize) {
+ errno = ERANGE;
+ return -1;
+ }
+ buf += n;
+ n_used += n;
+ bufsize -= n;
+ n = 0;
+ }
+
+ if (! exclude_dos_size) {
+ if (all || all_dos) {
+ if (determine_size) {
+ p = talloc_asprintf(
+ ctx,
+ ",SIZE:%.0f",
+ (double)size);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ ",SIZE:%.0f",
+ (double)size);
+ }
+ } else if (StrCaseCmp(name, "size") == 0) {
+ if (determine_size) {
+ p = talloc_asprintf(
+ ctx,
+ "%.0f",
+ (double)size);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ "%.0f",
+ (double)size);
+ }
+ }
+
+ if (!determine_size && n > bufsize) {
+ errno = ERANGE;
+ return -1;
+ }
+ buf += n;
+ n_used += n;
+ bufsize -= n;
+ n = 0;
+ }
+
+ if (! exclude_dos_create_time &&
+ attr_strings.create_time_attr != NULL) {
+ if (all || all_dos) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx,
+ ",%s:%lu",
+ attr_strings.create_time_attr,
+ create_time);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ ",%s:%lu",
+ attr_strings.create_time_attr,
+ create_time);
+ }
+ } else if (StrCaseCmp(name, attr_strings.create_time_attr) == 0) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx, "%lu", create_time);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ "%lu", create_time);
+ }
+ }
+
+ if (!determine_size && n > bufsize) {
+ errno = ERANGE;
+ return -1;
+ }
+ buf += n;
+ n_used += n;
+ bufsize -= n;
+ n = 0;
+ }
+
+ if (! exclude_dos_access_time) {
+ if (all || all_dos) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx,
+ ",%s:%lu",
+ attr_strings.access_time_attr,
+ access_time);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ ",%s:%lu",
+ attr_strings.access_time_attr,
+ access_time);
+ }
+ } else if (StrCaseCmp(name, attr_strings.access_time_attr) == 0) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx, "%lu", access_time);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ "%lu", access_time);
+ }
+ }
+
+ if (!determine_size && n > bufsize) {
+ errno = ERANGE;
+ return -1;
+ }
+ buf += n;
+ n_used += n;
+ bufsize -= n;
+ n = 0;
+ }
+
+ if (! exclude_dos_write_time) {
+ if (all || all_dos) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx,
+ ",%s:%lu",
+ attr_strings.write_time_attr,
+ write_time);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ ",%s:%lu",
+ attr_strings.write_time_attr,
+ write_time);
+ }
+ } else if (StrCaseCmp(name, attr_strings.write_time_attr) == 0) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx, "%lu", write_time);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ "%lu", write_time);
+ }
+ }
+
+ if (!determine_size && n > bufsize) {
+ errno = ERANGE;
+ return -1;
+ }
+ buf += n;
+ n_used += n;
+ bufsize -= n;
+ n = 0;
+ }
+
+ if (! exclude_dos_change_time) {
+ if (all || all_dos) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx,
+ ",%s:%lu",
+ attr_strings.change_time_attr,
+ change_time);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ ",%s:%lu",
+ attr_strings.change_time_attr,
+ change_time);
+ }
+ } else if (StrCaseCmp(name, attr_strings.change_time_attr) == 0) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx, "%lu", change_time);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ "%lu", change_time);
+ }
+ }
+
+ if (!determine_size && n > bufsize) {
+ errno = ERANGE;
+ return -1;
+ }
+ buf += n;
+ n_used += n;
+ bufsize -= n;
+ n = 0;
+ }
+
+ if (! exclude_dos_inode) {
+ if (all || all_dos) {
+ if (determine_size) {
+ p = talloc_asprintf(
+ ctx,
+ ",INODE:%.0f",
+ (double)ino);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ ",INODE:%.0f",
+ (double) ino);
+ }
+ } else if (StrCaseCmp(name, "inode") == 0) {
+ if (determine_size) {
+ p = talloc_asprintf(
+ ctx,
+ "%.0f",
+ (double) ino);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ "%.0f",
+ (double) ino);
+ }
+ }
+
+ if (!determine_size && n > bufsize) {
+ errno = ERANGE;
+ return -1;
+ }
+ buf += n;
+ n_used += n;
+ bufsize -= n;
+ n = 0;
+ }
+
+ /* Restore name pointer to its original value */
+ name -= 16;
+ }
+
+ if (n_used == 0) {
+ errno = ENOATTR;
+ return -1;
+ }
+
+ return n_used;
+}
+
+/*****************************************************
+set the ACLs on a file given an ascii description
+*******************************************************/
+static int
+cacl_set(TALLOC_CTX *ctx,
+ struct cli_state *cli,
+ struct cli_state *ipc_cli,
+ POLICY_HND *pol,
+ const char *filename,
+ const char *the_acl,
+ int mode,
+ int flags)
+{
+ int fnum;
+ int err = 0;
+ SEC_DESC *sd = NULL, *old;
+ SEC_ACL *dacl = NULL;
+ DOM_SID *owner_sid = NULL;
+ DOM_SID *group_sid = NULL;
+ uint32 i, j;
+ size_t sd_size;
+ int ret = 0;
+ char *p;
+ bool numeric = True;
+ char *targetpath = NULL;
+ struct cli_state *targetcli = NULL;
+
+ /* the_acl will be null for REMOVE_ALL operations */
+ if (the_acl) {
+ numeric = ((p = strchr(the_acl, ':')) != NULL &&
+ p > the_acl &&
+ p[-1] != '+');
+
+ /* if this is to set the entire ACL... */
+ if (*the_acl == '*') {
+ /* ... then increment past the first colon */
+ the_acl = p + 1;
+ }
+
+ sd = sec_desc_parse(ctx, ipc_cli, pol, numeric,
+ CONST_DISCARD(char *, the_acl));
+
+ if (!sd) {
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ /* SMBC_XATTR_MODE_REMOVE_ALL is the only caller
+ that doesn't deref sd */
+
+ if (!sd && (mode != SMBC_XATTR_MODE_REMOVE_ALL)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!cli_resolve_path(ctx, "", cli, filename,
+ &targetcli, &targetpath)) {
+ DEBUG(5,("cacl_set: Could not resolve %s\n", filename));
+ errno = ENOENT;
+ return -1;
+ }
+
+ /* The desired access below is the only one I could find that works
+ with NT4, W2KP and Samba */
+
+ fnum = cli_nt_create(targetcli, targetpath, CREATE_ACCESS_READ);
+
+ if (fnum == -1) {
+ DEBUG(5, ("cacl_set failed to open %s: %s\n",
+ targetpath, cli_errstr(targetcli)));
+ errno = 0;
+ return -1;
+ }
+
+ old = cli_query_secdesc(targetcli, fnum, ctx);
+
+ if (!old) {
+ DEBUG(5, ("cacl_set Failed to query old descriptor\n"));
+ errno = 0;
+ return -1;
+ }
+
+ cli_close(targetcli, fnum);
+
+ switch (mode) {
+ case SMBC_XATTR_MODE_REMOVE_ALL:
+ old->dacl->num_aces = 0;
+ dacl = old->dacl;
+ break;
+
+ case SMBC_XATTR_MODE_REMOVE:
+ for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
+ bool found = False;
+
+ for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
+ if (sec_ace_equal(&sd->dacl->aces[i],
+ &old->dacl->aces[j])) {
+ uint32 k;
+ for (k=j; k<old->dacl->num_aces-1;k++) {
+ old->dacl->aces[k] =
+ old->dacl->aces[k+1];
+ }
+ old->dacl->num_aces--;
+ found = True;
+ dacl = old->dacl;
+ break;
+ }
+ }
+
+ if (!found) {
+ err = ENOATTR;
+ ret = -1;
+ goto failed;
+ }
+ }
+ break;
+
+ case SMBC_XATTR_MODE_ADD:
+ for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
+ bool found = False;
+
+ for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
+ if (sid_equal(&sd->dacl->aces[i].trustee,
+ &old->dacl->aces[j].trustee)) {
+ if (!(flags & SMBC_XATTR_FLAG_CREATE)) {
+ err = EEXIST;
+ ret = -1;
+ goto failed;
+ }
+ old->dacl->aces[j] = sd->dacl->aces[i];
+ ret = -1;
+ found = True;
+ }
+ }
+
+ if (!found && (flags & SMBC_XATTR_FLAG_REPLACE)) {
+ err = ENOATTR;
+ ret = -1;
+ goto failed;
+ }
+
+ for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
+ add_ace(&old->dacl, &sd->dacl->aces[i], ctx);
+ }
+ }
+ dacl = old->dacl;
+ break;
+
+ case SMBC_XATTR_MODE_SET:
+ old = sd;
+ owner_sid = old->owner_sid;
+ group_sid = old->group_sid;
+ dacl = old->dacl;
+ break;
+
+ case SMBC_XATTR_MODE_CHOWN:
+ owner_sid = sd->owner_sid;
+ break;
+
+ case SMBC_XATTR_MODE_CHGRP:
+ group_sid = sd->group_sid;
+ break;
+ }
+
+ /* Denied ACE entries must come before allowed ones */
+ sort_acl(old->dacl);
+
+ /* Create new security descriptor and set it */
+ sd = make_sec_desc(ctx, old->revision, SEC_DESC_SELF_RELATIVE,
+ owner_sid, group_sid, NULL, dacl, &sd_size);
+
+ fnum = cli_nt_create(targetcli, targetpath,
+ WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS);
+
+ if (fnum == -1) {
+ DEBUG(5, ("cacl_set failed to open %s: %s\n",
+ targetpath, cli_errstr(targetcli)));
+ errno = 0;
+ return -1;
+ }
+
+ if (!cli_set_secdesc(targetcli, fnum, sd)) {
+ DEBUG(5, ("ERROR: secdesc set failed: %s\n",
+ cli_errstr(targetcli)));
+ ret = -1;
+ }
+
+ /* Clean up */
+
+failed:
+ cli_close(targetcli, fnum);
+
+ if (err != 0) {
+ errno = err;
+ }
+
+ return ret;
+}
+
+
+int
+SMBC_setxattr_ctx(SMBCCTX *context,
+ const char *fname,
+ const char *name,
+ const void *value,
+ size_t size,
+ int flags)
+{
+ int ret;
+ int ret2;
+ SMBCSRV *srv = NULL;
+ SMBCSRV *ipc_srv = NULL;
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *workgroup = NULL;
+ char *path = NULL;
+ DOS_ATTR_DESC *dad = NULL;
+ struct {
+ const char * create_time_attr;
+ const char * access_time_attr;
+ const char * write_time_attr;
+ const char * change_time_attr;
+ } attr_strings;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!fname) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ DEBUG(4, ("smbc_setxattr(%s, %s, %.*s)\n",
+ fname, name, (int) size, (const char*)value));
+
+ if (SMBC_parse_path(frame,
+ context,
+ fname,
+ &workgroup,
+ &server,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!user || user[0] == (char)0) {
+ user = talloc_strdup(frame, smbc_getUser(context));
+ if (!user) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ }
+
+ srv = SMBC_server(frame, context, True,
+ server, share, &workgroup, &user, &password);
+ if (!srv) {
+ TALLOC_FREE(frame);
+ return -1; /* errno set by SMBC_server */
+ }
+
+ if (! srv->no_nt_session) {
+ ipc_srv = SMBC_attr_server(frame, context, server, share,
+ &workgroup, &user, &password);
+ if (! ipc_srv) {
+ srv->no_nt_session = True;
+ }
+ } else {
+ ipc_srv = NULL;
+ }
+
+ /*
+ * Are they asking to set the entire set of known attributes?
+ */
+ if (StrCaseCmp(name, "system.*") == 0 ||
+ StrCaseCmp(name, "system.*+") == 0) {
+ /* Yup. */
+ char *namevalue =
+ talloc_asprintf(talloc_tos(), "%s:%s",
+ name+7, (const char *) value);
+ if (! namevalue) {
+ errno = ENOMEM;
+ ret = -1;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (ipc_srv) {
+ ret = cacl_set(talloc_tos(), srv->cli,
+ ipc_srv->cli, &ipc_srv->pol, path,
+ namevalue,
+ (*namevalue == '*'
+ ? SMBC_XATTR_MODE_SET
+ : SMBC_XATTR_MODE_ADD),
+ flags);
+ } else {
+ ret = 0;
+ }
+
+ /* get a DOS Attribute Descriptor with current attributes */
+ dad = dos_attr_query(context, talloc_tos(), path, srv);
+ if (dad) {
+ /* Overwrite old with new, using what was provided */
+ dos_attr_parse(context, dad, srv, namevalue);
+
+ /* Set the new DOS attributes */
+ if (! SMBC_setatr(context, srv, path,
+ dad->create_time,
+ dad->access_time,
+ dad->write_time,
+ dad->change_time,
+ dad->mode)) {
+
+ /* cause failure if NT failed too */
+ dad = NULL;
+ }
+ }
+
+ /* we only fail if both NT and DOS sets failed */
+ if (ret < 0 && ! dad) {
+ ret = -1; /* in case dad was null */
+ }
+ else {
+ ret = 0;
+ }
+
+ TALLOC_FREE(frame);
+ return ret;
+ }
+
+ /*
+ * Are they asking to set an access control element or to set
+ * the entire access control list?
+ */
+ if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
+ StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
+ StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
+
+ /* Yup. */
+ char *namevalue =
+ talloc_asprintf(talloc_tos(), "%s:%s",
+ name+19, (const char *) value);
+
+ if (! ipc_srv) {
+ ret = -1; /* errno set by SMBC_server() */
+ }
+ else if (! namevalue) {
+ errno = ENOMEM;
+ ret = -1;
+ } else {
+ ret = cacl_set(talloc_tos(), srv->cli,
+ ipc_srv->cli, &ipc_srv->pol, path,
+ namevalue,
+ (*namevalue == '*'
+ ? SMBC_XATTR_MODE_SET
+ : SMBC_XATTR_MODE_ADD),
+ flags);
+ }
+ TALLOC_FREE(frame);
+ return ret;
+ }
+
+ /*
+ * Are they asking to set the owner?
+ */
+ if (StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0) {
+
+ /* Yup. */
+ char *namevalue =
+ talloc_asprintf(talloc_tos(), "%s:%s",
+ name+19, (const char *) value);
+
+ if (! ipc_srv) {
+ ret = -1; /* errno set by SMBC_server() */
+ }
+ else if (! namevalue) {
+ errno = ENOMEM;
+ ret = -1;
+ } else {
+ ret = cacl_set(talloc_tos(), srv->cli,
+ ipc_srv->cli, &ipc_srv->pol, path,
+ namevalue, SMBC_XATTR_MODE_CHOWN, 0);
+ }
+ TALLOC_FREE(frame);
+ return ret;
+ }
+
+ /*
+ * Are they asking to set the group?
+ */
+ if (StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.group+") == 0) {
+
+ /* Yup. */
+ char *namevalue =
+ talloc_asprintf(talloc_tos(), "%s:%s",
+ name+19, (const char *) value);
+
+ if (! ipc_srv) {
+ /* errno set by SMBC_server() */
+ ret = -1;
+ }
+ else if (! namevalue) {
+ errno = ENOMEM;
+ ret = -1;
+ } else {
+ ret = cacl_set(talloc_tos(), srv->cli,
+ ipc_srv->cli, &ipc_srv->pol, path,
+ namevalue, SMBC_XATTR_MODE_CHGRP, 0);
+ }
+ TALLOC_FREE(frame);
+ return ret;
+ }
+
+ /* Determine whether to use old-style or new-style attribute names */
+ if (context->internal->full_time_names) {
+ /* new-style names */
+ attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME";
+ attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME";
+ attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME";
+ attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME";
+ } else {
+ /* old-style names */
+ attr_strings.create_time_attr = NULL;
+ attr_strings.access_time_attr = "system.dos_attr.A_TIME";
+ attr_strings.write_time_attr = "system.dos_attr.M_TIME";
+ attr_strings.change_time_attr = "system.dos_attr.C_TIME";
+ }
+
+ /*
+ * Are they asking to set a DOS attribute?
+ */
+ if (StrCaseCmp(name, "system.dos_attr.*") == 0 ||
+ StrCaseCmp(name, "system.dos_attr.mode") == 0 ||
+ (attr_strings.create_time_attr != NULL &&
+ StrCaseCmp(name, attr_strings.create_time_attr) == 0) ||
+ StrCaseCmp(name, attr_strings.access_time_attr) == 0 ||
+ StrCaseCmp(name, attr_strings.write_time_attr) == 0 ||
+ StrCaseCmp(name, attr_strings.change_time_attr) == 0) {
+
+ /* get a DOS Attribute Descriptor with current attributes */
+ dad = dos_attr_query(context, talloc_tos(), path, srv);
+ if (dad) {
+ char *namevalue =
+ talloc_asprintf(talloc_tos(), "%s:%s",
+ name+16, (const char *) value);
+ if (! namevalue) {
+ errno = ENOMEM;
+ ret = -1;
+ } else {
+ /* Overwrite old with provided new params */
+ dos_attr_parse(context, dad, srv, namevalue);
+
+ /* Set the new DOS attributes */
+ ret2 = SMBC_setatr(context, srv, path,
+ dad->create_time,
+ dad->access_time,
+ dad->write_time,
+ dad->change_time,
+ dad->mode);
+
+ /* ret2 has True (success) / False (failure) */
+ if (ret2) {
+ ret = 0;
+ } else {
+ ret = -1;
+ }
+ }
+ } else {
+ ret = -1;
+ }
+
+ TALLOC_FREE(frame);
+ return ret;
+ }
+
+ /* Unsupported attribute name */
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+}
+
+int
+SMBC_getxattr_ctx(SMBCCTX *context,
+ const char *fname,
+ const char *name,
+ const void *value,
+ size_t size)
+{
+ int ret;
+ SMBCSRV *srv = NULL;
+ SMBCSRV *ipc_srv = NULL;
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *workgroup = NULL;
+ char *path = NULL;
+ struct {
+ const char * create_time_attr;
+ const char * access_time_attr;
+ const char * write_time_attr;
+ const char * change_time_attr;
+ } attr_strings;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!fname) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ DEBUG(4, ("smbc_getxattr(%s, %s)\n", fname, name));
+
+ if (SMBC_parse_path(frame,
+ context,
+ fname,
+ &workgroup,
+ &server,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!user || user[0] == (char)0) {
+ user = talloc_strdup(frame, smbc_getUser(context));
+ if (!user) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ }
+
+ srv = SMBC_server(frame, context, True,
+ server, share, &workgroup, &user, &password);
+ if (!srv) {
+ TALLOC_FREE(frame);
+ return -1; /* errno set by SMBC_server */
+ }
+
+ if (! srv->no_nt_session) {
+ ipc_srv = SMBC_attr_server(frame, context, server, share,
+ &workgroup, &user, &password);
+ if (! ipc_srv) {
+ srv->no_nt_session = True;
+ }
+ } else {
+ ipc_srv = NULL;
+ }
+
+ /* Determine whether to use old-style or new-style attribute names */
+ if (context->internal->full_time_names) {
+ /* new-style names */
+ attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME";
+ attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME";
+ attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME";
+ attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME";
+ } else {
+ /* old-style names */
+ attr_strings.create_time_attr = NULL;
+ attr_strings.access_time_attr = "system.dos_attr.A_TIME";
+ attr_strings.write_time_attr = "system.dos_attr.M_TIME";
+ attr_strings.change_time_attr = "system.dos_attr.C_TIME";
+ }
+
+ /* Are they requesting a supported attribute? */
+ if (StrCaseCmp(name, "system.*") == 0 ||
+ StrnCaseCmp(name, "system.*!", 9) == 0 ||
+ StrCaseCmp(name, "system.*+") == 0 ||
+ StrnCaseCmp(name, "system.*+!", 10) == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
+ StrnCaseCmp(name, "system.nt_sec_desc.*!", 21) == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.*+") == 0 ||
+ StrnCaseCmp(name, "system.nt_sec_desc.*+!", 22) == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 ||
+ StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
+ StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0 ||
+ StrCaseCmp(name, "system.dos_attr.*") == 0 ||
+ StrnCaseCmp(name, "system.dos_attr.*!", 18) == 0 ||
+ StrCaseCmp(name, "system.dos_attr.mode") == 0 ||
+ StrCaseCmp(name, "system.dos_attr.size") == 0 ||
+ (attr_strings.create_time_attr != NULL &&
+ StrCaseCmp(name, attr_strings.create_time_attr) == 0) ||
+ StrCaseCmp(name, attr_strings.access_time_attr) == 0 ||
+ StrCaseCmp(name, attr_strings.write_time_attr) == 0 ||
+ StrCaseCmp(name, attr_strings.change_time_attr) == 0 ||
+ StrCaseCmp(name, "system.dos_attr.inode") == 0) {
+
+ /* Yup. */
+ ret = cacl_get(context, talloc_tos(), srv,
+ ipc_srv == NULL ? NULL : ipc_srv->cli,
+ &ipc_srv->pol, path,
+ CONST_DISCARD(char *, name),
+ CONST_DISCARD(char *, value), size);
+ if (ret < 0 && errno == 0) {
+ errno = SMBC_errno(context, srv->cli);
+ }
+ TALLOC_FREE(frame);
+ return ret;
+ }
+
+ /* Unsupported attribute name */
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+}
+
+
+int
+SMBC_removexattr_ctx(SMBCCTX *context,
+ const char *fname,
+ const char *name)
+{
+ int ret;
+ SMBCSRV *srv = NULL;
+ SMBCSRV *ipc_srv = NULL;
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *workgroup = NULL;
+ char *path = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!fname) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ DEBUG(4, ("smbc_removexattr(%s, %s)\n", fname, name));
+
+ if (SMBC_parse_path(frame,
+ context,
+ fname,
+ &workgroup,
+ &server,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!user || user[0] == (char)0) {
+ user = talloc_strdup(frame, smbc_getUser(context));
+ if (!user) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ }
+
+ srv = SMBC_server(frame, context, True,
+ server, share, &workgroup, &user, &password);
+ if (!srv) {
+ TALLOC_FREE(frame);
+ return -1; /* errno set by SMBC_server */
+ }
+
+ if (! srv->no_nt_session) {
+ ipc_srv = SMBC_attr_server(frame, context, server, share,
+ &workgroup, &user, &password);
+ if (! ipc_srv) {
+ srv->no_nt_session = True;
+ }
+ } else {
+ ipc_srv = NULL;
+ }
+
+ if (! ipc_srv) {
+ TALLOC_FREE(frame);
+ return -1; /* errno set by SMBC_attr_server */
+ }
+
+ /* Are they asking to set the entire ACL? */
+ if (StrCaseCmp(name, "system.nt_sec_desc.*") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.*+") == 0) {
+
+ /* Yup. */
+ ret = cacl_set(talloc_tos(), srv->cli,
+ ipc_srv->cli, &ipc_srv->pol, path,
+ NULL, SMBC_XATTR_MODE_REMOVE_ALL, 0);
+ TALLOC_FREE(frame);
+ return ret;
+ }
+
+ /*
+ * Are they asking to remove one or more spceific security descriptor
+ * attributes?
+ */
+ if (StrCaseCmp(name, "system.nt_sec_desc.revision") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.owner") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.owner+") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.group") == 0 ||
+ StrCaseCmp(name, "system.nt_sec_desc.group+") == 0 ||
+ StrnCaseCmp(name, "system.nt_sec_desc.acl", 22) == 0 ||
+ StrnCaseCmp(name, "system.nt_sec_desc.acl+", 23) == 0) {
+
+ /* Yup. */
+ ret = cacl_set(talloc_tos(), srv->cli,
+ ipc_srv->cli, &ipc_srv->pol, path,
+ name + 19, SMBC_XATTR_MODE_REMOVE, 0);
+ TALLOC_FREE(frame);
+ return ret;
+ }
+
+ /* Unsupported attribute name */
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+}
+
+int
+SMBC_listxattr_ctx(SMBCCTX *context,
+ const char *fname,
+ char *list,
+ size_t size)
+{
+ /*
+ * This isn't quite what listxattr() is supposed to do. This returns
+ * the complete set of attribute names, always, rather than only those
+ * attribute names which actually exist for a file. Hmmm...
+ */
+ size_t retsize;
+ const char supported_old[] =
+ "system.*\0"
+ "system.*+\0"
+ "system.nt_sec_desc.revision\0"
+ "system.nt_sec_desc.owner\0"
+ "system.nt_sec_desc.owner+\0"
+ "system.nt_sec_desc.group\0"
+ "system.nt_sec_desc.group+\0"
+ "system.nt_sec_desc.acl.*\0"
+ "system.nt_sec_desc.acl\0"
+ "system.nt_sec_desc.acl+\0"
+ "system.nt_sec_desc.*\0"
+ "system.nt_sec_desc.*+\0"
+ "system.dos_attr.*\0"
+ "system.dos_attr.mode\0"
+ "system.dos_attr.c_time\0"
+ "system.dos_attr.a_time\0"
+ "system.dos_attr.m_time\0"
+ ;
+ const char supported_new[] =
+ "system.*\0"
+ "system.*+\0"
+ "system.nt_sec_desc.revision\0"
+ "system.nt_sec_desc.owner\0"
+ "system.nt_sec_desc.owner+\0"
+ "system.nt_sec_desc.group\0"
+ "system.nt_sec_desc.group+\0"
+ "system.nt_sec_desc.acl.*\0"
+ "system.nt_sec_desc.acl\0"
+ "system.nt_sec_desc.acl+\0"
+ "system.nt_sec_desc.*\0"
+ "system.nt_sec_desc.*+\0"
+ "system.dos_attr.*\0"
+ "system.dos_attr.mode\0"
+ "system.dos_attr.create_time\0"
+ "system.dos_attr.access_time\0"
+ "system.dos_attr.write_time\0"
+ "system.dos_attr.change_time\0"
+ ;
+ const char * supported;
+
+ if (context->internal->full_time_names) {
+ supported = supported_new;
+ retsize = sizeof(supported_new);
+ } else {
+ supported = supported_old;
+ retsize = sizeof(supported_old);
+ }
+
+ if (size == 0) {
+ return retsize;
+ }
+
+ if (retsize > size) {
+ errno = ERANGE;
+ return -1;
+ }
+
+ /* this can't be strcpy() because there are embedded null characters */
+ memcpy(list, supported, retsize);
+ return retsize;
+}
diff --git a/source3/libsmb/namecache.c b/source3/libsmb/namecache.c
new file mode 100644
index 0000000000..ba706e5ee2
--- /dev/null
+++ b/source3/libsmb/namecache.c
@@ -0,0 +1,394 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ NetBIOS name cache module on top of gencache mechanism.
+
+ Copyright (C) Tim Potter 2002
+ Copyright (C) Rafal Szczesniak 2002
+ Copyright (C) Jeremy Allison 2007
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+#define NBTKEY_FMT "NBT/%s#%02X"
+
+/**
+ * Initialise namecache system. Function calls gencache
+ * initialisation function to perform necessary actions
+ *
+ * @return true upon successful initialisation of the cache or
+ * false on failure
+ **/
+
+bool namecache_enable(void)
+{
+ /*
+ * Check if name caching disabled by setting the name cache
+ * timeout to zero.
+ */
+
+ if (lp_name_cache_timeout() == 0) {
+ DEBUG(5, ("namecache_enable: disabling netbios name cache\n"));
+ return False;
+ }
+
+ /* Init namecache by calling gencache initialisation */
+
+ if (!gencache_init()) {
+ DEBUG(2, ("namecache_enable: "
+ "Couldn't initialise namecache on top of gencache.\n"));
+ return False;
+ }
+
+ /* I leave it for now, though I don't think we really
+ * need this (mimir, 27.09.2002) */
+ DEBUG(5, ("namecache_enable: enabling netbios namecache, timeout %d "
+ "seconds\n", lp_name_cache_timeout()));
+
+ return True;
+}
+
+/**
+ * Shutdown namecache. Routine calls gencache close function
+ * to safely close gencache file.
+ *
+ * @return true upon successful shutdown of the cache or
+ * false on failure
+ **/
+
+bool namecache_shutdown(void)
+{
+ if (!gencache_shutdown()) {
+ DEBUG(2, ("namecache_shutdown: "
+ "Couldn't close namecache on top of gencache.\n"));
+ return False;
+ }
+
+ DEBUG(5, ("namecache_shutdown: "
+ "netbios namecache closed successfully.\n"));
+ return True;
+}
+
+/**
+ * Generates a key for netbios name lookups on basis of
+ * netbios name and type.
+ * The caller must free returned key string when finished.
+ *
+ * @param name netbios name string (case insensitive)
+ * @param name_type netbios type of the name being looked up
+ *
+ * @return string consisted of uppercased name and appended
+ * type number
+ */
+
+static char* namecache_key(const char *name,
+ int name_type)
+{
+ char *keystr;
+ asprintf_strupper_m(&keystr, NBTKEY_FMT, name, name_type);
+
+ return keystr;
+}
+
+/**
+ * Store a name(s) in the name cache
+ *
+ * @param name netbios names array
+ * @param name_type integer netbios name type
+ * @param num_names number of names being stored
+ * @param ip_list array of in_addr structures containing
+ * ip addresses being stored
+ **/
+
+bool namecache_store(const char *name,
+ int name_type,
+ int num_names,
+ struct ip_service *ip_list)
+{
+ time_t expiry;
+ char *key, *value_string;
+ int i;
+ bool ret;
+
+ /*
+ * we use gecache call to avoid annoying debug messages about
+ * initialised namecache again and again...
+ */
+ if (!gencache_init()) {
+ return False;
+ }
+
+ if (name_type > 255) {
+ return False; /* Don't store non-real name types. */
+ }
+
+ if ( DEBUGLEVEL >= 5 ) {
+ TALLOC_CTX *ctx = talloc_stackframe();
+ char *addr = NULL;
+
+ DEBUG(5, ("namecache_store: storing %d address%s for %s#%02x: ",
+ num_names, num_names == 1 ? "": "es", name, name_type));
+
+ for (i = 0; i < num_names; i++) {
+ addr = print_canonical_sockaddr(ctx,
+ &ip_list[i].ss);
+ if (!addr) {
+ continue;
+ }
+ DEBUGADD(5, ("%s%s", addr,
+ (i == (num_names - 1) ? "" : ",")));
+
+ }
+ DEBUGADD(5, ("\n"));
+ TALLOC_FREE(ctx);
+ }
+
+ key = namecache_key(name, name_type);
+ if (!key) {
+ return False;
+ }
+
+ expiry = time(NULL) + lp_name_cache_timeout();
+
+ /*
+ * Generate string representation of ip addresses list
+ * First, store the number of ip addresses and then
+ * place each single ip
+ */
+ if (!ipstr_list_make(&value_string, ip_list, num_names)) {
+ SAFE_FREE(key);
+ SAFE_FREE(value_string);
+ return false;
+ }
+
+ /* set the entry */
+ ret = gencache_set(key, value_string, expiry);
+ SAFE_FREE(key);
+ SAFE_FREE(value_string);
+ return ret;
+}
+
+/**
+ * Look up a name in the cache.
+ *
+ * @param name netbios name to look up for
+ * @param name_type netbios name type of @param name
+ * @param ip_list mallocated list of IP addresses if found in the cache,
+ * NULL otherwise
+ * @param num_names number of entries found
+ *
+ * @return true upon successful fetch or
+ * false if name isn't found in the cache or has expired
+ **/
+
+bool namecache_fetch(const char *name,
+ int name_type,
+ struct ip_service **ip_list,
+ int *num_names)
+{
+ char *key, *value;
+ time_t timeout;
+
+ /* exit now if null pointers were passed as they're required further */
+ if (!ip_list || !num_names) {
+ return False;
+ }
+
+ if (!gencache_init()) {
+ return False;
+ }
+
+ if (name_type > 255) {
+ return False; /* Don't fetch non-real name types. */
+ }
+
+ *num_names = 0;
+
+ /*
+ * Use gencache interface - lookup the key
+ */
+ key = namecache_key(name, name_type);
+ if (!key) {
+ return False;
+ }
+
+ if (!gencache_get(key, &value, &timeout)) {
+ DEBUG(5, ("no entry for %s#%02X found.\n", name, name_type));
+ SAFE_FREE(key);
+ return False;
+ } else {
+ DEBUG(5, ("name %s#%02X found.\n", name, name_type));
+ }
+
+ /*
+ * Split up the stored value into the list of IP adresses
+ */
+ *num_names = ipstr_list_parse(value, ip_list);
+
+ SAFE_FREE(key);
+ SAFE_FREE(value);
+
+ return *num_names > 0; /* true only if some ip has been fetched */
+}
+
+/**
+ * Remove a namecache entry. Needed for site support.
+ *
+ **/
+
+bool namecache_delete(const char *name, int name_type)
+{
+ bool ret;
+ char *key;
+
+ if (!gencache_init())
+ return False;
+
+ if (name_type > 255) {
+ return False; /* Don't fetch non-real name types. */
+ }
+
+ key = namecache_key(name, name_type);
+ if (!key) {
+ return False;
+ }
+ ret = gencache_del(key);
+ SAFE_FREE(key);
+ return ret;
+}
+
+/**
+ * Delete single namecache entry. Look at the
+ * gencache_iterate definition.
+ *
+ **/
+
+static void flush_netbios_name(const char *key,
+ const char *value,
+ time_t timeout,
+ void *dptr)
+{
+ gencache_del(key);
+ DEBUG(5, ("Deleting entry %s\n", key));
+}
+
+/**
+ * Flush all names from the name cache.
+ * It's done by gencache_iterate()
+ *
+ * @return true upon successful deletion or
+ * false in case of an error
+ **/
+
+void namecache_flush(void)
+{
+ if (!gencache_init()) {
+ return;
+ }
+
+ /*
+ * iterate through each NBT cache's entry and flush it
+ * by flush_netbios_name function
+ */
+ gencache_iterate(flush_netbios_name, NULL, "NBT/*");
+ DEBUG(5, ("Namecache flushed\n"));
+}
+
+/* Construct a name status record key. */
+
+static char *namecache_status_record_key(const char *name,
+ int name_type1,
+ int name_type2,
+ const struct sockaddr_storage *keyip)
+{
+ char addr[INET6_ADDRSTRLEN];
+ char *keystr;
+
+ print_sockaddr(addr, sizeof(addr), keyip);
+ asprintf_strupper_m(&keystr, "NBT/%s#%02X.%02X.%s", name,
+ name_type1, name_type2, addr);
+ return keystr;
+}
+
+/* Store a name status record. */
+
+bool namecache_status_store(const char *keyname, int keyname_type,
+ int name_type, const struct sockaddr_storage *keyip,
+ const char *srvname)
+{
+ char *key;
+ time_t expiry;
+ bool ret;
+
+ if (!gencache_init()) {
+ return False;
+ }
+
+ key = namecache_status_record_key(keyname, keyname_type,
+ name_type, keyip);
+ if (!key)
+ return False;
+
+ expiry = time(NULL) + lp_name_cache_timeout();
+ ret = gencache_set(key, srvname, expiry);
+
+ if (ret) {
+ DEBUG(5, ("namecache_status_store: entry %s -> %s\n",
+ key, srvname ));
+ } else {
+ DEBUG(5, ("namecache_status_store: entry %s store failed.\n",
+ key ));
+ }
+
+ SAFE_FREE(key);
+ return ret;
+}
+
+/* Fetch a name status record. */
+
+bool namecache_status_fetch(const char *keyname,
+ int keyname_type,
+ int name_type,
+ const struct sockaddr_storage *keyip,
+ char *srvname_out)
+{
+ char *key = NULL;
+ char *value = NULL;
+ time_t timeout;
+
+ if (!gencache_init())
+ return False;
+
+ key = namecache_status_record_key(keyname, keyname_type,
+ name_type, keyip);
+ if (!key)
+ return False;
+
+ if (!gencache_get(key, &value, &timeout)) {
+ DEBUG(5, ("namecache_status_fetch: no entry for %s found.\n",
+ key));
+ SAFE_FREE(key);
+ return False;
+ } else {
+ DEBUG(5, ("namecache_status_fetch: key %s -> %s\n",
+ key, value ));
+ }
+
+ strlcpy(srvname_out, value, 16);
+ SAFE_FREE(key);
+ SAFE_FREE(value);
+ return True;
+}
diff --git a/source3/libsmb/namequery.c b/source3/libsmb/namequery.c
new file mode 100644
index 0000000000..24d7ee1a9c
--- /dev/null
+++ b/source3/libsmb/namequery.c
@@ -0,0 +1,2145 @@
+/*
+ Unix SMB/CIFS implementation.
+ name query routines
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Jeremy Allison 2007.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/* nmbd.c sets this to True. */
+bool global_in_nmbd = False;
+
+/****************************
+ * SERVER AFFINITY ROUTINES *
+ ****************************/
+
+ /* Server affinity is the concept of preferring the last domain
+ controller with whom you had a successful conversation */
+
+/****************************************************************************
+****************************************************************************/
+#define SAFKEY_FMT "SAF/DOMAIN/%s"
+#define SAF_TTL 900
+
+static char *saf_key(const char *domain)
+{
+ char *keystr;
+
+ asprintf_strupper_m(&keystr, SAFKEY_FMT, domain);
+
+ return keystr;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+bool saf_store( const char *domain, const char *servername )
+{
+ char *key;
+ time_t expire;
+ bool ret = False;
+
+ if ( !domain || !servername ) {
+ DEBUG(2,("saf_store: "
+ "Refusing to store empty domain or servername!\n"));
+ return False;
+ }
+
+ if ( (strlen(domain) == 0) || (strlen(servername) == 0) ) {
+ DEBUG(0,("saf_store: "
+ "refusing to store 0 length domain or servername!\n"));
+ return False;
+ }
+
+ if ( !gencache_init() )
+ return False;
+
+ key = saf_key( domain );
+ expire = time( NULL ) + SAF_TTL;
+
+ DEBUG(10,("saf_store: domain = [%s], server = [%s], expire = [%u]\n",
+ domain, servername, (unsigned int)expire ));
+
+ ret = gencache_set( key, servername, expire );
+
+ SAFE_FREE( key );
+
+ return ret;
+}
+
+bool saf_delete( const char *domain )
+{
+ char *key;
+ bool ret = False;
+
+ if ( !domain ) {
+ DEBUG(2,("saf_delete: Refusing to delete empty domain\n"));
+ return False;
+ }
+
+ if ( !gencache_init() )
+ return False;
+
+ key = saf_key(domain);
+ ret = gencache_del(key);
+
+ if (ret) {
+ DEBUG(10,("saf_delete: domain = [%s]\n", domain ));
+ }
+
+ SAFE_FREE( key );
+
+ return ret;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+char *saf_fetch( const char *domain )
+{
+ char *server = NULL;
+ time_t timeout;
+ bool ret = False;
+ char *key = NULL;
+
+ if ( !domain || strlen(domain) == 0) {
+ DEBUG(2,("saf_fetch: Empty domain name!\n"));
+ return NULL;
+ }
+
+ if ( !gencache_init() )
+ return False;
+
+ key = saf_key( domain );
+
+ ret = gencache_get( key, &server, &timeout );
+
+ SAFE_FREE( key );
+
+ if ( !ret ) {
+ DEBUG(5,("saf_fetch: failed to find server for \"%s\" domain\n",
+ domain ));
+ } else {
+ DEBUG(5,("saf_fetch: Returning \"%s\" for \"%s\" domain\n",
+ server, domain ));
+ }
+
+ return server;
+}
+
+/****************************************************************************
+ Generate a random trn_id.
+****************************************************************************/
+
+static int generate_trn_id(void)
+{
+ uint16 id;
+
+ generate_random_buffer((uint8 *)&id, sizeof(id));
+
+ return id % (unsigned)0x7FFF;
+}
+
+/****************************************************************************
+ Parse a node status response into an array of structures.
+****************************************************************************/
+
+static NODE_STATUS_STRUCT *parse_node_status(char *p,
+ int *num_names,
+ struct node_status_extra *extra)
+{
+ NODE_STATUS_STRUCT *ret;
+ int i;
+
+ *num_names = CVAL(p,0);
+
+ if (*num_names == 0)
+ return NULL;
+
+ ret = SMB_MALLOC_ARRAY(NODE_STATUS_STRUCT,*num_names);
+ if (!ret)
+ return NULL;
+
+ p++;
+ for (i=0;i< *num_names;i++) {
+ StrnCpy(ret[i].name,p,15);
+ trim_char(ret[i].name,'\0',' ');
+ 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));
+ }
+ /*
+ * Also, pick up the MAC address ...
+ */
+ if (extra) {
+ memcpy(&extra->mac_addr, p, 6); /* Fill in the mac addr */
+ }
+ 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.
+**************************************************************************/
+
+NODE_STATUS_STRUCT *node_status_query(int fd,
+ struct nmb_name *name,
+ const struct sockaddr_storage *to_ss,
+ int *num_names,
+ struct node_status_extra *extra)
+{
+ 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;
+ NODE_STATUS_STRUCT *ret;
+
+ ZERO_STRUCT(p);
+
+ if (to_ss->ss_family != AF_INET) {
+ /* Can't do node status to IPv6 */
+ return NULL;
+ }
+ 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 = ((const struct sockaddr_in *)to_ss)->sin_addr;
+ 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, extra);
+ 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,
+ const struct sockaddr_storage *to_ss,
+ fstring name)
+{
+ char addr[INET6_ADDRSTRLEN];
+ struct sockaddr_storage ss;
+ NODE_STATUS_STRUCT *status = NULL;
+ struct nmb_name nname;
+ int count, i;
+ int sock;
+ bool result = false;
+
+ if (lp_disable_netbios()) {
+ DEBUG(5,("name_status_find(%s#%02x): netbios is disabled\n",
+ q_name, q_type));
+ return False;
+ }
+
+ print_sockaddr(addr, sizeof(addr), to_ss);
+
+ DEBUG(10, ("name_status_find: looking up %s#%02x at %s\n", q_name,
+ q_type, addr));
+
+ /* Check the cache first. */
+
+ if (namecache_status_fetch(q_name, q_type, type, to_ss, name)) {
+ return True;
+ }
+
+ if (to_ss->ss_family != AF_INET) {
+ /* Can't do node status to IPv6 */
+ return false;
+ }
+
+ if (!interpret_string_addr(&ss, lp_socket_address(),
+ AI_NUMERICHOST|AI_PASSIVE)) {
+ zero_addr(&ss);
+ }
+
+ sock = open_socket_in(SOCK_DGRAM, 0, 3, &ss, 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_ss, &count, NULL);
+ 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_nstring(name, sizeof(fstring), status[i].name);
+
+ /* Store the result in the cache. */
+ /* but don't store an entry for 0x1c names here. Here we have
+ a single host and DOMAIN<0x1c> names should be a list of hosts */
+
+ if ( q_type != 0x1c ) {
+ namecache_status_store(q_name, q_type, type, to_ss, name);
+ }
+
+ result = true;
+
+ done:
+ SAFE_FREE(status);
+
+ DEBUG(10, ("name_status_find: name %sfound", result ? "" : "not "));
+
+ if (result)
+ DEBUGADD(10, (", name %s ip address is %s", name, addr));
+
+ DEBUG(10, ("\n"));
+
+ return result;
+}
+
+/*
+ comparison function used by sort_addr_list
+*/
+
+static int addr_compare(const struct sockaddr_storage *ss1,
+ const struct sockaddr_storage *ss2)
+{
+ int max_bits1=0, max_bits2=0;
+ int num_interfaces = iface_count();
+ int i;
+
+ /* Sort IPv6 addresses first. */
+ if (ss1->ss_family != ss2->ss_family) {
+ if (ss2->ss_family == AF_INET) {
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+
+ /* Here we know both addresses are of the same
+ * family. */
+
+ for (i=0;i<num_interfaces;i++) {
+ const struct sockaddr_storage *pss = iface_n_bcast(i);
+ unsigned char *p_ss1 = NULL;
+ unsigned char *p_ss2 = NULL;
+ unsigned char *p_if = NULL;
+ size_t len = 0;
+ int bits1, bits2;
+
+ if (pss->ss_family != ss1->ss_family) {
+ /* Ignore interfaces of the wrong type. */
+ continue;
+ }
+ if (pss->ss_family == AF_INET) {
+ p_if = (unsigned char *)
+ &((const struct sockaddr_in *)pss)->sin_addr;
+ p_ss1 = (unsigned char *)
+ &((const struct sockaddr_in *)ss1)->sin_addr;
+ p_ss2 = (unsigned char *)
+ &((const struct sockaddr_in *)ss2)->sin_addr;
+ len = 4;
+ }
+#if defined(HAVE_IPV6)
+ if (pss->ss_family == AF_INET6) {
+ p_if = (unsigned char *)
+ &((const struct sockaddr_in6 *)pss)->sin6_addr;
+ p_ss1 = (unsigned char *)
+ &((const struct sockaddr_in6 *)ss1)->sin6_addr;
+ p_ss2 = (unsigned char *)
+ &((const struct sockaddr_in6 *)ss2)->sin6_addr;
+ len = 16;
+ }
+#endif
+ if (!p_ss1 || !p_ss2 || !p_if || len == 0) {
+ continue;
+ }
+ bits1 = matching_len_bits(p_ss1, p_if, len);
+ bits2 = matching_len_bits(p_ss2, p_if, len);
+ max_bits1 = MAX(bits1, max_bits1);
+ max_bits2 = MAX(bits2, max_bits2);
+ }
+
+ /* Bias towards directly reachable IPs */
+ if (iface_local(ss1)) {
+ if (ss1->ss_family == AF_INET) {
+ max_bits1 += 32;
+ } else {
+ max_bits1 += 128;
+ }
+ }
+ if (iface_local(ss2)) {
+ if (ss2->ss_family == AF_INET) {
+ max_bits2 += 32;
+ } else {
+ max_bits2 += 128;
+ }
+ }
+ return max_bits2 - max_bits1;
+}
+
+/*******************************************************************
+ compare 2 ldap IPs by nearness to our interfaces - used in qsort
+*******************************************************************/
+
+int ip_service_compare(struct ip_service *ss1, struct ip_service *ss2)
+{
+ int result;
+
+ if ((result = addr_compare(&ss1->ss, &ss2->ss)) != 0) {
+ return result;
+ }
+
+ if (ss1->port > ss2->port) {
+ return 1;
+ }
+
+ if (ss1->port < ss2->port) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ sort an IP list so that names that are close to one of our interfaces
+ are at the top. This prevents the problem where a WINS server returns an IP
+ that is not reachable from our subnet as the first match
+*/
+
+static void sort_addr_list(struct sockaddr_storage *sslist, int count)
+{
+ if (count <= 1) {
+ return;
+ }
+
+ qsort(sslist, count, sizeof(struct sockaddr_storage),
+ QSORT_CAST addr_compare);
+}
+
+static void sort_service_list(struct ip_service *servlist, int count)
+{
+ if (count <= 1) {
+ return;
+ }
+
+ qsort(servlist, count, sizeof(struct ip_service),
+ QSORT_CAST ip_service_compare);
+}
+
+/**********************************************************************
+ Remove any duplicate address/port pairs in the list
+ *********************************************************************/
+
+static int remove_duplicate_addrs2(struct ip_service *iplist, int count )
+{
+ int i, j;
+
+ DEBUG(10,("remove_duplicate_addrs2: "
+ "looking for duplicate address/port pairs\n"));
+
+ /* one loop to remove duplicates */
+ for ( i=0; i<count; i++ ) {
+ if ( is_zero_addr(&iplist[i].ss)) {
+ continue;
+ }
+
+ for ( j=i+1; j<count; j++ ) {
+ if (addr_equal(&iplist[i].ss, &iplist[j].ss) &&
+ iplist[i].port == iplist[j].port) {
+ zero_addr(&iplist[j].ss);
+ }
+ }
+ }
+
+ /* one loop to clean up any holes we left */
+ /* first ip should never be a zero_ip() */
+ for (i = 0; i<count; ) {
+ if (is_zero_addr(&iplist[i].ss) ) {
+ if (i != count-1) {
+ memmove(&iplist[i], &iplist[i+1],
+ (count - i - 1)*sizeof(iplist[i]));
+ }
+ count--;
+ continue;
+ }
+ i++;
+ }
+
+ return count;
+}
+
+/****************************************************************************
+ 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.
+ *timed_out is set if we failed by timing out
+****************************************************************************/
+
+struct sockaddr_storage *name_query(int fd,
+ const char *name,
+ int name_type,
+ bool bcast,
+ bool recurse,
+ const struct sockaddr_storage *to_ss,
+ int *count,
+ int *flags,
+ bool *timed_out)
+{
+ 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 sockaddr_storage *ss_list = NULL;
+
+ if (lp_disable_netbios()) {
+ DEBUG(5,("name_query(%s#%02x): netbios is disabled\n",
+ name, name_type));
+ return NULL;
+ }
+
+ if (to_ss->ss_family != AF_INET) {
+ return NULL;
+ }
+
+ if (timed_out) {
+ *timed_out = false;
+ }
+
+ memset((char *)&p,'\0',sizeof(p));
+ (*count) = 0;
+ (*flags) = 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 = ((struct sockaddr_in *)to_ss)->sin_addr;
+ 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 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;
+ }
+
+ ss_list = SMB_REALLOC_ARRAY(ss_list,
+ struct sockaddr_storage,
+ (*count) +
+ nmb2->answers->rdlength/6);
+
+ if (!ss_list) {
+ DEBUG(0,("name_query: Realloc failed.\n"));
+ free_packet(p2);
+ return NULL;
+ }
+
+ DEBUG(2,("Got a positive name query response "
+ "from %s ( ",
+ inet_ntoa(p2->ip)));
+
+ for (i=0;i<nmb2->answers->rdlength/6;i++) {
+ struct in_addr ip;
+ putip((char *)&ip,&nmb2->answers->rdata[2+i*6]);
+ in_addr_to_sockaddr_storage(&ss_list[(*count)],
+ ip);
+ DEBUGADD(2,("%s ",inet_ntoa(ip)));
+ (*count)++;
+ }
+ DEBUGADD(2,(")\n"));
+
+ found=true;
+ retries=0;
+ /* We add the flags back ... */
+ if (nmb2->header.response)
+ (*flags) |= NM_FLAGS_RS;
+ if (nmb2->header.nm_flags.authoritative)
+ (*flags) |= NM_FLAGS_AA;
+ if (nmb2->header.nm_flags.trunc)
+ (*flags) |= NM_FLAGS_TC;
+ if (nmb2->header.nm_flags.recursion_desired)
+ (*flags) |= NM_FLAGS_RD;
+ if (nmb2->header.nm_flags.recursion_available)
+ (*flags) |= NM_FLAGS_RA;
+ if (nmb2->header.nm_flags.bcast)
+ (*flags) |= NM_FLAGS_B;
+ 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;
+ }
+ }
+
+ /* only set timed_out if we didn't fund what we where looking for*/
+
+ if ( !found && timed_out ) {
+ *timed_out = true;
+ }
+
+ /* sort the ip list so we choose close servers first if possible */
+ sort_addr_list(ss_list, *count);
+
+ return ss_list;
+}
+
+/********************************************************
+ Start parsing the lmhosts file.
+*********************************************************/
+
+XFILE *startlmhosts(const 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(TALLOC_CTX *ctx, XFILE *fp, char **pp_name, int *name_type,
+ struct sockaddr_storage *pss)
+{
+ char line[1024];
+
+ *pp_name = NULL;
+
+ while(!x_feof(fp) && !x_ferror(fp)) {
+ char *ip = NULL;
+ char *flags = NULL;
+ char *extra = NULL;
+ char *name = NULL;
+ const char *ptr;
+ char *ptr1 = NULL;
+ int count = 0;
+
+ *name_type = -1;
+
+ if (!fgets_slash(line,sizeof(line),fp)) {
+ continue;
+ }
+
+ if (*line == '#') {
+ continue;
+ }
+
+ ptr = line;
+
+ if (next_token_talloc(ctx, &ptr, &ip, NULL))
+ ++count;
+ if (next_token_talloc(ctx, &ptr, &name, NULL))
+ ++count;
+ if (next_token_talloc(ctx, &ptr, &flags, NULL))
+ ++count;
+ if (next_token_talloc(ctx, &ptr, &extra, NULL))
+ ++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;
+ }
+
+ if (!flags) {
+ flags = talloc_strdup(ctx, "");
+ if (!flags) {
+ 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;
+ }
+
+ if (!interpret_string_addr(pss, ip, AI_NUMERICHOST)) {
+ DEBUG(0,("getlmhostsent: invalid address "
+ "%s.\n", ip));
+ }
+
+ /* Extra feature. If the name ends in '#XX',
+ * where XX is a hex number, then only add that name type. */
+ if((ptr1 = strchr_m(name, '#')) != NULL) {
+ char *endptr;
+ ptr1++;
+
+ *name_type = (int)strtol(ptr1, &endptr, 16);
+ if(!*ptr1 || (endptr == ptr1)) {
+ DEBUG(0,("getlmhostsent: invalid name "
+ "%s containing '#'.\n", name));
+ continue;
+ }
+
+ *(--ptr1) = '\0'; /* Truncate at the '#' */
+ }
+
+ *pp_name = talloc_strdup(ctx, name);
+ if (!*pp_name) {
+ return false;
+ }
+ return true;
+ }
+
+ return false;
+}
+
+/********************************************************
+ Finish parsing the lmhosts file.
+*********************************************************/
+
+void endlmhosts(XFILE *fp)
+{
+ x_fclose(fp);
+}
+
+/********************************************************
+ convert an array if struct sockaddr_storage to struct ip_service
+ return false on failure. Port is set to PORT_NONE;
+*********************************************************/
+
+static bool convert_ss2service(struct ip_service **return_iplist,
+ const struct sockaddr_storage *ss_list,
+ int count)
+{
+ int i;
+
+ if ( count==0 || !ss_list )
+ return False;
+
+ /* copy the ip address; port will be PORT_NONE */
+ if ((*return_iplist = SMB_MALLOC_ARRAY(struct ip_service, count)) ==
+ NULL) {
+ DEBUG(0,("convert_ip2service: malloc failed "
+ "for %d enetries!\n", count ));
+ return False;
+ }
+
+ for ( i=0; i<count; i++ ) {
+ (*return_iplist)[i].ss = ss_list[i];
+ (*return_iplist)[i].port = PORT_NONE;
+ }
+
+ return true;
+}
+
+/********************************************************
+ Resolve via "bcast" method.
+*********************************************************/
+
+NTSTATUS name_resolve_bcast(const char *name,
+ int name_type,
+ struct ip_service **return_iplist,
+ int *return_count)
+{
+ int sock, i;
+ int num_interfaces = iface_count();
+ struct sockaddr_storage *ss_list;
+ struct sockaddr_storage ss;
+ NTSTATUS status;
+
+ if (lp_disable_netbios()) {
+ DEBUG(5,("name_resolve_bcast(%s#%02x): netbios is disabled\n",
+ name, name_type));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ *return_iplist = 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));
+
+ if (!interpret_string_addr(&ss, lp_socket_address(),
+ AI_NUMERICHOST|AI_PASSIVE)) {
+ zero_addr(&ss);
+ }
+
+ sock = open_socket_in( SOCK_DGRAM, 0, 3, &ss, true );
+ if (sock == -1) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ 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--) {
+ const struct sockaddr_storage *pss = iface_n_bcast(i);
+ int flags;
+
+ /* Done this way to fix compiler error on IRIX 5.x */
+ if (!pss) {
+ continue;
+ }
+ ss_list = name_query(sock, name, name_type, true,
+ true, pss, return_count, &flags, NULL);
+ if (ss_list) {
+ goto success;
+ }
+ }
+
+ /* failed - no response */
+
+ close(sock);
+ return NT_STATUS_UNSUCCESSFUL;
+
+success:
+
+ status = NT_STATUS_OK;
+ if (!convert_ss2service(return_iplist, ss_list, *return_count) )
+ status = NT_STATUS_INVALID_PARAMETER;
+
+ SAFE_FREE(ss_list);
+ close(sock);
+ return status;
+}
+
+/********************************************************
+ Resolve via "wins" method.
+*********************************************************/
+
+NTSTATUS resolve_wins(const char *name,
+ int name_type,
+ struct ip_service **return_iplist,
+ int *return_count)
+{
+ int sock, t, i;
+ char **wins_tags;
+ struct sockaddr_storage src_ss, *ss_list = NULL;
+ struct in_addr src_ip;
+ NTSTATUS status;
+
+ if (lp_disable_netbios()) {
+ DEBUG(5,("resolve_wins(%s#%02x): netbios is disabled\n",
+ name, name_type));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ *return_iplist = NULL;
+ *return_count = 0;
+
+ DEBUG(3,("resolve_wins: Attempting wins lookup for name %s<0x%x>\n",
+ name, name_type));
+
+ if (wins_srv_count() < 1) {
+ DEBUG(3,("resolve_wins: WINS server resolution selected "
+ "and no WINS servers listed.\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* we try a lookup on each of the WINS tags in turn */
+ wins_tags = wins_srv_tags();
+
+ if (!wins_tags) {
+ /* huh? no tags?? give up in disgust */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* the address we will be sending from */
+ if (!interpret_string_addr(&src_ss, lp_socket_address(),
+ AI_NUMERICHOST|AI_PASSIVE)) {
+ zero_addr(&src_ss);
+ }
+
+ if (src_ss.ss_family != AF_INET) {
+ char addr[INET6_ADDRSTRLEN];
+ print_sockaddr(addr, sizeof(addr), &src_ss);
+ DEBUG(3,("resolve_wins: cannot receive WINS replies "
+ "on IPv6 address %s\n",
+ addr));
+ wins_srv_tags_free(wins_tags);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ src_ip = ((struct sockaddr_in *)&src_ss)->sin_addr;
+
+ /* in the worst case we will try every wins server with every
+ tag! */
+ for (t=0; wins_tags && wins_tags[t]; t++) {
+ int srv_count = wins_srv_count_tag(wins_tags[t]);
+ for (i=0; i<srv_count; i++) {
+ struct sockaddr_storage wins_ss;
+ struct in_addr wins_ip;
+ int flags;
+ bool timed_out;
+
+ wins_ip = wins_srv_ip_tag(wins_tags[t], src_ip);
+
+ if (global_in_nmbd && ismyip_v4(wins_ip)) {
+ /* yikes! we'll loop forever */
+ continue;
+ }
+
+ /* skip any that have been unresponsive lately */
+ if (wins_srv_is_dead(wins_ip, src_ip)) {
+ continue;
+ }
+
+ DEBUG(3,("resolve_wins: using WINS server %s "
+ "and tag '%s'\n",
+ inet_ntoa(wins_ip), wins_tags[t]));
+
+ sock = open_socket_in(SOCK_DGRAM, 0, 3, &src_ss, true);
+ if (sock == -1) {
+ continue;
+ }
+
+ in_addr_to_sockaddr_storage(&wins_ss, wins_ip);
+ ss_list = name_query(sock,
+ name,
+ name_type,
+ false,
+ true,
+ &wins_ss,
+ return_count,
+ &flags,
+ &timed_out);
+
+ /* exit loop if we got a list of addresses */
+
+ if (ss_list)
+ goto success;
+
+ close(sock);
+
+ if (timed_out) {
+ /* Timed out wating for WINS server to respond.
+ * Mark it dead. */
+ wins_srv_died(wins_ip, src_ip);
+ } else {
+ /* The name definately isn't in this
+ group of WINS servers.
+ goto the next group */
+ break;
+ }
+ }
+ }
+
+ wins_srv_tags_free(wins_tags);
+ return NT_STATUS_NO_LOGON_SERVERS;
+
+success:
+
+ status = NT_STATUS_OK;
+ if (!convert_ss2service(return_iplist, ss_list, *return_count))
+ status = NT_STATUS_INVALID_PARAMETER;
+
+ SAFE_FREE(ss_list);
+ wins_srv_tags_free(wins_tags);
+ close(sock);
+
+ return status;
+}
+
+/********************************************************
+ Resolve via "lmhosts" method.
+*********************************************************/
+
+static NTSTATUS resolve_lmhosts(const char *name, int name_type,
+ struct ip_service **return_iplist,
+ int *return_count)
+{
+ /*
+ * "lmhosts" means parse the local lmhosts file.
+ */
+
+ XFILE *fp;
+ char *lmhost_name = NULL;
+ int name_type2;
+ struct sockaddr_storage return_ss;
+ NTSTATUS status = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+ TALLOC_CTX *ctx = NULL;
+
+ *return_iplist = NULL;
+ *return_count = 0;
+
+ DEBUG(3,("resolve_lmhosts: "
+ "Attempting lmhosts lookup for name %s<0x%x>\n",
+ name, name_type));
+
+ fp = startlmhosts(get_dyn_LMHOSTSFILE());
+
+ if ( fp == NULL )
+ return NT_STATUS_NO_SUCH_FILE;
+
+ ctx = talloc_init("resolve_lmhosts");
+ if (!ctx) {
+ endlmhosts(fp);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ while (getlmhostsent(ctx, fp, &lmhost_name, &name_type2, &return_ss)) {
+
+ if (!strequal(name, lmhost_name)) {
+ TALLOC_FREE(lmhost_name);
+ continue;
+ }
+
+ if ((name_type2 != -1) && (name_type != name_type2)) {
+ TALLOC_FREE(lmhost_name);
+ continue;
+ }
+
+ *return_iplist = SMB_REALLOC_ARRAY((*return_iplist),
+ struct ip_service,
+ (*return_count)+1);
+
+ if ((*return_iplist) == NULL) {
+ TALLOC_FREE(ctx);
+ endlmhosts(fp);
+ DEBUG(3,("resolve_lmhosts: malloc fail !\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*return_iplist)[*return_count].ss = return_ss;
+ (*return_iplist)[*return_count].port = PORT_NONE;
+ *return_count += 1;
+
+ /* we found something */
+ status = NT_STATUS_OK;
+
+ /* Multiple names only for DC lookup */
+ if (name_type != 0x1c)
+ break;
+ }
+
+ TALLOC_FREE(ctx);
+ endlmhosts(fp);
+ return status;
+}
+
+
+/********************************************************
+ Resolve via "hosts" method.
+*********************************************************/
+
+static NTSTATUS resolve_hosts(const char *name, int name_type,
+ struct ip_service **return_iplist,
+ int *return_count)
+{
+ /*
+ * "host" means do a localhost, or dns lookup.
+ */
+ struct addrinfo hints;
+ struct addrinfo *ailist = NULL;
+ struct addrinfo *res = NULL;
+ int ret = -1;
+ int i = 0;
+
+ if ( name_type != 0x20 && name_type != 0x0) {
+ DEBUG(5, ("resolve_hosts: not appropriate "
+ "for name type <0x%x>\n",
+ name_type));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ *return_iplist = NULL;
+ *return_count = 0;
+
+ DEBUG(3,("resolve_hosts: Attempting host lookup for name %s<0x%x>\n",
+ name, name_type));
+
+ ZERO_STRUCT(hints);
+ /* By default make sure it supports TCP. */
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_ADDRCONFIG;
+
+#if !defined(HAVE_IPV6)
+ /* Unless we have IPv6, we really only want IPv4 addresses back. */
+ hints.ai_family = AF_INET;
+#endif
+
+ ret = getaddrinfo(name,
+ NULL,
+ &hints,
+ &ailist);
+ if (ret) {
+ DEBUG(3,("resolve_hosts: getaddrinfo failed for name %s [%s]\n",
+ name,
+ gai_strerror(ret) ));
+ }
+
+ for (res = ailist; res; res = res->ai_next) {
+ struct sockaddr_storage ss;
+
+ if (!res->ai_addr || res->ai_addrlen == 0) {
+ continue;
+ }
+
+ ZERO_STRUCT(ss);
+ memcpy(&ss, res->ai_addr, res->ai_addrlen);
+
+ *return_count += 1;
+
+ *return_iplist = SMB_REALLOC_ARRAY(*return_iplist,
+ struct ip_service,
+ *return_count);
+ if (!*return_iplist) {
+ DEBUG(3,("resolve_hosts: malloc fail !\n"));
+ freeaddrinfo(ailist);
+ return NT_STATUS_NO_MEMORY;
+ }
+ (*return_iplist)[i].ss = ss;
+ (*return_iplist)[i].port = PORT_NONE;
+ i++;
+ }
+ if (ailist) {
+ freeaddrinfo(ailist);
+ }
+ if (*return_count) {
+ return NT_STATUS_OK;
+ }
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/********************************************************
+ Resolve via "ADS" method.
+*********************************************************/
+
+static NTSTATUS resolve_ads(const char *name,
+ int name_type,
+ const char *sitename,
+ struct ip_service **return_iplist,
+ int *return_count)
+{
+ int i, j;
+ NTSTATUS status;
+ TALLOC_CTX *ctx;
+ struct dns_rr_srv *dcs = NULL;
+ int numdcs = 0;
+ int numaddrs = 0;
+
+ if ((name_type != 0x1c) && (name_type != KDC_NAME_TYPE) &&
+ (name_type != 0x1b)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if ( (ctx = talloc_init("resolve_ads")) == NULL ) {
+ DEBUG(0,("resolve_ads: talloc_init() failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* The DNS code needs fixing to find IPv6 addresses... JRA. */
+
+ switch (name_type) {
+ case 0x1b:
+ DEBUG(5,("resolve_ads: Attempting to resolve "
+ "PDC for %s using DNS\n", name));
+ status = ads_dns_query_pdc(ctx, name, &dcs, &numdcs);
+ break;
+
+ case 0x1c:
+ DEBUG(5,("resolve_ads: Attempting to resolve "
+ "DCs for %s using DNS\n", name));
+ status = ads_dns_query_dcs(ctx, name, sitename, &dcs,
+ &numdcs);
+ break;
+ case KDC_NAME_TYPE:
+ DEBUG(5,("resolve_ads: Attempting to resolve "
+ "KDCs for %s using DNS\n", name));
+ status = ads_dns_query_kdcs(ctx, name, sitename, &dcs,
+ &numdcs);
+ break;
+ default:
+ status = NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if ( !NT_STATUS_IS_OK( status ) ) {
+ talloc_destroy(ctx);
+ return status;
+ }
+
+ for (i=0;i<numdcs;i++) {
+ numaddrs += MAX(dcs[i].num_ips,1);
+ }
+
+ if ((*return_iplist = SMB_MALLOC_ARRAY(struct ip_service, numaddrs)) ==
+ NULL ) {
+ DEBUG(0,("resolve_ads: malloc failed for %d entries\n",
+ numaddrs ));
+ talloc_destroy(ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* now unroll the list of IP addresses */
+
+ *return_count = 0;
+ i = 0;
+ j = 0;
+ while ( i < numdcs && (*return_count<numaddrs) ) {
+ struct ip_service *r = &(*return_iplist)[*return_count];
+
+ r->port = dcs[i].port;
+
+ /* If we don't have an IP list for a name, lookup it up */
+
+ if (!dcs[i].ss_s) {
+ interpret_string_addr(&r->ss, dcs[i].hostname, 0);
+ i++;
+ j = 0;
+ } else {
+ /* use the IP addresses from the SRV sresponse */
+
+ if ( j >= dcs[i].num_ips ) {
+ i++;
+ j = 0;
+ continue;
+ }
+
+ r->ss = dcs[i].ss_s[j];
+ j++;
+ }
+
+ /* make sure it is a valid IP. I considered checking the
+ * negative connection cache, but this is the wrong place
+ * for it. Maybe only as a hack. After think about it, if
+ * all of the IP addresses returned from DNS are dead, what
+ * hope does a netbios name lookup have ? The standard reason
+ * for falling back to netbios lookups is that our DNS server
+ * doesn't know anything about the DC's -- jerry */
+
+ if (!is_zero_addr(&r->ss)) {
+ (*return_count)++;
+ }
+ }
+
+ talloc_destroy(ctx);
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ 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.
+
+ Added support for ip addr/port to support ADS ldap servers.
+ the only place we currently care about the port is in the
+ resolve_hosts() when looking up DC's via SRV RR entries in DNS
+**********************************************************************/
+
+NTSTATUS internal_resolve_name(const char *name,
+ int name_type,
+ const char *sitename,
+ struct ip_service **return_iplist,
+ int *return_count,
+ const char *resolve_order)
+{
+ char *tok;
+ const char *ptr;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ int i;
+ TALLOC_CTX *frame = NULL;
+
+ *return_iplist = NULL;
+ *return_count = 0;
+
+ DEBUG(10, ("internal_resolve_name: looking up %s#%x (sitename %s)\n",
+ name, name_type, sitename ? sitename : NULL));
+
+ if (is_ipaddress(name)) {
+ if ((*return_iplist = SMB_MALLOC_P(struct ip_service)) ==
+ NULL) {
+ DEBUG(0,("internal_resolve_name: malloc fail !\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* ignore the port here */
+ (*return_iplist)->port = PORT_NONE;
+
+ /* if it's in the form of an IP address then get the lib to interpret it */
+ if (!interpret_string_addr(&(*return_iplist)->ss,
+ name, AI_NUMERICHOST)) {
+ DEBUG(1,("internal_resolve_name: interpret_string_addr "
+ "failed on %s\n",
+ name));
+ SAFE_FREE(*return_iplist);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ *return_count = 1;
+ return NT_STATUS_OK;
+ }
+
+ /* Check name cache */
+
+ if (namecache_fetch(name, name_type, return_iplist, return_count)) {
+ /* This could be a negative response */
+ if (*return_count > 0) {
+ return NT_STATUS_OK;
+ } else {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ /* set the name resolution order */
+
+ if (strcmp( resolve_order, "NULL") == 0) {
+ DEBUG(8,("internal_resolve_name: all lookups disabled\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!resolve_order[0]) {
+ ptr = "host";
+ } else {
+ ptr = resolve_order;
+ }
+
+ /* iterate through the name resolution backends */
+
+ frame = talloc_stackframe();
+ while (next_token_talloc(frame, &ptr, &tok, LIST_SEP)) {
+ if((strequal(tok, "host") || strequal(tok, "hosts"))) {
+ status = resolve_hosts(name, name_type, return_iplist,
+ return_count);
+ if (NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ } else if(strequal( tok, "kdc")) {
+ /* deal with KDC_NAME_TYPE names here.
+ * This will result in a SRV record lookup */
+ status = resolve_ads(name, KDC_NAME_TYPE, sitename,
+ return_iplist, return_count);
+ if (NT_STATUS_IS_OK(status)) {
+ /* Ensure we don't namecache
+ * this with the KDC port. */
+ name_type = KDC_NAME_TYPE;
+ goto done;
+ }
+ } else if(strequal( tok, "ads")) {
+ /* deal with 0x1c and 0x1b names here.
+ * This will result in a SRV record lookup */
+ status = resolve_ads(name, name_type, sitename,
+ return_iplist, return_count);
+ if (NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ } else if(strequal( tok, "lmhosts")) {
+ status = resolve_lmhosts(name, name_type,
+ return_iplist, return_count);
+ if (NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ } else if(strequal( tok, "wins")) {
+ /* don't resolve 1D via WINS */
+ if (name_type != 0x1D) {
+ status = resolve_wins(name, name_type,
+ return_iplist,
+ return_count);
+ if (NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ }
+ } else if(strequal( tok, "bcast")) {
+ status = name_resolve_bcast(name, name_type,
+ return_iplist,
+ return_count);
+ if (NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ } else {
+ DEBUG(0,("resolve_name: unknown name switch type %s\n",
+ tok));
+ }
+ }
+
+ /* All of the resolve_* functions above have returned false. */
+
+ TALLOC_FREE(frame);
+ SAFE_FREE(*return_iplist);
+ *return_count = 0;
+
+ return NT_STATUS_UNSUCCESSFUL;
+
+ 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 ) {
+ *return_count = remove_duplicate_addrs2(*return_iplist,
+ *return_count );
+ }
+
+ /* Save in name cache */
+ if ( DEBUGLEVEL >= 100 ) {
+ for (i = 0; i < *return_count && DEBUGLEVEL == 100; i++) {
+ char addr[INET6_ADDRSTRLEN];
+ print_sockaddr(addr, sizeof(addr),
+ &(*return_iplist)[i].ss);
+ DEBUG(100, ("Storing name %s of type %d (%s:%d)\n",
+ name,
+ name_type,
+ addr,
+ (*return_iplist)[i].port));
+ }
+ }
+
+ namecache_store(name, name_type, *return_count, *return_iplist);
+
+ /* Display some debugging info */
+
+ if ( DEBUGLEVEL >= 10 ) {
+ DEBUG(10, ("internal_resolve_name: returning %d addresses: ",
+ *return_count));
+
+ for (i = 0; i < *return_count; i++) {
+ char addr[INET6_ADDRSTRLEN];
+ print_sockaddr(addr, sizeof(addr),
+ &(*return_iplist)[i].ss);
+ DEBUGADD(10, ("%s:%d ",
+ addr,
+ (*return_iplist)[i].port));
+ }
+ DEBUG(10, ("\n"));
+ }
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/********************************************************
+ 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 sockaddr_storage *return_ss,
+ int name_type)
+{
+ struct ip_service *ss_list = NULL;
+ char *sitename = NULL;
+ int count = 0;
+
+ if (is_ipaddress(name)) {
+ return interpret_string_addr(return_ss, name, AI_NUMERICHOST);
+ }
+
+ sitename = sitename_fetch(lp_realm()); /* wild guess */
+
+ if (NT_STATUS_IS_OK(internal_resolve_name(name, name_type, sitename,
+ &ss_list, &count,
+ lp_name_resolve_order()))) {
+ int i;
+
+ /* only return valid addresses for TCP connections */
+ for (i=0; i<count; i++) {
+ if (!is_zero_addr(&ss_list[i].ss) &&
+ !is_broadcast_addr(&ss_list[i].ss)) {
+ *return_ss = ss_list[i].ss;
+ SAFE_FREE(ss_list);
+ SAFE_FREE(sitename);
+ return True;
+ }
+ }
+ }
+
+ SAFE_FREE(ss_list);
+ SAFE_FREE(sitename);
+ return False;
+}
+
+/********************************************************
+ Internal interface to resolve a name into a list of IP addresses.
+ 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.
+*********************************************************/
+
+NTSTATUS resolve_name_list(TALLOC_CTX *ctx,
+ const char *name,
+ int name_type,
+ struct sockaddr_storage **return_ss_arr,
+ unsigned int *p_num_entries)
+{
+ struct ip_service *ss_list = NULL;
+ char *sitename = NULL;
+ int count = 0;
+ int i;
+ unsigned int num_entries;
+ NTSTATUS status;
+
+ *p_num_entries = 0;
+ *return_ss_arr = NULL;
+
+ if (is_ipaddress(name)) {
+ *return_ss_arr = TALLOC_P(ctx, struct sockaddr_storage);
+ if (!*return_ss_arr) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (!interpret_string_addr(*return_ss_arr, name, AI_NUMERICHOST)) {
+ TALLOC_FREE(*return_ss_arr);
+ return NT_STATUS_BAD_NETWORK_NAME;
+ }
+ *p_num_entries = 1;
+ return NT_STATUS_OK;
+ }
+
+ sitename = sitename_fetch(lp_realm()); /* wild guess */
+
+ status = internal_resolve_name(name, name_type, sitename,
+ &ss_list, &count,
+ lp_name_resolve_order());
+ SAFE_FREE(sitename);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* only return valid addresses for TCP connections */
+ for (i=0, num_entries = 0; i<count; i++) {
+ if (!is_zero_addr(&ss_list[i].ss) &&
+ !is_broadcast_addr(&ss_list[i].ss)) {
+ num_entries++;
+ }
+ }
+ if (num_entries == 0) {
+ SAFE_FREE(ss_list);
+ return NT_STATUS_BAD_NETWORK_NAME;
+ }
+
+ *return_ss_arr = TALLOC_ARRAY(ctx,
+ struct sockaddr_storage,
+ num_entries);
+ if (!(*return_ss_arr)) {
+ SAFE_FREE(ss_list);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0, num_entries = 0; i<count; i++) {
+ if (!is_zero_addr(&ss_list[i].ss) &&
+ !is_broadcast_addr(&ss_list[i].ss)) {
+ (*return_ss_arr)[num_entries++] = ss_list[i].ss;
+ }
+ }
+
+ status = NT_STATUS_OK;
+ *p_num_entries = num_entries;
+
+ SAFE_FREE(ss_list);
+ return NT_STATUS_OK;
+}
+
+/********************************************************
+ Find the IP address of the master browser or DMB for a workgroup.
+*********************************************************/
+
+bool find_master_ip(const char *group, struct sockaddr_storage *master_ss)
+{
+ struct ip_service *ip_list = NULL;
+ int count = 0;
+ NTSTATUS status;
+
+ if (lp_disable_netbios()) {
+ DEBUG(5,("find_master_ip(%s): netbios is disabled\n", group));
+ return false;
+ }
+
+ status = internal_resolve_name(group, 0x1D, NULL, &ip_list, &count,
+ lp_name_resolve_order());
+ if (NT_STATUS_IS_OK(status)) {
+ *master_ss = ip_list[0].ss;
+ SAFE_FREE(ip_list);
+ return true;
+ }
+
+ status = internal_resolve_name(group, 0x1B, NULL, &ip_list, &count,
+ lp_name_resolve_order());
+ if (NT_STATUS_IS_OK(status)) {
+ *master_ss = ip_list[0].ss;
+ SAFE_FREE(ip_list);
+ return true;
+ }
+
+ SAFE_FREE(ip_list);
+ return false;
+}
+
+/********************************************************
+ Get the IP address list of the primary domain controller
+ for a domain.
+*********************************************************/
+
+bool get_pdc_ip(const char *domain, struct sockaddr_storage *pss)
+{
+ struct ip_service *ip_list = NULL;
+ int count = 0;
+ NTSTATUS status = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+
+ /* Look up #1B name */
+
+ if (lp_security() == SEC_ADS) {
+ status = internal_resolve_name(domain, 0x1b, NULL, &ip_list,
+ &count, "ads");
+ }
+
+ if (!NT_STATUS_IS_OK(status) || count == 0) {
+ status = internal_resolve_name(domain, 0x1b, NULL, &ip_list,
+ &count,
+ lp_name_resolve_order());
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ }
+
+ /* if we get more than 1 IP back we have to assume it is a
+ multi-homed PDC and not a mess up */
+
+ if ( count > 1 ) {
+ DEBUG(6,("get_pdc_ip: PDC has %d IP addresses!\n", count));
+ sort_service_list(ip_list, count);
+ }
+
+ *pss = ip_list[0].ss;
+ SAFE_FREE(ip_list);
+ return true;
+}
+
+/* Private enum type for lookups. */
+
+enum dc_lookup_type { DC_NORMAL_LOOKUP, DC_ADS_ONLY, DC_KDC_ONLY };
+
+/********************************************************
+ Get the IP address list of the domain controllers for
+ a domain.
+*********************************************************/
+
+static NTSTATUS get_dc_list(const char *domain,
+ const char *sitename,
+ struct ip_service **ip_list,
+ int *count,
+ enum dc_lookup_type lookup_type,
+ bool *ordered)
+{
+ char *resolve_order = NULL;
+ char *saf_servername = NULL;
+ char *pserver = NULL;
+ const char *p;
+ char *port_str = NULL;
+ int port;
+ char *name;
+ int num_addresses = 0;
+ int local_count, i, j;
+ struct ip_service *return_iplist = NULL;
+ struct ip_service *auto_ip_list = NULL;
+ bool done_auto_lookup = false;
+ int auto_count = 0;
+ NTSTATUS status;
+ TALLOC_CTX *ctx = talloc_init("get_dc_list");
+
+ *ip_list = NULL;
+ *count = 0;
+
+ if (!ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *ordered = False;
+
+ /* if we are restricted to solely using DNS for looking
+ up a domain controller, make sure that host lookups
+ are enabled for the 'name resolve order'. If host lookups
+ are disabled and ads_only is True, then set the string to
+ NULL. */
+
+ resolve_order = talloc_strdup(ctx, lp_name_resolve_order());
+ if (!resolve_order) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ strlower_m(resolve_order);
+ if (lookup_type == DC_ADS_ONLY) {
+ if (strstr( resolve_order, "host")) {
+ resolve_order = talloc_strdup(ctx, "ads");
+
+ /* DNS SRV lookups used by the ads resolver
+ are already sorted by priority and weight */
+ *ordered = true;
+ } else {
+ resolve_order = talloc_strdup(ctx, "NULL");
+ }
+ } else if (lookup_type == DC_KDC_ONLY) {
+ /* DNS SRV lookups used by the ads/kdc resolver
+ are already sorted by priority and weight */
+ *ordered = true;
+ resolve_order = talloc_strdup(ctx, "kdc");
+ }
+ if (!resolve_order) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ /* fetch the server we have affinity for. Add the
+ 'password server' list to a search for our domain controllers */
+
+ saf_servername = saf_fetch( domain);
+
+ if (strequal(domain, lp_workgroup()) || strequal(domain, lp_realm())) {
+ pserver = talloc_asprintf(NULL, "%s, %s",
+ saf_servername ? saf_servername : "",
+ lp_passwordserver());
+ } else {
+ pserver = talloc_asprintf(NULL, "%s, *",
+ saf_servername ? saf_servername : "");
+ }
+
+ SAFE_FREE(saf_servername);
+ if (!pserver) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ /* if we are starting from scratch, just lookup DOMAIN<0x1c> */
+
+ if (!*pserver ) {
+ DEBUG(10,("get_dc_list: no preferred domain controllers.\n"));
+ status = internal_resolve_name(domain, 0x1C, sitename, ip_list,
+ count, resolve_order);
+ goto out;
+ }
+
+ DEBUG(3,("get_dc_list: preferred server list: \"%s\"\n", pserver ));
+
+ /*
+ * if '*' appears in the "password server" list then add
+ * an auto lookup to the list of manually configured
+ * DC's. If any DC is listed by name, then the list should be
+ * considered to be ordered
+ */
+
+ p = pserver;
+ while (next_token_talloc(ctx, &p, &name, LIST_SEP)) {
+ if (!done_auto_lookup && strequal(name, "*")) {
+ status = internal_resolve_name(domain, 0x1C, sitename,
+ &auto_ip_list,
+ &auto_count,
+ resolve_order);
+ if (NT_STATUS_IS_OK(status)) {
+ num_addresses += auto_count;
+ }
+ done_auto_lookup = true;
+ DEBUG(8,("Adding %d DC's from auto lookup\n",
+ auto_count));
+ } else {
+ num_addresses++;
+ }
+ }
+
+ /* if we have no addresses and haven't done the auto lookup, then
+ just return the list of DC's. Or maybe we just failed. */
+
+ if ((num_addresses == 0)) {
+ if (done_auto_lookup) {
+ DEBUG(4,("get_dc_list: no servers found\n"));
+ status = NT_STATUS_NO_LOGON_SERVERS;
+ goto out;
+ }
+ status = internal_resolve_name(domain, 0x1C, sitename, ip_list,
+ count, resolve_order);
+ goto out;
+ }
+
+ if ((return_iplist = SMB_MALLOC_ARRAY(struct ip_service,
+ num_addresses)) == NULL) {
+ DEBUG(3,("get_dc_list: malloc fail !\n"));
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ p = pserver;
+ local_count = 0;
+
+ /* fill in the return list now with real IP's */
+
+ while ((local_count<num_addresses) &&
+ next_token_talloc(ctx, &p, &name, LIST_SEP)) {
+ struct sockaddr_storage name_ss;
+
+ /* copy any addersses from the auto lookup */
+
+ if (strequal(name, "*")) {
+ for (j=0; j<auto_count; j++) {
+ char addr[INET6_ADDRSTRLEN];
+ print_sockaddr(addr,
+ sizeof(addr),
+ &auto_ip_list[j].ss);
+ /* Check for and don't copy any
+ * known bad DC IP's. */
+ if(!NT_STATUS_IS_OK(check_negative_conn_cache(
+ domain,
+ addr))) {
+ DEBUG(5,("get_dc_list: "
+ "negative entry %s removed "
+ "from DC list\n",
+ addr));
+ continue;
+ }
+ return_iplist[local_count].ss =
+ auto_ip_list[j].ss;
+ return_iplist[local_count].port =
+ auto_ip_list[j].port;
+ local_count++;
+ }
+ continue;
+ }
+
+ /* added support for address:port syntax for ads
+ * (not that I think anyone will ever run the LDAP
+ * server in an AD domain on something other than
+ * port 389 */
+
+ port = (lp_security() == SEC_ADS) ? LDAP_PORT : PORT_NONE;
+ if ((port_str=strchr(name, ':')) != NULL) {
+ *port_str = '\0';
+ port_str++;
+ port = atoi(port_str);
+ }
+
+ /* explicit lookup; resolve_name() will
+ * handle names & IP addresses */
+ if (resolve_name( name, &name_ss, 0x20 )) {
+ char addr[INET6_ADDRSTRLEN];
+ print_sockaddr(addr,
+ sizeof(addr),
+ &name_ss);
+
+ /* Check for and don't copy any known bad DC IP's. */
+ if( !NT_STATUS_IS_OK(check_negative_conn_cache(domain,
+ addr)) ) {
+ DEBUG(5,("get_dc_list: negative entry %s "
+ "removed from DC list\n",
+ name ));
+ continue;
+ }
+
+ return_iplist[local_count].ss = name_ss;
+ return_iplist[local_count].port = port;
+ local_count++;
+ *ordered = true;
+ }
+ }
+
+ /* need to remove duplicates in the list if we have any
+ explicit password servers */
+
+ if (local_count) {
+ local_count = remove_duplicate_addrs2(return_iplist,
+ local_count );
+ }
+
+ if ( DEBUGLEVEL >= 4 ) {
+ DEBUG(4,("get_dc_list: returning %d ip addresses "
+ "in an %sordered list\n",
+ local_count,
+ *ordered ? "":"un"));
+ DEBUG(4,("get_dc_list: "));
+ for ( i=0; i<local_count; i++ ) {
+ char addr[INET6_ADDRSTRLEN];
+ print_sockaddr(addr,
+ sizeof(addr),
+ &return_iplist[i].ss);
+ DEBUGADD(4,("%s:%d ", addr, return_iplist[i].port ));
+ }
+ DEBUGADD(4,("\n"));
+ }
+
+ *ip_list = return_iplist;
+ *count = local_count;
+
+ status = ( *count != 0 ? NT_STATUS_OK : NT_STATUS_NO_LOGON_SERVERS );
+
+ out:
+
+ if (!NT_STATUS_IS_OK(status)) {
+ SAFE_FREE(return_iplist);
+ *ip_list = NULL;
+ *count = 0;
+ }
+
+ SAFE_FREE(auto_ip_list);
+ TALLOC_FREE(ctx);
+ return status;
+}
+
+/*********************************************************************
+ Small wrapper function to get the DC list and sort it if neccessary.
+*********************************************************************/
+
+NTSTATUS get_sorted_dc_list( const char *domain,
+ const char *sitename,
+ struct ip_service **ip_list,
+ int *count,
+ bool ads_only )
+{
+ bool ordered;
+ NTSTATUS status;
+ enum dc_lookup_type lookup_type = DC_NORMAL_LOOKUP;
+
+ *ip_list = NULL;
+ *count = 0;
+
+ DEBUG(8,("get_sorted_dc_list: attempting lookup "
+ "for name %s (sitename %s) using [%s]\n",
+ domain,
+ sitename ? sitename : "NULL",
+ (ads_only ? "ads" : lp_name_resolve_order())));
+
+ if (ads_only) {
+ lookup_type = DC_ADS_ONLY;
+ }
+
+ status = get_dc_list(domain, sitename, ip_list,
+ count, lookup_type, &ordered);
+ if (!NT_STATUS_IS_OK(status)) {
+ SAFE_FREE(*ip_list);
+ *count = 0;
+ return status;
+ }
+
+ /* only sort if we don't already have an ordered list */
+ if (!ordered) {
+ sort_service_list(*ip_list, *count);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ Get the KDC list - re-use all the logic in get_dc_list.
+*********************************************************************/
+
+NTSTATUS get_kdc_list( const char *realm,
+ const char *sitename,
+ struct ip_service **ip_list,
+ int *count)
+{
+ bool ordered;
+ NTSTATUS status;
+
+ *count = 0;
+ *ip_list = NULL;
+
+ status = get_dc_list(realm, sitename, ip_list,
+ count, DC_KDC_ONLY, &ordered);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ SAFE_FREE(*ip_list);
+ *count = 0;
+ return status;
+ }
+
+ /* only sort if we don't already have an ordered list */
+ if ( !ordered ) {
+ sort_service_list(*ip_list, *count);
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/libsmb/namequery_dc.c b/source3/libsmb/namequery_dc.c
new file mode 100644
index 0000000000..d080f8f0b7
--- /dev/null
+++ b/source3/libsmb/namequery_dc.c
@@ -0,0 +1,247 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon connection manager
+
+ Copyright (C) Tim Potter 2001
+ Copyright (C) Andrew Bartlett 2002
+ Copyright (C) Gerald Carter 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "includes.h"
+
+/**********************************************************************
+ Is this our primary domain ?
+**********************************************************************/
+
+#ifdef HAVE_KRB5
+static bool is_our_primary_domain(const char *domain)
+{
+ int role = lp_server_role();
+
+ if ((role == ROLE_DOMAIN_MEMBER) && strequal(lp_workgroup(), domain)) {
+ return True;
+ } else if (strequal(get_global_sam_name(), domain)) {
+ return True;
+ }
+ return False;
+}
+#endif
+
+/**************************************************************************
+ Find the name and IP address for a server in the realm/domain
+ *************************************************************************/
+
+static bool ads_dc_name(const char *domain,
+ const char *realm,
+ struct sockaddr_storage *dc_ss,
+ fstring srv_name)
+{
+ ADS_STRUCT *ads;
+ char *sitename;
+ int i;
+ char addr[INET6_ADDRSTRLEN];
+
+ if (!realm && strequal(domain, lp_workgroup())) {
+ realm = lp_realm();
+ }
+
+ sitename = sitename_fetch(realm);
+
+ /* Try this 3 times then give up. */
+ for( i =0 ; i < 3; i++) {
+ ads = ads_init(realm, domain, NULL);
+ if (!ads) {
+ SAFE_FREE(sitename);
+ return False;
+ }
+
+ DEBUG(4,("ads_dc_name: domain=%s\n", domain));
+
+#ifdef HAVE_ADS
+ /* we don't need to bind, just connect */
+ ads->auth.flags |= ADS_AUTH_NO_BIND;
+ ads_connect(ads);
+#endif
+
+ if (!ads->config.realm) {
+ SAFE_FREE(sitename);
+ ads_destroy(&ads);
+ return False;
+ }
+
+ /* Now we've found a server, see if our sitename
+ has changed. If so, we need to re-do the DNS query
+ to ensure we only find servers in our site. */
+
+ if (stored_sitename_changed(realm, sitename)) {
+ SAFE_FREE(sitename);
+ sitename = sitename_fetch(realm);
+ ads_destroy(&ads);
+ /* Ensure we don't cache the DC we just connected to. */
+ namecache_delete(realm, 0x1C);
+ namecache_delete(domain, 0x1C);
+ continue;
+ }
+
+#ifdef HAVE_KRB5
+ if (is_our_primary_domain(domain) && (ads->config.flags & NBT_SERVER_KDC)) {
+ if (ads_closest_dc(ads)) {
+ /* We're going to use this KDC for this realm/domain.
+ If we are using sites, then force the krb5 libs
+ to use this KDC. */
+
+ create_local_private_krb5_conf_for_domain(realm,
+ domain,
+ sitename,
+ &ads->ldap.ss);
+ } else {
+ create_local_private_krb5_conf_for_domain(realm,
+ domain,
+ NULL,
+ &ads->ldap.ss);
+ }
+ }
+#endif
+ break;
+ }
+
+ if (i == 3) {
+ DEBUG(1,("ads_dc_name: sitename (now \"%s\") keeps changing ???\n",
+ sitename ? sitename : ""));
+ SAFE_FREE(sitename);
+ return False;
+ }
+
+ SAFE_FREE(sitename);
+
+ fstrcpy(srv_name, ads->config.ldap_server_name);
+ strupper_m(srv_name);
+#ifdef HAVE_ADS
+ *dc_ss = ads->ldap.ss;
+#else
+ zero_addr(dc_ss);
+#endif
+ ads_destroy(&ads);
+
+ print_sockaddr(addr, sizeof(addr), dc_ss);
+ DEBUG(4,("ads_dc_name: using server='%s' IP=%s\n",
+ srv_name, addr));
+
+ return True;
+}
+
+/****************************************************************************
+ Utility function to return the name of a DC. The name is guaranteed to be
+ valid since we have already done a name_status_find on it
+ ***************************************************************************/
+
+static bool rpc_dc_name(const char *domain,
+ fstring srv_name,
+ struct sockaddr_storage *ss_out)
+{
+ struct ip_service *ip_list = NULL;
+ struct sockaddr_storage dc_ss;
+ int count, i;
+ NTSTATUS result;
+ char addr[INET6_ADDRSTRLEN];
+
+ /* get a list of all domain controllers */
+
+ if (!NT_STATUS_IS_OK(get_sorted_dc_list(domain, NULL, &ip_list, &count,
+ False))) {
+ DEBUG(3, ("Could not look up dc's for domain %s\n", domain));
+ return False;
+ }
+
+ /* Remove the entry we've already failed with (should be the PDC). */
+
+ for (i = 0; i < count; i++) {
+ if (is_zero_addr(&ip_list[i].ss))
+ continue;
+
+ if (name_status_find(domain, 0x1c, 0x20, &ip_list[i].ss, srv_name)) {
+ result = check_negative_conn_cache( domain, srv_name );
+ if ( NT_STATUS_IS_OK(result) ) {
+ dc_ss = ip_list[i].ss;
+ goto done;
+ }
+ }
+ }
+
+ SAFE_FREE(ip_list);
+
+ /* No-one to talk to )-: */
+ return False; /* Boo-hoo */
+
+ done:
+ /* We have the netbios name and IP address of a domain controller.
+ Ideally we should sent a SAMLOGON request to determine whether
+ the DC is alive and kicking. If we can catch a dead DC before
+ performing a cli_connect() we can avoid a 30-second timeout. */
+
+ print_sockaddr(addr, sizeof(addr), &dc_ss);
+ DEBUG(3, ("rpc_dc_name: Returning DC %s (%s) for domain %s\n", srv_name,
+ addr, domain));
+
+ *ss_out = dc_ss;
+ SAFE_FREE(ip_list);
+
+ return True;
+}
+
+/**********************************************************************
+ wrapper around ads and rpc methods of finds DC's
+**********************************************************************/
+
+bool get_dc_name(const char *domain,
+ const char *realm,
+ fstring srv_name,
+ struct sockaddr_storage *ss_out)
+{
+ struct sockaddr_storage dc_ss;
+ bool ret;
+ bool our_domain = False;
+
+ zero_addr(&dc_ss);
+
+ ret = False;
+
+ if ( strequal(lp_workgroup(), domain) || strequal(lp_realm(), realm) )
+ our_domain = True;
+
+ /* always try to obey what the admin specified in smb.conf
+ (for the local domain) */
+
+ if ( (our_domain && lp_security()==SEC_ADS) || realm ) {
+ ret = ads_dc_name(domain, realm, &dc_ss, srv_name);
+ }
+
+ if (!domain) {
+ /* if we have only the realm we can't do anything else */
+ return False;
+ }
+
+ if (!ret) {
+ /* fall back on rpc methods if the ADS methods fail */
+ ret = rpc_dc_name(domain, srv_name, &dc_ss);
+ }
+
+ *ss_out = dc_ss;
+
+ return ret;
+}
diff --git a/source3/libsmb/nmblib.c b/source3/libsmb/nmblib.c
new file mode 100644
index 0000000000..bfe5e7b97b
--- /dev/null
+++ b/source3/libsmb/nmblib.c
@@ -0,0 +1,1400 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios library routines
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Jeremy Allison 2007
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+
+int num_good_sends = 0;
+int num_good_receives = 0;
+
+static const struct opcode_names {
+ const 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 }
+};
+
+/****************************************************************************
+ 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, const 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+= MAX_NETBIOSNAME_LEN) {
+ DEBUGADD(4, (" %s %3x char ", hdr, i));
+
+ for (j = 0; j < MAX_NETBIOSNAME_LEN; j++) {
+ unsigned char 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 < MAX_NETBIOSNAME_LEN; j++) {
+ if (i+j >= res->rdlength)
+ break;
+ DEBUGADD(4, ("%02X", (unsigned char)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,
+ bool *got_pointer,int *ret)
+{
+ int loop_count=0;
+
+ while ((ubuf[*offset] & 0xC0) == 0xC0) {
+ if (!*got_pointer)
+ (*ret) += 2;
+ (*got_pointer)=True;
+ (*offset) = ((ubuf[*offset] & ~0xC0)<<8) | ubuf[(*offset)+1];
+ if (loop_count++ == 10 ||
+ (*offset) < 0 || (*offset)>(length-2)) {
+ return False;
+ }
+ }
+ return True;
+}
+
+/*******************************************************************
+ 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 ofs,int length, struct nmb_name *name)
+{
+ int m,n=0;
+ unsigned char *ubuf = (unsigned char *)inbuf;
+ int ret = 0;
+ bool got_pointer=False;
+ int loop_count=0;
+ int offset = ofs;
+
+ if (length - offset < 2)
+ return(0);
+
+ /* handle initial name pointers */
+ 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);
+
+ memset((char *)name,'\0',sizeof(*name));
+
+ /* the "compressed" part */
+ if (!got_pointer)
+ ret += m + 2;
+ offset++;
+ while (m > 0) {
+ unsigned char c1,c2;
+ c1 = ubuf[offset++]-'A';
+ c2 = ubuf[offset++]-'A';
+ if ((c1 & 0xF0) || (c2 & 0xF0) || (n > sizeof(name->name)-1))
+ return(0);
+ name->name[n++] = (c1<<4) | c2;
+ m -= 2;
+ }
+ name->name[n] = 0;
+
+ if (n==MAX_NETBIOSNAME_LEN) {
+ /* parse out the name type, its always
+ * in the 16th byte of the name */
+ name->name_type = ((unsigned char)name->name[15]) & 0xff;
+
+ /* remove trailing spaces */
+ name->name[15] = 0;
+ n = 14;
+ while (n && name->name[n]==' ')
+ name->name[n--] = 0;
+ }
+
+ /* now the domain parts (if any) */
+ n = 0;
+ 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);
+
+ 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++];
+
+ /*
+ * Watch for malicious loops.
+ */
+ if (loop_count++ == 10)
+ return 0;
+ }
+ name->scope[n++] = 0;
+
+ return(ret);
+}
+
+/****************************************************************************
+ Put a netbios name, padding(s) and a name type into a 16 character buffer.
+ name is already in DOS charset.
+ [15 bytes name + padding][1 byte name type].
+****************************************************************************/
+
+void put_name(char *dest, const char *name, int pad, unsigned int name_type)
+{
+ size_t len = strlen(name);
+
+ memcpy(dest, name, (len < MAX_NETBIOSNAME_LEN) ?
+ len : MAX_NETBIOSNAME_LEN - 1);
+ if (len < MAX_NETBIOSNAME_LEN - 1) {
+ memset(dest + len, pad, MAX_NETBIOSNAME_LEN - 1 - len);
+ }
+ dest[MAX_NETBIOSNAME_LEN - 1] = name_type;
+}
+
+/*******************************************************************
+ Put a compressed nmb name into a buffer. Return the length of the
+ compressed name.
+
+ Compressed names are really weird. The "compression" doubles the
+ size. The idea is that it also means that compressed names conform
+ to the doman name system. See RFC1002.
+
+ If buf == NULL this is a length calculation.
+******************************************************************/
+
+static int put_nmb_name(char *buf,int offset,struct nmb_name *name)
+{
+ int ret,m;
+ nstring buf1;
+ char *p;
+
+ if (strcmp(name->name,"*") == 0) {
+ /* special case for wildcard name */
+ put_name(buf1, "*", '\0', name->name_type);
+ } else {
+ put_name(buf1, name->name, ' ', name->name_type);
+ }
+
+ if (buf) {
+ buf[offset] = 0x20;
+ }
+
+ ret = 34;
+
+ for (m=0;m<MAX_NETBIOSNAME_LEN;m++) {
+ if (buf) {
+ buf[offset+1+2*m] = 'A' + ((buf1[m]>>4)&0xF);
+ buf[offset+2+2*m] = 'A' + (buf1[m]&0xF);
+ }
+ }
+ offset += 33;
+
+ if (buf) {
+ buf[offset] = 0;
+ }
+
+ if (name->scope[0]) {
+ /* XXXX this scope handling needs testing */
+ ret += strlen(name->scope) + 1;
+ if (buf) {
+ safe_strcpy(&buf[offset+1],name->scope,
+ sizeof(name->scope));
+
+ p = &buf[offset+1];
+ 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]);
+ }
+ }
+
+ return ret;
+}
+
+/*******************************************************************
+ Useful for debugging messages.
+******************************************************************/
+
+char *nmb_namestr(const struct nmb_name *n)
+{
+ fstring name;
+ char *result;
+
+ pull_ascii_fstring(name, n->name);
+ if (!n->scope[0])
+ result = talloc_asprintf(talloc_tos(), "%s<%02x>", name,
+ n->name_type);
+ else
+ result = talloc_asprintf(talloc_tos(), "%s<%02x>.%s", name,
+ n->name_type, n->scope);
+
+ SMB_ASSERT(result != NULL);
+ return result;
+}
+
+/*******************************************************************
+ Allocate and parse some resource records.
+******************************************************************/
+
+static bool parse_alloc_res_rec(char *inbuf,int *offset,int length,
+ struct res_rec **recs, int count)
+{
+ int i;
+
+ *recs = SMB_MALLOC_ARRAY(struct res_rec, count);
+ if (!*recs)
+ return(False);
+
+ 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) {
+ SAFE_FREE(*recs);
+ return(False);
+ }
+ (*recs)[i].rr_type = RSVAL(inbuf,(*offset));
+ (*recs)[i].rr_class = RSVAL(inbuf,(*offset)+2);
+ (*recs)[i].ttl = RIVAL(inbuf,(*offset)+4);
+ (*recs)[i].rdlength = RSVAL(inbuf,(*offset)+8);
+ (*offset) += 10;
+ if ((*recs)[i].rdlength>sizeof((*recs)[i].rdata) ||
+ (*offset)+(*recs)[i].rdlength > length) {
+ SAFE_FREE(*recs);
+ return(False);
+ }
+ memcpy((*recs)[i].rdata,inbuf+(*offset),(*recs)[i].rdlength);
+ (*offset) += (*recs)[i].rdlength;
+ }
+ return(True);
+}
+
+/*******************************************************************
+ Put a resource record into a packet.
+ If buf == NULL this is a length calculation.
+******************************************************************/
+
+static int put_res_rec(char *buf,int offset,struct res_rec *recs,int count)
+{
+ int ret=0;
+ int i;
+
+ for (i=0;i<count;i++) {
+ int l = put_nmb_name(buf,offset,&recs[i].rr_name);
+ offset += l;
+ ret += l;
+ if (buf) {
+ RSSVAL(buf,offset,recs[i].rr_type);
+ RSSVAL(buf,offset+2,recs[i].rr_class);
+ RSIVAL(buf,offset+4,recs[i].ttl);
+ RSSVAL(buf,offset+8,recs[i].rdlength);
+ memcpy(buf+offset+10,recs[i].rdata,recs[i].rdlength);
+ }
+ offset += 10+recs[i].rdlength;
+ ret += 10+recs[i].rdlength;
+ }
+
+ return ret;
+}
+
+/*******************************************************************
+ Put a compressed name pointer record into a packet.
+ If buf == NULL this is a length calculation.
+******************************************************************/
+
+static int put_compressed_name_ptr(unsigned char *buf,
+ int offset,
+ struct res_rec *rec,
+ int ptr_offset)
+{
+ int ret=0;
+ if (buf) {
+ buf[offset] = (0xC0 | ((ptr_offset >> 8) & 0xFF));
+ buf[offset+1] = (ptr_offset & 0xFF);
+ }
+ offset += 2;
+ ret += 2;
+ if (buf) {
+ 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.
+
+ This is documented in section 4.4.1 of RFC1002.
+******************************************************************/
+
+static bool parse_dgram(char *inbuf,int length,struct dgram_packet *dgram)
+{
+ int offset;
+ int flags;
+
+ memset((char *)dgram,'\0',sizeof(*dgram));
+
+ if (length < 14)
+ return(False);
+
+ dgram->header.msg_type = CVAL(inbuf,0);
+ flags = CVAL(inbuf,1);
+ dgram->header.flags.node_type = (enum node_type)((flags>>2)&3);
+ if (flags & 1)
+ dgram->header.flags.more = True;
+ if (flags & 2)
+ dgram->header.flags.first = True;
+ dgram->header.dgm_id = RSVAL(inbuf,2);
+ putip((char *)&dgram->header.source_ip,inbuf+4);
+ dgram->header.source_port = RSVAL(inbuf,8);
+ dgram->header.dgm_length = RSVAL(inbuf,10);
+ dgram->header.packet_offset = RSVAL(inbuf,12);
+
+ offset = 14;
+
+ if (dgram->header.msg_type == 0x10 ||
+ dgram->header.msg_type == 0x11 ||
+ dgram->header.msg_type == 0x12) {
+ offset += parse_nmb_name(inbuf,offset,length,
+ &dgram->source_name);
+ offset += parse_nmb_name(inbuf,offset,length,
+ &dgram->dest_name);
+ }
+
+ if (offset >= length || (length-offset > sizeof(dgram->data)))
+ return(False);
+
+ dgram->datasize = length-offset;
+ memcpy(dgram->data,inbuf+offset,dgram->datasize);
+
+ /* Paranioa. Ensure the last 2 bytes in the dgram buffer are
+ zero. This should be true anyway, just enforce it for
+ paranioa sake. JRA. */
+ SMB_ASSERT(dgram->datasize <= (sizeof(dgram->data)-2));
+ memset(&dgram->data[sizeof(dgram->data)-2], '\0', 2);
+
+ return(True);
+}
+
+/*******************************************************************
+ Parse a nmb packet. Return False if the packet can't be parsed
+ or is invalid for some reason, True otherwise.
+******************************************************************/
+
+static bool parse_nmb(char *inbuf,int length,struct nmb_packet *nmb)
+{
+ int nm_flags,offset;
+
+ 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);
+ nmb->header.nm_flags.bcast = (nm_flags&1)?True:False;
+ 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.rcode = CVAL(inbuf,3) & 0xF;
+ nmb->header.qdcount = RSVAL(inbuf,4);
+ nmb->header.ancount = RSVAL(inbuf,6);
+ nmb->header.nscount = RSVAL(inbuf,8);
+ nmb->header.arcount = RSVAL(inbuf,10);
+
+ if (nmb->header.qdcount) {
+ offset = parse_nmb_name(inbuf,12,length,
+ &nmb->question.question_name);
+ if (!offset)
+ return(False);
+
+ if (length - (12+offset) < 4)
+ return(False);
+ nmb->question.question_type = RSVAL(inbuf,12+offset);
+ nmb->question.question_class = RSVAL(inbuf,12+offset+2);
+
+ offset += 12+4;
+ } else {
+ offset = 12;
+ }
+
+ /* and any resource records */
+ if (nmb->header.ancount &&
+ !parse_alloc_res_rec(inbuf,&offset,length,&nmb->answers,
+ nmb->header.ancount))
+ return(False);
+
+ if (nmb->header.nscount &&
+ !parse_alloc_res_rec(inbuf,&offset,length,&nmb->nsrecs,
+ nmb->header.nscount))
+ return(False);
+
+ if (nmb->header.arcount &&
+ !parse_alloc_res_rec(inbuf,&offset,length,
+ &nmb->additional, nmb->header.arcount))
+ return(False);
+
+ return(True);
+}
+
+/*******************************************************************
+ '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 = SMB_MALLOC_P(struct packet_struct)) == 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 = SMB_MALLOC_ARRAY(
+ struct res_rec,nmb->header.ancount)) == 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 = SMB_MALLOC_ARRAY(
+ struct res_rec, nmb->header.nscount)) == 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 = SMB_MALLOC_ARRAY(
+ struct res_rec, nmb->header.arcount)) == 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 = SMB_MALLOC_P(struct packet_struct)) == 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.
+******************************************************************/
+
+static void free_nmb_packet(struct nmb_packet *nmb)
+{
+ 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. */
+}
+
+/*******************************************************************
+ Free up any resources associated with a packet.
+******************************************************************/
+
+void free_packet(struct packet_struct *packet)
+{
+ if (packet->locked)
+ return;
+ if (packet->packet_type == NMB_PACKET)
+ free_nmb_packet(&packet->packet.nmb);
+ else if (packet->packet_type == DGRAM_PACKET)
+ free_dgram_packet(&packet->packet.dgram);
+ ZERO_STRUCTPN(packet);
+ SAFE_FREE(packet);
+}
+
+/*******************************************************************
+ Parse a packet buffer into a packet structure.
+******************************************************************/
+
+struct packet_struct *parse_packet(char *buf,int length,
+ enum packet_type packet_type,
+ struct in_addr ip,
+ int port)
+{
+ struct packet_struct *p;
+ bool ok=False;
+
+ p = SMB_MALLOC_P(struct packet_struct);
+ if (!p)
+ return(NULL);
+
+ ZERO_STRUCTP(p); /* initialize for possible padding */
+
+ p->next = NULL;
+ p->prev = NULL;
+ p->ip = ip;
+ p->port = port;
+ 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;
+ }
+
+ if (!ok) {
+ free_packet(p);
+ return NULL;
+ }
+
+ return p;
+}
+
+/*******************************************************************
+ 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;
+ struct sockaddr_storage sa;
+ struct sockaddr_in *si = (struct sockaddr_in *)&sa;
+ char buf[MAX_DGRAM_SIZE];
+ int length;
+
+ length = read_udp_v4_socket(fd,buf,sizeof(buf),&sa);
+ if (length < MIN_DGRAM_SIZE || sa.ss_family != AF_INET) {
+ return NULL;
+ }
+
+ packet = parse_packet(buf,
+ length,
+ packet_type,
+ si->sin_addr,
+ ntohs(si->sin_port));
+ if (!packet)
+ return NULL;
+
+ 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);
+}
+
+/*******************************************************************
+ Send a udp packet on a already open socket.
+******************************************************************/
+
+static bool send_udp(int fd,char *buf,int len,struct in_addr ip,int port)
+{
+ bool ret = False;
+ int i;
+ struct sockaddr_in sock_out;
+
+ /* set the address and port */
+ 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( 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.
+ */
+
+ 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",
+ inet_ntoa(ip),port,strerror(errno)));
+
+ if (ret)
+ num_good_sends++;
+
+ return(ret);
+}
+
+/*******************************************************************
+ Build a dgram packet ready for sending.
+ If buf == NULL this is a length calculation.
+******************************************************************/
+
+static int build_dgram(char *buf, size_t len, struct dgram_packet *dgram)
+{
+ unsigned char *ubuf = (unsigned char *)buf;
+ int offset=0;
+
+ /* put in the header */
+ if (buf) {
+ ubuf[0] = dgram->header.msg_type;
+ ubuf[1] = (((int)dgram->header.flags.node_type)<<2);
+ if (dgram->header.flags.more)
+ ubuf[1] |= 1;
+ if (dgram->header.flags.first)
+ ubuf[1] |= 2;
+ RSSVAL(ubuf,2,dgram->header.dgm_id);
+ putip(ubuf+4,(char *)&dgram->header.source_ip);
+ RSSVAL(ubuf,8,dgram->header.source_port);
+ RSSVAL(ubuf,12,dgram->header.packet_offset);
+ }
+
+ offset = 14;
+
+ if (dgram->header.msg_type == 0x10 ||
+ dgram->header.msg_type == 0x11 ||
+ dgram->header.msg_type == 0x12) {
+ offset += put_nmb_name((char *)ubuf,offset,&dgram->source_name);
+ offset += put_nmb_name((char *)ubuf,offset,&dgram->dest_name);
+ }
+
+ if (buf) {
+ memcpy(ubuf+offset,dgram->data,dgram->datasize);
+ }
+ offset += dgram->datasize;
+
+ /* automatically set the dgm_length
+ * NOTE: RFC1002 says the dgm_length does *not*
+ * include the fourteen-byte header. crh
+ */
+ dgram->header.dgm_length = (offset - 14);
+ if (buf) {
+ RSSVAL(ubuf,10,dgram->header.dgm_length);
+ }
+
+ return offset;
+}
+
+/*******************************************************************
+ Build a nmb name
+*******************************************************************/
+
+void make_nmb_name( struct nmb_name *n, const char *name, int type)
+{
+ fstring unix_name;
+ memset( (char *)n, '\0', sizeof(struct nmb_name) );
+ fstrcpy(unix_name, name);
+ strupper_m(unix_name);
+ push_ascii(n->name, unix_name, sizeof(n->name), STR_TERMINATE);
+ n->name_type = (unsigned int)type & 0xFF;
+ push_ascii(n->scope, global_scope(), 64, STR_TERMINATE);
+}
+
+/*******************************************************************
+ 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.
+ If buf == NULL this is a length calculation.
+******************************************************************/
+
+static int build_nmb(char *buf, size_t len, struct nmb_packet *nmb)
+{
+ unsigned char *ubuf = (unsigned char *)buf;
+ int offset=0;
+
+ if (len && len < 12) {
+ return 0;
+ }
+
+ /* put in the header */
+ if (buf) {
+ 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 &&
+ 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 &&
+ 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);
+ RSSVAL(ubuf,offset+10,nmb->header.arcount);
+ }
+
+ offset += 12;
+ if (nmb->header.qdcount) {
+ /* XXXX this doesn't handle a qdcount of > 1 */
+ if (len) {
+ /* Length check. */
+ int extra = put_nmb_name(NULL,offset,
+ &nmb->question.question_name);
+ if (offset + extra > len) {
+ return 0;
+ }
+ }
+ offset += put_nmb_name((char *)ubuf,offset,
+ &nmb->question.question_name);
+ if (buf) {
+ RSSVAL(ubuf,offset,nmb->question.question_type);
+ RSSVAL(ubuf,offset+2,nmb->question.question_class);
+ }
+ offset += 4;
+ }
+
+ if (nmb->header.ancount) {
+ if (len) {
+ /* Length check. */
+ int extra = put_res_rec(NULL,offset,nmb->answers,
+ nmb->header.ancount);
+ if (offset + extra > len) {
+ return 0;
+ }
+ }
+ offset += put_res_rec((char *)ubuf,offset,nmb->answers,
+ nmb->header.ancount);
+ }
+
+ if (nmb->header.nscount) {
+ if (len) {
+ /* Length check. */
+ int extra = put_res_rec(NULL,offset,nmb->nsrecs,
+ nmb->header.nscount);
+ if (offset + extra > len) {
+ return 0;
+ }
+ }
+ offset += put_res_rec((char *)ubuf,offset,nmb->nsrecs,
+ nmb->header.nscount);
+ }
+
+ /*
+ * 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)) {
+
+ if (len) {
+ /* Length check. */
+ int extra = put_compressed_name_ptr(NULL,offset,
+ nmb->additional,12);
+ if (offset + extra > len) {
+ return 0;
+ }
+ }
+ offset += put_compressed_name_ptr(ubuf,offset,
+ nmb->additional,12);
+ } else if (nmb->header.arcount) {
+ if (len) {
+ /* Length check. */
+ int extra = put_res_rec(NULL,offset,nmb->additional,
+ nmb->header.arcount);
+ if (offset + extra > len) {
+ return 0;
+ }
+ }
+ offset += put_res_rec((char *)ubuf,offset,nmb->additional,
+ nmb->header.arcount);
+ }
+ return offset;
+}
+
+/*******************************************************************
+ Linearise a packet.
+******************************************************************/
+
+int build_packet(char *buf, size_t buflen, struct packet_struct *p)
+{
+ int len = 0;
+
+ switch (p->packet_type) {
+ case NMB_PACKET:
+ len = build_nmb(buf,buflen,&p->packet.nmb);
+ break;
+
+ case DGRAM_PACKET:
+ len = build_dgram(buf,buflen,&p->packet.dgram);
+ break;
+ }
+
+ return len;
+}
+
+/*******************************************************************
+ Send a packet_struct.
+******************************************************************/
+
+bool send_packet(struct packet_struct *p)
+{
+ char buf[1024];
+ int len=0;
+
+ memset(buf,'\0',sizeof(buf));
+
+ len = build_packet(buf, sizeof(buf), p);
+
+ if (!len)
+ return(False);
+
+ return(send_udp(p->fd,buf,len,p->ip,p->port));
+}
+
+/****************************************************************************
+ Receive a packet with timeout on a open UDP filedescriptor.
+ The timeout is in milliseconds
+***************************************************************************/
+
+struct packet_struct *receive_packet(int fd,enum packet_type type,int t)
+{
+ 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;
+ }
+
+ if (ret == 0) /* timeout */
+ 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);
+}
+
+/****************************************************************************
+ 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,
+ const char *mailslot_name)
+{
+ struct packet_struct *p;
+
+ p = receive_packet(fd, DGRAM_PACKET, t);
+
+ if (p && match_mailslot_name(p, mailslot_name)) {
+ return p;
+ }
+ if (p)
+ free_packet(p);
+
+ /* try the unexpected packet queue */
+ return receive_unexpected(DGRAM_PACKET, 0, mailslot_name);
+}
+
+/****************************************************************************
+ See if a datagram has the right mailslot name.
+***************************************************************************/
+
+bool match_mailslot_name(struct packet_struct *p, const char *mailslot_name)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ char *buf;
+
+ buf = &dgram->data[0];
+ buf -= 4;
+
+ buf = smb_buf(buf);
+
+ if (memcmp(buf, mailslot_name, strlen(mailslot_name)+1) == 0) {
+ return True;
+ }
+
+ return False;
+}
+
+/****************************************************************************
+ Return the number of bits that match between two len character buffers
+***************************************************************************/
+
+int matching_len_bits(unsigned char *p1, unsigned char *p2, size_t len)
+{
+ size_t i, j;
+ int ret = 0;
+ for (i=0; i<len; i++) {
+ if (p1[i] != p2[i])
+ break;
+ ret += 8;
+ }
+
+ if (i==len)
+ return ret;
+
+ for (j=0; j<8; j++) {
+ if ((p1[i] & (1<<(7-j))) != (p2[i] & (1<<(7-j))))
+ break;
+ ret++;
+ }
+
+ return ret;
+}
+
+static unsigned char sort_ip[4];
+
+/****************************************************************************
+ Compare two query reply records.
+***************************************************************************/
+
+static int name_query_comp(unsigned char *p1, unsigned char *p2)
+{
+ return matching_len_bits(p2+2, sort_ip, 4) -
+ matching_len_bits(p1+2, sort_ip, 4);
+}
+
+/****************************************************************************
+ 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);
+
+ qsort(data, n, 6, QSORT_CAST name_query_comp);
+}
+
+/****************************************************************************
+ Interpret the weird netbios "name" into a unix fstring. Return the name type.
+****************************************************************************/
+
+static int name_interpret(char *in, fstring name)
+{
+ int ret;
+ int len = (*in++) / 2;
+ fstring out_string;
+ char *out = out_string;
+
+ *out=0;
+
+ if (len > 30 || len<1)
+ return(0);
+
+ while (len--) {
+ if (in[0] < 'A' || in[0] > 'P' || in[1] < 'A' || in[1] > 'P') {
+ *out = 0;
+ return(0);
+ }
+ *out = ((in[0]-'A')<<4) + (in[1]-'A');
+ in += 2;
+ out++;
+ }
+ ret = out[-1];
+ out[-1] = 0;
+
+#ifdef NETBIOS_SCOPE
+ /* Handle any scope names */
+ while(*in) {
+ *out++ = '.'; /* Scope names are separated by periods */
+ len = *(unsigned char *)in++;
+ StrnCpy(out, in, len);
+ out += len;
+ *out=0;
+ in += len;
+ }
+#endif
+ pull_ascii_fstring(name, out_string);
+
+ return(ret);
+}
+
+/****************************************************************************
+ Mangle a name into netbios format.
+ 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 len;
+ nstring buf;
+ char *p = Out;
+
+ /* Safely copy the input string, In, into buf[]. */
+ if (strcmp(In,"*") == 0)
+ put_name(buf, "*", '\0', 0x00);
+ else {
+ /* We use an fstring here as mb dos names can expend x3 when
+ going to utf8. */
+ fstring buf_unix;
+ nstring buf_dos;
+
+ pull_ascii_fstring(buf_unix, In);
+ strupper_m(buf_unix);
+
+ push_ascii_nstring(buf_dos, buf_unix);
+ put_name(buf, buf_dos, ' ', name_type);
+ }
+
+ /* Place the length of the first field into the output buffer. */
+ p[0] = 32;
+ p++;
+
+ /* Now convert the name to the rfc1001/1002 format. */
+ for( i = 0; i < MAX_NETBIOSNAME_LEN; i++ ) {
+ p[i*2] = ( (buf[i] >> 4) & 0x000F ) + 'A';
+ p[(i*2)+1] = (buf[i] & 0x000F) + 'A';
+ }
+ p += 32;
+ p[0] = '\0';
+
+ /* Add the scope string. */
+ for( i = 0, len = 0; *(global_scope()) != '\0'; 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;
+ }
+ }
+
+ return( name_len(Out) );
+}
+
+/****************************************************************************
+ Find a pointer to a netbios name.
+****************************************************************************/
+
+static char *name_ptr(char *buf,int ofs)
+{
+ unsigned char c = *(unsigned char *)(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 (into a unix string) return name type.
+****************************************************************************/
+
+int name_extract(char *buf,int ofs, fstring name)
+{
+ char *p = name_ptr(buf,ofs);
+ int d = PTR_DIFF(p,buf+ofs);
+
+ name[0] = '\0';
+ 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 */
+ unsigned char *s = (unsigned char *)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);
+}
diff --git a/source3/libsmb/nterr.c b/source3/libsmb/nterr.c
new file mode 100644
index 0000000000..fc6340342f
--- /dev/null
+++ b/source3/libsmb/nterr.c
@@ -0,0 +1,759 @@
+/*
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* NT error codes. please read nterr.h */
+
+#include "includes.h"
+
+typedef struct
+{
+ const char *nt_errstr;
+ NTSTATUS nt_errcode;
+} nt_err_code_struct;
+
+static const 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_DS_NO_MORE_RIDS", NT_STATUS_DS_NO_MORE_RIDS },
+ { "NT_STATUS_NOT_A_REPARSE_POINT", NT_STATUS_NOT_A_REPARSE_POINT },
+ { "NT_STATUS_DOWNGRADE_DETECTED", NT_STATUS_DOWNGRADE_DETECTED },
+ { "NT_STATUS_NO_MORE_ENTRIES", NT_STATUS_NO_MORE_ENTRIES },
+ { "STATUS_MORE_ENTRIES", STATUS_MORE_ENTRIES },
+ { "STATUS_SOME_UNMAPPED", STATUS_SOME_UNMAPPED },
+ { "STATUS_NO_MORE_FILES", STATUS_NO_MORE_FILES },
+ { "NT_STATUS_RPC_CANNOT_SUPPORT", NT_STATUS_RPC_CANNOT_SUPPORT },
+ { NULL, NT_STATUS(0) }
+};
+
+/* These need sorting..... */
+
+nt_err_code_struct nt_err_desc[] =
+{
+ { "Success", NT_STATUS_OK },
+ { "Undetermined error", NT_STATUS_UNSUCCESSFUL },
+ { "Access denied", NT_STATUS_ACCESS_DENIED },
+ { "Account locked out", NT_STATUS_ACCOUNT_LOCKED_OUT },
+ { "Must change password", NT_STATUS_PASSWORD_MUST_CHANGE },
+ { "Password is too short", NT_STATUS_PWD_TOO_SHORT },
+ { "Password is too recent", NT_STATUS_PWD_TOO_RECENT },
+ { "Password history conflict", NT_STATUS_PWD_HISTORY_CONFLICT },
+ { "No logon servers", NT_STATUS_NO_LOGON_SERVERS },
+ { "Improperly formed account name", NT_STATUS_INVALID_ACCOUNT_NAME },
+ { "User exists", NT_STATUS_USER_EXISTS },
+ { "No such user", NT_STATUS_NO_SUCH_USER },
+ { "Group exists", NT_STATUS_GROUP_EXISTS },
+ { "No such group", NT_STATUS_NO_SUCH_GROUP },
+ { "Member not in group", NT_STATUS_MEMBER_NOT_IN_GROUP },
+ { "Wrong Password", NT_STATUS_WRONG_PASSWORD },
+ { "Ill formed password", NT_STATUS_ILL_FORMED_PASSWORD },
+ { "Password restriction", NT_STATUS_PASSWORD_RESTRICTION },
+ { "Logon failure", NT_STATUS_LOGON_FAILURE },
+ { "Account restriction", NT_STATUS_ACCOUNT_RESTRICTION },
+ { "Invalid logon hours", NT_STATUS_INVALID_LOGON_HOURS },
+ { "Invalid workstation", NT_STATUS_INVALID_WORKSTATION },
+ { "Password expired", NT_STATUS_PASSWORD_EXPIRED },
+ { "Account disabled", NT_STATUS_ACCOUNT_DISABLED },
+ { "Memory allocation error", NT_STATUS_NO_MEMORY },
+ { "No domain controllers located", NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND },
+ { "Named pipe not available", NT_STATUS_PIPE_NOT_AVAILABLE },
+ { "Not implemented", NT_STATUS_NOT_IMPLEMENTED },
+ { "Invalid information class", NT_STATUS_INVALID_INFO_CLASS },
+ { "Information length mismatch", NT_STATUS_INFO_LENGTH_MISMATCH },
+ { "Access violation", NT_STATUS_ACCESS_VIOLATION },
+ { "Invalid handle", NT_STATUS_INVALID_HANDLE },
+ { "Invalid parameter", NT_STATUS_INVALID_PARAMETER },
+ { "No memory", NT_STATUS_NO_MEMORY },
+ { "Buffer too small", NT_STATUS_BUFFER_TOO_SMALL },
+ { "Revision mismatch", NT_STATUS_REVISION_MISMATCH },
+ { "No such logon session", NT_STATUS_NO_SUCH_LOGON_SESSION },
+ { "No such privilege", NT_STATUS_NO_SUCH_PRIVILEGE },
+ { "Procedure not found", NT_STATUS_PROCEDURE_NOT_FOUND },
+ { "Server disabled", NT_STATUS_SERVER_DISABLED },
+ { "Invalid pipe state", NT_STATUS_INVALID_PIPE_STATE },
+ { "Named pipe busy", NT_STATUS_PIPE_BUSY },
+ { "Illegal function", NT_STATUS_ILLEGAL_FUNCTION },
+ { "Named pipe dicconnected", NT_STATUS_PIPE_DISCONNECTED },
+ { "Named pipe closing", NT_STATUS_PIPE_CLOSING },
+ { "Remote host not listening", NT_STATUS_REMOTE_NOT_LISTENING },
+ { "Duplicate name on network", NT_STATUS_DUPLICATE_NAME },
+ { "Print queue is full", NT_STATUS_PRINT_QUEUE_FULL },
+ { "No print spool space available", NT_STATUS_NO_SPOOL_SPACE },
+ { "The network name cannot be found", NT_STATUS_BAD_NETWORK_NAME },
+ { "The connection was refused", NT_STATUS_CONNECTION_REFUSED },
+ { "Too many names", NT_STATUS_TOO_MANY_NAMES },
+ { "Too many sessions", NT_STATUS_TOO_MANY_SESSIONS },
+ { "Invalid server state", NT_STATUS_INVALID_SERVER_STATE },
+ { "Invalid domain state", NT_STATUS_INVALID_DOMAIN_STATE },
+ { "Invalid domain role", NT_STATUS_INVALID_DOMAIN_ROLE },
+ { "No such domain", NT_STATUS_NO_SUCH_DOMAIN },
+ { "Domain exists", NT_STATUS_DOMAIN_EXISTS },
+ { "Domain limit exceeded", NT_STATUS_DOMAIN_LIMIT_EXCEEDED },
+ { "Bad logon session state", NT_STATUS_BAD_LOGON_SESSION_STATE },
+ { "Logon session collision", NT_STATUS_LOGON_SESSION_COLLISION },
+ { "Invalid logon type", NT_STATUS_INVALID_LOGON_TYPE },
+ { "Cancelled", NT_STATUS_CANCELLED },
+ { "Invalid computer name", NT_STATUS_INVALID_COMPUTER_NAME },
+ { "Logon server conflict", NT_STATUS_LOGON_SERVER_CONFLICT },
+ { "Time difference at domain controller", NT_STATUS_TIME_DIFFERENCE_AT_DC },
+ { "Pipe broken", NT_STATUS_PIPE_BROKEN },
+ { "Registry corrupt", NT_STATUS_REGISTRY_CORRUPT },
+ { "Too many secrets", NT_STATUS_TOO_MANY_SECRETS },
+ { "Too many SIDs", NT_STATUS_TOO_MANY_SIDS },
+ { "Lanmanager cross encryption required", NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED },
+ { "Log file full", NT_STATUS_LOG_FILE_FULL },
+ { "No trusted LSA secret", NT_STATUS_NO_TRUST_LSA_SECRET },
+ { "No trusted SAM account", NT_STATUS_NO_TRUST_SAM_ACCOUNT },
+ { "Trusted domain failure", NT_STATUS_TRUSTED_DOMAIN_FAILURE },
+ { "Trust relationship failure", NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE },
+ { "Trust failure", NT_STATUS_TRUST_FAILURE },
+ { "Netlogon service not started", NT_STATUS_NETLOGON_NOT_STARTED },
+ { "Account expired", NT_STATUS_ACCOUNT_EXPIRED },
+ { "Network credential conflict", NT_STATUS_NETWORK_CREDENTIAL_CONFLICT },
+ { "Remote session limit", NT_STATUS_REMOTE_SESSION_LIMIT },
+ { "No logon interdomain trust account", NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT },
+ { "No logon workstation trust account", NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT },
+ { "No logon server trust account", NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT },
+ { "Domain trust inconsistent", NT_STATUS_DOMAIN_TRUST_INCONSISTENT },
+ { "No user session key available", NT_STATUS_NO_USER_SESSION_KEY },
+ { "User session deleted", NT_STATUS_USER_SESSION_DELETED },
+ { "Insufficient server resources", NT_STATUS_INSUFF_SERVER_RESOURCES },
+ { "Insufficient logon information", NT_STATUS_INSUFFICIENT_LOGON_INFO },
+
+ { "License quota exceeded", NT_STATUS_LICENSE_QUOTA_EXCEEDED },
+ { "No more files", STATUS_NO_MORE_FILES },
+
+ { NULL, NT_STATUS(0) }
+};
+
+/*****************************************************************************
+ Returns an NT error message. not amazingly helpful, but better than a number.
+ *****************************************************************************/
+
+const char *nt_errstr(NTSTATUS nt_code)
+{
+ int idx = 0;
+ char *result;
+
+#ifdef HAVE_LDAP
+ if (NT_STATUS_TYPE(nt_code) == NT_STATUS_TYPE_LDAP) {
+ return ldap_err2string(NT_STATUS_LDAP_CODE(nt_code));
+ }
+#endif
+
+ while (nt_errs[idx].nt_errstr != NULL) {
+ if (NT_STATUS_EQUAL(nt_errs[idx].nt_errcode, nt_code)) {
+ return nt_errs[idx].nt_errstr;
+ }
+ idx++;
+ }
+
+ result = talloc_asprintf(talloc_tos(), "NT code 0x%08x",
+ NT_STATUS_V(nt_code));
+ SMB_ASSERT(result != NULL);
+ return result;
+}
+
+/************************************************************************
+ Print friendler version fo NT error code
+ ***********************************************************************/
+
+const char *get_friendly_nt_error_msg(NTSTATUS nt_code)
+{
+ int idx = 0;
+
+ while (nt_err_desc[idx].nt_errstr != NULL) {
+ if (NT_STATUS_V(nt_err_desc[idx].nt_errcode) == NT_STATUS_V(nt_code)) {
+ return nt_err_desc[idx].nt_errstr;
+ }
+ idx++;
+ }
+
+ /* fall back to NT_STATUS_XXX string */
+
+ return nt_errstr(nt_code);
+}
+
+/*****************************************************************************
+ Returns an NT_STATUS constant as a string for inclusion in autogen C code.
+ *****************************************************************************/
+
+const char *get_nt_error_c_code(NTSTATUS nt_code)
+{
+ char *result;
+ 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++;
+ }
+
+ result = talloc_asprintf(talloc_tos(), "NT_STATUS(0x%08x)",
+ NT_STATUS_V(nt_code));
+ SMB_ASSERT(result);
+ return result;
+}
+
+/*****************************************************************************
+ 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;
+}
+
+/**
+ * Squash an NT_STATUS in line with security requirements.
+ * In an attempt to avoid giving the whole game away when users
+ * are authenticating, NT replaces both NT_STATUS_NO_SUCH_USER and
+ * NT_STATUS_WRONG_PASSWORD with NT_STATUS_LOGON_FAILURE in certain situations
+ * (session setups in particular).
+ *
+ * @param nt_status NTSTATUS input for squashing.
+ * @return the 'squashed' nt_status
+ **/
+
+NTSTATUS nt_status_squash(NTSTATUS nt_status)
+{
+ if NT_STATUS_IS_OK(nt_status) {
+ return nt_status;
+ } else if NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER) {
+ /* Match WinXP and don't give the game away */
+ return NT_STATUS_LOGON_FAILURE;
+
+ } else if NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD) {
+ /* Match WinXP and don't give the game away */
+ return NT_STATUS_LOGON_FAILURE;
+ } else {
+ return nt_status;
+ }
+}
diff --git a/source3/libsmb/ntlm_check.c b/source3/libsmb/ntlm_check.c
new file mode 100644
index 0000000000..ae10d7373d
--- /dev/null
+++ b/source3/libsmb/ntlm_check.c
@@ -0,0 +1,463 @@
+/*
+ Unix SMB/CIFS implementation.
+ Password and authentication handling
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Copyright (C) Andrew Bartlett 2001-2003
+ Copyright (C) Gerald Carter 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+/****************************************************************************
+ Core of smb password checking routine.
+****************************************************************************/
+
+static bool smb_pwd_check_ntlmv1(const DATA_BLOB *nt_response,
+ const uchar *part_passwd,
+ const DATA_BLOB *sec_blob,
+ DATA_BLOB *user_sess_key)
+{
+ /* Finish the encryption of part_passwd. */
+ uchar p24[24];
+
+ if (part_passwd == NULL) {
+ DEBUG(10,("No password set - DISALLOWING access\n"));
+ /* No password set - always false ! */
+ return False;
+ }
+
+ if (sec_blob->length != 8) {
+ DEBUG(0, ("smb_pwd_check_ntlmv1: incorrect challenge size (%lu)\n",
+ (unsigned long)sec_blob->length));
+ return False;
+ }
+
+ if (nt_response->length != 24) {
+ DEBUG(0, ("smb_pwd_check_ntlmv1: incorrect password length (%lu)\n",
+ (unsigned long)nt_response->length));
+ return False;
+ }
+
+ SMBOWFencrypt(part_passwd, sec_blob->data, p24);
+ if (user_sess_key != NULL) {
+ *user_sess_key = data_blob(NULL, 16);
+ SMBsesskeygen_ntv1(part_passwd, NULL, user_sess_key->data);
+ }
+
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("Part password (P16) was |\n"));
+ dump_data(100, part_passwd, 16);
+ DEBUGADD(100,("Password from client was |\n"));
+ dump_data(100, nt_response->data, nt_response->length);
+ DEBUGADD(100,("Given challenge was |\n"));
+ dump_data(100, sec_blob->data, sec_blob->length);
+ DEBUGADD(100,("Value from encryption was |\n"));
+ dump_data(100, p24, 24);
+#endif
+ return (memcmp(p24, nt_response->data, 24) == 0);
+}
+
+/****************************************************************************
+ Core of smb password checking routine. (NTLMv2, LMv2)
+ Note: The same code works with both NTLMv2 and LMv2.
+****************************************************************************/
+
+static bool smb_pwd_check_ntlmv2(const DATA_BLOB *ntv2_response,
+ const uchar *part_passwd,
+ const DATA_BLOB *sec_blob,
+ const char *user, const char *domain,
+ bool upper_case_domain, /* should the domain be transformed into upper case? */
+ DATA_BLOB *user_sess_key)
+{
+ /* Finish the encryption of part_passwd. */
+ uchar kr[16];
+ uchar value_from_encryption[16];
+ uchar client_response[16];
+ DATA_BLOB client_key_data;
+ bool res;
+
+ if (part_passwd == NULL) {
+ DEBUG(10,("No password set - DISALLOWING access\n"));
+ /* No password set - always False */
+ return False;
+ }
+
+ if (sec_blob->length != 8) {
+ DEBUG(0, ("smb_pwd_check_ntlmv2: incorrect challenge size (%lu)\n",
+ (unsigned long)sec_blob->length));
+ return False;
+ }
+
+ if (ntv2_response->length < 24) {
+ /* We MUST have more than 16 bytes, or the stuff below will go
+ crazy. No known implementation sends less than the 24 bytes
+ for LMv2, let alone NTLMv2. */
+ DEBUG(0, ("smb_pwd_check_ntlmv2: incorrect password length (%lu)\n",
+ (unsigned long)ntv2_response->length));
+ return False;
+ }
+
+ client_key_data = data_blob(ntv2_response->data+16, ntv2_response->length-16);
+ /*
+ todo: should we be checking this for anything? We can't for LMv2,
+ but for NTLMv2 it is meant to contain the current time etc.
+ */
+
+ memcpy(client_response, ntv2_response->data, sizeof(client_response));
+
+ if (!ntv2_owf_gen(part_passwd, user, domain, upper_case_domain, kr)) {
+ return False;
+ }
+
+ SMBOWFencrypt_ntv2(kr, sec_blob, &client_key_data, value_from_encryption);
+ if (user_sess_key != NULL) {
+ *user_sess_key = data_blob(NULL, 16);
+ SMBsesskeygen_ntv2(kr, value_from_encryption, user_sess_key->data);
+ }
+
+#if DEBUG_PASSWORD
+ DEBUG(100,("Part password (P16) was |\n"));
+ dump_data(100, part_passwd, 16);
+ DEBUGADD(100,("Password from client was |\n"));
+ dump_data(100, ntv2_response->data, ntv2_response->length);
+ DEBUGADD(100,("Variable data from client was |\n"));
+ dump_data(100, client_key_data.data, client_key_data.length);
+ DEBUGADD(100,("Given challenge was |\n"));
+ dump_data(100, sec_blob->data, sec_blob->length);
+ DEBUGADD(100,("Value from encryption was |\n"));
+ dump_data(100, value_from_encryption, 16);
+#endif
+ data_blob_clear_free(&client_key_data);
+ res = (memcmp(value_from_encryption, client_response, 16) == 0);
+ if ((!res) && (user_sess_key != NULL))
+ data_blob_clear_free(user_sess_key);
+ return res;
+}
+
+/**
+ * Check a challenge-response password against the value of the NT or
+ * LM password hash.
+ *
+ * @param mem_ctx talloc context
+ * @param challenge 8-byte challenge. If all zero, forces plaintext comparison
+ * @param nt_response 'unicode' NT response to the challenge, or unicode password
+ * @param lm_response ASCII or LANMAN response to the challenge, or password in DOS code page
+ * @param username internal Samba username, for log messages
+ * @param client_username username the client used
+ * @param client_domain domain name the client used (may be mapped)
+ * @param nt_pw MD4 unicode password from our passdb or similar
+ * @param lm_pw LANMAN ASCII password from our passdb or similar
+ * @param user_sess_key User session key
+ * @param lm_sess_key LM session key (first 8 bytes of the LM hash)
+ */
+
+NTSTATUS ntlm_password_check(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *challenge,
+ const DATA_BLOB *lm_response,
+ const DATA_BLOB *nt_response,
+ const DATA_BLOB *lm_interactive_pwd,
+ const DATA_BLOB *nt_interactive_pwd,
+ const char *username,
+ const char *client_username,
+ const char *client_domain,
+ const uint8 *lm_pw, const uint8 *nt_pw,
+ DATA_BLOB *user_sess_key,
+ DATA_BLOB *lm_sess_key)
+{
+ unsigned char zeros[8];
+
+ ZERO_STRUCT(zeros);
+
+ if (nt_pw == NULL) {
+ DEBUG(3,("ntlm_password_check: NO NT password stored for user %s.\n",
+ username));
+ }
+
+ if (nt_interactive_pwd && nt_interactive_pwd->length && nt_pw) {
+ if (nt_interactive_pwd->length != 16) {
+ DEBUG(3,("ntlm_password_check: Interactive logon: Invalid NT password length (%d) supplied for user %s\n", (int)nt_interactive_pwd->length,
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ if (memcmp(nt_interactive_pwd->data, nt_pw, 16) == 0) {
+ if (user_sess_key) {
+ *user_sess_key = data_blob(NULL, 16);
+ SMBsesskeygen_ntv1(nt_pw, NULL, user_sess_key->data);
+ }
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(3,("ntlm_password_check: Interactive logon: NT password check failed for user %s\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ } else if (lm_interactive_pwd && lm_interactive_pwd->length && lm_pw) {
+ if (lm_interactive_pwd->length != 16) {
+ DEBUG(3,("ntlm_password_check: Interactive logon: Invalid LANMAN password length (%d) supplied for user %s\n", (int)lm_interactive_pwd->length,
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ if (!lp_lanman_auth()) {
+ DEBUG(3,("ntlm_password_check: Interactive logon: only LANMAN password supplied for user %s, and LM passwords are disabled!\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ if (memcmp(lm_interactive_pwd->data, lm_pw, 16) == 0) {
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(3,("ntlm_password_check: Interactive logon: LANMAN password check failed for user %s\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ }
+
+ /* Check for cleartext netlogon. Used by Exchange 5.5. */
+ if (challenge->length == sizeof(zeros) &&
+ (memcmp(challenge->data, zeros, challenge->length) == 0 )) {
+
+ DEBUG(4,("ntlm_password_check: checking plaintext passwords for user %s\n",
+ username));
+ if (nt_pw && nt_response->length) {
+ unsigned char pwhash[16];
+ mdfour(pwhash, nt_response->data, nt_response->length);
+ if (memcmp(pwhash, nt_pw, sizeof(pwhash)) == 0) {
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(3,("ntlm_password_check: NT (Unicode) plaintext password check failed for user %s\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ } else if (!lp_lanman_auth()) {
+ DEBUG(3,("ntlm_password_check: (plaintext password check) LANMAN passwords NOT PERMITTED for user %s\n",
+ username));
+
+ } else if (lm_pw && lm_response->length) {
+ uchar dospwd[14];
+ uchar p16[16];
+ ZERO_STRUCT(dospwd);
+
+ memcpy(dospwd, lm_response->data, MIN(lm_response->length, sizeof(dospwd)));
+ /* Only the fisrt 14 chars are considered, password need not be null terminated. */
+
+ /* we *might* need to upper-case the string here */
+ E_P16((const unsigned char *)dospwd, p16);
+
+ if (memcmp(p16, lm_pw, sizeof(p16)) == 0) {
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(3,("ntlm_password_check: LANMAN (ASCII) plaintext password check failed for user %s\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ } else {
+ DEBUG(3, ("Plaintext authentication for user %s attempted, but neither NT nor LM passwords available\n", username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ }
+
+ if (nt_response->length != 0 && nt_response->length < 24) {
+ DEBUG(2,("ntlm_password_check: invalid NT password length (%lu) for user %s\n",
+ (unsigned long)nt_response->length, username));
+ }
+
+ if (nt_response->length >= 24 && nt_pw) {
+ if (nt_response->length > 24) {
+ /* We have the NT MD4 hash challenge available - see if we can
+ use it
+ */
+ DEBUG(4,("ntlm_password_check: Checking NTLMv2 password with domain [%s]\n", client_domain));
+ if (smb_pwd_check_ntlmv2( nt_response,
+ nt_pw, challenge,
+ client_username,
+ client_domain,
+ False,
+ user_sess_key)) {
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(4,("ntlm_password_check: Checking NTLMv2 password with uppercased version of domain [%s]\n", client_domain));
+ if (smb_pwd_check_ntlmv2( nt_response,
+ nt_pw, challenge,
+ client_username,
+ client_domain,
+ True,
+ user_sess_key)) {
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(4,("ntlm_password_check: Checking NTLMv2 password without a domain\n"));
+ if (smb_pwd_check_ntlmv2( nt_response,
+ nt_pw, challenge,
+ client_username,
+ "",
+ False,
+ user_sess_key)) {
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(3,("ntlm_password_check: NTLMv2 password check failed\n"));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ }
+
+ if (lp_ntlm_auth()) {
+ /* We have the NT MD4 hash challenge available - see if we can
+ use it (ie. does it exist in the smbpasswd file).
+ */
+ DEBUG(4,("ntlm_password_check: Checking NT MD4 password\n"));
+ if (smb_pwd_check_ntlmv1(nt_response,
+ nt_pw, challenge,
+ user_sess_key)) {
+ /* The LM session key for this response is not very secure,
+ so use it only if we otherwise allow LM authentication */
+
+ if (lp_lanman_auth() && lm_pw) {
+ uint8 first_8_lm_hash[16];
+ memcpy(first_8_lm_hash, lm_pw, 8);
+ memset(first_8_lm_hash + 8, '\0', 8);
+ if (lm_sess_key) {
+ *lm_sess_key = data_blob(first_8_lm_hash, 16);
+ }
+ }
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(3,("ntlm_password_check: NT MD4 password check failed for user %s\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ } else {
+ DEBUG(2,("ntlm_password_check: NTLMv1 passwords NOT PERMITTED for user %s\n",
+ username));
+ /* no return, becouse we might pick up LMv2 in the LM field */
+ }
+ }
+
+ if (lm_response->length == 0) {
+ DEBUG(3,("ntlm_password_check: NEITHER LanMan nor NT password supplied for user %s\n",
+ username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ if (lm_response->length < 24) {
+ DEBUG(2,("ntlm_password_check: invalid LanMan password length (%lu) for user %s\n",
+ (unsigned long)nt_response->length, username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ if (!lp_lanman_auth()) {
+ DEBUG(3,("ntlm_password_check: Lanman passwords NOT PERMITTED for user %s\n",
+ username));
+ } else if (!lm_pw) {
+ DEBUG(3,("ntlm_password_check: NO LanMan password set for user %s (and no NT password supplied)\n",
+ username));
+ } else {
+ DEBUG(4,("ntlm_password_check: Checking LM password\n"));
+ if (smb_pwd_check_ntlmv1(lm_response,
+ lm_pw, challenge,
+ NULL)) {
+ uint8 first_8_lm_hash[16];
+ memcpy(first_8_lm_hash, lm_pw, 8);
+ memset(first_8_lm_hash + 8, '\0', 8);
+ if (user_sess_key) {
+ *user_sess_key = data_blob(first_8_lm_hash, 16);
+ }
+
+ if (lm_sess_key) {
+ *lm_sess_key = data_blob(first_8_lm_hash, 16);
+ }
+ return NT_STATUS_OK;
+ }
+ }
+
+ if (!nt_pw) {
+ DEBUG(4,("ntlm_password_check: LM password check failed for user, no NT password %s\n",username));
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+ /* This is for 'LMv2' authentication. almost NTLMv2 but limited to 24 bytes.
+ - related to Win9X, legacy NAS pass-though authentication
+ */
+ DEBUG(4,("ntlm_password_check: Checking LMv2 password with domain %s\n", client_domain));
+ if (smb_pwd_check_ntlmv2( lm_response,
+ nt_pw, challenge,
+ client_username,
+ client_domain,
+ False,
+ NULL)) {
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(4,("ntlm_password_check: Checking LMv2 password with upper-cased version of domain %s\n", client_domain));
+ if (smb_pwd_check_ntlmv2( lm_response,
+ nt_pw, challenge,
+ client_username,
+ client_domain,
+ True,
+ NULL)) {
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(4,("ntlm_password_check: Checking LMv2 password without a domain\n"));
+ if (smb_pwd_check_ntlmv2( lm_response,
+ nt_pw, challenge,
+ client_username,
+ "",
+ False,
+ NULL)) {
+ return NT_STATUS_OK;
+ }
+
+ /* Apparently NT accepts NT responses in the LM field
+ - I think this is related to Win9X pass-though authentication
+ */
+ DEBUG(4,("ntlm_password_check: Checking NT MD4 password in LM field\n"));
+ if (lp_ntlm_auth()) {
+ if (smb_pwd_check_ntlmv1(lm_response,
+ nt_pw, challenge,
+ NULL)) {
+ /* The session key for this response is still very odd.
+ It not very secure, so use it only if we otherwise
+ allow LM authentication */
+
+ if (lp_lanman_auth() && lm_pw) {
+ uint8 first_8_lm_hash[16];
+ memcpy(first_8_lm_hash, lm_pw, 8);
+ memset(first_8_lm_hash + 8, '\0', 8);
+ if (user_sess_key) {
+ *user_sess_key = data_blob(first_8_lm_hash, 16);
+ }
+
+ if (lm_sess_key) {
+ *lm_sess_key = data_blob(first_8_lm_hash, 16);
+ }
+ }
+ return NT_STATUS_OK;
+ }
+ DEBUG(3,("ntlm_password_check: LM password, NT MD4 password in LM field and LMv2 failed for user %s\n",username));
+ } else {
+ DEBUG(3,("ntlm_password_check: LM password and LMv2 failed for user %s, and NT MD4 password in LM field not permitted\n",username));
+ }
+ return NT_STATUS_WRONG_PASSWORD;
+}
+
diff --git a/source3/libsmb/ntlmssp.c b/source3/libsmb/ntlmssp.c
new file mode 100644
index 0000000000..a0e54ce769
--- /dev/null
+++ b/source3/libsmb/ntlmssp.c
@@ -0,0 +1,1251 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 3.0
+ handle NLTMSSP, server side
+
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Andrew Bartlett 2001-2003
+ Copyright (C) Andrew Bartlett 2005 (Updated from gensec).
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+static NTSTATUS ntlmssp_client_initial(struct ntlmssp_state *ntlmssp_state,
+ DATA_BLOB reply, DATA_BLOB *next_request);
+static NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *ntlmssp_state,
+ const DATA_BLOB in, DATA_BLOB *out);
+static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_state *ntlmssp_state,
+ const DATA_BLOB reply, DATA_BLOB *next_request);
+static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state,
+ const DATA_BLOB request, DATA_BLOB *reply);
+
+/**
+ * Callbacks for NTLMSSP - for both client and server operating modes
+ *
+ */
+
+static const struct ntlmssp_callbacks {
+ enum NTLMSSP_ROLE role;
+ enum NTLM_MESSAGE_TYPE ntlmssp_command;
+ NTSTATUS (*fn)(struct ntlmssp_state *ntlmssp_state,
+ DATA_BLOB in, DATA_BLOB *out);
+} ntlmssp_callbacks[] = {
+ {NTLMSSP_CLIENT, NTLMSSP_INITIAL, ntlmssp_client_initial},
+ {NTLMSSP_SERVER, NTLMSSP_NEGOTIATE, ntlmssp_server_negotiate},
+ {NTLMSSP_CLIENT, NTLMSSP_CHALLENGE, ntlmssp_client_challenge},
+ {NTLMSSP_SERVER, NTLMSSP_AUTH, ntlmssp_server_auth},
+ {NTLMSSP_CLIENT, NTLMSSP_UNKNOWN, NULL},
+ {NTLMSSP_SERVER, NTLMSSP_UNKNOWN, NULL}
+};
+
+
+/**
+ * Print out the NTLMSSP flags for debugging
+ * @param neg_flags The flags from the packet
+ */
+
+void debug_ntlmssp_flags(uint32 neg_flags)
+{
+ DEBUG(3,("Got NTLMSSP neg_flags=0x%08x\n", neg_flags));
+
+ if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_UNICODE\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_OEM)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_OEM\n"));
+ if (neg_flags & NTLMSSP_REQUEST_TARGET)
+ DEBUGADD(4, (" NTLMSSP_REQUEST_TARGET\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_SIGN)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_SIGN\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_SEAL)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_SEAL\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_DATAGRAM_STYLE)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_DATAGRAM_STYLE\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_LM_KEY)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_LM_KEY\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_NETWARE)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NETWARE\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_NTLM)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NTLM\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_ALWAYS_SIGN\n"));
+ if (neg_flags & NTLMSSP_CHAL_ACCEPT_RESPONSE)
+ DEBUGADD(4, (" NTLMSSP_CHAL_ACCEPT_RESPONSE\n"));
+ if (neg_flags & NTLMSSP_CHAL_NON_NT_SESSION_KEY)
+ DEBUGADD(4, (" NTLMSSP_CHAL_NON_NT_SESSION_KEY\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_NTLM2)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_NTLM2\n"));
+ if (neg_flags & NTLMSSP_CHAL_TARGET_INFO)
+ DEBUGADD(4, (" NTLMSSP_CHAL_TARGET_INFO\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_VERSION)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_VERSION\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_128)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_128\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_KEY_EXCH\n"));
+ if (neg_flags & NTLMSSP_NEGOTIATE_56)
+ DEBUGADD(4, (" NTLMSSP_NEGOTIATE_56\n"));
+}
+
+/**
+ * Default challenge generation code.
+ *
+ */
+
+static const uint8 *get_challenge(const struct ntlmssp_state *ntlmssp_state)
+{
+ static uchar chal[8];
+ generate_random_buffer(chal, sizeof(chal));
+
+ return chal;
+}
+
+/**
+ * Default 'we can set the challenge to anything we like' implementation
+ *
+ */
+
+static bool may_set_challenge(const struct ntlmssp_state *ntlmssp_state)
+{
+ return True;
+}
+
+/**
+ * Default 'we can set the challenge to anything we like' implementation
+ *
+ * Does not actually do anything, as the value is always in the structure anyway.
+ *
+ */
+
+static NTSTATUS set_challenge(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *challenge)
+{
+ SMB_ASSERT(challenge->length == 8);
+ return NT_STATUS_OK;
+}
+
+/**
+ * Set a username on an NTLMSSP context - ensures it is talloc()ed
+ *
+ */
+
+NTSTATUS ntlmssp_set_username(NTLMSSP_STATE *ntlmssp_state, const char *user)
+{
+ ntlmssp_state->user = talloc_strdup(ntlmssp_state->mem_ctx, user ? user : "" );
+ if (!ntlmssp_state->user) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+}
+
+/**
+ * Store NT and LM hashes on an NTLMSSP context - ensures they are talloc()ed
+ *
+ */
+NTSTATUS ntlmssp_set_hashes(NTLMSSP_STATE *ntlmssp_state,
+ const unsigned char lm_hash[16],
+ const unsigned char nt_hash[16])
+{
+ ntlmssp_state->lm_hash = (unsigned char *)
+ TALLOC_MEMDUP(ntlmssp_state->mem_ctx, lm_hash, 16);
+ ntlmssp_state->nt_hash = (unsigned char *)
+ TALLOC_MEMDUP(ntlmssp_state->mem_ctx, nt_hash, 16);
+ if (!ntlmssp_state->lm_hash || !ntlmssp_state->nt_hash) {
+ TALLOC_FREE(ntlmssp_state->lm_hash);
+ TALLOC_FREE(ntlmssp_state->nt_hash);
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+}
+
+/**
+ * Converts a password to the hashes on an NTLMSSP context.
+ *
+ */
+NTSTATUS ntlmssp_set_password(NTLMSSP_STATE *ntlmssp_state, const char *password)
+{
+ if (!password) {
+ ntlmssp_state->lm_hash = NULL;
+ ntlmssp_state->nt_hash = NULL;
+ } else {
+ unsigned char lm_hash[16];
+ unsigned char nt_hash[16];
+
+ E_deshash(password, lm_hash);
+ E_md4hash(password, nt_hash);
+ return ntlmssp_set_hashes(ntlmssp_state, lm_hash, nt_hash);
+ }
+ return NT_STATUS_OK;
+}
+
+/**
+ * Set a domain on an NTLMSSP context - ensures it is talloc()ed
+ *
+ */
+NTSTATUS ntlmssp_set_domain(NTLMSSP_STATE *ntlmssp_state, const char *domain)
+{
+ ntlmssp_state->domain = talloc_strdup(ntlmssp_state->mem_ctx, domain ? domain : "" );
+ if (!ntlmssp_state->domain) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+}
+
+/**
+ * Set a workstation on an NTLMSSP context - ensures it is talloc()ed
+ *
+ */
+NTSTATUS ntlmssp_set_workstation(NTLMSSP_STATE *ntlmssp_state, const char *workstation)
+{
+ ntlmssp_state->workstation = talloc_strdup(ntlmssp_state->mem_ctx, workstation);
+ if (!ntlmssp_state->workstation) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+}
+
+/**
+ * Store a DATA_BLOB containing an NTLMSSP response, for use later.
+ * This copies the data blob
+ */
+
+NTSTATUS ntlmssp_store_response(NTLMSSP_STATE *ntlmssp_state,
+ DATA_BLOB response)
+{
+ ntlmssp_state->stored_response = data_blob_talloc(ntlmssp_state->mem_ctx,
+ response.data, response.length);
+ return NT_STATUS_OK;
+}
+
+/**
+ * Request features for the NTLMSSP negotiation
+ *
+ * @param ntlmssp_state NTLMSSP state
+ * @param feature_list List of space seperated features requested from NTLMSSP.
+ */
+void ntlmssp_want_feature_list(NTLMSSP_STATE *ntlmssp_state, char *feature_list)
+{
+ /*
+ * We need to set this to allow a later SetPassword
+ * via the SAMR pipe to succeed. Strange.... We could
+ * also add NTLMSSP_NEGOTIATE_SEAL here. JRA.
+ */
+ if (in_list("NTLMSSP_FEATURE_SESSION_KEY", feature_list, True)) {
+ ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN;
+ }
+ if (in_list("NTLMSSP_FEATURE_SIGN", feature_list, True)) {
+ ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN;
+ }
+ if(in_list("NTLMSSP_FEATURE_SEAL", feature_list, True)) {
+ ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SEAL;
+ }
+}
+
+/**
+ * Request a feature for the NTLMSSP negotiation
+ *
+ * @param ntlmssp_state NTLMSSP state
+ * @param feature Bit flag specifying the requested feature
+ */
+void ntlmssp_want_feature(NTLMSSP_STATE *ntlmssp_state, uint32 feature)
+{
+ /* As per JRA's comment above */
+ if (feature & NTLMSSP_FEATURE_SESSION_KEY) {
+ ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN;
+ }
+ if (feature & NTLMSSP_FEATURE_SIGN) {
+ ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN;
+ }
+ if (feature & NTLMSSP_FEATURE_SEAL) {
+ ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_SEAL;
+ }
+}
+
+/**
+ * Next state function for the NTLMSSP state machine
+ *
+ * @param ntlmssp_state NTLMSSP State
+ * @param in The packet in from the NTLMSSP partner, as a DATA_BLOB
+ * @param out The reply, as an allocated DATA_BLOB, caller to free.
+ * @return Errors, NT_STATUS_MORE_PROCESSING_REQUIRED or NT_STATUS_OK.
+ */
+
+NTSTATUS ntlmssp_update(NTLMSSP_STATE *ntlmssp_state,
+ const DATA_BLOB in, DATA_BLOB *out)
+{
+ DATA_BLOB input;
+ uint32 ntlmssp_command;
+ int i;
+
+ if (ntlmssp_state->expected_state == NTLMSSP_DONE) {
+ /* Called update after negotiations finished. */
+ DEBUG(1, ("Called NTLMSSP after state machine was 'done'\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ *out = data_blob_null;
+
+ if (!in.length && ntlmssp_state->stored_response.length) {
+ input = ntlmssp_state->stored_response;
+
+ /* we only want to read the stored response once - overwrite it */
+ ntlmssp_state->stored_response = data_blob_null;
+ } else {
+ input = in;
+ }
+
+ if (!input.length) {
+ switch (ntlmssp_state->role) {
+ case NTLMSSP_CLIENT:
+ ntlmssp_command = NTLMSSP_INITIAL;
+ break;
+ case NTLMSSP_SERVER:
+ /* 'datagram' mode - no neg packet */
+ ntlmssp_command = NTLMSSP_NEGOTIATE;
+ break;
+ }
+ } else {
+ if (!msrpc_parse(&input, "Cd",
+ "NTLMSSP",
+ &ntlmssp_command)) {
+ DEBUG(1, ("Failed to parse NTLMSSP packet, could not extract NTLMSSP command\n"));
+ dump_data(2, input.data, input.length);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ if (ntlmssp_command != ntlmssp_state->expected_state) {
+ DEBUG(1, ("got NTLMSSP command %u, expected %u\n", ntlmssp_command, ntlmssp_state->expected_state));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ for (i=0; ntlmssp_callbacks[i].fn; i++) {
+ if (ntlmssp_callbacks[i].role == ntlmssp_state->role
+ && ntlmssp_callbacks[i].ntlmssp_command == ntlmssp_command) {
+ return ntlmssp_callbacks[i].fn(ntlmssp_state, input, out);
+ }
+ }
+
+ DEBUG(1, ("failed to find NTLMSSP callback for NTLMSSP mode %u, command %u\n",
+ ntlmssp_state->role, ntlmssp_command));
+
+ return NT_STATUS_INVALID_PARAMETER;
+}
+
+/**
+ * End an NTLMSSP state machine
+ *
+ * @param ntlmssp_state NTLMSSP State, free()ed by this function
+ */
+
+void ntlmssp_end(NTLMSSP_STATE **ntlmssp_state)
+{
+ TALLOC_CTX *mem_ctx = (*ntlmssp_state)->mem_ctx;
+
+ (*ntlmssp_state)->ref_count--;
+
+ if ((*ntlmssp_state)->ref_count == 0) {
+ data_blob_free(&(*ntlmssp_state)->chal);
+ data_blob_free(&(*ntlmssp_state)->lm_resp);
+ data_blob_free(&(*ntlmssp_state)->nt_resp);
+
+ talloc_destroy(mem_ctx);
+ }
+
+ *ntlmssp_state = NULL;
+ return;
+}
+
+/**
+ * Determine correct target name flags for reply, given server role
+ * and negotiated flags
+ *
+ * @param ntlmssp_state NTLMSSP State
+ * @param neg_flags The flags from the packet
+ * @param chal_flags The flags to be set in the reply packet
+ * @return The 'target name' string.
+ */
+
+static const char *ntlmssp_target_name(struct ntlmssp_state *ntlmssp_state,
+ uint32 neg_flags, uint32 *chal_flags)
+{
+ if (neg_flags & NTLMSSP_REQUEST_TARGET) {
+ *chal_flags |= NTLMSSP_CHAL_TARGET_INFO;
+ *chal_flags |= NTLMSSP_REQUEST_TARGET;
+ if (ntlmssp_state->server_role == ROLE_STANDALONE) {
+ *chal_flags |= NTLMSSP_TARGET_TYPE_SERVER;
+ return ntlmssp_state->get_global_myname();
+ } else {
+ *chal_flags |= NTLMSSP_TARGET_TYPE_DOMAIN;
+ return ntlmssp_state->get_domain();
+ };
+ } else {
+ return "";
+ }
+}
+
+static void ntlmssp_handle_neg_flags(struct ntlmssp_state *ntlmssp_state,
+ uint32 neg_flags, bool allow_lm) {
+ if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) {
+ ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE;
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_OEM;
+ ntlmssp_state->unicode = True;
+ } else {
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_UNICODE;
+ ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_OEM;
+ ntlmssp_state->unicode = False;
+ }
+
+ if ((neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) && allow_lm) {
+ /* other end forcing us to use LM */
+ ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_LM_KEY;
+ ntlmssp_state->use_ntlmv2 = False;
+ } else {
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
+ }
+
+ if (!(neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN)) {
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
+ }
+
+ if (!(neg_flags & NTLMSSP_NEGOTIATE_NTLM2)) {
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
+ }
+
+ if (!(neg_flags & NTLMSSP_NEGOTIATE_128)) {
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_128;
+ }
+
+ if (!(neg_flags & NTLMSSP_NEGOTIATE_56)) {
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_56;
+ }
+
+ if (!(neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH)) {
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_KEY_EXCH;
+ }
+
+ if (!(neg_flags & NTLMSSP_NEGOTIATE_SIGN)) {
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SIGN;
+ }
+
+ if (!(neg_flags & NTLMSSP_NEGOTIATE_SEAL)) {
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_SEAL;
+ }
+
+ /* Woop Woop - unknown flag for Windows compatibility...
+ What does this really do ? JRA. */
+ if (!(neg_flags & NTLMSSP_NEGOTIATE_VERSION)) {
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_VERSION;
+ }
+
+ if ((neg_flags & NTLMSSP_REQUEST_TARGET)) {
+ ntlmssp_state->neg_flags |= NTLMSSP_REQUEST_TARGET;
+ }
+}
+
+/**
+ Weaken NTLMSSP keys to cope with down-level clients and servers.
+
+ We probably should have some parameters to control this, but as
+ it only occours for LM_KEY connections, and this is controlled
+ by the client lanman auth/lanman auth parameters, it isn't too bad.
+*/
+
+DATA_BLOB ntlmssp_weaken_keys(NTLMSSP_STATE *ntlmssp_state, TALLOC_CTX *mem_ctx)
+{
+ DATA_BLOB weakened_key = data_blob_talloc(mem_ctx,
+ ntlmssp_state->session_key.data,
+ ntlmssp_state->session_key.length);
+
+ /* Nothing to weaken. We certainly don't want to 'extend' the length... */
+ if (weakened_key.length < 16) {
+ /* perhaps there was no key? */
+ return weakened_key;
+ }
+
+ /* Key weakening not performed on the master key for NTLM2
+ and does not occour for NTLM1. Therefore we only need
+ to do this for the LM_KEY.
+ */
+
+ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) {
+ /* LM key doesn't support 128 bit crypto, so this is
+ * the best we can do. If you negotiate 128 bit, but
+ * not 56, you end up with 40 bit... */
+ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_56) {
+ weakened_key.data[7] = 0xa0;
+ } else { /* forty bits */
+ weakened_key.data[5] = 0xe5;
+ weakened_key.data[6] = 0x38;
+ weakened_key.data[7] = 0xb0;
+ }
+ weakened_key.length = 8;
+ }
+ return weakened_key;
+}
+
+/**
+ * Next state function for the Negotiate packet
+ *
+ * @param ntlmssp_state NTLMSSP State
+ * @param request The request, as a DATA_BLOB
+ * @param request The reply, as an allocated DATA_BLOB, caller to free.
+ * @return Errors or MORE_PROCESSING_REQUIRED if a reply is sent.
+ */
+
+static NTSTATUS ntlmssp_server_negotiate(struct ntlmssp_state *ntlmssp_state,
+ const DATA_BLOB request, DATA_BLOB *reply)
+{
+ DATA_BLOB struct_blob;
+ const char *dnsname;
+ char *dnsdomname = NULL;
+ uint32 neg_flags = 0;
+ uint32 ntlmssp_command, chal_flags;
+ const uint8 *cryptkey;
+ const char *target_name;
+
+ /* parse the NTLMSSP packet */
+#if 0
+ file_save("ntlmssp_negotiate.dat", request.data, request.length);
+#endif
+
+ if (request.length) {
+ if ((request.length < 16) || !msrpc_parse(&request, "Cdd",
+ "NTLMSSP",
+ &ntlmssp_command,
+ &neg_flags)) {
+ DEBUG(1, ("ntlmssp_server_negotiate: failed to parse NTLMSSP Negotiate of length %u\n",
+ (unsigned int)request.length));
+ dump_data(2, request.data, request.length);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ debug_ntlmssp_flags(neg_flags);
+ }
+
+ ntlmssp_handle_neg_flags(ntlmssp_state, neg_flags, lp_lanman_auth());
+
+ /* Ask our caller what challenge they would like in the packet */
+ cryptkey = ntlmssp_state->get_challenge(ntlmssp_state);
+
+ /* Check if we may set the challenge */
+ if (!ntlmssp_state->may_set_challenge(ntlmssp_state)) {
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
+ }
+
+ /* The flags we send back are not just the negotiated flags,
+ * they are also 'what is in this packet'. Therfore, we
+ * operate on 'chal_flags' from here on
+ */
+
+ chal_flags = ntlmssp_state->neg_flags;
+
+ /* get the right name to fill in as 'target' */
+ target_name = ntlmssp_target_name(ntlmssp_state,
+ neg_flags, &chal_flags);
+ if (target_name == NULL)
+ return NT_STATUS_INVALID_PARAMETER;
+
+ ntlmssp_state->chal = data_blob_talloc(ntlmssp_state->mem_ctx, cryptkey, 8);
+ ntlmssp_state->internal_chal = data_blob_talloc(ntlmssp_state->mem_ctx, cryptkey, 8);
+
+ /* This should be a 'netbios domain -> DNS domain' mapping */
+ dnsdomname = get_mydnsdomname(ntlmssp_state->mem_ctx);
+ if (!dnsdomname) {
+ dnsdomname = talloc_strdup(ntlmssp_state->mem_ctx, "");
+ }
+ if (!dnsdomname) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ strlower_m(dnsdomname);
+
+ dnsname = get_mydnsfullname();
+ if (!dnsname) {
+ dnsname = "";
+ }
+
+ /* This creates the 'blob' of names that appears at the end of the packet */
+ if (chal_flags & NTLMSSP_CHAL_TARGET_INFO)
+ {
+ msrpc_gen(&struct_blob, "aaaaa",
+ NTLMSSP_NAME_TYPE_DOMAIN, target_name,
+ NTLMSSP_NAME_TYPE_SERVER, ntlmssp_state->get_global_myname(),
+ NTLMSSP_NAME_TYPE_DOMAIN_DNS, dnsdomname,
+ NTLMSSP_NAME_TYPE_SERVER_DNS, dnsname,
+ 0, "");
+ } else {
+ struct_blob = data_blob_null;
+ }
+
+ {
+ /* Marshel the packet in the right format, be it unicode or ASCII */
+ const char *gen_string;
+ if (ntlmssp_state->unicode) {
+ gen_string = "CdUdbddB";
+ } else {
+ gen_string = "CdAdbddB";
+ }
+
+ msrpc_gen(reply, gen_string,
+ "NTLMSSP",
+ NTLMSSP_CHALLENGE,
+ target_name,
+ chal_flags,
+ cryptkey, 8,
+ 0, 0,
+ struct_blob.data, struct_blob.length);
+ }
+
+ data_blob_free(&struct_blob);
+
+ ntlmssp_state->expected_state = NTLMSSP_AUTH;
+
+ return NT_STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+/**
+ * Next state function for the Authenticate packet
+ *
+ * @param ntlmssp_state NTLMSSP State
+ * @param request The request, as a DATA_BLOB
+ * @param request The reply, as an allocated DATA_BLOB, caller to free.
+ * @return Errors or NT_STATUS_OK.
+ */
+
+static NTSTATUS ntlmssp_server_auth(struct ntlmssp_state *ntlmssp_state,
+ const DATA_BLOB request, DATA_BLOB *reply)
+{
+ DATA_BLOB encrypted_session_key = data_blob_null;
+ DATA_BLOB user_session_key = data_blob_null;
+ DATA_BLOB lm_session_key = data_blob_null;
+ DATA_BLOB session_key = data_blob_null;
+ uint32 ntlmssp_command, auth_flags;
+ NTSTATUS nt_status = NT_STATUS_OK;
+
+ /* used by NTLM2 */
+ bool doing_ntlm2 = False;
+
+ uchar session_nonce[16];
+ uchar session_nonce_hash[16];
+
+ const char *parse_string;
+ char *domain = NULL;
+ char *user = NULL;
+ char *workstation = NULL;
+
+ /* parse the NTLMSSP packet */
+ *reply = data_blob_null;
+
+#if 0
+ file_save("ntlmssp_auth.dat", request.data, request.length);
+#endif
+
+ if (ntlmssp_state->unicode) {
+ parse_string = "CdBBUUUBd";
+ } else {
+ parse_string = "CdBBAAABd";
+ }
+
+ data_blob_free(&ntlmssp_state->lm_resp);
+ data_blob_free(&ntlmssp_state->nt_resp);
+
+ ntlmssp_state->user = NULL;
+ ntlmssp_state->domain = NULL;
+ ntlmssp_state->workstation = NULL;
+
+ /* now the NTLMSSP encoded auth hashes */
+ if (!msrpc_parse(&request, parse_string,
+ "NTLMSSP",
+ &ntlmssp_command,
+ &ntlmssp_state->lm_resp,
+ &ntlmssp_state->nt_resp,
+ &domain,
+ &user,
+ &workstation,
+ &encrypted_session_key,
+ &auth_flags)) {
+ SAFE_FREE(domain);
+ SAFE_FREE(user);
+ SAFE_FREE(workstation);
+ data_blob_free(&encrypted_session_key);
+ auth_flags = 0;
+
+ /* Try again with a shorter string (Win9X truncates this packet) */
+ if (ntlmssp_state->unicode) {
+ parse_string = "CdBBUUU";
+ } else {
+ parse_string = "CdBBAAA";
+ }
+
+ /* now the NTLMSSP encoded auth hashes */
+ if (!msrpc_parse(&request, parse_string,
+ "NTLMSSP",
+ &ntlmssp_command,
+ &ntlmssp_state->lm_resp,
+ &ntlmssp_state->nt_resp,
+ &domain,
+ &user,
+ &workstation)) {
+ DEBUG(1, ("ntlmssp_server_auth: failed to parse NTLMSSP (tried both formats):\n"));
+ dump_data(2, request.data, request.length);
+ SAFE_FREE(domain);
+ SAFE_FREE(user);
+ SAFE_FREE(workstation);
+
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ if (auth_flags)
+ ntlmssp_handle_neg_flags(ntlmssp_state, auth_flags, lp_lanman_auth());
+
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_domain(ntlmssp_state, domain))) {
+ SAFE_FREE(domain);
+ SAFE_FREE(user);
+ SAFE_FREE(workstation);
+ data_blob_free(&encrypted_session_key);
+ return nt_status;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_username(ntlmssp_state, user))) {
+ SAFE_FREE(domain);
+ SAFE_FREE(user);
+ SAFE_FREE(workstation);
+ data_blob_free(&encrypted_session_key);
+ return nt_status;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_set_workstation(ntlmssp_state, workstation))) {
+ SAFE_FREE(domain);
+ SAFE_FREE(user);
+ SAFE_FREE(workstation);
+ data_blob_free(&encrypted_session_key);
+ return nt_status;
+ }
+
+ SAFE_FREE(domain);
+ SAFE_FREE(user);
+ SAFE_FREE(workstation);
+
+ DEBUG(3,("Got user=[%s] domain=[%s] workstation=[%s] len1=%lu len2=%lu\n",
+ ntlmssp_state->user, ntlmssp_state->domain, ntlmssp_state->workstation, (unsigned long)ntlmssp_state->lm_resp.length, (unsigned long)ntlmssp_state->nt_resp.length));
+
+#if 0
+ file_save("nthash1.dat", &ntlmssp_state->nt_resp.data, &ntlmssp_state->nt_resp.length);
+ file_save("lmhash1.dat", &ntlmssp_state->lm_resp.data, &ntlmssp_state->lm_resp.length);
+#endif
+
+ /* NTLM2 uses a 'challenge' that is made of up both the server challenge, and a
+ client challenge
+
+ However, the NTLM2 flag may still be set for the real NTLMv2 logins, be careful.
+ */
+ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
+ if (ntlmssp_state->nt_resp.length == 24 && ntlmssp_state->lm_resp.length == 24) {
+ struct MD5Context md5_session_nonce_ctx;
+ SMB_ASSERT(ntlmssp_state->internal_chal.data && ntlmssp_state->internal_chal.length == 8);
+
+ doing_ntlm2 = True;
+
+ memcpy(session_nonce, ntlmssp_state->internal_chal.data, 8);
+ memcpy(&session_nonce[8], ntlmssp_state->lm_resp.data, 8);
+
+ MD5Init(&md5_session_nonce_ctx);
+ MD5Update(&md5_session_nonce_ctx, session_nonce, 16);
+ MD5Final(session_nonce_hash, &md5_session_nonce_ctx);
+
+ ntlmssp_state->chal = data_blob_talloc(ntlmssp_state->mem_ctx, session_nonce_hash, 8);
+
+ /* LM response is no longer useful */
+ data_blob_free(&ntlmssp_state->lm_resp);
+
+ /* We changed the effective challenge - set it */
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_state->set_challenge(ntlmssp_state, &ntlmssp_state->chal))) {
+ data_blob_free(&encrypted_session_key);
+ return nt_status;
+ }
+
+ /* LM Key is incompatible. */
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
+ }
+ }
+
+ /*
+ * Note we don't check here for NTLMv2 auth settings. If NTLMv2 auth
+ * is required (by "ntlm auth = no" and "lm auth = no" being set in the
+ * smb.conf file) and no NTLMv2 response was sent then the password check
+ * will fail here. JRA.
+ */
+
+ /* Finally, actually ask if the password is OK */
+
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_state->check_password(ntlmssp_state,
+ &user_session_key, &lm_session_key))) {
+ data_blob_free(&encrypted_session_key);
+ return nt_status;
+ }
+
+ dump_data_pw("NT session key:\n", user_session_key.data, user_session_key.length);
+ dump_data_pw("LM first-8:\n", lm_session_key.data, lm_session_key.length);
+
+ /* Handle the different session key derivation for NTLM2 */
+ if (doing_ntlm2) {
+ if (user_session_key.data && user_session_key.length == 16) {
+ session_key = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 16);
+ hmac_md5(user_session_key.data, session_nonce,
+ sizeof(session_nonce), session_key.data);
+ DEBUG(10,("ntlmssp_server_auth: Created NTLM2 session key.\n"));
+ dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length);
+
+ } else {
+ DEBUG(10,("ntlmssp_server_auth: Failed to create NTLM2 session key.\n"));
+ session_key = data_blob_null;
+ }
+ } else if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) {
+ if (lm_session_key.data && lm_session_key.length >= 8) {
+ if (ntlmssp_state->lm_resp.data && ntlmssp_state->lm_resp.length == 24) {
+ session_key = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 16);
+ if (session_key.data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ SMBsesskeygen_lm_sess_key(lm_session_key.data, ntlmssp_state->lm_resp.data,
+ session_key.data);
+ DEBUG(10,("ntlmssp_server_auth: Created NTLM session key.\n"));
+ } else {
+ uint8 zeros[24];
+ ZERO_STRUCT(zeros);
+ session_key = data_blob_talloc(
+ ntlmssp_state->mem_ctx, NULL, 16);
+ if (session_key.data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ SMBsesskeygen_lm_sess_key(
+ lm_session_key.data, zeros,
+ session_key.data);
+ }
+ dump_data_pw("LM session key:\n", session_key.data,
+ session_key.length);
+ } else {
+ DEBUG(10,("ntlmssp_server_auth: Failed to create NTLM session key.\n"));
+ session_key = data_blob_null;
+ }
+ } else if (user_session_key.data) {
+ session_key = user_session_key;
+ DEBUG(10,("ntlmssp_server_auth: Using unmodified nt session key.\n"));
+ dump_data_pw("unmodified session key:\n", session_key.data, session_key.length);
+ } else if (lm_session_key.data) {
+ session_key = lm_session_key;
+ DEBUG(10,("ntlmssp_server_auth: Using unmodified lm session key.\n"));
+ dump_data_pw("unmodified session key:\n", session_key.data, session_key.length);
+ } else {
+ DEBUG(10,("ntlmssp_server_auth: Failed to create unmodified session key.\n"));
+ session_key = data_blob_null;
+ }
+
+ /* With KEY_EXCH, the client supplies the proposed session key,
+ but encrypts it with the long-term key */
+ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
+ if (!encrypted_session_key.data || encrypted_session_key.length != 16) {
+ data_blob_free(&encrypted_session_key);
+ DEBUG(1, ("Client-supplied KEY_EXCH session key was of invalid length (%u)!\n",
+ (unsigned int)encrypted_session_key.length));
+ return NT_STATUS_INVALID_PARAMETER;
+ } else if (!session_key.data || session_key.length != 16) {
+ DEBUG(5, ("server session key is invalid (len == %u), cannot do KEY_EXCH!\n",
+ (unsigned int)session_key.length));
+ ntlmssp_state->session_key = session_key;
+ } else {
+ dump_data_pw("KEY_EXCH session key (enc):\n", encrypted_session_key.data, encrypted_session_key.length);
+ SamOEMhash(encrypted_session_key.data,
+ session_key.data,
+ encrypted_session_key.length);
+ ntlmssp_state->session_key = data_blob_talloc(ntlmssp_state->mem_ctx,
+ encrypted_session_key.data,
+ encrypted_session_key.length);
+ dump_data_pw("KEY_EXCH session key:\n", encrypted_session_key.data,
+ encrypted_session_key.length);
+ }
+ } else {
+ ntlmssp_state->session_key = session_key;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ ntlmssp_state->session_key = data_blob_null;
+ } else if (ntlmssp_state->session_key.length) {
+ nt_status = ntlmssp_sign_init(ntlmssp_state);
+ }
+
+ data_blob_free(&encrypted_session_key);
+
+ /* Only one authentication allowed per server state. */
+ ntlmssp_state->expected_state = NTLMSSP_DONE;
+
+ return nt_status;
+}
+
+/**
+ * Create an NTLMSSP state machine
+ *
+ * @param ntlmssp_state NTLMSSP State, allocated by this function
+ */
+
+NTSTATUS ntlmssp_server_start(NTLMSSP_STATE **ntlmssp_state)
+{
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_init("NTLMSSP context");
+
+ *ntlmssp_state = TALLOC_ZERO_P(mem_ctx, NTLMSSP_STATE);
+ if (!*ntlmssp_state) {
+ DEBUG(0,("ntlmssp_server_start: talloc failed!\n"));
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*ntlmssp_state)->role = NTLMSSP_SERVER;
+
+ (*ntlmssp_state)->mem_ctx = mem_ctx;
+ (*ntlmssp_state)->get_challenge = get_challenge;
+ (*ntlmssp_state)->set_challenge = set_challenge;
+ (*ntlmssp_state)->may_set_challenge = may_set_challenge;
+
+ (*ntlmssp_state)->get_global_myname = global_myname;
+ (*ntlmssp_state)->get_domain = lp_workgroup;
+ (*ntlmssp_state)->server_role = ROLE_DOMAIN_MEMBER; /* a good default */
+
+ (*ntlmssp_state)->expected_state = NTLMSSP_NEGOTIATE;
+
+ (*ntlmssp_state)->ref_count = 1;
+
+ (*ntlmssp_state)->neg_flags =
+ NTLMSSP_NEGOTIATE_128 |
+ NTLMSSP_NEGOTIATE_56 |
+ NTLMSSP_NEGOTIATE_VERSION |
+ NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
+ NTLMSSP_NEGOTIATE_NTLM |
+ NTLMSSP_NEGOTIATE_NTLM2 |
+ NTLMSSP_NEGOTIATE_KEY_EXCH |
+ NTLMSSP_NEGOTIATE_SIGN |
+ NTLMSSP_NEGOTIATE_SEAL;
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ Client side NTLMSSP
+*********************************************************************/
+
+/**
+ * Next state function for the Initial packet
+ *
+ * @param ntlmssp_state NTLMSSP State
+ * @param request The request, as a DATA_BLOB. reply.data must be NULL
+ * @param request The reply, as an allocated DATA_BLOB, caller to free.
+ * @return Errors or NT_STATUS_OK.
+ */
+
+static NTSTATUS ntlmssp_client_initial(struct ntlmssp_state *ntlmssp_state,
+ DATA_BLOB reply, DATA_BLOB *next_request)
+{
+ if (ntlmssp_state->unicode) {
+ ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_UNICODE;
+ } else {
+ ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_OEM;
+ }
+
+ if (ntlmssp_state->use_ntlmv2) {
+ ntlmssp_state->neg_flags |= NTLMSSP_NEGOTIATE_NTLM2;
+ }
+
+ /* generate the ntlmssp negotiate packet */
+ msrpc_gen(next_request, "CddAA",
+ "NTLMSSP",
+ NTLMSSP_NEGOTIATE,
+ ntlmssp_state->neg_flags,
+ ntlmssp_state->get_domain(),
+ ntlmssp_state->get_global_myname());
+
+ ntlmssp_state->expected_state = NTLMSSP_CHALLENGE;
+
+ return NT_STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+/**
+ * Next state function for the Challenge Packet. Generate an auth packet.
+ *
+ * @param ntlmssp_state NTLMSSP State
+ * @param request The request, as a DATA_BLOB. reply.data must be NULL
+ * @param request The reply, as an allocated DATA_BLOB, caller to free.
+ * @return Errors or NT_STATUS_OK.
+ */
+
+static NTSTATUS ntlmssp_client_challenge(struct ntlmssp_state *ntlmssp_state,
+ const DATA_BLOB reply, DATA_BLOB *next_request)
+{
+ uint32 chal_flags, ntlmssp_command, unkn1, unkn2;
+ DATA_BLOB server_domain_blob;
+ DATA_BLOB challenge_blob;
+ DATA_BLOB struct_blob = data_blob_null;
+ char *server_domain;
+ const char *chal_parse_string;
+ const char *auth_gen_string;
+ DATA_BLOB lm_response = data_blob_null;
+ DATA_BLOB nt_response = data_blob_null;
+ DATA_BLOB session_key = data_blob_null;
+ DATA_BLOB encrypted_session_key = data_blob_null;
+ NTSTATUS nt_status = NT_STATUS_OK;
+
+ if (!msrpc_parse(&reply, "CdBd",
+ "NTLMSSP",
+ &ntlmssp_command,
+ &server_domain_blob,
+ &chal_flags)) {
+ DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#1)\n"));
+ dump_data(2, reply.data, reply.length);
+
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ data_blob_free(&server_domain_blob);
+
+ DEBUG(3, ("Got challenge flags:\n"));
+ debug_ntlmssp_flags(chal_flags);
+
+ ntlmssp_handle_neg_flags(ntlmssp_state, chal_flags, lp_client_lanman_auth());
+
+ if (ntlmssp_state->unicode) {
+ if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) {
+ chal_parse_string = "CdUdbddB";
+ } else {
+ chal_parse_string = "CdUdbdd";
+ }
+ auth_gen_string = "CdBBUUUBd";
+ } else {
+ if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) {
+ chal_parse_string = "CdAdbddB";
+ } else {
+ chal_parse_string = "CdAdbdd";
+ }
+
+ auth_gen_string = "CdBBAAABd";
+ }
+
+ DEBUG(3, ("NTLMSSP: Set final flags:\n"));
+ debug_ntlmssp_flags(ntlmssp_state->neg_flags);
+
+ if (!msrpc_parse(&reply, chal_parse_string,
+ "NTLMSSP",
+ &ntlmssp_command,
+ &server_domain,
+ &chal_flags,
+ &challenge_blob, 8,
+ &unkn1, &unkn2,
+ &struct_blob)) {
+ DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#2)\n"));
+ dump_data(2, reply.data, reply.length);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ntlmssp_state->server_domain = talloc_strdup(ntlmssp_state->mem_ctx,
+ server_domain);
+
+ SAFE_FREE(server_domain);
+ if (challenge_blob.length != 8) {
+ data_blob_free(&struct_blob);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!ntlmssp_state->nt_hash || !ntlmssp_state->lm_hash) {
+ uchar zeros[16];
+ /* do nothing - blobs are zero length */
+
+ ZERO_STRUCT(zeros);
+
+ /* session key is all zeros */
+ session_key = data_blob_talloc(ntlmssp_state->mem_ctx, zeros, 16);
+
+ /* not doing NLTM2 without a password */
+ ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
+ } else if (ntlmssp_state->use_ntlmv2) {
+
+ if (!struct_blob.length) {
+ /* be lazy, match win2k - we can't do NTLMv2 without it */
+ DEBUG(1, ("Server did not provide 'target information', required for NTLMv2\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* TODO: if the remote server is standalone, then we should replace 'domain'
+ with the server name as supplied above */
+
+ if (!SMBNTLMv2encrypt_hash(ntlmssp_state->user,
+ ntlmssp_state->domain,
+ ntlmssp_state->nt_hash, &challenge_blob,
+ &struct_blob,
+ &lm_response, &nt_response, &session_key)) {
+ data_blob_free(&challenge_blob);
+ data_blob_free(&struct_blob);
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
+ struct MD5Context md5_session_nonce_ctx;
+ uchar session_nonce[16];
+ uchar session_nonce_hash[16];
+ uchar user_session_key[16];
+
+ lm_response = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 24);
+ generate_random_buffer(lm_response.data, 8);
+ memset(lm_response.data+8, 0, 16);
+
+ memcpy(session_nonce, challenge_blob.data, 8);
+ memcpy(&session_nonce[8], lm_response.data, 8);
+
+ MD5Init(&md5_session_nonce_ctx);
+ MD5Update(&md5_session_nonce_ctx, challenge_blob.data, 8);
+ MD5Update(&md5_session_nonce_ctx, lm_response.data, 8);
+ MD5Final(session_nonce_hash, &md5_session_nonce_ctx);
+
+ DEBUG(5, ("NTLMSSP challenge set by NTLM2\n"));
+ DEBUG(5, ("challenge is: \n"));
+ dump_data(5, session_nonce_hash, 8);
+
+ nt_response = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 24);
+ SMBNTencrypt_hash(ntlmssp_state->nt_hash,
+ session_nonce_hash,
+ nt_response.data);
+
+ session_key = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 16);
+
+ SMBsesskeygen_ntv1(ntlmssp_state->nt_hash, NULL, user_session_key);
+ hmac_md5(user_session_key, session_nonce, sizeof(session_nonce), session_key.data);
+ dump_data_pw("NTLM2 session key:\n", session_key.data, session_key.length);
+ } else {
+ /* lanman auth is insecure, it may be disabled */
+ if (lp_client_lanman_auth()) {
+ lm_response = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 24);
+ SMBencrypt_hash(ntlmssp_state->lm_hash,challenge_blob.data,
+ lm_response.data);
+ }
+
+ nt_response = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 24);
+ SMBNTencrypt_hash(ntlmssp_state->nt_hash,challenge_blob.data,
+ nt_response.data);
+
+ session_key = data_blob_talloc(ntlmssp_state->mem_ctx, NULL, 16);
+ if ((ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY)
+ && lp_client_lanman_auth()) {
+ SMBsesskeygen_lm_sess_key(ntlmssp_state->lm_hash, lm_response.data,
+ session_key.data);
+ dump_data_pw("LM session key\n", session_key.data, session_key.length);
+ } else {
+ SMBsesskeygen_ntv1(ntlmssp_state->nt_hash, NULL, session_key.data);
+ dump_data_pw("NT session key:\n", session_key.data, session_key.length);
+ }
+ }
+ data_blob_free(&struct_blob);
+
+ /* Key exchange encryptes a new client-generated session key with
+ the password-derived key */
+ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
+ /* Make up a new session key */
+ uint8 client_session_key[16];
+ generate_random_buffer(client_session_key, sizeof(client_session_key));
+
+ /* Encrypt the new session key with the old one */
+ encrypted_session_key = data_blob(client_session_key, sizeof(client_session_key));
+ dump_data_pw("KEY_EXCH session key:\n", encrypted_session_key.data, encrypted_session_key.length);
+ SamOEMhash(encrypted_session_key.data, session_key.data, encrypted_session_key.length);
+ dump_data_pw("KEY_EXCH session key (enc):\n", encrypted_session_key.data, encrypted_session_key.length);
+
+ /* Mark the new session key as the 'real' session key */
+ data_blob_free(&session_key);
+ session_key = data_blob_talloc(ntlmssp_state->mem_ctx, client_session_key, sizeof(client_session_key));
+ }
+
+ /* this generates the actual auth packet */
+ if (!msrpc_gen(next_request, auth_gen_string,
+ "NTLMSSP",
+ NTLMSSP_AUTH,
+ lm_response.data, lm_response.length,
+ nt_response.data, nt_response.length,
+ ntlmssp_state->domain,
+ ntlmssp_state->user,
+ ntlmssp_state->get_global_myname(),
+ encrypted_session_key.data, encrypted_session_key.length,
+ ntlmssp_state->neg_flags)) {
+
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ data_blob_free(&encrypted_session_key);
+
+ data_blob_free(&ntlmssp_state->chal);
+
+ ntlmssp_state->session_key = session_key;
+
+ ntlmssp_state->chal = challenge_blob;
+ ntlmssp_state->lm_resp = lm_response;
+ ntlmssp_state->nt_resp = nt_response;
+
+ ntlmssp_state->expected_state = NTLMSSP_DONE;
+
+ if (!NT_STATUS_IS_OK(nt_status = ntlmssp_sign_init(ntlmssp_state))) {
+ DEBUG(1, ("Could not setup NTLMSSP signing/sealing system (error was: %s)\n", nt_errstr(nt_status)));
+ }
+
+ return nt_status;
+}
+
+NTSTATUS ntlmssp_client_start(NTLMSSP_STATE **ntlmssp_state)
+{
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_init("NTLMSSP Client context");
+
+ *ntlmssp_state = TALLOC_ZERO_P(mem_ctx, NTLMSSP_STATE);
+ if (!*ntlmssp_state) {
+ DEBUG(0,("ntlmssp_client_start: talloc failed!\n"));
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*ntlmssp_state)->role = NTLMSSP_CLIENT;
+
+ (*ntlmssp_state)->mem_ctx = mem_ctx;
+
+ (*ntlmssp_state)->get_global_myname = global_myname;
+ (*ntlmssp_state)->get_domain = lp_workgroup;
+
+ (*ntlmssp_state)->unicode = True;
+
+ (*ntlmssp_state)->use_ntlmv2 = lp_client_ntlmv2_auth();
+
+ (*ntlmssp_state)->expected_state = NTLMSSP_INITIAL;
+
+ (*ntlmssp_state)->ref_count = 1;
+
+ (*ntlmssp_state)->neg_flags =
+ NTLMSSP_NEGOTIATE_128 |
+ NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
+ NTLMSSP_NEGOTIATE_NTLM |
+ NTLMSSP_NEGOTIATE_NTLM2 |
+ NTLMSSP_NEGOTIATE_KEY_EXCH |
+ NTLMSSP_REQUEST_TARGET;
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/libsmb/ntlmssp_parse.c b/source3/libsmb/ntlmssp_parse.c
new file mode 100644
index 0000000000..70377cba7d
--- /dev/null
+++ b/source3/libsmb/ntlmssp_parse.c
@@ -0,0 +1,384 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple kerberos5/SPNEGO routines
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
+ Copyright (C) Andrew Bartlett 2002-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/*
+ 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)
+ a = address (input is char *unix_string)
+ (1 byte type, 1 byte length, unicode/ASCII string, all inline)
+ A = ASCII string (input is unix string)
+ B = data blob (pointer + length)
+ b = data blob in header (pointer + length)
+ D
+ 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 'A':
+ s = va_arg(ap, char *);
+ head_size += 8;
+ data_size += str_ascii_charnum(s);
+ break;
+ case 'a':
+ n = va_arg(ap, int);
+ s = va_arg(ap, char *);
+ data_size += (str_charnum(s) * 2) + 4;
+ 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 'A':
+ s = va_arg(ap, char *);
+ n = str_ascii_charnum(s);
+ 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;
+ push_string(NULL, blob->data+data_ofs,
+ s, n, STR_ASCII|STR_NOALIGN);
+ data_ofs += n;
+ break;
+ case 'a':
+ n = va_arg(ap, int);
+ SSVAL(blob->data, data_ofs, n); data_ofs += 2;
+ s = va_arg(ap, char *);
+ n = str_charnum(s);
+ SSVAL(blob->data, data_ofs, n*2); data_ofs += 2;
+ if (0 < n) {
+ 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;
+ if (n && b) /* don't follow null pointers... */
+ 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 *);
+ n = str_charnum(s) + 1;
+ head_ofs += push_string(NULL, blob->data+head_ofs, s, n,
+ STR_ASCII|STR_TERMINATE);
+ break;
+ }
+ }
+ va_end(ap);
+
+ return true;
+}
+
+
+/* a helpful macro to avoid running over the end of our blob */
+#define NEED_DATA(amount) \
+if ((head_ofs + amount) > blob->length) { \
+ va_end(ap); \
+ return False; \
+}
+
+/*
+ this is a tiny msrpc packet parser. This the the partner of msrpc_gen
+
+ format specifiers are:
+
+ U = unicode string (output is unix string)
+ A = ascii string
+ B = data blob
+ b = data blob in header
+ d = word (4 bytes)
+ C = constant ascii string
+ */
+
+bool msrpc_parse(const DATA_BLOB *blob,
+ const char *format, ...)
+{
+ int i;
+ va_list ap;
+ char **ps, *s;
+ DATA_BLOB *b;
+ size_t head_ofs = 0;
+ uint16 len1, len2;
+ uint32 ptr;
+ uint32 *v;
+
+ va_start(ap, format);
+ for (i=0; format[i]; i++) {
+ switch (format[i]) {
+ case 'U':
+ NEED_DATA(8);
+ 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;
+
+ ps = va_arg(ap, char **);
+ if (len1 == 0 && len2 == 0) {
+ *ps = smb_xstrdup("");
+ } else {
+ /* make sure its in the right format
+ * be strict */
+ if ((len1 != len2) || (ptr + len1 < ptr) ||
+ (ptr + len1 < len1) ||
+ (ptr + len1 > blob->length)) {
+ va_end(ap);
+ return false;
+ }
+ if (len1 & 1) {
+ /* if odd length and unicode */
+ va_end(ap);
+ return false;
+ }
+ if (blob->data + ptr <
+ (uint8 *)(unsigned long)ptr ||
+ blob->data + ptr < blob->data) {
+ va_end(ap);
+ return false;
+ }
+
+ if (0 < len1) {
+ char *p = NULL;
+ pull_string_talloc(talloc_tos(),
+ NULL,
+ 0,
+ &p,
+ blob->data + ptr,
+ len1,
+ STR_UNICODE|STR_NOALIGN);
+ if (p) {
+ (*ps) = smb_xstrdup(p);
+ TALLOC_FREE(p);
+ } else {
+ (*ps) = smb_xstrdup("");
+ }
+ } else {
+ (*ps) = smb_xstrdup("");
+ }
+ }
+ break;
+ case 'A':
+ NEED_DATA(8);
+ 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;
+
+ ps = va_arg(ap, char **);
+ /* make sure its in the right format - be strict */
+ if (len1 == 0 && len2 == 0) {
+ *ps = smb_xstrdup("");
+ } else {
+ if ((len1 != len2) || (ptr + len1 < ptr) ||
+ (ptr + len1 < len1) ||
+ (ptr + len1 > blob->length)) {
+ va_end(ap);
+ return false;
+ }
+
+ if (blob->data + ptr <
+ (uint8 *)(unsigned long)ptr ||
+ blob->data + ptr < blob->data) {
+ va_end(ap);
+ return false;
+ }
+
+ if (0 < len1) {
+ char *p = NULL;
+ pull_string_talloc(talloc_tos(),
+ NULL,
+ 0,
+ &p,
+ blob->data + ptr,
+ len1,
+ STR_ASCII|STR_NOALIGN);
+ if (p) {
+ (*ps) = smb_xstrdup(p);
+ TALLOC_FREE(p);
+ } else {
+ (*ps) = smb_xstrdup("");
+ }
+ } else {
+ (*ps) = smb_xstrdup("");
+ }
+ }
+ break;
+ case 'B':
+ NEED_DATA(8);
+ 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;
+
+ b = (DATA_BLOB *)va_arg(ap, void *);
+ if (len1 == 0 && len2 == 0) {
+ *b = data_blob_null;
+ } else {
+ /* make sure its in the right format
+ * be strict */
+ if ((len1 != len2) || (ptr + len1 < ptr) ||
+ (ptr + len1 < len1) ||
+ (ptr + len1 > blob->length)) {
+ va_end(ap);
+ return false;
+ }
+
+ if (blob->data + ptr <
+ (uint8 *)(unsigned long)ptr ||
+ blob->data + ptr < blob->data) {
+ va_end(ap);
+ return false;
+ }
+
+ *b = data_blob(blob->data + ptr, len1);
+ }
+ break;
+ case 'b':
+ b = (DATA_BLOB *)va_arg(ap, void *);
+ len1 = va_arg(ap, unsigned);
+ /* make sure its in the right format - be strict */
+ NEED_DATA(len1);
+ if (blob->data + head_ofs < (uint8 *)head_ofs ||
+ blob->data + head_ofs < blob->data) {
+ va_end(ap);
+ return false;
+ }
+
+ *b = data_blob(blob->data + head_ofs, len1);
+ head_ofs += len1;
+ break;
+ case 'd':
+ v = va_arg(ap, uint32 *);
+ NEED_DATA(4);
+ *v = IVAL(blob->data, head_ofs); head_ofs += 4;
+ break;
+ case 'C':
+ s = va_arg(ap, char *);
+
+ if (blob->data + head_ofs < (uint8 *)head_ofs ||
+ blob->data + head_ofs < blob->data) {
+ va_end(ap);
+ return false;
+ }
+
+ {
+ char *p = NULL;
+ size_t ret = pull_string_talloc(talloc_tos(),
+ NULL,
+ 0,
+ &p,
+ blob->data+head_ofs,
+ blob->length - head_ofs,
+ STR_ASCII|STR_TERMINATE);
+ if (ret == (size_t)-1 || p == NULL) {
+ va_end(ap);
+ return false;
+ }
+ head_ofs += ret;
+ if (strcmp(s, p) != 0) {
+ TALLOC_FREE(p);
+ va_end(ap);
+ return false;
+ }
+ TALLOC_FREE(p);
+ }
+ break;
+ }
+ }
+ va_end(ap);
+
+ return True;
+}
diff --git a/source3/libsmb/ntlmssp_sign.c b/source3/libsmb/ntlmssp_sign.c
new file mode 100644
index 0000000000..8413c8066b
--- /dev/null
+++ b/source3/libsmb/ntlmssp_sign.c
@@ -0,0 +1,468 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Version 3.0
+ * NTLMSSP Signing routines
+ * Copyright (C) Andrew Bartlett 2003-2005
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+
+#define CLI_SIGN "session key to client-to-server signing key magic constant"
+#define CLI_SEAL "session key to client-to-server sealing key magic constant"
+#define SRV_SIGN "session key to server-to-client signing key magic constant"
+#define SRV_SEAL "session key to server-to-client sealing key magic constant"
+
+/**
+ * Some notes on then NTLM2 code:
+ *
+ * NTLM2 is a AEAD system. This means that the data encrypted is not
+ * all the data that is signed. In DCE-RPC case, the headers of the
+ * DCE-RPC packets are also signed. This prevents some of the
+ * fun-and-games one might have by changing them.
+ *
+ */
+
+static void calc_ntlmv2_key(unsigned char subkey[16],
+ DATA_BLOB session_key,
+ const char *constant)
+{
+ struct MD5Context ctx3;
+ MD5Init(&ctx3);
+ MD5Update(&ctx3, session_key.data, session_key.length);
+ MD5Update(&ctx3, (const unsigned char *)constant, strlen(constant)+1);
+ MD5Final(subkey, &ctx3);
+}
+
+enum ntlmssp_direction {
+ NTLMSSP_SEND,
+ NTLMSSP_RECEIVE
+};
+
+static NTSTATUS ntlmssp_make_packet_signature(NTLMSSP_STATE *ntlmssp_state,
+ const uchar *data, size_t length,
+ const uchar *whole_pdu, size_t pdu_length,
+ enum ntlmssp_direction direction,
+ DATA_BLOB *sig,
+ bool encrypt_sig)
+{
+ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
+ HMACMD5Context ctx;
+ uchar seq_num[4];
+ uchar digest[16];
+
+ *sig = data_blob(NULL, NTLMSSP_SIG_SIZE);
+ if (!sig->data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (direction) {
+ case NTLMSSP_SEND:
+ DEBUG(100,("ntlmssp_make_packet_signature: SEND seq = %u, len = %u, pdu_len = %u\n",
+ ntlmssp_state->ntlm2_send_seq_num,
+ (unsigned int)length,
+ (unsigned int)pdu_length));
+
+ SIVAL(seq_num, 0, ntlmssp_state->ntlm2_send_seq_num);
+ ntlmssp_state->ntlm2_send_seq_num++;
+ hmac_md5_init_limK_to_64(ntlmssp_state->send_sign_key, 16, &ctx);
+ break;
+ case NTLMSSP_RECEIVE:
+
+ DEBUG(100,("ntlmssp_make_packet_signature: RECV seq = %u, len = %u, pdu_len = %u\n",
+ ntlmssp_state->ntlm2_recv_seq_num,
+ (unsigned int)length,
+ (unsigned int)pdu_length));
+
+ SIVAL(seq_num, 0, ntlmssp_state->ntlm2_recv_seq_num);
+ ntlmssp_state->ntlm2_recv_seq_num++;
+ hmac_md5_init_limK_to_64(ntlmssp_state->recv_sign_key, 16, &ctx);
+ break;
+ }
+
+ dump_data_pw("pdu data ", whole_pdu, pdu_length);
+
+ hmac_md5_update(seq_num, 4, &ctx);
+ hmac_md5_update(whole_pdu, pdu_length, &ctx);
+ hmac_md5_final(digest, &ctx);
+
+ if (encrypt_sig && (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH)) {
+ switch (direction) {
+ case NTLMSSP_SEND:
+ smb_arc4_crypt(ntlmssp_state->send_seal_arc4_state, digest, 8);
+ break;
+ case NTLMSSP_RECEIVE:
+ smb_arc4_crypt(ntlmssp_state->recv_seal_arc4_state, digest, 8);
+ break;
+ }
+ }
+
+ SIVAL(sig->data, 0, NTLMSSP_SIGN_VERSION);
+ memcpy(sig->data + 4, digest, 8);
+ memcpy(sig->data + 12, seq_num, 4);
+
+ dump_data_pw("ntlmssp v2 sig ", sig->data, sig->length);
+
+ } else {
+ uint32 crc;
+ crc = crc32_calc_buffer((const char *)data, length);
+ if (!msrpc_gen(sig, "dddd", NTLMSSP_SIGN_VERSION, 0, crc, ntlmssp_state->ntlmv1_seq_num)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ntlmssp_state->ntlmv1_seq_num++;
+
+ dump_data_pw("ntlmssp hash:\n", ntlmssp_state->ntlmv1_arc4_state,
+ sizeof(ntlmssp_state->ntlmv1_arc4_state));
+ smb_arc4_crypt(ntlmssp_state->ntlmv1_arc4_state, sig->data+4, sig->length-4);
+ }
+ return NT_STATUS_OK;
+}
+
+NTSTATUS ntlmssp_sign_packet(NTLMSSP_STATE *ntlmssp_state,
+ const uchar *data, size_t length,
+ const uchar *whole_pdu, size_t pdu_length,
+ DATA_BLOB *sig)
+{
+ NTSTATUS nt_status;
+
+ if (!(ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SIGN)) {
+ DEBUG(3, ("NTLMSSP Signing not negotiated - cannot sign packet!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!ntlmssp_state->session_key.length) {
+ DEBUG(3, ("NO session key, cannot check sign packet\n"));
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ nt_status = ntlmssp_make_packet_signature(ntlmssp_state,
+ data, length,
+ whole_pdu, pdu_length,
+ NTLMSSP_SEND, sig, True);
+
+ return nt_status;
+}
+
+/**
+ * Check the signature of an incoming packet
+ * @note caller *must* check that the signature is the size it expects
+ *
+ */
+
+NTSTATUS ntlmssp_check_packet(NTLMSSP_STATE *ntlmssp_state,
+ const uchar *data, size_t length,
+ const uchar *whole_pdu, size_t pdu_length,
+ const DATA_BLOB *sig)
+{
+ DATA_BLOB local_sig;
+ NTSTATUS nt_status;
+
+ if (!ntlmssp_state->session_key.length) {
+ DEBUG(3, ("NO session key, cannot check packet signature\n"));
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ if (sig->length < 8) {
+ DEBUG(0, ("NTLMSSP packet check failed due to short signature (%lu bytes)!\n",
+ (unsigned long)sig->length));
+ }
+
+ nt_status = ntlmssp_make_packet_signature(ntlmssp_state,
+ data, length,
+ whole_pdu, pdu_length,
+ NTLMSSP_RECEIVE, &local_sig, True);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("NTLMSSP packet check failed with %s\n", nt_errstr(nt_status)));
+ data_blob_free(&local_sig);
+ return nt_status;
+ }
+
+ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
+ if (local_sig.length != sig->length ||
+ memcmp(local_sig.data, sig->data, sig->length) != 0) {
+ DEBUG(5, ("BAD SIG NTLM2: wanted signature of\n"));
+ dump_data(5, local_sig.data, local_sig.length);
+
+ DEBUG(5, ("BAD SIG: got signature of\n"));
+ dump_data(5, sig->data, sig->length);
+
+ DEBUG(0, ("NTLMSSP NTLM2 packet check failed due to invalid signature!\n"));
+ data_blob_free(&local_sig);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ } else {
+ if (local_sig.length != sig->length ||
+ memcmp(local_sig.data + 8, sig->data + 8, sig->length - 8) != 0) {
+ DEBUG(5, ("BAD SIG NTLM1: wanted signature of\n"));
+ dump_data(5, local_sig.data, local_sig.length);
+
+ DEBUG(5, ("BAD SIG: got signature of\n"));
+ dump_data(5, sig->data, sig->length);
+
+ DEBUG(0, ("NTLMSSP NTLM1 packet check failed due to invalid signature!\n"));
+ data_blob_free(&local_sig);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+ dump_data_pw("checked ntlmssp signature\n", sig->data, sig->length);
+ DEBUG(10,("ntlmssp_check_packet: NTLMSSP signature OK !\n"));
+
+ data_blob_free(&local_sig);
+ return NT_STATUS_OK;
+}
+
+/**
+ * Seal data with the NTLMSSP algorithm
+ *
+ */
+
+NTSTATUS ntlmssp_seal_packet(NTLMSSP_STATE *ntlmssp_state,
+ uchar *data, size_t length,
+ uchar *whole_pdu, size_t pdu_length,
+ DATA_BLOB *sig)
+{
+ if (!(ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_SEAL)) {
+ DEBUG(3, ("NTLMSSP Sealing not negotiated - cannot seal packet!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!ntlmssp_state->session_key.length) {
+ DEBUG(3, ("NO session key, cannot seal packet\n"));
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ DEBUG(10,("ntlmssp_seal_data: seal\n"));
+ dump_data_pw("ntlmssp clear data\n", data, length);
+ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
+ /* The order of these two operations matters - we must first seal the packet,
+ then seal the sequence number - this is becouse the send_seal_hash is not
+ constant, but is is rather updated with each iteration */
+ NTSTATUS nt_status = ntlmssp_make_packet_signature(ntlmssp_state,
+ data, length,
+ whole_pdu, pdu_length,
+ NTLMSSP_SEND, sig, False);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ smb_arc4_crypt(ntlmssp_state->send_seal_arc4_state, data, length);
+ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
+ smb_arc4_crypt(ntlmssp_state->send_seal_arc4_state, sig->data+4, 8);
+ }
+ } else {
+ uint32 crc;
+ crc = crc32_calc_buffer((const char *)data, length);
+ if (!msrpc_gen(sig, "dddd", NTLMSSP_SIGN_VERSION, 0, crc, ntlmssp_state->ntlmv1_seq_num)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* The order of these two operations matters - we must first seal the packet,
+ then seal the sequence number - this is becouse the ntlmv1_arc4_state is not
+ constant, but is is rather updated with each iteration */
+
+ dump_data_pw("ntlmv1 arc4 state:\n", ntlmssp_state->ntlmv1_arc4_state,
+ sizeof(ntlmssp_state->ntlmv1_arc4_state));
+ smb_arc4_crypt(ntlmssp_state->ntlmv1_arc4_state, data, length);
+
+ dump_data_pw("ntlmv1 arc4 state:\n", ntlmssp_state->ntlmv1_arc4_state,
+ sizeof(ntlmssp_state->ntlmv1_arc4_state));
+
+ smb_arc4_crypt(ntlmssp_state->ntlmv1_arc4_state, sig->data+4, sig->length-4);
+
+ ntlmssp_state->ntlmv1_seq_num++;
+ }
+ dump_data_pw("ntlmssp signature\n", sig->data, sig->length);
+ dump_data_pw("ntlmssp sealed data\n", data, length);
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * Unseal data with the NTLMSSP algorithm
+ *
+ */
+
+NTSTATUS ntlmssp_unseal_packet(NTLMSSP_STATE *ntlmssp_state,
+ uchar *data, size_t length,
+ uchar *whole_pdu, size_t pdu_length,
+ DATA_BLOB *sig)
+{
+ if (!ntlmssp_state->session_key.length) {
+ DEBUG(3, ("NO session key, cannot unseal packet\n"));
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ DEBUG(10,("ntlmssp_unseal_packet: seal\n"));
+ dump_data_pw("ntlmssp sealed data\n", data, length);
+
+ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
+ /* First unseal the data. */
+ smb_arc4_crypt(ntlmssp_state->recv_seal_arc4_state, data, length);
+ dump_data_pw("ntlmv2 clear data\n", data, length);
+ } else {
+ smb_arc4_crypt(ntlmssp_state->ntlmv1_arc4_state, data, length);
+ dump_data_pw("ntlmv1 clear data\n", data, length);
+ }
+ return ntlmssp_check_packet(ntlmssp_state, data, length, whole_pdu, pdu_length, sig);
+}
+
+/**
+ Initialise the state for NTLMSSP signing.
+*/
+NTSTATUS ntlmssp_sign_init(NTLMSSP_STATE *ntlmssp_state)
+{
+ unsigned char p24[24];
+ TALLOC_CTX *mem_ctx;
+ ZERO_STRUCT(p24);
+
+ mem_ctx = talloc_init("weak_keys");
+ if (!mem_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DEBUG(3, ("NTLMSSP Sign/Seal - Initialising with flags:\n"));
+ debug_ntlmssp_flags(ntlmssp_state->neg_flags);
+
+ if (ntlmssp_state->session_key.length < 8) {
+ TALLOC_FREE(mem_ctx);
+ DEBUG(3, ("NO session key, cannot intialise signing\n"));
+ return NT_STATUS_NO_USER_SESSION_KEY;
+ }
+
+ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
+ DATA_BLOB weak_session_key = ntlmssp_state->session_key;
+ const char *send_sign_const;
+ const char *send_seal_const;
+ const char *recv_sign_const;
+ const char *recv_seal_const;
+
+ switch (ntlmssp_state->role) {
+ case NTLMSSP_CLIENT:
+ send_sign_const = CLI_SIGN;
+ send_seal_const = CLI_SEAL;
+ recv_sign_const = SRV_SIGN;
+ recv_seal_const = SRV_SEAL;
+ break;
+ case NTLMSSP_SERVER:
+ send_sign_const = SRV_SIGN;
+ send_seal_const = SRV_SEAL;
+ recv_sign_const = CLI_SIGN;
+ recv_seal_const = CLI_SEAL;
+ break;
+ default:
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ /**
+ Weaken NTLMSSP keys to cope with down-level clients, servers and export restrictions.
+ We probably should have some parameters to control this, once we get NTLM2 working.
+ */
+
+ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_128) {
+ ;
+ } else if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_56) {
+ weak_session_key.length = 7;
+ } else { /* forty bits */
+ weak_session_key.length = 5;
+ }
+
+ dump_data_pw("NTLMSSP weakend master key:\n",
+ weak_session_key.data,
+ weak_session_key.length);
+
+ /* SEND: sign key */
+ calc_ntlmv2_key(ntlmssp_state->send_sign_key,
+ ntlmssp_state->session_key, send_sign_const);
+ dump_data_pw("NTLMSSP send sign key:\n",
+ ntlmssp_state->send_sign_key, 16);
+
+ /* SEND: seal ARCFOUR pad */
+ calc_ntlmv2_key(ntlmssp_state->send_seal_key,
+ weak_session_key, send_seal_const);
+ dump_data_pw("NTLMSSP send seal key:\n",
+ ntlmssp_state->send_seal_key, 16);
+
+ smb_arc4_init(ntlmssp_state->send_seal_arc4_state,
+ ntlmssp_state->send_seal_key, 16);
+
+ dump_data_pw("NTLMSSP send seal arc4 state:\n",
+ ntlmssp_state->send_seal_arc4_state,
+ sizeof(ntlmssp_state->send_seal_arc4_state));
+
+ /* RECV: sign key */
+ calc_ntlmv2_key(ntlmssp_state->recv_sign_key,
+ ntlmssp_state->session_key, recv_sign_const);
+ dump_data_pw("NTLMSSP recv send sign key:\n",
+ ntlmssp_state->recv_sign_key, 16);
+
+ /* RECV: seal ARCFOUR pad */
+ calc_ntlmv2_key(ntlmssp_state->recv_seal_key,
+ weak_session_key, recv_seal_const);
+
+ dump_data_pw("NTLMSSP recv seal key:\n",
+ ntlmssp_state->recv_seal_key, 16);
+
+ smb_arc4_init(ntlmssp_state->recv_seal_arc4_state,
+ ntlmssp_state->recv_seal_key, 16);
+
+ dump_data_pw("NTLMSSP recv seal arc4 state:\n",
+ ntlmssp_state->recv_seal_arc4_state,
+ sizeof(ntlmssp_state->recv_seal_arc4_state));
+
+ ntlmssp_state->ntlm2_send_seq_num = 0;
+ ntlmssp_state->ntlm2_recv_seq_num = 0;
+
+
+ } else {
+#if 0
+ /* Hmmm. Shouldn't we also weaken keys for ntlmv1 ? JRA. */
+
+ DATA_BLOB weak_session_key = ntlmssp_state->session_key;
+ /**
+ Weaken NTLMSSP keys to cope with down-level clients, servers and export restrictions.
+ We probably should have some parameters to control this, once we get NTLM2 working.
+ */
+
+ if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_128) {
+ ;
+ } else if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_56) {
+ weak_session_key.length = 6;
+ } else { /* forty bits */
+ weak_session_key.length = 5;
+ }
+ dump_data_pw("NTLMSSP weakend master key:\n",
+ weak_session_key.data,
+ weak_session_key.length);
+#endif
+
+ DATA_BLOB weak_session_key = ntlmssp_weaken_keys(ntlmssp_state, mem_ctx);
+
+ DEBUG(5, ("NTLMSSP Sign/Seal - using NTLM1\n"));
+
+ smb_arc4_init(ntlmssp_state->ntlmv1_arc4_state,
+ weak_session_key.data, weak_session_key.length);
+
+ dump_data_pw("NTLMv1 arc4 state:\n", ntlmssp_state->ntlmv1_arc4_state,
+ sizeof(ntlmssp_state->ntlmv1_arc4_state));
+
+ ntlmssp_state->ntlmv1_seq_num = 0;
+ }
+
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_OK;
+}
diff --git a/source3/libsmb/passchange.c b/source3/libsmb/passchange.c
new file mode 100644
index 0000000000..4c76234e0c
--- /dev/null
+++ b/source3/libsmb/passchange.c
@@ -0,0 +1,257 @@
+/*
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/*************************************************************
+ Change a password on a remote machine using IPC calls.
+*************************************************************/
+
+NTSTATUS remote_password_change(const char *remote_machine, const char *user_name,
+ const char *old_passwd, const char *new_passwd,
+ char **err_str)
+{
+ struct nmb_name calling, called;
+ struct cli_state *cli;
+ struct rpc_pipe_client *pipe_hnd;
+ struct sockaddr_storage ss;
+
+ NTSTATUS result;
+ bool pass_must_change = False;
+
+ *err_str = NULL;
+
+ if(!resolve_name( remote_machine, &ss, 0x20)) {
+ asprintf(err_str, "Unable to find an IP address for machine "
+ "%s.\n", remote_machine);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ cli = cli_initialise();
+ if (!cli) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ result = cli_connect(cli, remote_machine, &ss);
+ if (!NT_STATUS_IS_OK(result)) {
+ asprintf(err_str, "Unable to connect to SMB server on "
+ "machine %s. Error was : %s.\n",
+ remote_machine, nt_errstr(result));
+ cli_shutdown(cli);
+ return result;
+ }
+
+ make_nmb_name(&calling, global_myname() , 0x0);
+ make_nmb_name(&called , remote_machine, 0x20);
+
+ if (!cli_session_request(cli, &calling, &called)) {
+ asprintf(err_str, "machine %s rejected the session setup. "
+ "Error was : %s.\n",
+ remote_machine, cli_errstr(cli) );
+ result = cli_nt_error(cli);
+ cli_shutdown(cli);
+ return result;
+ }
+
+ cli->protocol = PROTOCOL_NT1;
+
+ if (!cli_negprot(cli)) {
+ asprintf(err_str, "machine %s rejected the negotiate "
+ "protocol. Error was : %s.\n",
+ remote_machine, cli_errstr(cli) );
+ result = cli_nt_error(cli);
+ cli_shutdown(cli);
+ return result;
+ }
+
+ /* Given things like SMB signing, restrict anonymous and the like,
+ try an authenticated connection first */
+ result = cli_session_setup(cli, user_name,
+ old_passwd, strlen(old_passwd)+1,
+ old_passwd, strlen(old_passwd)+1, "");
+
+ if (!NT_STATUS_IS_OK(result)) {
+
+ /* Password must change or Password expired are the only valid
+ * error conditions here from where we can proceed, the rest like
+ * account locked out or logon failure will lead to errors later
+ * anyway */
+
+ if (!NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) &&
+ !NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED)) {
+ asprintf(err_str, "Could not connect to machine %s: "
+ "%s\n", remote_machine, cli_errstr(cli));
+ cli_shutdown(cli);
+ return result;
+ }
+
+ pass_must_change = True;
+
+ /*
+ * 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.
+ */
+
+ result = cli_session_setup(cli, "", "", 0, "", 0, "");
+
+ if (!NT_STATUS_IS_OK(result)) {
+ asprintf(err_str, "machine %s rejected the session "
+ "setup. Error was : %s.\n",
+ remote_machine, cli_errstr(cli) );
+ cli_shutdown(cli);
+ return result;
+ }
+
+ cli_init_creds(cli, "", "", NULL);
+ } else {
+ cli_init_creds(cli, user_name, "", old_passwd);
+ }
+
+ if (!cli_send_tconX(cli, "IPC$", "IPC", "", 1)) {
+ asprintf(err_str, "machine %s rejected the tconX on the IPC$ "
+ "share. Error was : %s.\n",
+ remote_machine, cli_errstr(cli) );
+ result = cli_nt_error(cli);
+ cli_shutdown(cli);
+ return result;
+ }
+
+ /* Try not to give the password away too easily */
+
+ if (!pass_must_change) {
+ result = cli_rpc_pipe_open_ntlmssp(cli,
+ &ndr_table_samr.syntax_id,
+ PIPE_AUTH_LEVEL_PRIVACY,
+ "", /* what domain... ? */
+ user_name,
+ old_passwd,
+ &pipe_hnd);
+ } else {
+ /*
+ * If the user password must be changed the ntlmssp bind will
+ * fail the same way as the session setup above did. The
+ * difference ist that with a pipe bind we don't get a good
+ * error message, the result will be that the rpc call below
+ * will just fail. So we do it anonymously, there's no other
+ * way.
+ */
+ result = cli_rpc_pipe_open_noauth(
+ cli, &ndr_table_samr.syntax_id, &pipe_hnd);
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ if (lp_client_lanman_auth()) {
+ /* Use the old RAP method. */
+ if (!cli_oem_change_password(cli, user_name, new_passwd, old_passwd)) {
+ asprintf(err_str, "machine %s rejected the "
+ "password change: Error was : %s.\n",
+ remote_machine, cli_errstr(cli) );
+ result = cli_nt_error(cli);
+ cli_shutdown(cli);
+ return result;
+ }
+ } else {
+ asprintf(err_str, "SAMR connection to machine %s "
+ "failed. Error was %s, but LANMAN password "
+ "changed are disabled\n",
+ nt_errstr(result), remote_machine);
+ result = cli_nt_error(cli);
+ cli_shutdown(cli);
+ return result;
+ }
+ }
+
+ result = rpccli_samr_chgpasswd_user2(pipe_hnd, talloc_tos(),
+ user_name, new_passwd, old_passwd);
+ if (NT_STATUS_IS_OK(result)) {
+ /* Great - it all worked! */
+ cli_shutdown(cli);
+ return NT_STATUS_OK;
+
+ } else if (!(NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)
+ || NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL))) {
+ /* it failed, but for reasons such as wrong password, too short etc ... */
+
+ asprintf(err_str, "machine %s rejected the password change: "
+ "Error was : %s.\n",
+ remote_machine, get_friendly_nt_error_msg(result));
+ cli_shutdown(cli);
+ return result;
+ }
+
+ /* OK, that failed, so try again... */
+ TALLOC_FREE(pipe_hnd);
+
+ /* Try anonymous NTLMSSP... */
+ cli_init_creds(cli, "", "", NULL);
+
+ result = NT_STATUS_UNSUCCESSFUL;
+
+ /* OK, this is ugly, but... try an anonymous pipe. */
+ result = cli_rpc_pipe_open_noauth(cli, &ndr_table_samr.syntax_id,
+ &pipe_hnd);
+
+ if ( NT_STATUS_IS_OK(result) &&
+ (NT_STATUS_IS_OK(result = rpccli_samr_chgpasswd_user2(
+ pipe_hnd, talloc_tos(), user_name,
+ new_passwd, old_passwd)))) {
+ /* Great - it all worked! */
+ cli_shutdown(cli);
+ return NT_STATUS_OK;
+ } else {
+ if (!(NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)
+ || NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL))) {
+ /* it failed, but again it was due to things like new password too short */
+
+ asprintf(err_str, "machine %s rejected the "
+ "(anonymous) password change: Error was : "
+ "%s.\n", remote_machine,
+ get_friendly_nt_error_msg(result));
+ cli_shutdown(cli);
+ return result;
+ }
+
+ /* We have failed to change the user's password, and we think the server
+ just might not support SAMR password changes, so fall back */
+
+ if (lp_client_lanman_auth()) {
+ /* Use the old RAP method. */
+ if (cli_oem_change_password(cli, user_name, new_passwd, old_passwd)) {
+ /* SAMR failed, but the old LanMan protocol worked! */
+
+ cli_shutdown(cli);
+ return NT_STATUS_OK;
+ }
+ asprintf(err_str, "machine %s rejected the password "
+ "change: Error was : %s.\n",
+ remote_machine, cli_errstr(cli) );
+ result = cli_nt_error(cli);
+ cli_shutdown(cli);
+ return result;
+ } else {
+ asprintf(err_str, "SAMR connection to machine %s "
+ "failed. Error was %s, but LANMAN password "
+ "changed are disabled\n",
+ nt_errstr(result), remote_machine);
+ cli_shutdown(cli);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+}
diff --git a/source3/libsmb/pwd_cache.c b/source3/libsmb/pwd_cache.c
new file mode 100644
index 0000000000..071e729e8c
--- /dev/null
+++ b/source3/libsmb/pwd_cache.c
@@ -0,0 +1,61 @@
+/*
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/****************************************************************************
+ Initialises a password structure.
+****************************************************************************/
+
+static void pwd_init(struct pwd_info *pwd)
+{
+ memset((char *)pwd->password , '\0', sizeof(pwd->password ));
+
+ pwd->null_pwd = True; /* safest option... */
+}
+
+/****************************************************************************
+ Stores a cleartext password.
+****************************************************************************/
+
+void pwd_set_cleartext(struct pwd_info *pwd, const char *clr)
+{
+ pwd_init(pwd);
+ if (clr) {
+ fstrcpy(pwd->password, clr);
+ pwd->null_pwd = False;
+ } else {
+ pwd->null_pwd = True;
+ }
+
+ pwd->cleartext = True;
+}
+
+/****************************************************************************
+ Gets a cleartext password.
+****************************************************************************/
+
+void pwd_get_cleartext(struct pwd_info *pwd, fstring clr)
+{
+ if (pwd->cleartext)
+ fstrcpy(clr, pwd->password);
+ else
+ clr[0] = 0;
+
+}
diff --git a/source3/libsmb/samlogon_cache.c b/source3/libsmb/samlogon_cache.c
new file mode 100644
index 0000000000..4abe5bb6de
--- /dev/null
+++ b/source3/libsmb/samlogon_cache.c
@@ -0,0 +1,258 @@
+/*
+ Unix SMB/CIFS implementation.
+ Net_sam_logon info3 helpers
+ Copyright (C) Alexander Bokovoy 2002.
+ Copyright (C) Andrew Bartlett 2002.
+ Copyright (C) Gerald Carter 2003.
+ Copyright (C) Tim Potter 2003.
+ Copyright (C) Guenther Deschner 2008.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+#define NETSAMLOGON_TDB "netsamlogon_cache.tdb"
+
+static TDB_CONTEXT *netsamlogon_tdb = NULL;
+
+/***********************************************************************
+ open the tdb
+ ***********************************************************************/
+
+bool netsamlogon_cache_init(void)
+{
+ if (!netsamlogon_tdb) {
+ netsamlogon_tdb = tdb_open_log(lock_path(NETSAMLOGON_TDB), 0,
+ TDB_DEFAULT, O_RDWR | O_CREAT, 0600);
+ }
+
+ return (netsamlogon_tdb != NULL);
+}
+
+
+/***********************************************************************
+ Shutdown samlogon_cache database
+***********************************************************************/
+
+bool netsamlogon_cache_shutdown(void)
+{
+ if (netsamlogon_tdb) {
+ return (tdb_close(netsamlogon_tdb) == 0);
+ }
+
+ return true;
+}
+
+/***********************************************************************
+ Clear cache getpwnam and getgroups entries from the winbindd cache
+***********************************************************************/
+
+void netsamlogon_clear_cached_user(struct netr_SamInfo3 *info3)
+{
+ DOM_SID user_sid;
+ fstring keystr, tmp;
+
+ if (!info3) {
+ return;
+ }
+
+ if (!netsamlogon_cache_init()) {
+ DEBUG(0,("netsamlogon_clear_cached_user: cannot open "
+ "%s for write!\n",
+ NETSAMLOGON_TDB));
+ return;
+ }
+ sid_copy(&user_sid, info3->base.domain_sid);
+ sid_append_rid(&user_sid, info3->base.rid);
+
+ /* Prepare key as DOMAIN-SID/USER-RID string */
+ slprintf(keystr, sizeof(keystr), "%s", sid_to_fstring(tmp, &user_sid));
+
+ DEBUG(10,("netsamlogon_clear_cached_user: SID [%s]\n", keystr));
+
+ tdb_delete_bystring(netsamlogon_tdb, keystr);
+}
+
+/***********************************************************************
+ Store a netr_SamInfo3 structure in a tdb for later user
+ username should be in UTF-8 format
+***********************************************************************/
+
+bool netsamlogon_cache_store(const char *username, struct netr_SamInfo3 *info3)
+{
+ TDB_DATA data;
+ fstring keystr, tmp;
+ bool result = false;
+ DOM_SID user_sid;
+ time_t t = time(NULL);
+ TALLOC_CTX *mem_ctx;
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+ struct netsamlogoncache_entry r;
+
+ if (!info3) {
+ return false;
+ }
+
+ if (!netsamlogon_cache_init()) {
+ DEBUG(0,("netsamlogon_cache_store: cannot open %s for write!\n",
+ NETSAMLOGON_TDB));
+ return false;
+ }
+
+ sid_copy(&user_sid, info3->base.domain_sid);
+ sid_append_rid(&user_sid, info3->base.rid);
+
+ /* Prepare key as DOMAIN-SID/USER-RID string */
+ slprintf(keystr, sizeof(keystr), "%s", sid_to_fstring(tmp, &user_sid));
+
+ DEBUG(10,("netsamlogon_cache_store: SID [%s]\n", keystr));
+
+ /* Prepare data */
+
+ if (!(mem_ctx = TALLOC_P( NULL, int))) {
+ DEBUG(0,("netsamlogon_cache_store: talloc() failed!\n"));
+ return false;
+ }
+
+ /* only Samba fills in the username, not sure why NT doesn't */
+ /* so we fill it in since winbindd_getpwnam() makes use of it */
+
+ if (!info3->base.account_name.string) {
+ info3->base.account_name.string = talloc_strdup(info3, username);
+ }
+
+ r.timestamp = t;
+ r.info3 = *info3;
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_DEBUG(netsamlogoncache_entry, &r);
+ }
+
+ ndr_err = ndr_push_struct_blob(&blob, mem_ctx, &r,
+ (ndr_push_flags_fn_t)ndr_push_netsamlogoncache_entry);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(0,("netsamlogon_cache_store: failed to push entry to cache\n"));
+ TALLOC_FREE(mem_ctx);
+ return false;
+ }
+
+ data.dsize = blob.length;
+ data.dptr = blob.data;
+
+ if (tdb_store_bystring(netsamlogon_tdb, keystr, data, TDB_REPLACE) != -1) {
+ result = true;
+ }
+
+ TALLOC_FREE(mem_ctx);
+
+ return result;
+}
+
+/***********************************************************************
+ Retrieves a netr_SamInfo3 structure from a tdb. Caller must
+ free the user_info struct (malloc()'d memory)
+***********************************************************************/
+
+struct netr_SamInfo3 *netsamlogon_cache_get(TALLOC_CTX *mem_ctx, const DOM_SID *user_sid)
+{
+ struct netr_SamInfo3 *info3 = NULL;
+ TDB_DATA data;
+ fstring keystr, tmp;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ struct netsamlogoncache_entry r;
+
+ if (!netsamlogon_cache_init()) {
+ DEBUG(0,("netsamlogon_cache_get: cannot open %s for write!\n",
+ NETSAMLOGON_TDB));
+ return false;
+ }
+
+ /* Prepare key as DOMAIN-SID/USER-RID string */
+ slprintf(keystr, sizeof(keystr), "%s", sid_to_fstring(tmp, user_sid));
+ DEBUG(10,("netsamlogon_cache_get: SID [%s]\n", keystr));
+ data = tdb_fetch_bystring( netsamlogon_tdb, keystr );
+
+ if (!data.dptr) {
+ return NULL;
+ }
+
+ info3 = TALLOC_ZERO_P(mem_ctx, struct netr_SamInfo3);
+ if (!info3) {
+ goto done;
+ }
+
+ blob = data_blob_const(data.dptr, data.dsize);
+
+ ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, &r,
+ (ndr_pull_flags_fn_t)ndr_pull_netsamlogoncache_entry);
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_DEBUG(netsamlogoncache_entry, &r);
+ }
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(0,("netsamlogon_cache_get: failed to pull entry from cache\n"));
+ tdb_delete(netsamlogon_tdb, data);
+ TALLOC_FREE(info3);
+ goto done;
+ }
+
+ info3 = (struct netr_SamInfo3 *)talloc_memdup(mem_ctx, &r.info3,
+ sizeof(r.info3));
+
+ done:
+ SAFE_FREE(data.dptr);
+
+ return info3;
+
+#if 0 /* The netsamlogon cache needs to hang around. Something about
+ this feels wrong, but it is the only way we can get all of the
+ groups. The old universal groups cache didn't expire either.
+ --jerry */
+ {
+ time_t now = time(NULL);
+ uint32 time_diff;
+
+ /* is the entry expired? */
+ time_diff = now - t;
+
+ if ( (time_diff < 0 ) || (time_diff > lp_winbind_cache_time()) ) {
+ DEBUG(10,("netsamlogon_cache_get: cache entry expired \n"));
+ tdb_delete( netsamlogon_tdb, key );
+ TALLOC_FREE( user );
+ }
+ }
+#endif
+}
+
+bool netsamlogon_cache_have(const DOM_SID *user_sid)
+{
+ TALLOC_CTX *mem_ctx = talloc_init("netsamlogon_cache_have");
+ struct netr_SamInfo3 *info3 = NULL;
+ bool result;
+
+ if (!mem_ctx)
+ return False;
+
+ info3 = netsamlogon_cache_get(mem_ctx, user_sid);
+
+ result = (info3 != NULL);
+
+ talloc_destroy(mem_ctx);
+
+ return result;
+}
diff --git a/source3/libsmb/smb_seal.c b/source3/libsmb/smb_seal.c
new file mode 100644
index 0000000000..a81ae9afd5
--- /dev/null
+++ b/source3/libsmb/smb_seal.c
@@ -0,0 +1,497 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB Transport encryption (sealing) code.
+ Copyright (C) Jeremy Allison 2007.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/******************************************************************************
+ Pull out the encryption context for this packet. 0 means global context.
+******************************************************************************/
+
+NTSTATUS get_enc_ctx_num(const uint8_t *buf, uint16 *p_enc_ctx_num)
+{
+ if (smb_len(buf) < 8) {
+ return NT_STATUS_INVALID_BUFFER_SIZE;
+ }
+
+ if (buf[4] == 0xFF) {
+ if (buf[5] == 'S' && buf [6] == 'M' && buf[7] == 'B') {
+ /* Not an encrypted buffer. */
+ return NT_STATUS_NOT_FOUND;
+ }
+ if (buf[5] == 'E') {
+ *p_enc_ctx_num = SVAL(buf,6);
+ return NT_STATUS_OK;
+ }
+ }
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+}
+
+/******************************************************************************
+ Generic code for client and server.
+ Is encryption turned on ?
+******************************************************************************/
+
+bool common_encryption_on(struct smb_trans_enc_state *es)
+{
+ return ((es != NULL) && es->enc_on);
+}
+
+/******************************************************************************
+ Generic code for client and server.
+ NTLM decrypt an incoming buffer.
+ Abartlett tells me that SSPI puts the signature first before the encrypted
+ output, so cope with the same for compatibility.
+******************************************************************************/
+
+NTSTATUS common_ntlm_decrypt_buffer(NTLMSSP_STATE *ntlmssp_state, char *buf)
+{
+ NTSTATUS status;
+ size_t buf_len = smb_len(buf) + 4; /* Don't forget the 4 length bytes. */
+ size_t data_len;
+ char *inbuf;
+ DATA_BLOB sig;
+
+ if (buf_len < 8 + NTLMSSP_SIG_SIZE) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+
+ inbuf = (char *)smb_xmemdup(buf, buf_len);
+
+ /* Adjust for the signature. */
+ data_len = buf_len - 8 - NTLMSSP_SIG_SIZE;
+
+ /* Point at the signature. */
+ sig = data_blob_const(inbuf+8, NTLMSSP_SIG_SIZE);
+
+ status = ntlmssp_unseal_packet(ntlmssp_state,
+ (unsigned char *)inbuf + 8 + NTLMSSP_SIG_SIZE, /* 4 byte len + 0xFF 'E' <enc> <ctx> */
+ data_len,
+ (unsigned char *)inbuf + 8 + NTLMSSP_SIG_SIZE,
+ data_len,
+ &sig);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ SAFE_FREE(inbuf);
+ return status;
+ }
+
+ memcpy(buf + 8, inbuf + 8 + NTLMSSP_SIG_SIZE, data_len);
+
+ /* Reset the length and overwrite the header. */
+ smb_setlen(buf,data_len + 4);
+
+ SAFE_FREE(inbuf);
+ return NT_STATUS_OK;
+}
+
+/******************************************************************************
+ Generic code for client and server.
+ NTLM encrypt an outgoing buffer. Return the encrypted pointer in ppbuf_out.
+ Abartlett tells me that SSPI puts the signature first before the encrypted
+ output, so do the same for compatibility.
+******************************************************************************/
+
+NTSTATUS common_ntlm_encrypt_buffer(NTLMSSP_STATE *ntlmssp_state,
+ uint16 enc_ctx_num,
+ char *buf,
+ char **ppbuf_out)
+{
+ NTSTATUS status;
+ char *buf_out;
+ size_t data_len = smb_len(buf) - 4; /* Ignore the 0xFF SMB bytes. */
+ DATA_BLOB sig;
+
+ *ppbuf_out = NULL;
+
+ if (data_len == 0) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+
+ /*
+ * We know smb_len can't return a value > 128k, so no int overflow
+ * check needed.
+ */
+
+ buf_out = SMB_XMALLOC_ARRAY(char, 8 + NTLMSSP_SIG_SIZE + data_len);
+
+ /* Copy the data from the original buffer. */
+
+ memcpy(buf_out + 8 + NTLMSSP_SIG_SIZE, buf + 8, data_len);
+
+ smb_set_enclen(buf_out, smb_len(buf) + NTLMSSP_SIG_SIZE, enc_ctx_num);
+
+ sig = data_blob(NULL, NTLMSSP_SIG_SIZE);
+
+ status = ntlmssp_seal_packet(ntlmssp_state,
+ (unsigned char *)buf_out + 8 + NTLMSSP_SIG_SIZE, /* 4 byte len + 0xFF 'S' <enc> <ctx> */
+ data_len,
+ (unsigned char *)buf_out + 8 + NTLMSSP_SIG_SIZE,
+ data_len,
+ &sig);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ data_blob_free(&sig);
+ SAFE_FREE(buf_out);
+ return status;
+ }
+
+ /* First 16 data bytes are signature for SSPI compatibility. */
+ memcpy(buf_out + 8, sig.data, NTLMSSP_SIG_SIZE);
+ *ppbuf_out = buf_out;
+ return NT_STATUS_OK;
+}
+
+/******************************************************************************
+ Generic code for client and server.
+ gss-api decrypt an incoming buffer. We insist that the size of the
+ unwrapped buffer must be smaller or identical to the incoming buffer.
+******************************************************************************/
+
+#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
+static NTSTATUS common_gss_decrypt_buffer(struct smb_tran_enc_state_gss *gss_state, char *buf)
+{
+ gss_ctx_id_t gss_ctx = gss_state->gss_ctx;
+ OM_uint32 ret = 0;
+ OM_uint32 minor = 0;
+ int flags_got = 0;
+ gss_buffer_desc in_buf, out_buf;
+ size_t buf_len = smb_len(buf) + 4; /* Don't forget the 4 length bytes. */
+
+ if (buf_len < 8) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+
+ in_buf.value = buf + 8;
+ in_buf.length = buf_len - 8;
+
+ ret = gss_unwrap(&minor,
+ gss_ctx,
+ &in_buf,
+ &out_buf,
+ &flags_got, /* did we get sign+seal ? */
+ (gss_qop_t *) NULL);
+
+ if (ret != GSS_S_COMPLETE) {
+ ADS_STATUS adss = ADS_ERROR_GSS(ret, minor);
+ DEBUG(0,("common_gss_encrypt_buffer: gss_unwrap failed. Error %s\n",
+ ads_errstr(adss) ));
+ return map_nt_error_from_gss(ret, minor);
+ }
+
+ if (out_buf.length > in_buf.length) {
+ DEBUG(0,("common_gss_encrypt_buffer: gss_unwrap size (%u) too large (%u) !\n",
+ (unsigned int)out_buf.length,
+ (unsigned int)in_buf.length ));
+ gss_release_buffer(&minor, &out_buf);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ memcpy(buf + 8, out_buf.value, out_buf.length);
+ /* Reset the length and overwrite the header. */
+ smb_setlen(buf, out_buf.length + 4);
+
+ gss_release_buffer(&minor, &out_buf);
+ return NT_STATUS_OK;
+}
+
+/******************************************************************************
+ Generic code for client and server.
+ gss-api encrypt an outgoing buffer. Return the alloced encrypted pointer in buf_out.
+******************************************************************************/
+
+static NTSTATUS common_gss_encrypt_buffer(struct smb_tran_enc_state_gss *gss_state,
+ uint16 enc_ctx_num,
+ char *buf,
+ char **ppbuf_out)
+{
+ gss_ctx_id_t gss_ctx = gss_state->gss_ctx;
+ OM_uint32 ret = 0;
+ OM_uint32 minor = 0;
+ int flags_got = 0;
+ gss_buffer_desc in_buf, out_buf;
+ size_t buf_len = smb_len(buf) + 4; /* Don't forget the 4 length bytes. */
+
+ *ppbuf_out = NULL;
+
+ if (buf_len < 8) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+
+ in_buf.value = buf + 8;
+ in_buf.length = buf_len - 8;
+
+ ret = gss_wrap(&minor,
+ gss_ctx,
+ true, /* we want sign+seal. */
+ GSS_C_QOP_DEFAULT,
+ &in_buf,
+ &flags_got, /* did we get sign+seal ? */
+ &out_buf);
+
+ if (ret != GSS_S_COMPLETE) {
+ ADS_STATUS adss = ADS_ERROR_GSS(ret, minor);
+ DEBUG(0,("common_gss_encrypt_buffer: gss_wrap failed. Error %s\n",
+ ads_errstr(adss) ));
+ return map_nt_error_from_gss(ret, minor);
+ }
+
+ if (!flags_got) {
+ /* Sign+seal not supported. */
+ gss_release_buffer(&minor, &out_buf);
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ /* Ya see - this is why I *hate* gss-api. I don't
+ * want to have to malloc another buffer of the
+ * same size + 8 bytes just to get a continuous
+ * header + buffer, but gss won't let me pass in
+ * a pre-allocated buffer. Bastards (and you know
+ * who you are....). I might fix this by
+ * going to "encrypt_and_send" passing in a file
+ * descriptor and doing scatter-gather write with
+ * TCP cork on Linux. But I shouldn't have to
+ * bother :-*(. JRA.
+ */
+
+ *ppbuf_out = (char *)SMB_MALLOC(out_buf.length + 8); /* We know this can't wrap. */
+ if (!*ppbuf_out) {
+ gss_release_buffer(&minor, &out_buf);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ memcpy(*ppbuf_out+8, out_buf.value, out_buf.length);
+ smb_set_enclen(*ppbuf_out, out_buf.length + 4, enc_ctx_num);
+
+ gss_release_buffer(&minor, &out_buf);
+ return NT_STATUS_OK;
+}
+#endif
+
+/******************************************************************************
+ Generic code for client and server.
+ Encrypt an outgoing buffer. Return the alloced encrypted pointer in buf_out.
+******************************************************************************/
+
+NTSTATUS common_encrypt_buffer(struct smb_trans_enc_state *es, char *buffer, char **buf_out)
+{
+ if (!common_encryption_on(es)) {
+ /* Not encrypting. */
+ *buf_out = buffer;
+ return NT_STATUS_OK;
+ }
+
+ switch (es->smb_enc_type) {
+ case SMB_TRANS_ENC_NTLM:
+ return common_ntlm_encrypt_buffer(es->s.ntlmssp_state, es->enc_ctx_num, buffer, buf_out);
+#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
+ case SMB_TRANS_ENC_GSS:
+ return common_gss_encrypt_buffer(es->s.gss_state, es->enc_ctx_num, buffer, buf_out);
+#endif
+ default:
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+}
+
+/******************************************************************************
+ Generic code for client and server.
+ Decrypt an incoming SMB buffer. Replaces the data within it.
+ New data must be less than or equal to the current length.
+******************************************************************************/
+
+NTSTATUS common_decrypt_buffer(struct smb_trans_enc_state *es, char *buf)
+{
+ if (!common_encryption_on(es)) {
+ /* Not decrypting. */
+ return NT_STATUS_OK;
+ }
+
+ switch (es->smb_enc_type) {
+ case SMB_TRANS_ENC_NTLM:
+ return common_ntlm_decrypt_buffer(es->s.ntlmssp_state, buf);
+#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
+ case SMB_TRANS_ENC_GSS:
+ return common_gss_decrypt_buffer(es->s.gss_state, buf);
+#endif
+ default:
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+}
+
+#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
+/******************************************************************************
+ Shutdown a gss encryption state.
+******************************************************************************/
+
+static void common_free_gss_state(struct smb_tran_enc_state_gss **pp_gss_state)
+{
+ OM_uint32 minor = 0;
+ struct smb_tran_enc_state_gss *gss_state = *pp_gss_state;
+
+ if (gss_state->creds != GSS_C_NO_CREDENTIAL) {
+ gss_release_cred(&minor, &gss_state->creds);
+ }
+ if (gss_state->gss_ctx != GSS_C_NO_CONTEXT) {
+ gss_delete_sec_context(&minor, &gss_state->gss_ctx, NULL);
+ }
+ SAFE_FREE(*pp_gss_state);
+}
+#endif
+
+/******************************************************************************
+ Shutdown an encryption state.
+******************************************************************************/
+
+void common_free_encryption_state(struct smb_trans_enc_state **pp_es)
+{
+ struct smb_trans_enc_state *es = *pp_es;
+
+ if (es == NULL) {
+ return;
+ }
+
+ if (es->smb_enc_type == SMB_TRANS_ENC_NTLM) {
+ if (es->s.ntlmssp_state) {
+ ntlmssp_end(&es->s.ntlmssp_state);
+ }
+ }
+#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
+ if (es->smb_enc_type == SMB_TRANS_ENC_GSS) {
+ /* Free the gss context handle. */
+ if (es->s.gss_state) {
+ common_free_gss_state(&es->s.gss_state);
+ }
+ }
+#endif
+ SAFE_FREE(es);
+ *pp_es = NULL;
+}
+
+/******************************************************************************
+ Free an encryption-allocated buffer.
+******************************************************************************/
+
+void common_free_enc_buffer(struct smb_trans_enc_state *es, char *buf)
+{
+ if (!common_encryption_on(es)) {
+ return;
+ }
+
+ if (es->smb_enc_type == SMB_TRANS_ENC_NTLM) {
+ SAFE_FREE(buf);
+ return;
+ }
+
+#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
+ if (es->smb_enc_type == SMB_TRANS_ENC_GSS) {
+ OM_uint32 min;
+ gss_buffer_desc rel_buf;
+ rel_buf.value = buf;
+ rel_buf.length = smb_len(buf) + 4;
+ gss_release_buffer(&min, &rel_buf);
+ }
+#endif
+}
+
+/******************************************************************************
+ Client side encryption.
+******************************************************************************/
+
+/******************************************************************************
+ Is client encryption on ?
+******************************************************************************/
+
+bool cli_encryption_on(struct cli_state *cli)
+{
+ /* If we supported multiple encrytion contexts
+ * here we'd look up based on tid.
+ */
+ return common_encryption_on(cli->trans_enc_state);
+}
+
+/******************************************************************************
+ Shutdown a client encryption state.
+******************************************************************************/
+
+void cli_free_encryption_context(struct cli_state *cli)
+{
+ common_free_encryption_state(&cli->trans_enc_state);
+}
+
+/******************************************************************************
+ Free an encryption-allocated buffer.
+******************************************************************************/
+
+void cli_free_enc_buffer(struct cli_state *cli, char *buf)
+{
+ /* We know this is an smb buffer, and we
+ * didn't malloc, only copy, for a keepalive,
+ * so ignore non-session messages. */
+
+ if(CVAL(buf,0)) {
+ return;
+ }
+
+ /* If we supported multiple encrytion contexts
+ * here we'd look up based on tid.
+ */
+ common_free_enc_buffer(cli->trans_enc_state, buf);
+}
+
+/******************************************************************************
+ Decrypt an incoming buffer.
+******************************************************************************/
+
+NTSTATUS cli_decrypt_message(struct cli_state *cli)
+{
+ NTSTATUS status;
+ uint16 enc_ctx_num;
+
+ /* Ignore non-session messages. */
+ if(CVAL(cli->inbuf,0)) {
+ return NT_STATUS_OK;
+ }
+
+ status = get_enc_ctx_num((const uint8_t *)cli->inbuf, &enc_ctx_num);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (enc_ctx_num != cli->trans_enc_state->enc_ctx_num) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ return common_decrypt_buffer(cli->trans_enc_state, cli->inbuf);
+}
+
+/******************************************************************************
+ Encrypt an outgoing buffer. Return the encrypted pointer in buf_out.
+******************************************************************************/
+
+NTSTATUS cli_encrypt_message(struct cli_state *cli, char *buf, char **buf_out)
+{
+ /* Ignore non-session messages. */
+ if (CVAL(buf,0)) {
+ return NT_STATUS_OK;
+ }
+
+ /* If we supported multiple encrytion contexts
+ * here we'd look up based on tid.
+ */
+ return common_encrypt_buffer(cli->trans_enc_state, buf, buf_out);
+}
diff --git a/source3/libsmb/smb_share_modes.c b/source3/libsmb/smb_share_modes.c
new file mode 100644
index 0000000000..16b3b10925
--- /dev/null
+++ b/source3/libsmb/smb_share_modes.c
@@ -0,0 +1,520 @@
+/*
+ Samba share mode database library external interface library.
+ Used by non-Samba products needing access to the Samba share mode db.
+
+ Copyright (C) Jeremy Allison 2005 - 2006
+
+ sharemodes_procid functions (C) Copyright (C) Volker Lendecke 2005
+
+ ** NOTE! The following LGPL license applies to this module only.
+ ** This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "smb_share_modes.h"
+
+/* Database context handle. */
+struct smbdb_ctx {
+ TDB_CONTEXT *smb_tdb;
+};
+
+/* Remove the paranoid malloc checker. */
+#ifdef malloc
+#undef malloc
+#endif
+
+int smb_create_share_mode_entry_ex(struct smbdb_ctx *db_ctx, uint64_t dev,
+ uint64_t ino, const struct smb_share_mode_entry *new_entry,
+ const char *sharepath, const char *filename);
+
+static bool sharemodes_procid_equal(const struct server_id *p1, const struct server_id *p2)
+{
+ return (p1->pid == p2->pid);
+}
+
+static pid_t sharemodes_procid_to_pid(const struct server_id *proc)
+{
+ return proc->pid;
+}
+
+/*
+ * open/close sharemode database.
+ */
+
+struct smbdb_ctx *smb_share_mode_db_open(const char *db_path)
+{
+ struct smbdb_ctx *smb_db = (struct smbdb_ctx *)malloc(sizeof(struct smbdb_ctx));
+
+ if (!smb_db) {
+ return NULL;
+ }
+
+ memset(smb_db, '\0', sizeof(struct smbdb_ctx));
+
+ smb_db->smb_tdb = tdb_open(db_path,
+ 0, TDB_DEFAULT|TDB_CLEAR_IF_FIRST,
+ O_RDWR|O_CREAT,
+ 0644);
+
+ if (!smb_db->smb_tdb) {
+ free(smb_db);
+ return NULL;
+ }
+
+ /* Should check that this is the correct version.... */
+ return smb_db;
+}
+
+/* key and data records in the tdb locking database */
+struct locking_key {
+ SMB_DEV_T dev;
+ SMB_INO_T inode;
+};
+
+int smb_share_mode_db_close(struct smbdb_ctx *db_ctx)
+{
+ int ret = tdb_close(db_ctx->smb_tdb);
+ free(db_ctx);
+ return ret;
+}
+
+static TDB_DATA get_locking_key(uint64_t dev, uint64_t ino)
+{
+ static struct locking_key lk;
+ TDB_DATA ld;
+
+ memset(&lk, '\0', sizeof(struct locking_key));
+ lk.dev = (SMB_DEV_T)dev;
+ lk.inode = (SMB_INO_T)ino;
+ ld.dptr = (uint8 *)&lk;
+ ld.dsize = sizeof(lk);
+ return ld;
+}
+
+/*
+ * lock/unlock entry in sharemode database.
+ */
+
+int smb_lock_share_mode_entry(struct smbdb_ctx *db_ctx,
+ uint64_t dev,
+ uint64_t ino)
+{
+ return tdb_chainlock(db_ctx->smb_tdb, get_locking_key(dev, ino));
+}
+
+int smb_unlock_share_mode_entry(struct smbdb_ctx *db_ctx,
+ uint64_t dev,
+ uint64_t ino)
+{
+ return tdb_chainunlock(db_ctx->smb_tdb, get_locking_key(dev, ino));
+}
+
+/*
+ * Check if an external smb_share_mode_entry and an internal share_mode entry match.
+ */
+
+static int share_mode_entry_equal(const struct smb_share_mode_entry *e_entry,
+ const struct share_mode_entry *entry)
+{
+ return (sharemodes_procid_equal(&e_entry->pid, &entry->pid) &&
+ e_entry->file_id == (uint32_t)entry->share_file_id &&
+ e_entry->open_time.tv_sec == entry->time.tv_sec &&
+ e_entry->open_time.tv_usec == entry->time.tv_usec &&
+ e_entry->share_access == (uint32_t)entry->share_access &&
+ e_entry->access_mask == (uint32_t)entry->access_mask &&
+ e_entry->dev == entry->id.devid &&
+ e_entry->ino == entry->id.inode);
+}
+
+/*
+ * Create an internal Samba share_mode entry from an external smb_share_mode_entry.
+ */
+
+static void create_share_mode_entry(struct share_mode_entry *out,
+ const struct smb_share_mode_entry *in)
+{
+ memset(out, '\0', sizeof(struct share_mode_entry));
+
+ out->pid = in->pid;
+ out->share_file_id = (unsigned long)in->file_id;
+ out->time.tv_sec = in->open_time.tv_sec;
+ out->time.tv_usec = in->open_time.tv_usec;
+ out->share_access = in->share_access;
+ out->access_mask = in->access_mask;
+ out->id.devid = in->dev;
+ out->id.inode = in->ino;
+ out->uid = (uint32)geteuid();
+ out->flags = 0;
+}
+
+/*
+ * Return the current share mode list for an open file.
+ * This uses similar (but simplified) logic to locking/locking.c
+ */
+
+int smb_get_share_mode_entries(struct smbdb_ctx *db_ctx,
+ uint64_t dev,
+ uint64_t ino,
+ struct smb_share_mode_entry **pp_list,
+ unsigned char *p_delete_on_close)
+{
+ TDB_DATA db_data;
+ struct smb_share_mode_entry *list = NULL;
+ int num_share_modes = 0;
+ struct locking_data *ld = NULL; /* internal samba db state. */
+ struct share_mode_entry *shares = NULL;
+ size_t i;
+ int list_num;
+
+ *pp_list = NULL;
+ *p_delete_on_close = 0;
+
+ db_data = tdb_fetch(db_ctx->smb_tdb, get_locking_key(dev, ino));
+ if (!db_data.dptr) {
+ return 0;
+ }
+
+ ld = (struct locking_data *)db_data.dptr;
+ num_share_modes = ld->u.s.num_share_mode_entries;
+
+ if (!num_share_modes) {
+ free(db_data.dptr);
+ return 0;
+ }
+
+ list = (struct smb_share_mode_entry *)malloc(sizeof(struct smb_share_mode_entry)*num_share_modes);
+ if (!list) {
+ free(db_data.dptr);
+ return -1;
+ }
+
+ memset(list, '\0', num_share_modes * sizeof(struct smb_share_mode_entry));
+
+ shares = (struct share_mode_entry *)(db_data.dptr + sizeof(struct locking_data));
+
+ list_num = 0;
+ for (i = 0; i < num_share_modes; i++) {
+ struct share_mode_entry *share = &shares[i];
+ struct smb_share_mode_entry *sme = &list[list_num];
+ struct server_id pid = share->pid;
+
+ /* Check this process really exists. */
+ if (kill(sharemodes_procid_to_pid(&pid), 0) == -1 && (errno == ESRCH)) {
+ continue; /* No longer exists. */
+ }
+
+ /* Ignore deferred open entries. */
+ if (share->op_type == DEFERRED_OPEN_ENTRY) {
+ continue;
+ }
+
+ /* Copy into the external list. */
+ sme->dev = share->id.devid;
+ sme->ino = share->id.inode;
+ sme->share_access = (uint32_t)share->share_access;
+ sme->access_mask = (uint32_t)share->access_mask;
+ sme->open_time.tv_sec = share->time.tv_sec;
+ sme->open_time.tv_usec = share->time.tv_usec;
+ sme->file_id = (uint32_t)share->share_file_id;
+ sme->pid = share->pid;
+ list_num++;
+ }
+
+ if (list_num == 0) {
+ free(db_data.dptr);
+ free(list);
+ return 0;
+ }
+
+ *p_delete_on_close = ld->u.s.delete_on_close;
+ *pp_list = list;
+ free(db_data.dptr);
+ return list_num;
+}
+
+/*
+ * Create an entry in the Samba share mode db.
+ */
+
+int smb_create_share_mode_entry_ex(struct smbdb_ctx *db_ctx,
+ uint64_t dev,
+ uint64_t ino,
+ const struct smb_share_mode_entry *new_entry,
+ const char *sharepath, /* Must be absolute utf8 path. */
+ const char *filename) /* Must be relative utf8 path. */
+{
+ TDB_DATA db_data;
+ TDB_DATA locking_key = get_locking_key(dev, ino);
+ int orig_num_share_modes = 0;
+ struct locking_data *ld = NULL; /* internal samba db state. */
+ struct share_mode_entry *shares = NULL;
+ uint8 *new_data_p = NULL;
+ size_t new_data_size = 0;
+
+ db_data = tdb_fetch(db_ctx->smb_tdb, locking_key);
+ if (!db_data.dptr) {
+ /* We must create the entry. */
+ db_data.dptr = (uint8 *)malloc(
+ sizeof(struct locking_data) +
+ sizeof(struct share_mode_entry) +
+ strlen(sharepath) + 1 +
+ strlen(filename) + 1);
+ if (!db_data.dptr) {
+ return -1;
+ }
+ ld = (struct locking_data *)db_data.dptr;
+ memset(ld, '\0', sizeof(struct locking_data));
+ ld->u.s.num_share_mode_entries = 1;
+ ld->u.s.delete_on_close = 0;
+ ld->u.s.delete_token_size = 0;
+ shares = (struct share_mode_entry *)(db_data.dptr + sizeof(struct locking_data));
+ create_share_mode_entry(shares, new_entry);
+
+ memcpy(db_data.dptr + sizeof(struct locking_data) + sizeof(struct share_mode_entry),
+ sharepath,
+ strlen(sharepath) + 1);
+ memcpy(db_data.dptr + sizeof(struct locking_data) + sizeof(struct share_mode_entry) +
+ strlen(sharepath) + 1,
+ filename,
+ strlen(filename) + 1);
+
+ db_data.dsize = sizeof(struct locking_data) + sizeof(struct share_mode_entry) +
+ strlen(sharepath) + 1 +
+ strlen(filename) + 1;
+ if (tdb_store(db_ctx->smb_tdb, locking_key, db_data, TDB_INSERT) == -1) {
+ free(db_data.dptr);
+ return -1;
+ }
+ free(db_data.dptr);
+ return 0;
+ }
+
+ /* Entry exists, we must add a new entry. */
+ new_data_p = (uint8 *)malloc(
+ db_data.dsize + sizeof(struct share_mode_entry));
+ if (!new_data_p) {
+ free(db_data.dptr);
+ return -1;
+ }
+
+ ld = (struct locking_data *)db_data.dptr;
+ orig_num_share_modes = ld->u.s.num_share_mode_entries;
+
+ /* Copy the original data. */
+ memcpy(new_data_p, db_data.dptr, sizeof(struct locking_data) + (orig_num_share_modes * sizeof(struct share_mode_entry)));
+
+ /* Add in the new share mode */
+ shares = (struct share_mode_entry *)(new_data_p + sizeof(struct locking_data) +
+ (orig_num_share_modes * sizeof(struct share_mode_entry)));
+
+ create_share_mode_entry(shares, new_entry);
+
+ ld = (struct locking_data *)new_data_p;
+ ld->u.s.num_share_mode_entries++;
+
+ /* Append the original delete_token and filenames. */
+ memcpy(new_data_p + sizeof(struct locking_data) + (ld->u.s.num_share_mode_entries * sizeof(struct share_mode_entry)),
+ db_data.dptr + sizeof(struct locking_data) + (orig_num_share_modes * sizeof(struct share_mode_entry)),
+ db_data.dsize - sizeof(struct locking_data) - (orig_num_share_modes * sizeof(struct share_mode_entry)));
+
+ new_data_size = db_data.dsize + sizeof(struct share_mode_entry);
+
+ free(db_data.dptr);
+
+ db_data.dptr = new_data_p;
+ db_data.dsize = new_data_size;
+
+ if (tdb_store(db_ctx->smb_tdb, locking_key, db_data, TDB_REPLACE) == -1) {
+ free(db_data.dptr);
+ return -1;
+ }
+ free(db_data.dptr);
+ return 0;
+}
+
+/*
+ * Create an entry in the Samba share mode db. Original interface - doesn't
+ * Distinguish between share path and filename. Fudge this by using a
+ * sharepath of / and a relative filename of (filename+1).
+ */
+
+int smb_create_share_mode_entry(struct smbdb_ctx *db_ctx,
+ uint64_t dev,
+ uint64_t ino,
+ const struct smb_share_mode_entry *new_entry,
+ const char *filename) /* Must be absolute utf8 path. */
+{
+ if (*filename != '/') {
+ abort();
+ }
+ return smb_create_share_mode_entry_ex(db_ctx, dev, ino, new_entry,
+ "/", &filename[1]);
+}
+
+int smb_delete_share_mode_entry(struct smbdb_ctx *db_ctx,
+ uint64_t dev,
+ uint64_t ino,
+ const struct smb_share_mode_entry *del_entry)
+{
+ TDB_DATA db_data;
+ TDB_DATA locking_key = get_locking_key(dev, ino);
+ int orig_num_share_modes = 0;
+ struct locking_data *ld = NULL; /* internal samba db state. */
+ struct share_mode_entry *shares = NULL;
+ uint8 *new_data_p = NULL;
+ size_t remaining_size = 0;
+ size_t i, num_share_modes;
+ const uint8 *remaining_ptr = NULL;
+
+ db_data = tdb_fetch(db_ctx->smb_tdb, locking_key);
+ if (!db_data.dptr) {
+ return -1; /* Error - missing entry ! */
+ }
+
+ ld = (struct locking_data *)db_data.dptr;
+ orig_num_share_modes = ld->u.s.num_share_mode_entries;
+ shares = (struct share_mode_entry *)(db_data.dptr + sizeof(struct locking_data));
+
+ if (orig_num_share_modes == 1) {
+ /* Only one entry - better be ours... */
+ if (!share_mode_entry_equal(del_entry, shares)) {
+ /* Error ! We can't delete someone else's entry ! */
+ free(db_data.dptr);
+ return -1;
+ }
+ /* It's ours - just remove the entire record. */
+ free(db_data.dptr);
+ return tdb_delete(db_ctx->smb_tdb, locking_key);
+ }
+
+ /* More than one - allocate a new record minus the one we'll delete. */
+ new_data_p = (uint8 *)malloc(
+ db_data.dsize - sizeof(struct share_mode_entry));
+ if (!new_data_p) {
+ free(db_data.dptr);
+ return -1;
+ }
+
+ /* Copy the header. */
+ memcpy(new_data_p, db_data.dptr, sizeof(struct locking_data));
+
+ num_share_modes = 0;
+ for (i = 0; i < orig_num_share_modes; i++) {
+ struct share_mode_entry *share = &shares[i];
+ struct server_id pid = share->pid;
+
+ /* Check this process really exists. */
+ if (kill(sharemodes_procid_to_pid(&pid), 0) == -1 && (errno == ESRCH)) {
+ continue; /* No longer exists. */
+ }
+
+ if (share_mode_entry_equal(del_entry, share)) {
+ continue; /* This is our delete taget. */
+ }
+
+ memcpy(new_data_p + sizeof(struct locking_data) +
+ (num_share_modes * sizeof(struct share_mode_entry)),
+ share, sizeof(struct share_mode_entry) );
+
+ num_share_modes++;
+ }
+
+ if (num_share_modes == 0) {
+ /* None left after pruning. Delete record. */
+ free(db_data.dptr);
+ free(new_data_p);
+ return tdb_delete(db_ctx->smb_tdb, locking_key);
+ }
+
+ /* Copy any delete token plus the terminating filenames. */
+ remaining_ptr = db_data.dptr + sizeof(struct locking_data) + (orig_num_share_modes * sizeof(struct share_mode_entry));
+ remaining_size = db_data.dsize - (remaining_ptr - db_data.dptr);
+
+ memcpy(new_data_p + sizeof(struct locking_data) + (num_share_modes * sizeof(struct share_mode_entry)),
+ remaining_ptr,
+ remaining_size);
+
+ free(db_data.dptr);
+
+ db_data.dptr = new_data_p;
+
+ /* Re-save smaller record. */
+ ld = (struct locking_data *)db_data.dptr;
+ ld->u.s.num_share_mode_entries = num_share_modes;
+
+ db_data.dsize = sizeof(struct locking_data) + (num_share_modes * sizeof(struct share_mode_entry)) + remaining_size;
+
+ if (tdb_store(db_ctx->smb_tdb, locking_key, db_data, TDB_REPLACE) == -1) {
+ free(db_data.dptr);
+ return -1;
+ }
+ free(db_data.dptr);
+ return 0;
+}
+
+int smb_change_share_mode_entry(struct smbdb_ctx *db_ctx,
+ uint64_t dev,
+ uint64_t ino,
+ const struct smb_share_mode_entry *set_entry,
+ const struct smb_share_mode_entry *new_entry)
+{
+ TDB_DATA db_data;
+ TDB_DATA locking_key = get_locking_key(dev, ino);
+ int num_share_modes = 0;
+ struct locking_data *ld = NULL; /* internal samba db state. */
+ struct share_mode_entry *shares = NULL;
+ size_t i;
+ int found_entry = 0;
+
+ db_data = tdb_fetch(db_ctx->smb_tdb, locking_key);
+ if (!db_data.dptr) {
+ return -1; /* Error - missing entry ! */
+ }
+
+ ld = (struct locking_data *)db_data.dptr;
+ num_share_modes = ld->u.s.num_share_mode_entries;
+ shares = (struct share_mode_entry *)(db_data.dptr + sizeof(struct locking_data));
+
+ for (i = 0; i < num_share_modes; i++) {
+ struct share_mode_entry *share = &shares[i];
+ struct server_id pid = share->pid;
+
+ /* Check this process really exists. */
+ if (kill(sharemodes_procid_to_pid(&pid), 0) == -1 && (errno == ESRCH)) {
+ continue; /* No longer exists. */
+ }
+
+ if (share_mode_entry_equal(set_entry, share)) {
+ create_share_mode_entry(share, new_entry);
+ found_entry = 1;
+ break;
+ }
+ }
+
+ if (!found_entry) {
+ free(db_data.dptr);
+ return -1;
+ }
+
+ /* Save modified data. */
+ if (tdb_store(db_ctx->smb_tdb, locking_key, db_data, TDB_REPLACE) == -1) {
+ free(db_data.dptr);
+ return -1;
+ }
+ free(db_data.dptr);
+ return 0;
+}
diff --git a/source3/libsmb/smb_signing.c b/source3/libsmb/smb_signing.c
new file mode 100644
index 0000000000..ea1eb05cfb
--- /dev/null
+++ b/source3/libsmb/smb_signing.c
@@ -0,0 +1,1004 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB Signing Code
+ Copyright (C) Jeremy Allison 2003.
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2002-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/* Lookup a packet's MID (multiplex id) and figure out it's sequence number */
+struct outstanding_packet_lookup {
+ struct outstanding_packet_lookup *prev, *next;
+ uint16 mid;
+ uint32 reply_seq_num;
+ bool can_delete; /* Set to False in trans state. */
+};
+
+struct smb_basic_signing_context {
+ DATA_BLOB mac_key;
+ uint32 send_seq_num;
+ struct outstanding_packet_lookup *outstanding_packet_list;
+};
+
+static bool store_sequence_for_reply(struct outstanding_packet_lookup **list,
+ uint16 mid, uint32 reply_seq_num)
+{
+ struct outstanding_packet_lookup *t;
+
+ /* Ensure we only add a mid once. */
+ for (t = *list; t; t = t->next) {
+ if (t->mid == mid) {
+ return False;
+ }
+ }
+
+ t = SMB_XMALLOC_P(struct outstanding_packet_lookup);
+ ZERO_STRUCTP(t);
+
+ t->mid = mid;
+ t->reply_seq_num = reply_seq_num;
+ t->can_delete = True;
+
+ /*
+ * Add to the *start* of the list not the end of the list.
+ * This ensures that the *last* send sequence with this mid
+ * is returned by preference.
+ * This can happen if the mid wraps and one of the early
+ * mid numbers didn't get a reply and is still lurking on
+ * the list. JRA. Found by Fran Fabrizio <fran@cis.uab.edu>.
+ */
+
+ DLIST_ADD(*list, t);
+ DEBUG(10,("store_sequence_for_reply: stored seq = %u mid = %u\n",
+ (unsigned int)reply_seq_num, (unsigned int)mid ));
+ return True;
+}
+
+static bool get_sequence_for_reply(struct outstanding_packet_lookup **list,
+ uint16 mid, uint32 *reply_seq_num)
+{
+ struct outstanding_packet_lookup *t;
+
+ for (t = *list; t; t = t->next) {
+ if (t->mid == mid) {
+ *reply_seq_num = t->reply_seq_num;
+ DEBUG(10,("get_sequence_for_reply: found seq = %u mid = %u\n",
+ (unsigned int)t->reply_seq_num, (unsigned int)t->mid ));
+ if (t->can_delete) {
+ DLIST_REMOVE(*list, t);
+ SAFE_FREE(t);
+ }
+ return True;
+ }
+ }
+ return False;
+}
+
+static bool set_sequence_can_delete_flag(struct outstanding_packet_lookup **list, uint16 mid, bool can_delete_entry)
+{
+ struct outstanding_packet_lookup *t;
+
+ for (t = *list; t; t = t->next) {
+ if (t->mid == mid) {
+ t->can_delete = can_delete_entry;
+ return True;
+ }
+ }
+ return False;
+}
+
+/***********************************************************
+ SMB signing - Common code before we set a new signing implementation
+************************************************************/
+
+static bool cli_set_smb_signing_common(struct cli_state *cli)
+{
+ if (!cli->sign_info.allow_smb_signing) {
+ return False;
+ }
+
+ if (!cli->sign_info.negotiated_smb_signing
+ && !cli->sign_info.mandatory_signing) {
+ return False;
+ }
+
+ if (cli->sign_info.doing_signing) {
+ return False;
+ }
+
+ if (cli->sign_info.free_signing_context)
+ cli->sign_info.free_signing_context(&cli->sign_info);
+
+ /* These calls are INCOMPATIBLE with SMB signing */
+ cli->readbraw_supported = False;
+ cli->writebraw_supported = False;
+
+ return True;
+}
+
+/***********************************************************
+ SMB signing - Common code for 'real' implementations
+************************************************************/
+
+static bool set_smb_signing_real_common(struct smb_sign_info *si)
+{
+ if (si->mandatory_signing) {
+ DEBUG(5, ("Mandatory SMB signing enabled!\n"));
+ }
+
+ si->doing_signing = True;
+ DEBUG(5, ("SMB signing enabled!\n"));
+
+ return True;
+}
+
+static void mark_packet_signed(char *outbuf)
+{
+ uint16 flags2;
+ flags2 = SVAL(outbuf,smb_flg2);
+ flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES;
+ SSVAL(outbuf,smb_flg2, flags2);
+}
+
+/***********************************************************
+ SMB signing - NULL implementation - calculate a MAC to send.
+************************************************************/
+
+static void null_sign_outgoing_message(char *outbuf, struct smb_sign_info *si)
+{
+ /* we can't zero out the sig, as we might be trying to send a
+ session request - which is NBT-level, not SMB level and doesn't
+ have the field */
+ return;
+}
+
+/***********************************************************
+ SMB signing - NULL implementation - check a MAC sent by server.
+************************************************************/
+
+static bool null_check_incoming_message(const char *inbuf,
+ struct smb_sign_info *si,
+ bool must_be_ok)
+{
+ return True;
+}
+
+/***********************************************************
+ SMB signing - NULL implementation - free signing context
+************************************************************/
+
+static void null_free_signing_context(struct smb_sign_info *si)
+{
+ return;
+}
+
+/**
+ SMB signing - NULL implementation - setup the MAC key.
+
+ @note Used as an initialisation only - it will not correctly
+ shut down a real signing mechanism
+*/
+
+static bool null_set_signing(struct smb_sign_info *si)
+{
+ si->signing_context = NULL;
+
+ si->sign_outgoing_message = null_sign_outgoing_message;
+ si->check_incoming_message = null_check_incoming_message;
+ si->free_signing_context = null_free_signing_context;
+
+ return True;
+}
+
+/**
+ * Free the signing context
+ */
+
+static void free_signing_context(struct smb_sign_info *si)
+{
+ if (si->free_signing_context) {
+ si->free_signing_context(si);
+ si->signing_context = NULL;
+ }
+
+ null_set_signing(si);
+}
+
+
+static bool signing_good(const char *inbuf, struct smb_sign_info *si,
+ bool good, uint32 seq, bool must_be_ok)
+{
+ if (good) {
+
+ if (!si->doing_signing) {
+ si->doing_signing = True;
+ }
+
+ if (!si->seen_valid) {
+ si->seen_valid = True;
+ }
+
+ } else {
+ if (!si->mandatory_signing && !si->seen_valid) {
+
+ if (!must_be_ok) {
+ return True;
+ }
+ /* Non-mandatory signing - just turn off if this is the first bad packet.. */
+ DEBUG(5, ("srv_check_incoming_message: signing negotiated but not required and peer\n"
+ "isn't sending correct signatures. Turning off.\n"));
+ si->negotiated_smb_signing = False;
+ si->allow_smb_signing = False;
+ si->doing_signing = False;
+ free_signing_context(si);
+ return True;
+ } else if (!must_be_ok) {
+ /* This packet is known to be unsigned */
+ return True;
+ } else {
+ /* Mandatory signing or bad packet after signing started - fail and disconnect. */
+ if (seq)
+ DEBUG(0, ("signing_good: BAD SIG: seq %u\n", (unsigned int)seq));
+ return False;
+ }
+ }
+ return True;
+}
+
+/***********************************************************
+ SMB signing - Simple implementation - calculate a MAC on the packet
+************************************************************/
+
+static void simple_packet_signature(struct smb_basic_signing_context *data,
+ const uchar *buf, uint32 seq_number,
+ unsigned char calc_md5_mac[16])
+{
+ const size_t offset_end_of_sig = (smb_ss_field + 8);
+ unsigned char sequence_buf[8];
+ struct MD5Context md5_ctx;
+#if 0
+ /* JRA - apparently this is incorrect. */
+ unsigned char key_buf[16];
+#endif
+
+ /*
+ * Firstly put the sequence number into the first 4 bytes.
+ * and zero out the next 4 bytes.
+ *
+ * We do this here, to avoid modifying the packet.
+ */
+
+ DEBUG(10,("simple_packet_signature: sequence number %u\n", seq_number ));
+
+ SIVAL(sequence_buf, 0, seq_number);
+ SIVAL(sequence_buf, 4, 0);
+
+ /* Calculate the 16 byte MAC - but don't alter the data in the
+ incoming packet.
+
+ This makes for a bit of fussing about, but it's not too bad.
+ */
+ MD5Init(&md5_ctx);
+
+ /* intialise with the key */
+ MD5Update(&md5_ctx, data->mac_key.data, data->mac_key.length);
+#if 0
+ /* JRA - apparently this is incorrect. */
+ /* NB. When making and verifying SMB signatures, Windows apparently
+ zero-pads the key to 128 bits if it isn't long enough.
+ From Nalin Dahyabhai <nalin@redhat.com> */
+ if (data->mac_key.length < sizeof(key_buf)) {
+ memset(key_buf, 0, sizeof(key_buf));
+ MD5Update(&md5_ctx, key_buf, sizeof(key_buf) - data->mac_key.length);
+ }
+#endif
+
+ /* copy in the first bit of the SMB header */
+ MD5Update(&md5_ctx, buf + 4, smb_ss_field - 4);
+
+ /* copy in the sequence number, instead of the signature */
+ MD5Update(&md5_ctx, sequence_buf, sizeof(sequence_buf));
+
+ /* copy in the rest of the packet in, skipping the signature */
+ MD5Update(&md5_ctx, buf + offset_end_of_sig,
+ smb_len(buf) - (offset_end_of_sig - 4));
+
+ /* calculate the MD5 sig */
+ MD5Final(calc_md5_mac, &md5_ctx);
+}
+
+
+/***********************************************************
+ SMB signing - Client implementation - send the MAC.
+************************************************************/
+
+static void client_sign_outgoing_message(char *outbuf, struct smb_sign_info *si)
+{
+ unsigned char calc_md5_mac[16];
+ struct smb_basic_signing_context *data =
+ (struct smb_basic_signing_context *)si->signing_context;
+
+ if (!si->doing_signing)
+ return;
+
+ /* JRA Paranioa test - we should be able to get rid of this... */
+ if (smb_len(outbuf) < (smb_ss_field + 8 - 4)) {
+ DEBUG(1, ("client_sign_outgoing_message: Logic error. Can't check signature on short packet! smb_len = %u\n",
+ smb_len(outbuf) ));
+ abort();
+ }
+
+ /* mark the packet as signed - BEFORE we sign it...*/
+ mark_packet_signed(outbuf);
+
+ simple_packet_signature(data, (const unsigned char *)outbuf,
+ data->send_seq_num, calc_md5_mac);
+
+ DEBUG(10, ("client_sign_outgoing_message: sent SMB signature of\n"));
+ dump_data(10, calc_md5_mac, 8);
+
+ memcpy(&outbuf[smb_ss_field], calc_md5_mac, 8);
+
+/* cli->outbuf[smb_ss_field+2]=0;
+ Uncomment this to test if the remote server actually verifies signatures...*/
+
+ /* Instead of re-introducing the trans_info_conect we
+ used to have here, we use the fact that during a
+ SMBtrans/SMBtrans2/SMBnttrans send that the mid stays
+ constant. This means that calling store_sequence_for_reply()
+ will return False for all trans secondaries, as the mid is already
+ on the stored sequence list. As the send_seqence_number must
+ remain constant for all primary+secondary trans sends, we
+ only increment the send sequence number when we successfully
+ add a new entry to the outstanding sequence list. This means
+ I can isolate the fix here rather than re-adding the trans
+ signing on/off calls in libsmb/clitrans2.c JRA.
+ */
+
+ if (store_sequence_for_reply(&data->outstanding_packet_list, SVAL(outbuf,smb_mid), data->send_seq_num + 1)) {
+ data->send_seq_num += 2;
+ }
+}
+
+/***********************************************************
+ SMB signing - Client implementation - check a MAC sent by server.
+************************************************************/
+
+static bool client_check_incoming_message(const char *inbuf,
+ struct smb_sign_info *si,
+ bool must_be_ok)
+{
+ bool good;
+ uint32 reply_seq_number;
+ unsigned char calc_md5_mac[16];
+ unsigned char *server_sent_mac;
+
+ struct smb_basic_signing_context *data =
+ (struct smb_basic_signing_context *)si->signing_context;
+
+ if (!si->doing_signing)
+ return True;
+
+ if (smb_len(inbuf) < (smb_ss_field + 8 - 4)) {
+ DEBUG(1, ("client_check_incoming_message: Can't check signature on short packet! smb_len = %u\n", smb_len(inbuf)));
+ return False;
+ }
+
+ if (!get_sequence_for_reply(&data->outstanding_packet_list, SVAL(inbuf, smb_mid), &reply_seq_number)) {
+ DEBUG(1, ("client_check_incoming_message: received message "
+ "with mid %u with no matching send record.\n", (unsigned int)SVAL(inbuf, smb_mid) ));
+ return False;
+ }
+
+ simple_packet_signature(data, (const unsigned char *)inbuf,
+ reply_seq_number, calc_md5_mac);
+
+ server_sent_mac = (unsigned char *)&inbuf[smb_ss_field];
+ good = (memcmp(server_sent_mac, calc_md5_mac, 8) == 0);
+
+ if (!good) {
+ DEBUG(5, ("client_check_incoming_message: BAD SIG: wanted SMB signature of\n"));
+ dump_data(5, calc_md5_mac, 8);
+
+ DEBUG(5, ("client_check_incoming_message: BAD SIG: got SMB signature of\n"));
+ dump_data(5, server_sent_mac, 8);
+#if 1 /* JRATEST */
+ {
+ int i;
+ for (i = -5; i < 5; i++) {
+ simple_packet_signature(data, (const unsigned char *)inbuf, reply_seq_number+i, calc_md5_mac);
+ if (memcmp(server_sent_mac, calc_md5_mac, 8) == 0) {
+ DEBUG(0,("client_check_incoming_message: out of seq. seq num %u matches. \
+We were expecting seq %u\n", reply_seq_number+i, reply_seq_number ));
+ break;
+ }
+ }
+ }
+#endif /* JRATEST */
+
+ } else {
+ DEBUG(10, ("client_check_incoming_message: seq %u: got good SMB signature of\n", (unsigned int)reply_seq_number));
+ dump_data(10, server_sent_mac, 8);
+ }
+ return signing_good(inbuf, si, good, reply_seq_number, must_be_ok);
+}
+
+/***********************************************************
+ SMB signing - Simple implementation - free signing context
+************************************************************/
+
+static void simple_free_signing_context(struct smb_sign_info *si)
+{
+ struct smb_basic_signing_context *data =
+ (struct smb_basic_signing_context *)si->signing_context;
+ struct outstanding_packet_lookup *list;
+ struct outstanding_packet_lookup *next;
+
+ for (list = data->outstanding_packet_list; list; list = next) {
+ next = list->next;
+ DLIST_REMOVE(data->outstanding_packet_list, list);
+ SAFE_FREE(list);
+ }
+
+ data_blob_free(&data->mac_key);
+
+ SAFE_FREE(si->signing_context);
+
+ return;
+}
+
+/***********************************************************
+ SMB signing - Simple implementation - setup the MAC key.
+************************************************************/
+
+bool cli_simple_set_signing(struct cli_state *cli,
+ const DATA_BLOB user_session_key,
+ const DATA_BLOB response)
+{
+ struct smb_basic_signing_context *data;
+
+ if (!user_session_key.length)
+ return False;
+
+ if (!cli_set_smb_signing_common(cli)) {
+ return False;
+ }
+
+ if (!set_smb_signing_real_common(&cli->sign_info)) {
+ return False;
+ }
+
+ data = SMB_XMALLOC_P(struct smb_basic_signing_context);
+ memset(data, '\0', sizeof(*data));
+
+ cli->sign_info.signing_context = data;
+
+ data->mac_key = data_blob(NULL, response.length + user_session_key.length);
+
+ memcpy(&data->mac_key.data[0], user_session_key.data, user_session_key.length);
+
+ DEBUG(10, ("cli_simple_set_signing: user_session_key\n"));
+ dump_data(10, user_session_key.data, user_session_key.length);
+
+ if (response.length) {
+ memcpy(&data->mac_key.data[user_session_key.length],response.data, response.length);
+ DEBUG(10, ("cli_simple_set_signing: response_data\n"));
+ dump_data(10, response.data, response.length);
+ } else {
+ DEBUG(10, ("cli_simple_set_signing: NULL response_data\n"));
+ }
+
+ dump_data_pw("MAC ssession key is:\n", data->mac_key.data, data->mac_key.length);
+
+ /* Initialise the sequence number */
+ data->send_seq_num = 0;
+
+ /* Initialise the list of outstanding packets */
+ data->outstanding_packet_list = NULL;
+
+ cli->sign_info.sign_outgoing_message = client_sign_outgoing_message;
+ cli->sign_info.check_incoming_message = client_check_incoming_message;
+ cli->sign_info.free_signing_context = simple_free_signing_context;
+
+ return True;
+}
+
+/***********************************************************
+ SMB signing - TEMP implementation - calculate a MAC to send.
+************************************************************/
+
+static void temp_sign_outgoing_message(char *outbuf, struct smb_sign_info *si)
+{
+ /* mark the packet as signed - BEFORE we sign it...*/
+ mark_packet_signed(outbuf);
+
+ /* I wonder what BSRSPYL stands for - but this is what MS
+ actually sends! */
+ memcpy(&outbuf[smb_ss_field], "BSRSPYL ", 8);
+ return;
+}
+
+/***********************************************************
+ SMB signing - TEMP implementation - check a MAC sent by server.
+************************************************************/
+
+static bool temp_check_incoming_message(const char *inbuf,
+ struct smb_sign_info *si, bool foo)
+{
+ return True;
+}
+
+/***********************************************************
+ SMB signing - TEMP implementation - free signing context
+************************************************************/
+
+static void temp_free_signing_context(struct smb_sign_info *si)
+{
+ return;
+}
+
+/***********************************************************
+ SMB signing - NULL implementation - setup the MAC key.
+************************************************************/
+
+bool cli_null_set_signing(struct cli_state *cli)
+{
+ return null_set_signing(&cli->sign_info);
+}
+
+/***********************************************************
+ SMB signing - temp implementation - setup the MAC key.
+************************************************************/
+
+bool cli_temp_set_signing(struct cli_state *cli)
+{
+ if (!cli_set_smb_signing_common(cli)) {
+ return False;
+ }
+
+ cli->sign_info.signing_context = NULL;
+
+ cli->sign_info.sign_outgoing_message = temp_sign_outgoing_message;
+ cli->sign_info.check_incoming_message = temp_check_incoming_message;
+ cli->sign_info.free_signing_context = temp_free_signing_context;
+
+ return True;
+}
+
+void cli_free_signing_context(struct cli_state *cli)
+{
+ free_signing_context(&cli->sign_info);
+}
+
+/**
+ * Sign a packet with the current mechanism
+ */
+
+void cli_calculate_sign_mac(struct cli_state *cli, char *buf)
+{
+ cli->sign_info.sign_outgoing_message(buf, &cli->sign_info);
+}
+
+/**
+ * Check a packet with the current mechanism
+ * @return False if we had an established signing connection
+ * which had a bad checksum, True otherwise.
+ */
+
+bool cli_check_sign_mac(struct cli_state *cli, char *buf)
+{
+ if (!cli->sign_info.check_incoming_message(buf, &cli->sign_info, True)) {
+ free_signing_context(&cli->sign_info);
+ return False;
+ }
+ return True;
+}
+
+/***********************************************************
+ Enter trans/trans2/nttrans state.
+************************************************************/
+
+bool client_set_trans_sign_state_on(struct cli_state *cli, uint16 mid)
+{
+ struct smb_sign_info *si = &cli->sign_info;
+ struct smb_basic_signing_context *data = (struct smb_basic_signing_context *)si->signing_context;
+
+ if (!si->doing_signing) {
+ return True;
+ }
+
+ if (!data) {
+ return False;
+ }
+
+ if (!set_sequence_can_delete_flag(&data->outstanding_packet_list, mid, False)) {
+ return False;
+ }
+
+ return True;
+}
+
+/***********************************************************
+ Leave trans/trans2/nttrans state.
+************************************************************/
+
+bool client_set_trans_sign_state_off(struct cli_state *cli, uint16 mid)
+{
+ uint32 reply_seq_num;
+ struct smb_sign_info *si = &cli->sign_info;
+ struct smb_basic_signing_context *data = (struct smb_basic_signing_context *)si->signing_context;
+
+ if (!si->doing_signing) {
+ return True;
+ }
+
+ if (!data) {
+ return False;
+ }
+
+ if (!set_sequence_can_delete_flag(&data->outstanding_packet_list, mid, True)) {
+ return False;
+ }
+
+ /* Now delete the stored mid entry. */
+ if (!get_sequence_for_reply(&data->outstanding_packet_list, mid, &reply_seq_num)) {
+ return False;
+ }
+
+ return True;
+}
+
+/***********************************************************
+ Is client signing on ?
+************************************************************/
+
+bool client_is_signing_on(struct cli_state *cli)
+{
+ struct smb_sign_info *si = &cli->sign_info;
+ return si->doing_signing;
+}
+
+/***********************************************************
+ SMB signing - Server implementation - send the MAC.
+************************************************************/
+
+static void srv_sign_outgoing_message(char *outbuf, struct smb_sign_info *si)
+{
+ unsigned char calc_md5_mac[16];
+ struct smb_basic_signing_context *data =
+ (struct smb_basic_signing_context *)si->signing_context;
+ uint32 send_seq_number = data->send_seq_num-1;
+ uint16 mid;
+
+ if (!si->doing_signing) {
+ return;
+ }
+
+ /* JRA Paranioa test - we should be able to get rid of this... */
+ if (smb_len(outbuf) < (smb_ss_field + 8 - 4)) {
+ DEBUG(1, ("srv_sign_outgoing_message: Logic error. Can't send signature on short packet! smb_len = %u\n",
+ smb_len(outbuf) ));
+ abort();
+ }
+
+ /* mark the packet as signed - BEFORE we sign it...*/
+ mark_packet_signed(outbuf);
+
+ mid = SVAL(outbuf, smb_mid);
+
+ /* See if this is a reply for a deferred packet. */
+ get_sequence_for_reply(&data->outstanding_packet_list, mid, &send_seq_number);
+
+ simple_packet_signature(data, (const unsigned char *)outbuf, send_seq_number, calc_md5_mac);
+
+ DEBUG(10, ("srv_sign_outgoing_message: seq %u: sent SMB signature of\n", (unsigned int)send_seq_number));
+ dump_data(10, calc_md5_mac, 8);
+
+ memcpy(&outbuf[smb_ss_field], calc_md5_mac, 8);
+
+/* cli->outbuf[smb_ss_field+2]=0;
+ Uncomment this to test if the remote client actually verifies signatures...*/
+}
+
+/***********************************************************
+ SMB signing - Server implementation - check a MAC sent by server.
+************************************************************/
+
+static bool srv_check_incoming_message(const char *inbuf,
+ struct smb_sign_info *si,
+ bool must_be_ok)
+{
+ bool good;
+ struct smb_basic_signing_context *data =
+ (struct smb_basic_signing_context *)si->signing_context;
+ uint32 reply_seq_number = data->send_seq_num;
+ uint32 saved_seq;
+ unsigned char calc_md5_mac[16];
+ unsigned char *server_sent_mac;
+
+ if (!si->doing_signing)
+ return True;
+
+ if (smb_len(inbuf) < (smb_ss_field + 8 - 4)) {
+ DEBUG(1, ("srv_check_incoming_message: Can't check signature on short packet! smb_len = %u\n", smb_len(inbuf)));
+ return False;
+ }
+
+ /* We always increment the sequence number. */
+ data->send_seq_num += 2;
+
+ saved_seq = reply_seq_number;
+ simple_packet_signature(data, (const unsigned char *)inbuf, reply_seq_number, calc_md5_mac);
+
+ server_sent_mac = (unsigned char *)&inbuf[smb_ss_field];
+ good = (memcmp(server_sent_mac, calc_md5_mac, 8) == 0);
+
+ if (!good) {
+
+ if (saved_seq) {
+ DEBUG(0, ("srv_check_incoming_message: BAD SIG: seq %u wanted SMB signature of\n",
+ (unsigned int)saved_seq));
+ dump_data(5, calc_md5_mac, 8);
+
+ DEBUG(0, ("srv_check_incoming_message: BAD SIG: seq %u got SMB signature of\n",
+ (unsigned int)reply_seq_number));
+ dump_data(5, server_sent_mac, 8);
+ }
+
+#if 1 /* JRATEST */
+ {
+ int i;
+ reply_seq_number -= 5;
+ for (i = 0; i < 10; i++, reply_seq_number++) {
+ simple_packet_signature(data, (const unsigned char *)inbuf, reply_seq_number, calc_md5_mac);
+ if (memcmp(server_sent_mac, calc_md5_mac, 8) == 0) {
+ DEBUG(0,("srv_check_incoming_message: out of seq. seq num %u matches. \
+We were expecting seq %u\n", reply_seq_number, saved_seq ));
+ break;
+ }
+ }
+ }
+#endif /* JRATEST */
+
+ } else {
+ DEBUG(10, ("srv_check_incoming_message: seq %u: (current is %u) got good SMB signature of\n", (unsigned int)reply_seq_number, (unsigned int)data->send_seq_num));
+ dump_data(10, server_sent_mac, 8);
+ }
+
+ return (signing_good(inbuf, si, good, saved_seq, must_be_ok));
+}
+
+/***********************************************************
+ SMB signing - server API's.
+************************************************************/
+
+static struct smb_sign_info srv_sign_info = {
+ null_sign_outgoing_message,
+ null_check_incoming_message,
+ null_free_signing_context,
+ NULL,
+ False,
+ False,
+ False,
+ False
+};
+
+/***********************************************************
+ Turn signing off or on for oplock break code.
+************************************************************/
+
+bool srv_oplock_set_signing(bool onoff)
+{
+ bool ret = srv_sign_info.doing_signing;
+ srv_sign_info.doing_signing = onoff;
+ return ret;
+}
+
+/***********************************************************
+ Called to validate an incoming packet from the client.
+************************************************************/
+
+bool srv_check_sign_mac(const char *inbuf, bool must_be_ok)
+{
+ /* Check if it's a non-session message. */
+ if(CVAL(inbuf,0)) {
+ return True;
+ }
+
+ return srv_sign_info.check_incoming_message(inbuf, &srv_sign_info, must_be_ok);
+}
+
+/***********************************************************
+ Called to sign an outgoing packet to the client.
+************************************************************/
+
+void srv_calculate_sign_mac(char *outbuf)
+{
+ /* Check if it's a non-session message. */
+ if(CVAL(outbuf,0)) {
+ return;
+ }
+
+ srv_sign_info.sign_outgoing_message(outbuf, &srv_sign_info);
+}
+
+/***********************************************************
+ Called by server to defer an outgoing packet.
+************************************************************/
+
+void srv_defer_sign_response(uint16 mid)
+{
+ struct smb_basic_signing_context *data;
+
+ if (!srv_sign_info.doing_signing)
+ return;
+
+ data = (struct smb_basic_signing_context *)srv_sign_info.signing_context;
+
+ if (!data)
+ return;
+
+ /*
+ * Ensure we only store this mid reply once...
+ */
+
+ store_sequence_for_reply(&data->outstanding_packet_list, mid,
+ data->send_seq_num-1);
+}
+
+/***********************************************************
+ Called to remove sequence records when a deferred packet is
+ cancelled by mid. This should never find one....
+************************************************************/
+
+void srv_cancel_sign_response(uint16 mid)
+{
+ struct smb_basic_signing_context *data;
+ uint32 dummy_seq;
+
+ if (!srv_sign_info.doing_signing)
+ return;
+
+ data = (struct smb_basic_signing_context *)srv_sign_info.signing_context;
+
+ if (!data)
+ return;
+
+ DEBUG(10,("srv_cancel_sign_response: for mid %u\n", (unsigned int)mid ));
+
+ while (get_sequence_for_reply(&data->outstanding_packet_list, mid, &dummy_seq))
+ ;
+
+ /* cancel doesn't send a reply so doesn't burn a sequence number. */
+ data->send_seq_num -= 1;
+}
+
+/***********************************************************
+ Called by server negprot when signing has been negotiated.
+************************************************************/
+
+void srv_set_signing_negotiated(void)
+{
+ srv_sign_info.allow_smb_signing = True;
+ srv_sign_info.negotiated_smb_signing = True;
+ if (lp_server_signing() == Required)
+ srv_sign_info.mandatory_signing = True;
+
+ srv_sign_info.sign_outgoing_message = temp_sign_outgoing_message;
+ srv_sign_info.check_incoming_message = temp_check_incoming_message;
+ srv_sign_info.free_signing_context = temp_free_signing_context;
+}
+
+/***********************************************************
+ Returns whether signing is active. We can't use sendfile or raw
+ reads/writes if it is.
+************************************************************/
+
+bool srv_is_signing_active(void)
+{
+ return srv_sign_info.doing_signing;
+}
+
+
+/***********************************************************
+ Returns whether signing is negotiated. We can't use it unless it was
+ in the negprot.
+************************************************************/
+
+bool srv_is_signing_negotiated(void)
+{
+ return srv_sign_info.negotiated_smb_signing;
+}
+
+/***********************************************************
+ Returns whether signing is actually happening
+************************************************************/
+
+bool srv_signing_started(void)
+{
+ struct smb_basic_signing_context *data;
+
+ if (!srv_sign_info.doing_signing) {
+ return False;
+ }
+
+ data = (struct smb_basic_signing_context *)srv_sign_info.signing_context;
+ if (!data)
+ return False;
+
+ if (data->send_seq_num == 0) {
+ return False;
+ }
+
+ return True;
+}
+
+/***********************************************************
+ Turn on signing from this packet onwards.
+************************************************************/
+
+void srv_set_signing(const DATA_BLOB user_session_key, const DATA_BLOB response)
+{
+ struct smb_basic_signing_context *data;
+
+ if (!user_session_key.length)
+ return;
+
+ if (!srv_sign_info.negotiated_smb_signing && !srv_sign_info.mandatory_signing) {
+ DEBUG(5,("srv_set_signing: signing negotiated = %u, mandatory_signing = %u. Not allowing smb signing.\n",
+ (unsigned int)srv_sign_info.negotiated_smb_signing,
+ (unsigned int)srv_sign_info.mandatory_signing ));
+ return;
+ }
+
+ /* Once we've turned on, ignore any more sessionsetups. */
+ if (srv_sign_info.doing_signing) {
+ return;
+ }
+
+ if (srv_sign_info.free_signing_context)
+ srv_sign_info.free_signing_context(&srv_sign_info);
+
+ srv_sign_info.doing_signing = True;
+
+ data = SMB_XMALLOC_P(struct smb_basic_signing_context);
+ memset(data, '\0', sizeof(*data));
+
+ srv_sign_info.signing_context = data;
+
+ data->mac_key = data_blob(NULL, response.length + user_session_key.length);
+
+ memcpy(&data->mac_key.data[0], user_session_key.data, user_session_key.length);
+ if (response.length)
+ memcpy(&data->mac_key.data[user_session_key.length],response.data, response.length);
+
+ dump_data_pw("MAC ssession key is:\n", data->mac_key.data, data->mac_key.length);
+
+ DEBUG(3,("srv_set_signing: turning on SMB signing: signing negotiated = %s, mandatory_signing = %s.\n",
+ BOOLSTR(srv_sign_info.negotiated_smb_signing),
+ BOOLSTR(srv_sign_info.mandatory_signing) ));
+
+ /* Initialise the sequence number */
+ data->send_seq_num = 0;
+
+ /* Initialise the list of outstanding packets */
+ data->outstanding_packet_list = NULL;
+
+ srv_sign_info.sign_outgoing_message = srv_sign_outgoing_message;
+ srv_sign_info.check_incoming_message = srv_check_incoming_message;
+ srv_sign_info.free_signing_context = simple_free_signing_context;
+}
diff --git a/source3/libsmb/smbdes.c b/source3/libsmb/smbdes.c
new file mode 100644
index 0000000000..98d5cd05b7
--- /dev/null
+++ b/source3/libsmb/smbdes.c
@@ -0,0 +1,420 @@
+/*
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/* 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 x_or(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);
+
+ x_or(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);
+
+ x_or(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);
+}
+
+/* Convert a 7 byte string to an 8 byte key. */
+static void str_to_key(const unsigned char str[7], unsigned char key[8])
+{
+ 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);
+ }
+}
+
+
+void des_crypt56(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};
+ des_crypt56(p16, sp8, p14, 1);
+ des_crypt56(p16+8, sp8, p14+7, 1);
+}
+
+void E_P24(const unsigned char *p21, const unsigned char *c8, unsigned char *p24)
+{
+ des_crypt56(p24, c8, p21, 1);
+ des_crypt56(p24+8, c8, p21+7, 1);
+ des_crypt56(p24+16, c8, p21+14, 1);
+}
+
+void D_P16(const unsigned char *p14, const unsigned char *in, unsigned char *out)
+{
+ des_crypt56(out, in, p14, 0);
+ des_crypt56(out+8, in+8, p14+7, 0);
+}
+
+void E_old_pw_hash( unsigned char *p14, const unsigned char *in, unsigned char *out)
+{
+ des_crypt56(out, in, p14, 1);
+ des_crypt56(out+8, in+8, p14+7, 1);
+}
+
+/* forward des encryption with a 128 bit key */
+void des_crypt128(unsigned char out[8], const unsigned char in[8], const unsigned char key[16])
+{
+ unsigned char buf[8];
+
+ des_crypt56(buf, in, key, 1);
+ des_crypt56(out, buf, key+9, 1);
+}
+
+/* forward des encryption with a 64 bit key */
+void des_crypt64(unsigned char out[8], const unsigned char in[8], const unsigned char key[8])
+{
+ unsigned char buf[8];
+ unsigned char key2[8];
+
+ memset(key2,'\0',8);
+ des_crypt56(buf, in, key, 1);
+ key2[0] = key[7];
+ des_crypt56(out, buf, key2, 1);
+}
+
+/* des encryption with a 112 bit (14 byte) key */
+/* Note that if the forw is 1, and key is actually 8 bytes of key, followed by 6 bytes of zeros,
+ this is identical to des_crypt64(). JRA. */
+
+void des_crypt112(unsigned char out[8], const unsigned char in[8], const unsigned char key[14], int forw)
+{
+ unsigned char buf[8];
+ des_crypt56(buf, in, key, forw);
+ des_crypt56(out, buf, key+7, forw);
+}
+
+void cred_hash3(unsigned char *out, const unsigned char *in, const unsigned char *key, int forw)
+{
+ unsigned char key2[8];
+
+ memset(key2,'\0',8);
+ des_crypt56(out, in, key, forw);
+ key2[0] = key[7];
+ des_crypt56(out + 8, in + 8, key2, forw);
+}
+
+/* des encryption of a 16 byte lump of data with a 112 bit key */
+/* Note that if the key is actually 8 bytes of key, followed by 6 bytes of zeros,
+ this is identical to cred_hash3(). JRA. */
+
+void des_crypt112_16(unsigned char out[16], unsigned char in[16], const unsigned char key[14], int forw)
+{
+ des_crypt56(out, in, key, forw);
+ des_crypt56(out + 8, in + 8, key+7, forw);
+}
+
+/*****************************************************************
+ arc4 crypt/decrypt with a 16 byte key.
+*****************************************************************/
+
+void SamOEMhash( unsigned char *data, const unsigned char key[16], size_t len)
+{
+ unsigned char arc4_state[258];
+
+ smb_arc4_init(arc4_state, key, 16);
+ smb_arc4_crypt(arc4_state, data, len);
+}
+
+void SamOEMhashBlob( unsigned char *data, size_t len, DATA_BLOB *key)
+{
+ unsigned char arc4_state[258];
+
+ smb_arc4_init(arc4_state, key->data, key->length);
+ smb_arc4_crypt(arc4_state, data, len);
+}
+
+/* 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);
+
+ des_crypt56(out, in, s, forw);
+ des_crypt56(out+8, in+8, s+7, forw);
+}
diff --git a/source3/libsmb/smbencrypt.c b/source3/libsmb/smbencrypt.c
new file mode 100644
index 0000000000..0742976635
--- /dev/null
+++ b/source3/libsmb/smbencrypt.c
@@ -0,0 +1,898 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ 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.
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2002-2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "byteorder.h"
+
+void SMBencrypt_hash(const uchar lm_hash[16], const uchar *c8, uchar p24[24])
+{
+ uchar p21[21];
+
+ memset(p21,'\0',21);
+ memcpy(p21, lm_hash, 16);
+
+ SMBOWFencrypt(p21, c8, p24);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("SMBencrypt_hash: lm#, challenge, response\n"));
+ dump_data(100, p21, 16);
+ dump_data(100, c8, 8);
+ dump_data(100, p24, 24);
+#endif
+}
+
+/*
+ This implements the X/Open SMB password encryption
+ It takes a password ('unix' string), a 8 byte "crypt key"
+ and puts 24 bytes of encrypted password into p24
+
+ Returns False if password must have been truncated to create LM hash
+*/
+
+bool SMBencrypt(const char *passwd, const uchar *c8, uchar p24[24])
+{
+ bool ret;
+ uchar lm_hash[16];
+
+ ret = E_deshash(passwd, lm_hash);
+ SMBencrypt_hash(lm_hash, c8, p24);
+ return ret;
+}
+
+/**
+ * Creates the MD4 Hash of the users password in NT UNICODE.
+ * @param passwd password in 'unix' charset.
+ * @param p16 return password hashed with md4, caller allocated 16 byte buffer
+ */
+
+void E_md4hash(const char *passwd, uchar p16[16])
+{
+ int len;
+ smb_ucs2_t wpwd[129];
+
+ /* 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);
+ ZERO_STRUCT(wpwd);
+}
+
+/**
+ * Creates the MD5 Hash of a combination of 16 byte salt and 16 byte NT hash.
+ * @param 16 byte salt.
+ * @param 16 byte NT hash.
+ * @param 16 byte return hashed with md5, caller allocated 16 byte buffer
+ */
+
+void E_md5hash(const uchar salt[16], const uchar nthash[16], uchar hash_out[16])
+{
+ struct MD5Context tctx;
+ uchar array[32];
+
+ memset(hash_out, '\0', 16);
+ memcpy(array, salt, 16);
+ memcpy(&array[16], nthash, 16);
+ MD5Init(&tctx);
+ MD5Update(&tctx, array, 32);
+ MD5Final(hash_out, &tctx);
+}
+
+/**
+ * Creates the DES forward-only Hash of the users password in DOS ASCII charset
+ * @param passwd password in 'unix' charset.
+ * @param p16 return password hashed with DES, caller allocated 16 byte buffer
+ * @return False if password was > 14 characters, and therefore may be incorrect, otherwise True
+ * @note p16 is filled in regardless
+ */
+
+bool E_deshash(const char *passwd, uchar p16[16])
+{
+ bool ret = True;
+ fstring dospwd;
+ ZERO_STRUCT(dospwd);
+
+ /* Password must be converted to DOS charset - null terminated, uppercase. */
+ push_ascii(dospwd, passwd, sizeof(dospwd), STR_UPPER|STR_TERMINATE);
+
+ /* Only the fisrt 14 chars are considered, password need not be null terminated. */
+ E_P16((const unsigned char *)dospwd, p16);
+
+ if (strlen(dospwd) > 14) {
+ ret = False;
+ }
+
+ ZERO_STRUCT(dospwd);
+
+ return ret;
+}
+
+/**
+ * Creates the MD4 and DES (LM) Hash of the users password.
+ * MD4 is of the NT Unicode, DES is of the DOS UPPERCASE password.
+ * @param passwd password in 'unix' charset.
+ * @param nt_p16 return password hashed with md4, caller allocated 16 byte buffer
+ * @param p16 return password hashed with des, caller allocated 16 byte buffer
+ */
+
+/* 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])
+{
+ /* Calculate the MD4 hash (NT compatible) of the password */
+ memset(nt_p16, '\0', 16);
+ E_md4hash(pwd, nt_p16);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("nt_lm_owf_gen: pwd, nt#\n"));
+ dump_data(120, (uint8 *)pwd, strlen(pwd));
+ dump_data(100, nt_p16, 16);
+#endif
+
+ E_deshash(pwd, (uchar *)p16);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("nt_lm_owf_gen: pwd, lm#\n"));
+ dump_data(120, (uint8 *)pwd, strlen(pwd));
+ dump_data(100, p16, 16);
+#endif
+}
+
+/* Does both the NTLMv2 owfs of a user's password */
+bool ntv2_owf_gen(const uchar owf[16],
+ const char *user_in, const char *domain_in,
+ bool upper_case_domain, /* Transform the domain into UPPER case */
+ uchar kr_buf[16])
+{
+ smb_ucs2_t *user;
+ smb_ucs2_t *domain;
+
+ size_t user_byte_len;
+ size_t domain_byte_len;
+
+ HMACMD5Context ctx;
+
+ if (!push_ucs2_allocate(&user, user_in, &user_byte_len)) {
+ DEBUG(0, ("push_uss2_allocate() for user failed: %s\n",
+ strerror(errno)));
+ return False;
+ }
+
+ if (!push_ucs2_allocate(&domain, domain_in, &domain_byte_len)) {
+ DEBUG(0, ("push_uss2_allocate() for domain failed: %s\n",
+ strerror(errno)));
+ SAFE_FREE(user);
+ return False;
+ }
+
+ strupper_w(user);
+
+ if (upper_case_domain)
+ strupper_w(domain);
+
+ SMB_ASSERT(user_byte_len >= 2);
+ SMB_ASSERT(domain_byte_len >= 2);
+
+ /* We don't want null termination */
+ user_byte_len = user_byte_len - 2;
+ domain_byte_len = domain_byte_len - 2;
+
+ hmac_md5_init_limK_to_64(owf, 16, &ctx);
+ hmac_md5_update((const unsigned char *)user, user_byte_len, &ctx);
+ hmac_md5_update((const unsigned char *)domain, domain_byte_len, &ctx);
+ hmac_md5_final(kr_buf, &ctx);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("ntv2_owf_gen: user, domain, owfkey, kr\n"));
+ dump_data(100, (uint8 *)user, user_byte_len);
+ dump_data(100, (uint8 *)domain, domain_byte_len);
+ dump_data(100, (uint8 *)owf, 16);
+ dump_data(100, (uint8 *)kr_buf, 16);
+#endif
+
+ SAFE_FREE(user);
+ SAFE_FREE(domain);
+ return True;
+}
+
+/* Does the des encryption from the NT or LM MD4 hash. */
+void SMBOWFencrypt(const uchar passwd[16], const uchar *c8, uchar p24[24])
+{
+ uchar p21[21];
+
+ ZERO_STRUCT(p21);
+
+ memcpy(p21, passwd, 16);
+ E_P24(p21, c8, p24);
+}
+
+/* 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])
+{
+ uchar p21[21];
+
+ 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, p21, 21);
+ dump_data(100, ntlmchalresp, 8);
+ dump_data(100, p24, 24);
+#endif
+}
+
+
+/* Does the des encryption. */
+
+void SMBNTencrypt_hash(const uchar nt_hash[16], uchar *c8, uchar *p24)
+{
+ uchar p21[21];
+
+ memset(p21,'\0',21);
+ memcpy(p21, nt_hash, 16);
+ SMBOWFencrypt(p21, c8, p24);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("SMBNTencrypt: nt#, challenge, response\n"));
+ dump_data(100, p21, 16);
+ dump_data(100, c8, 8);
+ dump_data(100, p24, 24);
+#endif
+}
+
+/* Does the NT MD4 hash then des encryption. Plaintext version of the above. */
+
+void SMBNTencrypt(const char *passwd, uchar *c8, uchar *p24)
+{
+ uchar nt_hash[16];
+ E_md4hash(passwd, nt_hash);
+ SMBNTencrypt_hash(nt_hash, c8, p24);
+}
+
+/* Does the md5 encryption from the Key Response for NTLMv2. */
+void SMBOWFencrypt_ntv2(const uchar kr[16],
+ const DATA_BLOB *srv_chal,
+ const DATA_BLOB *cli_chal,
+ uchar 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(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])
+{
+ /* a very nice, 128 bit, variable session key */
+
+ 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])
+{
+ /* yes, this session key does not change - yes, this
+ is a problem - but it is 128 bits */
+
+ mdfour((unsigned char *)sess_key, kr, 16);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("SMBsesskeygen_ntv1:\n"));
+ dump_data(100, sess_key, 16);
+#endif
+}
+
+void SMBsesskeygen_lm_sess_key(const uchar lm_hash[16],
+ const uchar lm_resp[24], /* only uses 8 */
+ uint8 sess_key[16])
+{
+ uchar p24[24];
+ uchar partial_lm_hash[16];
+
+ memcpy(partial_lm_hash, lm_hash, 8);
+ memset(partial_lm_hash + 8, 0xbd, 8);
+
+ SMBOWFencrypt(partial_lm_hash, lm_resp, p24);
+
+ memcpy(sess_key, p24, 16);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100, ("SMBsesskeygen_lmv1_jerry:\n"));
+ dump_data(100, sess_key, 16);
+#endif
+}
+
+DATA_BLOB NTLMv2_generate_names_blob(const char *hostname,
+ const char *domain)
+{
+ DATA_BLOB names_blob = data_blob_null;
+
+ msrpc_gen(&names_blob, "aaa",
+ NTLMSSP_NAME_TYPE_DOMAIN, domain,
+ NTLMSSP_NAME_TYPE_SERVER, hostname,
+ 0, "");
+ return names_blob;
+}
+
+static DATA_BLOB NTLMv2_generate_client_data(const DATA_BLOB *names_blob)
+{
+ uchar client_chal[8];
+ DATA_BLOB response = data_blob_null;
+ char long_date[8];
+
+ generate_random_buffer(client_chal, sizeof(client_chal));
+
+ put_long_date(long_date, time(NULL));
+
+ /* See http://www.ubiqx.org/cifs/SMB.html#SMB.8.5 */
+
+ msrpc_gen(&response, "ddbbdb",
+ 0x00000101, /* Header */
+ 0, /* 'Reserved' */
+ long_date, 8, /* Timestamp */
+ client_chal, 8, /* client challenge */
+ 0, /* Unknown */
+ names_blob->data, names_blob->length); /* End of name list */
+
+ return response;
+}
+
+static DATA_BLOB NTLMv2_generate_response(const uchar ntlm_v2_hash[16],
+ const DATA_BLOB *server_chal,
+ const DATA_BLOB *names_blob)
+{
+ uchar ntlmv2_response[16];
+ DATA_BLOB ntlmv2_client_data;
+ DATA_BLOB final_response;
+
+ /* NTLMv2 */
+ /* generate some data to pass into the response function - including
+ the hostname and domain name of the server */
+ ntlmv2_client_data = NTLMv2_generate_client_data(names_blob);
+
+ /* Given that data, and the challenge from the server, generate a response */
+ SMBOWFencrypt_ntv2(ntlm_v2_hash, server_chal, &ntlmv2_client_data, ntlmv2_response);
+
+ final_response = data_blob(NULL, sizeof(ntlmv2_response) + ntlmv2_client_data.length);
+
+ memcpy(final_response.data, ntlmv2_response, sizeof(ntlmv2_response));
+
+ memcpy(final_response.data+sizeof(ntlmv2_response),
+ ntlmv2_client_data.data, ntlmv2_client_data.length);
+
+ data_blob_free(&ntlmv2_client_data);
+
+ return final_response;
+}
+
+static DATA_BLOB LMv2_generate_response(const uchar ntlm_v2_hash[16],
+ const DATA_BLOB *server_chal)
+{
+ uchar lmv2_response[16];
+ DATA_BLOB lmv2_client_data = data_blob(NULL, 8);
+ DATA_BLOB final_response = data_blob(NULL, 24);
+
+ /* LMv2 */
+ /* client-supplied random data */
+ generate_random_buffer(lmv2_client_data.data, lmv2_client_data.length);
+
+ /* Given that data, and the challenge from the server, generate a response */
+ SMBOWFencrypt_ntv2(ntlm_v2_hash, server_chal, &lmv2_client_data, lmv2_response);
+ memcpy(final_response.data, lmv2_response, sizeof(lmv2_response));
+
+ /* after the first 16 bytes is the random data we generated above,
+ so the server can verify us with it */
+ memcpy(final_response.data+sizeof(lmv2_response),
+ lmv2_client_data.data, lmv2_client_data.length);
+
+ data_blob_free(&lmv2_client_data);
+
+ return final_response;
+}
+
+bool SMBNTLMv2encrypt_hash(const char *user, const char *domain, const uchar nt_hash[16],
+ const DATA_BLOB *server_chal,
+ const DATA_BLOB *names_blob,
+ DATA_BLOB *lm_response, DATA_BLOB *nt_response,
+ DATA_BLOB *user_session_key)
+{
+ uchar ntlm_v2_hash[16];
+
+ /* We don't use the NT# directly. Instead we use it mashed up with
+ the username and domain.
+ This prevents username swapping during the auth exchange
+ */
+ if (!ntv2_owf_gen(nt_hash, user, domain, False, ntlm_v2_hash)) {
+ return False;
+ }
+
+ if (nt_response) {
+ *nt_response = NTLMv2_generate_response(ntlm_v2_hash, server_chal,
+ names_blob);
+ if (user_session_key) {
+ *user_session_key = data_blob(NULL, 16);
+
+ /* The NTLMv2 calculations also provide a session key, for signing etc later */
+ /* use only the first 16 bytes of nt_response for session key */
+ SMBsesskeygen_ntv2(ntlm_v2_hash, nt_response->data, user_session_key->data);
+ }
+ }
+
+ /* LMv2 */
+
+ if (lm_response) {
+ *lm_response = LMv2_generate_response(ntlm_v2_hash, server_chal);
+ }
+
+ return True;
+}
+
+/* Plaintext version of the above. */
+
+bool SMBNTLMv2encrypt(const char *user, const char *domain, const char *password,
+ const DATA_BLOB *server_chal,
+ const DATA_BLOB *names_blob,
+ DATA_BLOB *lm_response, DATA_BLOB *nt_response,
+ DATA_BLOB *user_session_key)
+{
+ uchar nt_hash[16];
+ E_md4hash(password, nt_hash);
+
+ return SMBNTLMv2encrypt_hash(user, domain, nt_hash,
+ server_chal,
+ names_blob,
+ lm_response, nt_response,
+ user_session_key);
+}
+
+/***********************************************************
+ encode a password buffer with a unicode password. The buffer
+ is filled with random data to make it harder to attack.
+************************************************************/
+bool encode_pw_buffer(uint8 buffer[516], const char *password, int string_flags)
+{
+ uchar new_pw[512];
+ size_t new_pw_len;
+
+ /* the incoming buffer can be any alignment. */
+ string_flags |= STR_NOALIGN;
+
+ new_pw_len = push_string(NULL, new_pw,
+ password,
+ sizeof(new_pw), string_flags);
+
+ memcpy(&buffer[512 - new_pw_len], new_pw, new_pw_len);
+
+ generate_random_buffer(buffer, 512 - new_pw_len);
+
+ /*
+ * The length of the new password is in the last 4 bytes of
+ * the data buffer.
+ */
+ SIVAL(buffer, 512, new_pw_len);
+ ZERO_STRUCT(new_pw);
+ 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(TALLOC_CTX *ctx,
+ uint8 in_buffer[516],
+ char **pp_new_pwrd,
+ uint32 *new_pw_len,
+ int string_flags)
+{
+ int byte_len=0;
+
+ *pp_new_pwrd = NULL;
+ *new_pw_len = 0;
+
+ /* the incoming buffer can be any alignment. */
+ string_flags |= STR_NOALIGN;
+
+ /*
+ Warning !!! : This function is called from some rpc call.
+ The password IN the buffer may be 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 the size of the password buffer */
+ if ( (byte_len < 0) || (byte_len > 512)) {
+ 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. */
+ *new_pw_len = pull_string_talloc(ctx,
+ NULL,
+ 0,
+ pp_new_pwrd,
+ &in_buffer[512 - byte_len],
+ byte_len,
+ string_flags);
+
+ if (!*pp_new_pwrd || *new_pw_len == 0) {
+ DEBUG(0, ("decode_pw_buffer: pull_string_talloc failed\n"));
+ return false;
+ }
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("decode_pw_buffer: new_pwrd: "));
+ dump_data(100, (uint8 *)*pp_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;
+}
+
+/***********************************************************
+ Decode an arc4 encrypted password change buffer.
+************************************************************/
+
+void encode_or_decode_arc4_passwd_buffer(unsigned char pw_buf[532], const DATA_BLOB *psession_key)
+{
+ struct MD5Context tctx;
+ unsigned char key_out[16];
+
+ /* Confounder is last 16 bytes. */
+
+ MD5Init(&tctx);
+ MD5Update(&tctx, &pw_buf[516], 16);
+ MD5Update(&tctx, psession_key->data, psession_key->length);
+ MD5Final(key_out, &tctx);
+ /* arc4 with key_out. */
+ SamOEMhash(pw_buf, key_out, 516);
+}
+
+/***********************************************************
+ Encrypt/Decrypt used for LSA secrets and trusted domain
+ passwords.
+************************************************************/
+
+void sess_crypt_blob(DATA_BLOB *out, const DATA_BLOB *in, const DATA_BLOB *session_key, int forward)
+{
+ int i, k;
+
+ for (i=0,k=0;
+ i<in->length;
+ i += 8, k += 7) {
+ uint8 bin[8], bout[8], key[7];
+
+ memset(bin, 0, 8);
+ memcpy(bin, &in->data[i], MIN(8, in->length-i));
+
+ if (k + 7 > session_key->length) {
+ k = (session_key->length - k);
+ }
+ memcpy(key, &session_key->data[k], 7);
+
+ des_crypt56(bout, bin, key, forward?1:0);
+
+ memcpy(&out->data[i], bout, MIN(8, in->length-i));
+ }
+}
+
+/* Decrypts password-blob with session-key
+ * @param nt_hash NT hash for the session key
+ * @param data_in DATA_BLOB encrypted password
+ *
+ * Returns cleartext password in CH_UNIX
+ * Caller must free the returned string
+ */
+
+char *decrypt_trustdom_secret(uint8_t nt_hash[16], DATA_BLOB *data_in)
+{
+ DATA_BLOB data_out, sess_key;
+ uint32_t length;
+ uint32_t version;
+ fstring cleartextpwd;
+
+ if (!data_in || !nt_hash)
+ return NULL;
+
+ /* hashed twice with md4 */
+ mdfour(nt_hash, nt_hash, 16);
+
+ /* 16-Byte session-key */
+ sess_key = data_blob(nt_hash, 16);
+ if (sess_key.data == NULL)
+ return NULL;
+
+ data_out = data_blob(NULL, data_in->length);
+ if (data_out.data == NULL)
+ return NULL;
+
+ /* decrypt with des3 */
+ sess_crypt_blob(&data_out, data_in, &sess_key, 0);
+
+ /* 4 Byte length, 4 Byte version */
+ length = IVAL(data_out.data, 0);
+ version = IVAL(data_out.data, 4);
+
+ if (length > data_in->length - 8) {
+ DEBUG(0,("decrypt_trustdom_secret: invalid length (%d)\n", length));
+ return NULL;
+ }
+
+ if (version != 1) {
+ DEBUG(0,("decrypt_trustdom_secret: unknown version number (%d)\n", version));
+ return NULL;
+ }
+
+ rpcstr_pull(cleartextpwd, data_out.data + 8, sizeof(fstring), length, 0 );
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("decrypt_trustdom_secret: length is: %d, version is: %d, password is: %s\n",
+ length, version, cleartextpwd));
+#endif
+
+ data_blob_free(&data_out);
+ data_blob_free(&sess_key);
+
+ return SMB_STRDUP(cleartextpwd);
+
+}
+
+/* encode a wkssvc_PasswordBuffer:
+ *
+ * similar to samr_CryptPasswordEx. Different: 8byte confounder (instead of
+ * 16byte), confounder in front of the 516 byte buffer (instead of after that
+ * buffer), calling MD5Update() first with session_key and then with confounder
+ * (vice versa in samr) - Guenther */
+
+void encode_wkssvc_join_password_buffer(TALLOC_CTX *mem_ctx,
+ const char *pwd,
+ DATA_BLOB *session_key,
+ struct wkssvc_PasswordBuffer **pwd_buf)
+{
+ uint8_t buffer[516];
+ struct MD5Context ctx;
+ struct wkssvc_PasswordBuffer *my_pwd_buf = NULL;
+ DATA_BLOB confounded_session_key;
+ int confounder_len = 8;
+ uint8_t confounder[8];
+
+ my_pwd_buf = talloc_zero(mem_ctx, struct wkssvc_PasswordBuffer);
+ if (!my_pwd_buf) {
+ return;
+ }
+
+ confounded_session_key = data_blob_talloc(mem_ctx, NULL, 16);
+
+ encode_pw_buffer(buffer, pwd, STR_UNICODE);
+
+ generate_random_buffer((uint8_t *)confounder, confounder_len);
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, session_key->data, session_key->length);
+ MD5Update(&ctx, confounder, confounder_len);
+ MD5Final(confounded_session_key.data, &ctx);
+
+ SamOEMhashBlob(buffer, 516, &confounded_session_key);
+
+ memcpy(&my_pwd_buf->data[0], confounder, confounder_len);
+ memcpy(&my_pwd_buf->data[8], buffer, 516);
+
+ data_blob_free(&confounded_session_key);
+
+ *pwd_buf = my_pwd_buf;
+}
+
+WERROR decode_wkssvc_join_password_buffer(TALLOC_CTX *mem_ctx,
+ struct wkssvc_PasswordBuffer *pwd_buf,
+ DATA_BLOB *session_key,
+ char **pwd)
+{
+ uint8_t buffer[516];
+ struct MD5Context ctx;
+ uint32_t pwd_len;
+
+ DATA_BLOB confounded_session_key;
+
+ int confounder_len = 8;
+ uint8_t confounder[8];
+
+ *pwd = NULL;
+
+ if (!pwd_buf) {
+ return WERR_BAD_PASSWORD;
+ }
+
+ if (session_key->length != 16) {
+ DEBUG(10,("invalid session key\n"));
+ return WERR_BAD_PASSWORD;
+ }
+
+ confounded_session_key = data_blob_talloc(mem_ctx, NULL, 16);
+
+ memcpy(&confounder, &pwd_buf->data[0], confounder_len);
+ memcpy(&buffer, &pwd_buf->data[8], 516);
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, session_key->data, session_key->length);
+ MD5Update(&ctx, confounder, confounder_len);
+ MD5Final(confounded_session_key.data, &ctx);
+
+ SamOEMhashBlob(buffer, 516, &confounded_session_key);
+
+ if (!decode_pw_buffer(mem_ctx, buffer, pwd, &pwd_len, STR_UNICODE)) {
+ data_blob_free(&confounded_session_key);
+ return WERR_BAD_PASSWORD;
+ }
+
+ data_blob_free(&confounded_session_key);
+
+ return WERR_OK;
+}
+
+DATA_BLOB decrypt_drsuapi_blob(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *session_key,
+ bool rcrypt,
+ uint32_t rid,
+ const DATA_BLOB *buffer)
+{
+ DATA_BLOB confounder;
+ DATA_BLOB enc_buffer;
+
+ struct MD5Context md5;
+ uint8_t _enc_key[16];
+ DATA_BLOB enc_key;
+
+ DATA_BLOB dec_buffer;
+
+ uint32_t crc32_given;
+ uint32_t crc32_calc;
+ DATA_BLOB checked_buffer;
+
+ DATA_BLOB plain_buffer;
+
+ /*
+ * the combination "c[3] s[1] e[1] d[0]..."
+ * was successful!!!!!!!!!!!!!!!!!!!!!!!!!!
+ */
+
+ /*
+ * the first 16 bytes at the beginning are the confounder
+ * followed by the 4 byte crc32 checksum
+ */
+ if (buffer->length < 20) {
+ return data_blob_const(NULL, 0);
+ }
+ confounder = data_blob_const(buffer->data, 16);
+ enc_buffer = data_blob_const(buffer->data + 16, buffer->length - 16);
+
+ /*
+ * build the encryption key md5 over the session key followed
+ * by the confounder
+ *
+ * here the gensec session key is used and
+ * not the dcerpc ncacn_ip_tcp "SystemLibraryDTC" key!
+ */
+ enc_key = data_blob_const(_enc_key, sizeof(_enc_key));
+ MD5Init(&md5);
+ MD5Update(&md5, session_key->data, session_key->length);
+ MD5Update(&md5, confounder.data, confounder.length);
+ MD5Final(enc_key.data, &md5);
+
+ /*
+ * copy the encrypted buffer part and
+ * decrypt it using the created encryption key using arcfour
+ */
+ dec_buffer = data_blob_talloc(mem_ctx, enc_buffer.data, enc_buffer.length);
+ if (!dec_buffer.data) {
+ return data_blob_const(NULL, 0);
+ }
+ SamOEMhashBlob(dec_buffer.data, dec_buffer.length, &enc_key);
+
+ /*
+ * the first 4 byte are the crc32 checksum
+ * of the remaining bytes
+ */
+ crc32_given = IVAL(dec_buffer.data, 0);
+ crc32_calc = crc32_calc_buffer((const char *)dec_buffer.data + 4 , dec_buffer.length - 4);
+ if (crc32_given != crc32_calc) {
+ DEBUG(1,("CRC32: given[0x%08X] calc[0x%08X]\n",
+ crc32_given, crc32_calc));
+ return data_blob_const(NULL, 0);
+ }
+ checked_buffer = data_blob_talloc(mem_ctx, dec_buffer.data + 4, dec_buffer.length - 4);
+ if (!checked_buffer.data) {
+ return data_blob_const(NULL, 0);
+ }
+
+ /*
+ * some attributes seem to be in a usable form after this decryption
+ * (supplementalCredentials, priorValue, currentValue, trustAuthOutgoing,
+ * trustAuthIncoming, initialAuthOutgoing, initialAuthIncoming)
+ * At least supplementalCredentials contains plaintext
+ * like "Primary:Kerberos" (in unicode form)
+ *
+ * some attributes seem to have some additional encryption
+ * dBCSPwd, unicodePwd, ntPwdHistory, lmPwdHistory
+ *
+ * it's the sam_rid_crypt() function, as the value is constant,
+ * so it doesn't depend on sessionkeys.
+ */
+ if (rcrypt) {
+ uint32_t i, num_hashes;
+
+ if ((checked_buffer.length % 16) != 0) {
+ return data_blob_const(NULL, 0);
+ }
+
+ plain_buffer = data_blob_talloc(mem_ctx, checked_buffer.data, checked_buffer.length);
+ if (!plain_buffer.data) {
+ return data_blob_const(NULL, 0);
+ }
+
+ num_hashes = plain_buffer.length / 16;
+ for (i = 0; i < num_hashes; i++) {
+ uint32_t offset = i * 16;
+ sam_pwd_hash(rid, checked_buffer.data + offset, plain_buffer.data + offset, 0);
+ }
+ } else {
+ plain_buffer = checked_buffer;
+ }
+
+ return plain_buffer;
+}
+
+
diff --git a/source3/libsmb/smberr.c b/source3/libsmb/smberr.c
new file mode 100644
index 0000000000..f4a13983f0
--- /dev/null
+++ b/source3/libsmb/smberr.c
@@ -0,0 +1,266 @@
+/*
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/* 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
+{
+ const char *name;
+ int code;
+ const 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"},
+ {"ERRbaddirectory", ERRbaddirectory, "Bad directory name"},
+ {"ERRunknownlevel",ERRunknownlevel, "Unknown info level"},
+ {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;
+ const char *e_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
+****************************************************************************/
+const char *smb_dos_err_name(uint8 e_class, uint16 num)
+{
+ char *result;
+ int i,j;
+
+ for (i=0;err_classes[i].e_class;i++)
+ if (err_classes[i].code == e_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;
+ }
+ }
+ result = talloc_asprintf(talloc_tos(), "%d", num);
+ SMB_ASSERT(result != NULL);
+ return result;
+ }
+
+ result = talloc_asprintf(talloc_tos(), "Error: Unknown error class "
+ "(%d,%d)", e_class,num);
+ SMB_ASSERT(result != NULL);
+ return result;
+}
+
+/* Return a string for a DOS error */
+
+const 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.
+****************************************************************************/
+const char *smb_dos_err_class(uint8 e_class)
+{
+ char *result;
+ int i;
+
+ for (i=0;err_classes[i].e_class;i++) {
+ if (err_classes[i].code == e_class) {
+ return err_classes[i].e_class;
+ }
+ }
+
+ result = talloc_asprintf(talloc_tos(), "Error: Unknown class (%d)",
+ e_class);
+ SMB_ASSERT(result != NULL);
+ return result;
+}
+
+/****************************************************************************
+return a SMB string from an SMB buffer
+****************************************************************************/
+char *smb_dos_errstr(char *inbuf)
+{
+ char *result;
+ int e_class = CVAL(inbuf,smb_rcls);
+ int num = SVAL(inbuf,smb_err);
+ int i,j;
+
+ for (i=0;err_classes[i].e_class;i++)
+ if (err_classes[i].code == e_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)
+ result = talloc_asprintf(
+ talloc_tos(), "%s - %s (%s)",
+ err_classes[i].e_class,
+ err[j].name,err[j].message);
+ else
+ result = talloc_asprintf(
+ talloc_tos(), "%s - %s",
+ err_classes[i].e_class,
+ err[j].name);
+ goto done;
+ }
+ }
+
+ result = talloc_asprintf(talloc_tos(), "%s - %d",
+ err_classes[i].e_class, num);
+ goto done;
+ }
+
+ result = talloc_asprintf(talloc_tos(), "Error: Unknown error (%d,%d)",
+ e_class, num);
+ done:
+ SMB_ASSERT(result != NULL);
+ return result;
+}
+
+/*****************************************************************************
+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/spnego.c b/source3/libsmb/spnego.c
new file mode 100644
index 0000000000..57b2d8060b
--- /dev/null
+++ b/source3/libsmb/spnego.c
@@ -0,0 +1,346 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ RFC2478 Compliant SPNEGO implementation
+
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_AUTH
+
+static bool read_negTokenInit(ASN1_DATA *asn1, negTokenInit_t *token)
+{
+ ZERO_STRUCTP(token);
+
+ asn1_start_tag(asn1, ASN1_CONTEXT(0));
+ asn1_start_tag(asn1, ASN1_SEQUENCE(0));
+
+ while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
+ int i;
+
+ switch (asn1->data[asn1->ofs]) {
+ /* Read mechTypes */
+ case ASN1_CONTEXT(0):
+ asn1_start_tag(asn1, ASN1_CONTEXT(0));
+ asn1_start_tag(asn1, ASN1_SEQUENCE(0));
+
+ token->mechTypes = SMB_MALLOC_P(const char *);
+ for (i = 0; !asn1->has_error &&
+ 0 < asn1_tag_remaining(asn1); i++) {
+ char *p_oid = NULL;
+ token->mechTypes =
+ SMB_REALLOC_ARRAY(token->mechTypes, const char *, i + 2);
+ if (!token->mechTypes) {
+ asn1->has_error = True;
+ return False;
+ }
+ asn1_read_OID(asn1, &p_oid);
+ token->mechTypes[i] = p_oid;
+ }
+ token->mechTypes[i] = NULL;
+
+ asn1_end_tag(asn1);
+ asn1_end_tag(asn1);
+ break;
+ /* Read reqFlags */
+ case ASN1_CONTEXT(1):
+ asn1_start_tag(asn1, ASN1_CONTEXT(1));
+ asn1_read_Integer(asn1, &token->reqFlags);
+ token->reqFlags |= SPNEGO_REQ_FLAG;
+ asn1_end_tag(asn1);
+ break;
+ /* Read mechToken */
+ case ASN1_CONTEXT(2):
+ asn1_start_tag(asn1, ASN1_CONTEXT(2));
+ asn1_read_OctetString(asn1, &token->mechToken);
+ asn1_end_tag(asn1);
+ break;
+ /* Read mecListMIC */
+ case ASN1_CONTEXT(3):
+ asn1_start_tag(asn1, ASN1_CONTEXT(3));
+ if (asn1->data[asn1->ofs] == ASN1_OCTET_STRING) {
+ asn1_read_OctetString(asn1,
+ &token->mechListMIC);
+ } else {
+ /* RFC 2478 says we have an Octet String here,
+ but W2k sends something different... */
+ char *mechListMIC;
+ asn1_push_tag(asn1, ASN1_SEQUENCE(0));
+ asn1_push_tag(asn1, ASN1_CONTEXT(0));
+ asn1_read_GeneralString(asn1, &mechListMIC);
+ asn1_pop_tag(asn1);
+ asn1_pop_tag(asn1);
+
+ token->mechListMIC =
+ data_blob(mechListMIC, strlen(mechListMIC));
+ SAFE_FREE(mechListMIC);
+ }
+ asn1_end_tag(asn1);
+ break;
+ default:
+ asn1->has_error = True;
+ break;
+ }
+ }
+
+ asn1_end_tag(asn1);
+ asn1_end_tag(asn1);
+
+ return !asn1->has_error;
+}
+
+static bool write_negTokenInit(ASN1_DATA *asn1, negTokenInit_t *token)
+{
+ asn1_push_tag(asn1, ASN1_CONTEXT(0));
+ asn1_push_tag(asn1, ASN1_SEQUENCE(0));
+
+ /* Write mechTypes */
+ if (token->mechTypes && *token->mechTypes) {
+ int i;
+
+ asn1_push_tag(asn1, ASN1_CONTEXT(0));
+ asn1_push_tag(asn1, ASN1_SEQUENCE(0));
+ for (i = 0; token->mechTypes[i]; i++) {
+ asn1_write_OID(asn1, token->mechTypes[i]);
+ }
+ asn1_pop_tag(asn1);
+ asn1_pop_tag(asn1);
+ }
+
+ /* write reqFlags */
+ if (token->reqFlags & SPNEGO_REQ_FLAG) {
+ int flags = token->reqFlags & ~SPNEGO_REQ_FLAG;
+
+ asn1_push_tag(asn1, ASN1_CONTEXT(1));
+ asn1_write_Integer(asn1, flags);
+ asn1_pop_tag(asn1);
+ }
+
+ /* write mechToken */
+ if (token->mechToken.data) {
+ asn1_push_tag(asn1, ASN1_CONTEXT(2));
+ asn1_write_OctetString(asn1, token->mechToken.data,
+ token->mechToken.length);
+ asn1_pop_tag(asn1);
+ }
+
+ /* write mechListMIC */
+ if (token->mechListMIC.data) {
+ asn1_push_tag(asn1, ASN1_CONTEXT(3));
+#if 0
+ /* This is what RFC 2478 says ... */
+ asn1_write_OctetString(asn1, token->mechListMIC.data,
+ token->mechListMIC.length);
+#else
+ /* ... but unfortunately this is what Windows
+ sends/expects */
+ asn1_push_tag(asn1, ASN1_SEQUENCE(0));
+ asn1_push_tag(asn1, ASN1_CONTEXT(0));
+ asn1_push_tag(asn1, ASN1_GENERAL_STRING);
+ asn1_write(asn1, token->mechListMIC.data,
+ token->mechListMIC.length);
+ asn1_pop_tag(asn1);
+ asn1_pop_tag(asn1);
+ asn1_pop_tag(asn1);
+#endif
+ asn1_pop_tag(asn1);
+ }
+
+ asn1_pop_tag(asn1);
+ asn1_pop_tag(asn1);
+
+ return !asn1->has_error;
+}
+
+static bool read_negTokenTarg(ASN1_DATA *asn1, negTokenTarg_t *token)
+{
+ ZERO_STRUCTP(token);
+
+ asn1_start_tag(asn1, ASN1_CONTEXT(1));
+ asn1_start_tag(asn1, ASN1_SEQUENCE(0));
+
+ while (!asn1->has_error && 0 < asn1_tag_remaining(asn1)) {
+ switch (asn1->data[asn1->ofs]) {
+ case ASN1_CONTEXT(0):
+ asn1_start_tag(asn1, ASN1_CONTEXT(0));
+ asn1_start_tag(asn1, ASN1_ENUMERATED);
+ asn1_read_uint8(asn1, &token->negResult);
+ asn1_end_tag(asn1);
+ asn1_end_tag(asn1);
+ break;
+ case ASN1_CONTEXT(1):
+ asn1_start_tag(asn1, ASN1_CONTEXT(1));
+ asn1_read_OID(asn1, &token->supportedMech);
+ asn1_end_tag(asn1);
+ break;
+ case ASN1_CONTEXT(2):
+ asn1_start_tag(asn1, ASN1_CONTEXT(2));
+ asn1_read_OctetString(asn1, &token->responseToken);
+ asn1_end_tag(asn1);
+ break;
+ case ASN1_CONTEXT(3):
+ asn1_start_tag(asn1, ASN1_CONTEXT(3));
+ asn1_read_OctetString(asn1, &token->mechListMIC);
+ asn1_end_tag(asn1);
+ break;
+ default:
+ asn1->has_error = True;
+ break;
+ }
+ }
+
+ asn1_end_tag(asn1);
+ asn1_end_tag(asn1);
+
+ return !asn1->has_error;
+}
+
+static bool write_negTokenTarg(ASN1_DATA *asn1, negTokenTarg_t *token)
+{
+ asn1_push_tag(asn1, ASN1_CONTEXT(1));
+ asn1_push_tag(asn1, ASN1_SEQUENCE(0));
+
+ asn1_push_tag(asn1, ASN1_CONTEXT(0));
+ asn1_write_enumerated(asn1, token->negResult);
+ asn1_pop_tag(asn1);
+
+ if (token->supportedMech) {
+ asn1_push_tag(asn1, ASN1_CONTEXT(1));
+ asn1_write_OID(asn1, token->supportedMech);
+ asn1_pop_tag(asn1);
+ }
+
+ if (token->responseToken.data) {
+ asn1_push_tag(asn1, ASN1_CONTEXT(2));
+ asn1_write_OctetString(asn1, token->responseToken.data,
+ token->responseToken.length);
+ asn1_pop_tag(asn1);
+ }
+
+ if (token->mechListMIC.data) {
+ asn1_push_tag(asn1, ASN1_CONTEXT(3));
+ asn1_write_OctetString(asn1, token->mechListMIC.data,
+ token->mechListMIC.length);
+ asn1_pop_tag(asn1);
+ }
+
+ asn1_pop_tag(asn1);
+ asn1_pop_tag(asn1);
+
+ return !asn1->has_error;
+}
+
+ssize_t read_spnego_data(DATA_BLOB data, SPNEGO_DATA *token)
+{
+ ASN1_DATA asn1;
+ ssize_t ret = -1;
+
+ ZERO_STRUCTP(token);
+ ZERO_STRUCT(asn1);
+ asn1_load(&asn1, data);
+
+ switch (asn1.data[asn1.ofs]) {
+ case ASN1_APPLICATION(0):
+ asn1_start_tag(&asn1, ASN1_APPLICATION(0));
+ asn1_check_OID(&asn1, OID_SPNEGO);
+ if (read_negTokenInit(&asn1, &token->negTokenInit)) {
+ token->type = SPNEGO_NEG_TOKEN_INIT;
+ }
+ asn1_end_tag(&asn1);
+ break;
+ case ASN1_CONTEXT(1):
+ if (read_negTokenTarg(&asn1, &token->negTokenTarg)) {
+ token->type = SPNEGO_NEG_TOKEN_TARG;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!asn1.has_error) ret = asn1.ofs;
+ asn1_free(&asn1);
+
+ return ret;
+}
+
+ssize_t write_spnego_data(DATA_BLOB *blob, SPNEGO_DATA *spnego)
+{
+ ASN1_DATA asn1;
+ ssize_t ret = -1;
+
+ ZERO_STRUCT(asn1);
+
+ switch (spnego->type) {
+ case SPNEGO_NEG_TOKEN_INIT:
+ asn1_push_tag(&asn1, ASN1_APPLICATION(0));
+ asn1_write_OID(&asn1, OID_SPNEGO);
+ write_negTokenInit(&asn1, &spnego->negTokenInit);
+ asn1_pop_tag(&asn1);
+ break;
+ case SPNEGO_NEG_TOKEN_TARG:
+ write_negTokenTarg(&asn1, &spnego->negTokenTarg);
+ break;
+ default:
+ asn1.has_error = True;
+ break;
+ }
+
+ if (!asn1.has_error) {
+ *blob = data_blob(asn1.data, asn1.length);
+ ret = asn1.ofs;
+ }
+ asn1_free(&asn1);
+
+ return ret;
+}
+
+bool free_spnego_data(SPNEGO_DATA *spnego)
+{
+ bool ret = True;
+
+ if (!spnego) goto out;
+
+ switch(spnego->type) {
+ case SPNEGO_NEG_TOKEN_INIT:
+ if (spnego->negTokenInit.mechTypes) {
+ int i;
+ for (i = 0; spnego->negTokenInit.mechTypes[i]; i++) {
+ free(CONST_DISCARD(char *,spnego->negTokenInit.mechTypes[i]));
+ }
+ free(spnego->negTokenInit.mechTypes);
+ }
+ data_blob_free(&spnego->negTokenInit.mechToken);
+ data_blob_free(&spnego->negTokenInit.mechListMIC);
+ break;
+ case SPNEGO_NEG_TOKEN_TARG:
+ if (spnego->negTokenTarg.supportedMech) {
+ free(spnego->negTokenTarg.supportedMech);
+ }
+ data_blob_free(&spnego->negTokenTarg.responseToken);
+ data_blob_free(&spnego->negTokenTarg.mechListMIC);
+ break;
+ default:
+ ret = False;
+ break;
+ }
+ ZERO_STRUCTP(spnego);
+out:
+ return ret;
+}
diff --git a/source3/libsmb/trustdom_cache.c b/source3/libsmb/trustdom_cache.c
new file mode 100644
index 0000000000..6755de3814
--- /dev/null
+++ b/source3/libsmb/trustdom_cache.c
@@ -0,0 +1,338 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Trusted domain names cache on top of gencache.
+
+ Copyright (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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_ALL /* there's no proper class yet */
+
+#define TDOMKEY_FMT "TDOM/%s"
+#define TDOMTSKEY "TDOMCACHE/TIMESTAMP"
+
+
+/**
+ * @file trustdom_cache.c
+ *
+ * Implementation of trusted domain names cache useful when
+ * samba acts as domain member server. In such case, caching
+ * domain names currently trusted gives a performance gain
+ * because there's no need to query PDC each time we need
+ * list of trusted domains
+ **/
+
+
+/**
+ * Initialise trustdom name caching system. Call gencache
+ * initialisation routine to perform necessary activities.
+ *
+ * @return true upon successful cache initialisation or
+ * false if cache init failed
+ **/
+
+bool trustdom_cache_enable(void)
+{
+ /* Init trustdom cache by calling gencache initialisation */
+ if (!gencache_init()) {
+ DEBUG(2, ("trustdomcache_enable: Couldn't initialise trustdom cache on top of gencache.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+
+/**
+ * Shutdown trustdom name caching system. Calls gencache
+ * shutdown function.
+ *
+ * @return true upon successful cache close or
+ * false if it failed
+ **/
+
+bool trustdom_cache_shutdown(void)
+{
+ /* Close trustdom cache by calling gencache shutdown */
+ if (!gencache_shutdown()) {
+ DEBUG(2, ("trustdomcache_shutdown: Couldn't shutdown trustdom cache on top of gencache.\n"));
+ return False;
+ }
+
+ return True;
+}
+
+
+/**
+ * Form up trustdom name key. It is based only
+ * on domain name now.
+ *
+ * @param name trusted domain name
+ * @return cache key for use in gencache mechanism
+ **/
+
+static char* trustdom_cache_key(const char* name)
+{
+ char* keystr = NULL;
+ asprintf_strupper_m(&keystr, TDOMKEY_FMT, name);
+
+ return keystr;
+}
+
+
+/**
+ * Store trusted domain in gencache as the domain name (key)
+ * and trusted domain's SID (value)
+ *
+ * @param name trusted domain name
+ * @param alt_name alternative trusted domain name (used in ADS domains)
+ * @param sid trusted domain's SID
+ * @param timeout cache entry expiration time
+ * @return true upon successful value storing or
+ * false if store attempt failed
+ **/
+
+bool trustdom_cache_store(char* name, char* alt_name, const DOM_SID *sid,
+ time_t timeout)
+{
+ char *key, *alt_key;
+ fstring sid_string;
+ bool ret;
+
+ /*
+ * we use gecache call to avoid annoying debug messages
+ * about initialised trustdom
+ */
+ if (!gencache_init())
+ return False;
+
+ DEBUG(5, ("trustdom_store: storing SID %s of domain %s\n",
+ sid_string_dbg(sid), name));
+
+ key = trustdom_cache_key(name);
+ alt_key = alt_name ? trustdom_cache_key(alt_name) : NULL;
+
+ /* Generate string representation domain SID */
+ sid_to_fstring(sid_string, sid);
+
+ /*
+ * try to put the names in the cache
+ */
+ if (alt_key) {
+ ret = gencache_set(alt_key, sid_string, timeout);
+ if ( ret ) {
+ ret = gencache_set(key, sid_string, timeout);
+ }
+ SAFE_FREE(alt_key);
+ SAFE_FREE(key);
+ return ret;
+ }
+
+ ret = gencache_set(key, sid_string, timeout);
+ SAFE_FREE(key);
+ return ret;
+}
+
+
+/**
+ * Fetch trusted domain's SID from the gencache.
+ * This routine can also be used to check whether given
+ * domain is currently trusted one.
+ *
+ * @param name trusted domain name
+ * @param sid trusted domain's SID to be returned
+ * @return true if entry is found or
+ * false if has expired/doesn't exist
+ **/
+
+bool trustdom_cache_fetch(const char* name, DOM_SID* sid)
+{
+ char *key = NULL, *value = NULL;
+ time_t timeout;
+
+ /* init the cache */
+ if (!gencache_init())
+ return False;
+
+ /* exit now if null pointers were passed as they're required further */
+ if (!sid)
+ return False;
+
+ /* prepare a key and get the value */
+ key = trustdom_cache_key(name);
+ if (!key)
+ return False;
+
+ if (!gencache_get(key, &value, &timeout)) {
+ DEBUG(5, ("no entry for trusted domain %s found.\n", name));
+ SAFE_FREE(key);
+ return False;
+ } else {
+ SAFE_FREE(key);
+ DEBUG(5, ("trusted domain %s found (%s)\n", name, value));
+ }
+
+ /* convert sid string representation into DOM_SID structure */
+ if(! string_to_sid(sid, value)) {
+ sid = NULL;
+ SAFE_FREE(value);
+ return False;
+ }
+
+ SAFE_FREE(value);
+ return True;
+}
+
+
+/*******************************************************************
+ fetch the timestamp from the last update
+*******************************************************************/
+
+uint32 trustdom_cache_fetch_timestamp( void )
+{
+ char *value = NULL;
+ time_t timeout;
+ uint32 timestamp;
+
+ /* init the cache */
+ if (!gencache_init())
+ return False;
+
+ if (!gencache_get(TDOMTSKEY, &value, &timeout)) {
+ DEBUG(5, ("no timestamp for trusted domain cache located.\n"));
+ SAFE_FREE(value);
+ return 0;
+ }
+
+ timestamp = atoi(value);
+
+ SAFE_FREE(value);
+ return timestamp;
+}
+
+/*******************************************************************
+ store the timestamp from the last update
+*******************************************************************/
+
+bool trustdom_cache_store_timestamp( uint32 t, time_t timeout )
+{
+ fstring value;
+
+ /* init the cache */
+ if (!gencache_init())
+ return False;
+
+ fstr_sprintf(value, "%d", t );
+
+ if (!gencache_set(TDOMTSKEY, value, timeout)) {
+ DEBUG(5, ("failed to set timestamp for trustdom_cache\n"));
+ return False;
+ }
+
+ return True;
+}
+
+
+/**
+ * Delete single trustdom entry. Look at the
+ * gencache_iterate definition.
+ *
+ **/
+
+static void flush_trustdom_name(const char* key, const char *value, time_t timeout, void* dptr)
+{
+ gencache_del(key);
+ DEBUG(5, ("Deleting entry %s\n", key));
+}
+
+
+/**
+ * Flush all the trusted domains entries from the cache.
+ **/
+
+void trustdom_cache_flush(void)
+{
+ if (!gencache_init())
+ return;
+
+ /*
+ * iterate through each TDOM cache's entry and flush it
+ * by flush_trustdom_name function
+ */
+ gencache_iterate(flush_trustdom_name, NULL, trustdom_cache_key("*"));
+ DEBUG(5, ("Trusted domains cache flushed\n"));
+}
+
+/********************************************************************
+ update the trustdom_cache if needed
+********************************************************************/
+#define TRUSTDOM_UPDATE_INTERVAL 600
+
+void update_trustdom_cache( void )
+{
+ char **domain_names;
+ DOM_SID *dom_sids;
+ uint32 num_domains;
+ uint32 last_check;
+ int time_diff;
+ TALLOC_CTX *mem_ctx = NULL;
+ time_t now = time(NULL);
+ int i;
+
+ /* get the timestamp. We have to initialise it if the last timestamp == 0 */
+ if ( (last_check = trustdom_cache_fetch_timestamp()) == 0 )
+ trustdom_cache_store_timestamp(0, now+TRUSTDOM_UPDATE_INTERVAL);
+
+ time_diff = (int) (now - last_check);
+
+ if ( (time_diff > 0) && (time_diff < TRUSTDOM_UPDATE_INTERVAL) ) {
+ DEBUG(10,("update_trustdom_cache: not time to update trustdom_cache yet\n"));
+ return;
+ }
+
+ /* note that we don't lock the timestamp. This prevents this
+ smbd from blocking all other smbd daemons while we
+ enumerate the trusted domains */
+ trustdom_cache_store_timestamp(now, now+TRUSTDOM_UPDATE_INTERVAL);
+
+ if ( !(mem_ctx = talloc_init("update_trustdom_cache")) ) {
+ DEBUG(0,("update_trustdom_cache: talloc_init() failed!\n"));
+ goto done;
+ }
+
+ /* get the domains and store them */
+
+ if ( enumerate_domain_trusts(mem_ctx, lp_workgroup(), &domain_names,
+ &num_domains, &dom_sids)) {
+ for ( i=0; i<num_domains; i++ ) {
+ trustdom_cache_store( domain_names[i], NULL, &dom_sids[i],
+ now+TRUSTDOM_UPDATE_INTERVAL);
+ }
+ } else {
+ /* we failed to fetch the list of trusted domains - restore the old
+ timestamp */
+ trustdom_cache_store_timestamp(last_check,
+ last_check+TRUSTDOM_UPDATE_INTERVAL);
+ }
+
+done:
+ talloc_destroy( mem_ctx );
+
+ return;
+}
diff --git a/source3/libsmb/trusts_util.c b/source3/libsmb/trusts_util.c
new file mode 100644
index 0000000000..08a49930b4
--- /dev/null
+++ b/source3/libsmb/trusts_util.c
@@ -0,0 +1,283 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Routines to operate on various trust relationships
+ * Copyright (C) Andrew Bartlett 2001
+ * Copyright (C) Rafal Szczesniak 2003
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+
+/*********************************************************
+ 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 rpc_pipe_client *cli, TALLOC_CTX *mem_ctx,
+ const unsigned char orig_trust_passwd_hash[16],
+ const char *new_trust_pwd_cleartext,
+ const unsigned char new_trust_passwd_hash[16],
+ uint32 sec_channel_type)
+{
+ NTSTATUS result;
+ uint32_t neg_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS;
+
+ result = rpccli_netlogon_setup_creds(cli,
+ cli->desthost, /* server name */
+ lp_workgroup(), /* domain */
+ global_myname(), /* client name */
+ global_myname(), /* machine account name */
+ orig_trust_passwd_hash,
+ sec_channel_type,
+ &neg_flags);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(3,("just_change_the_password: unable to setup creds (%s)!\n",
+ nt_errstr(result)));
+ return result;
+ }
+
+ if (neg_flags & NETLOGON_NEG_PASSWORD_SET2) {
+
+ struct netr_Authenticator clnt_creds, srv_cred;
+ struct netr_CryptPassword new_password;
+ struct samr_CryptPassword password_buf;
+
+ netlogon_creds_client_step(cli->dc, &clnt_creds);
+
+ encode_pw_buffer(password_buf.data, new_trust_pwd_cleartext, STR_UNICODE);
+
+ SamOEMhash(password_buf.data, cli->dc->sess_key, 516);
+ memcpy(new_password.data, password_buf.data, 512);
+ new_password.length = IVAL(password_buf.data, 512);
+
+ result = rpccli_netr_ServerPasswordSet2(cli, mem_ctx,
+ cli->dc->remote_machine,
+ cli->dc->mach_acct,
+ sec_channel_type,
+ global_myname(),
+ &clnt_creds,
+ &srv_cred,
+ &new_password);
+
+ /* Always check returned credentials. */
+ if (!netlogon_creds_client_check(cli->dc, &srv_cred.cred)) {
+ DEBUG(0,("rpccli_netr_ServerPasswordSet2: "
+ "credentials chain check failed\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ } else {
+
+ struct netr_Authenticator clnt_creds, srv_cred;
+ struct samr_Password new_password;
+
+ netlogon_creds_client_step(cli->dc, &clnt_creds);
+
+ cred_hash3(new_password.hash,
+ new_trust_passwd_hash,
+ cli->dc->sess_key, 1);
+
+ result = rpccli_netr_ServerPasswordSet(cli, mem_ctx,
+ cli->dc->remote_machine,
+ cli->dc->mach_acct,
+ sec_channel_type,
+ global_myname(),
+ &clnt_creds,
+ &srv_cred,
+ &new_password);
+
+ /* Always check returned credentials. */
+ if (!netlogon_creds_client_check(cli->dc, &srv_cred.cred)) {
+ DEBUG(0,("rpccli_netr_ServerPasswordSet: "
+ "credentials chain check failed\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ 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 rpc_pipe_client *cli, TALLOC_CTX *mem_ctx,
+ const char *domain,
+ unsigned char orig_trust_passwd_hash[16],
+ uint32 sec_channel_type)
+{
+ 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);
+
+ if ((new_trust_passwd = talloc_strdup(mem_ctx, str)) == NULL) {
+ DEBUG(0, ("talloc_strdup failed\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ E_md4hash(new_trust_passwd, new_trust_passwd_hash);
+
+ nt_status = just_change_the_password(cli, mem_ctx,
+ orig_trust_passwd_hash,
+ new_trust_passwd,
+ new_trust_passwd_hash,
+ sec_channel_type);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(3,("%s : trust_pw_change_and_store_it: Changed password.\n",
+ current_timestring(debug_ctx(), 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, domain, sec_channel_type)) {
+ 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 rpc_pipe_client *cli,
+ TALLOC_CTX *mem_ctx,
+ const char *domain)
+{
+ unsigned char old_trust_passwd_hash[16];
+ uint32 sec_channel_type = 0;
+
+ if (!secrets_fetch_trust_account_password(domain,
+ old_trust_passwd_hash,
+ NULL, &sec_channel_type)) {
+ 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, domain,
+ old_trust_passwd_hash,
+ sec_channel_type);
+}
+
+/*********************************************************************
+ Enumerate the list of trusted domains from a DC
+*********************************************************************/
+
+bool enumerate_domain_trusts( TALLOC_CTX *mem_ctx, const char *domain,
+ char ***domain_names, uint32 *num_domains,
+ DOM_SID **sids )
+{
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ fstring dc_name;
+ struct sockaddr_storage dc_ss;
+ uint32 enum_ctx = 0;
+ struct cli_state *cli = NULL;
+ struct rpc_pipe_client *lsa_pipe;
+ bool retry;
+ struct lsa_DomainList dom_list;
+ int i;
+
+ *domain_names = NULL;
+ *num_domains = 0;
+ *sids = NULL;
+
+ /* lookup a DC first */
+
+ if ( !get_dc_name(domain, NULL, dc_name, &dc_ss) ) {
+ DEBUG(3,("enumerate_domain_trusts: can't locate a DC for domain %s\n",
+ domain));
+ return False;
+ }
+
+ /* setup the anonymous connection */
+
+ result = cli_full_connection( &cli, global_myname(), dc_name, &dc_ss, 0, "IPC$", "IPC",
+ "", "", "", 0, Undefined, &retry);
+ if ( !NT_STATUS_IS_OK(result) )
+ goto done;
+
+ /* open the LSARPC_PIPE */
+
+ result = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc.syntax_id,
+ &lsa_pipe);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* get a handle */
+
+ result = rpccli_lsa_open_policy(lsa_pipe, mem_ctx, True,
+ LSA_POLICY_VIEW_LOCAL_INFORMATION, &pol);
+ if ( !NT_STATUS_IS_OK(result) )
+ goto done;
+
+ /* Lookup list of trusted domains */
+
+ result = rpccli_lsa_EnumTrustDom(lsa_pipe, mem_ctx,
+ &pol,
+ &enum_ctx,
+ &dom_list,
+ (uint32_t)-1);
+ if ( !NT_STATUS_IS_OK(result) )
+ goto done;
+
+ *num_domains = dom_list.count;
+
+ *domain_names = TALLOC_ZERO_ARRAY(mem_ctx, char *, *num_domains);
+ if (!*domain_names) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ *sids = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, *num_domains);
+ if (!*sids) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ for (i=0; i< *num_domains; i++) {
+ (*domain_names)[i] = CONST_DISCARD(char *, dom_list.domains[i].name.string);
+ (*sids)[i] = *dom_list.domains[i].sid;
+ }
+
+done:
+ /* cleanup */
+ if (cli) {
+ DEBUG(10,("enumerate_domain_trusts: shutting down connection...\n"));
+ cli_shutdown( cli );
+ }
+
+ return NT_STATUS_IS_OK(result);
+}
diff --git a/source3/libsmb/unexpected.c b/source3/libsmb/unexpected.c
new file mode 100644
index 0000000000..df4d2119e2
--- /dev/null
+++ b/source3/libsmb/unexpected.c
@@ -0,0 +1,202 @@
+/*
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+
+static TDB_CONTEXT *tdbd = NULL;
+
+/* the key type used in the unexpected 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
+ erroneously 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;
+ uint32_t enc_ip;
+
+ if (!tdbd) {
+ tdbd = tdb_open_log(lock_path("unexpected.tdb"), 0,
+ 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));
+
+ /* Encode the ip addr and port. */
+ enc_ip = ntohl(p->ip.s_addr);
+ SIVAL(buf,0,enc_ip);
+ SSVAL(buf,4,p->port);
+
+ len = build_packet(&buf[6], sizeof(buf)-6, p) + 6;
+
+ ZERO_STRUCT(key); /* needed for potential alignment */
+
+ key.packet_type = p->packet_type;
+ key.timestamp = p->timestamp;
+ key.count = count++;
+
+ kbuf.dptr = (uint8_t *)&key;
+ kbuf.dsize = sizeof(key);
+ dbuf.dptr = (uint8_t *)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;
+
+ if (kbuf.dsize != sizeof(key)) {
+ tdb_delete(ttdb, kbuf);
+ }
+
+ 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);
+}
+
+struct receive_unexpected_state {
+ struct packet_struct *matched_packet;
+ int match_id;
+ enum packet_type match_type;
+ const 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 *private_data)
+{
+ struct receive_unexpected_state *state =
+ (struct receive_unexpected_state *)private_data;
+ struct unexpected_key key;
+ struct in_addr ip;
+ uint32_t enc_ip;
+ int port;
+ struct packet_struct *p;
+
+ if (kbuf.dsize != sizeof(key)) {
+ return 0;
+ }
+
+ memcpy(&key, kbuf.dptr, sizeof(key));
+
+ if (key.packet_type != state->match_type) return 0;
+
+ if (dbuf.dsize < 6) {
+ return 0;
+ }
+
+ /* Decode the ip addr and port. */
+ enc_ip = IVAL(dbuf.dptr,0);
+ ip.s_addr = htonl(enc_ip);
+ port = SVAL(dbuf.dptr,4);
+
+ p = parse_packet((char *)&dbuf.dptr[6],
+ dbuf.dsize-6,
+ state->match_type,
+ ip,
+ port);
+
+ if ((state->match_type == NMB_PACKET &&
+ p->packet.nmb.header.name_trn_id == state->match_id) ||
+ (state->match_type == DGRAM_PACKET &&
+ match_mailslot_name(p, state->match_name))) {
+ state->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,
+ const char *mailslot_name)
+{
+ TDB_CONTEXT *tdb2;
+ struct receive_unexpected_state state;
+
+ tdb2 = tdb_open_log(lock_path("unexpected.tdb"), 0, 0, O_RDONLY, 0);
+ if (!tdb2) return NULL;
+
+ state.matched_packet = NULL;
+ state.match_id = id;
+ state.match_type = packet_type;
+ state.match_name = mailslot_name;
+
+ tdb_traverse(tdb2, traverse_match, &state);
+
+ tdb_close(tdb2);
+
+ return state.matched_packet;
+}