diff options
Diffstat (limited to 'source4/libcli')
129 files changed, 37934 insertions, 0 deletions
diff --git a/source4/libcli/auth/config.mk b/source4/libcli/auth/config.mk new file mode 100644 index 0000000000..498c2af258 --- /dev/null +++ b/source4/libcli/auth/config.mk @@ -0,0 +1,17 @@ +################################# +# Start SUBSYSTEM LIBCLI_AUTH +[SUBSYSTEM::LIBCLI_AUTH] +PUBLIC_DEPENDENCIES = \ + MSRPC_PARSE \ + LIBSAMBA-HOSTCONFIG +# End SUBSYSTEM LIBCLI_AUTH +################################# + +LIBCLI_AUTH_OBJ_FILES = $(addprefix $(libclisrcdir)/auth/, \ + credentials.o \ + session.o \ + smbencrypt.o \ + smbdes.o) + +PUBLIC_HEADERS += $(libclisrcdir)/auth/credentials.h +$(eval $(call proto_header_template,$(libclisrcdir)/auth/proto.h,$(LIBCLI_AUTH_OBJ_FILES:.o=.c))) diff --git a/source4/libcli/auth/credentials.c b/source4/libcli/auth/credentials.c new file mode 100644 index 0000000000..3c77b0836d --- /dev/null +++ b/source4/libcli/auth/credentials.c @@ -0,0 +1,375 @@ +/* + Unix SMB/CIFS implementation. + + code to manipulate domain credentials + + Copyright (C) Andrew Tridgell 1997-2003 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 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" +#include "system/time.h" +#include "auth/auth.h" +#include "../lib/crypto/crypto.h" +#include "libcli/auth/libcli_auth.h" + +/* + initialise the credentials state for old-style 64 bit session keys + + this call is made after the netr_ServerReqChallenge call +*/ +static void creds_init_64bit(struct creds_CredentialState *creds, + const struct netr_Credential *client_challenge, + const struct netr_Credential *server_challenge, + const struct samr_Password *machine_password) +{ + uint32_t sum[2]; + uint8_t sum2[8]; + + sum[0] = IVAL(client_challenge->data, 0) + IVAL(server_challenge->data, 0); + sum[1] = IVAL(client_challenge->data, 4) + IVAL(server_challenge->data, 4); + + SIVAL(sum2,0,sum[0]); + SIVAL(sum2,4,sum[1]); + + ZERO_STRUCT(creds->session_key); + + des_crypt128(creds->session_key, sum2, machine_password->hash); + + des_crypt112(creds->client.data, client_challenge->data, creds->session_key, 1); + des_crypt112(creds->server.data, server_challenge->data, creds->session_key, 1); + + creds->seed = creds->client; +} + +/* + initialise the credentials state for ADS-style 128 bit session keys + + this call is made after the netr_ServerReqChallenge call +*/ +static void creds_init_128bit(struct creds_CredentialState *creds, + const struct netr_Credential *client_challenge, + const struct netr_Credential *server_challenge, + const struct samr_Password *machine_password) +{ + unsigned char zero[4], tmp[16]; + HMACMD5Context ctx; + struct MD5Context md5; + + ZERO_STRUCT(creds->session_key); + + memset(zero, 0, sizeof(zero)); + + hmac_md5_init_rfc2104(machine_password->hash, sizeof(machine_password->hash), &ctx); + MD5Init(&md5); + MD5Update(&md5, zero, sizeof(zero)); + MD5Update(&md5, client_challenge->data, 8); + MD5Update(&md5, server_challenge->data, 8); + MD5Final(tmp, &md5); + hmac_md5_update(tmp, sizeof(tmp), &ctx); + hmac_md5_final(creds->session_key, &ctx); + + creds->client = *client_challenge; + creds->server = *server_challenge; + + des_crypt112(creds->client.data, client_challenge->data, creds->session_key, 1); + des_crypt112(creds->server.data, server_challenge->data, creds->session_key, 1); + + creds->seed = creds->client; +} + + +/* + step the credentials to the next element in the chain, updating the + current client and server credentials and the seed +*/ +static void creds_step(struct creds_CredentialState *creds) +{ + struct netr_Credential time_cred; + + DEBUG(5,("\tseed %08x:%08x\n", + IVAL(creds->seed.data, 0), IVAL(creds->seed.data, 4))); + + SIVAL(time_cred.data, 0, IVAL(creds->seed.data, 0) + creds->sequence); + SIVAL(time_cred.data, 4, IVAL(creds->seed.data, 4)); + + DEBUG(5,("\tseed+time %08x:%08x\n", IVAL(time_cred.data, 0), IVAL(time_cred.data, 4))); + + des_crypt112(creds->client.data, time_cred.data, creds->session_key, 1); + + DEBUG(5,("\tCLIENT %08x:%08x\n", + IVAL(creds->client.data, 0), IVAL(creds->client.data, 4))); + + SIVAL(time_cred.data, 0, IVAL(creds->seed.data, 0) + creds->sequence + 1); + SIVAL(time_cred.data, 4, IVAL(creds->seed.data, 4)); + + DEBUG(5,("\tseed+time+1 %08x:%08x\n", + IVAL(time_cred.data, 0), IVAL(time_cred.data, 4))); + + des_crypt112(creds->server.data, time_cred.data, creds->session_key, 1); + + DEBUG(5,("\tSERVER %08x:%08x\n", + IVAL(creds->server.data, 0), IVAL(creds->server.data, 4))); + + creds->seed = time_cred; +} + + +/* + DES encrypt a 8 byte LMSessionKey buffer using the Netlogon session key +*/ +void creds_des_encrypt_LMKey(struct creds_CredentialState *creds, struct netr_LMSessionKey *key) +{ + struct netr_LMSessionKey tmp; + des_crypt56(tmp.key, key->key, creds->session_key, 1); + *key = tmp; +} + +/* + DES decrypt a 8 byte LMSessionKey buffer using the Netlogon session key +*/ +void creds_des_decrypt_LMKey(struct creds_CredentialState *creds, struct netr_LMSessionKey *key) +{ + struct netr_LMSessionKey tmp; + des_crypt56(tmp.key, key->key, creds->session_key, 0); + *key = tmp; +} + +/* + DES encrypt a 16 byte password buffer using the session key +*/ +void creds_des_encrypt(struct creds_CredentialState *creds, struct samr_Password *pass) +{ + struct samr_Password tmp; + des_crypt112_16(tmp.hash, pass->hash, creds->session_key, 1); + *pass = tmp; +} + +/* + DES decrypt a 16 byte password buffer using the session key +*/ +void creds_des_decrypt(struct creds_CredentialState *creds, struct samr_Password *pass) +{ + struct samr_Password tmp; + des_crypt112_16(tmp.hash, pass->hash, creds->session_key, 0); + *pass = tmp; +} + +/* + ARCFOUR encrypt/decrypt a password buffer using the session key +*/ +void creds_arcfour_crypt(struct creds_CredentialState *creds, uint8_t *data, size_t len) +{ + DATA_BLOB session_key = data_blob(creds->session_key, 16); + + arcfour_crypt_blob(data, len, &session_key); + + data_blob_free(&session_key); +} + +/***************************************************************** +The above functions are common to the client and server interface +next comes the client specific functions +******************************************************************/ + +/* + initialise the credentials chain and return the first client + credentials +*/ +void creds_client_init(struct creds_CredentialState *creds, + const struct netr_Credential *client_challenge, + const struct netr_Credential *server_challenge, + const struct samr_Password *machine_password, + struct netr_Credential *initial_credential, + uint32_t negotiate_flags) +{ + creds->sequence = time(NULL); + creds->negotiate_flags = negotiate_flags; + + dump_data_pw("Client chall", client_challenge->data, sizeof(client_challenge->data)); + dump_data_pw("Server chall", server_challenge->data, sizeof(server_challenge->data)); + dump_data_pw("Machine Pass", machine_password->hash, sizeof(machine_password->hash)); + + if (negotiate_flags & NETLOGON_NEG_128BIT) { + creds_init_128bit(creds, client_challenge, server_challenge, machine_password); + } else { + creds_init_64bit(creds, client_challenge, server_challenge, machine_password); + } + + dump_data_pw("Session key", creds->session_key, 16); + dump_data_pw("Credential ", creds->client.data, 8); + + *initial_credential = creds->client; +} + +/* + step the 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 creds_client_authenticator(struct creds_CredentialState *creds, + struct netr_Authenticator *next) +{ + creds->sequence += 2; + creds_step(creds); + + next->cred = creds->client; + next->timestamp = creds->sequence; +} + +/* + check that a credentials reply from a server is correct +*/ +bool creds_client_check(struct creds_CredentialState *creds, + const struct netr_Credential *received_credentials) +{ + if (!received_credentials || + memcmp(received_credentials->data, creds->server.data, 8) != 0) { + DEBUG(2,("credentials check failed\n")); + return false; + } + return true; +} + + +/***************************************************************** +The above functions are common to the client and server interface +next comes the server specific functions +******************************************************************/ + +/* + initialise the credentials chain and return the first server + credentials +*/ +void creds_server_init(struct creds_CredentialState *creds, + const struct netr_Credential *client_challenge, + const struct netr_Credential *server_challenge, + const struct samr_Password *machine_password, + struct netr_Credential *initial_credential, + uint32_t negotiate_flags) +{ + if (negotiate_flags & NETLOGON_NEG_128BIT) { + creds_init_128bit(creds, client_challenge, server_challenge, + machine_password); + } else { + creds_init_64bit(creds, client_challenge, server_challenge, + machine_password); + } + + *initial_credential = creds->server; + creds->negotiate_flags = negotiate_flags; +} + +/* + check that a credentials reply from a server is correct +*/ +bool creds_server_check(const struct creds_CredentialState *creds, + const struct netr_Credential *received_credentials) +{ + if (memcmp(received_credentials->data, creds->client.data, 8) != 0) { + DEBUG(2,("credentials check failed\n")); + dump_data_pw("client creds", creds->client.data, 8); + dump_data_pw("calc creds", received_credentials->data, 8); + return false; + } + return true; +} + +NTSTATUS creds_server_step_check(struct creds_CredentialState *creds, + struct netr_Authenticator *received_authenticator, + struct netr_Authenticator *return_authenticator) +{ + if (!received_authenticator || !return_authenticator) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (!creds) { + return NT_STATUS_ACCESS_DENIED; + } + + /* TODO: this may allow the a replay attack on a non-signed + connection. Should we check that this is increasing? */ + creds->sequence = received_authenticator->timestamp; + creds_step(creds); + if (creds_server_check(creds, &received_authenticator->cred)) { + return_authenticator->cred = creds->server; + return_authenticator->timestamp = creds->sequence; + return NT_STATUS_OK; + } else { + ZERO_STRUCTP(return_authenticator); + return NT_STATUS_ACCESS_DENIED; + } +} + +void creds_decrypt_samlogon(struct creds_CredentialState *creds, + uint16_t validation_level, + union netr_Validation *validation) +{ + static const char zeros[16]; + + struct netr_SamBaseInfo *base = NULL; + switch (validation_level) { + case 2: + if (validation->sam2) { + base = &validation->sam2->base; + } + break; + case 3: + if (validation->sam3) { + base = &validation->sam3->base; + } + break; + case 6: + if (validation->sam6) { + base = &validation->sam6->base; + } + break; + default: + /* If we can't find it, we can't very well decrypt it */ + return; + } + + if (!base) { + return; + } + + /* find and decyrpt the session keys, return in parameters above */ + if (validation_level == 6) { + /* they aren't encrypted! */ + } else if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) { + if (memcmp(base->key.key, zeros, + sizeof(base->key.key)) != 0) { + creds_arcfour_crypt(creds, + base->key.key, + sizeof(base->key.key)); + } + + if (memcmp(base->LMSessKey.key, zeros, + sizeof(base->LMSessKey.key)) != 0) { + creds_arcfour_crypt(creds, + base->LMSessKey.key, + sizeof(base->LMSessKey.key)); + } + } else { + if (memcmp(base->LMSessKey.key, zeros, + sizeof(base->LMSessKey.key)) != 0) { + creds_des_decrypt_LMKey(creds, + &base->LMSessKey); + } + } +} diff --git a/source4/libcli/auth/credentials.h b/source4/libcli/auth/credentials.h new file mode 100644 index 0000000000..4e11cb090f --- /dev/null +++ b/source4/libcli/auth/credentials.h @@ -0,0 +1,46 @@ +/* + Unix SMB/CIFS implementation. + + code to manipulate domain credentials + + Copyright (C) Andrew Tridgell 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 "librpc/gen_ndr/netlogon.h" + +struct creds_CredentialState { + uint32_t negotiate_flags; + uint8_t session_key[16]; + uint32_t sequence; + struct netr_Credential seed; + struct netr_Credential client; + struct netr_Credential server; + uint16_t secure_channel_type; + const char *domain; + const char *computer_name; + const char *account_name; + struct dom_sid *sid; +}; + +/* for the timebeing, use the same neg flags as Samba3. */ +/* The 7 here seems to be required to get Win2k not to downgrade us + to NT4. Actually, anything other than 1ff would seem to do... */ +#define NETLOGON_NEG_AUTH2_FLAGS 0x000701ff + +/* these are the flags that ADS clients use */ +#define NETLOGON_NEG_AUTH2_ADS_FLAGS (0x200fbffb | NETLOGON_NEG_ARCFOUR | NETLOGON_NEG_128BIT | NETLOGON_NEG_SCHANNEL) + + diff --git a/source4/libcli/auth/libcli_auth.h b/source4/libcli/auth/libcli_auth.h new file mode 100644 index 0000000000..ec1c1e7d98 --- /dev/null +++ b/source4/libcli/auth/libcli_auth.h @@ -0,0 +1,24 @@ +/* + samba -- Unix SMB/CIFS implementation. + + 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/>. +*/ +#ifndef __LIBCLI_AUTH_H__ +#define __LIBCLI_AUTH_H__ + +#include "librpc/gen_ndr/netlogon.h" +#include "libcli/auth/credentials.h" +#include "libcli/auth/proto.h" + +#endif /* __LIBCLI_AUTH_H__ */ diff --git a/source4/libcli/auth/session.c b/source4/libcli/auth/session.c new file mode 100644 index 0000000000..10c728662d --- /dev/null +++ b/source4/libcli/auth/session.c @@ -0,0 +1,218 @@ +/* + Unix SMB/CIFS implementation. + + code to encrypt/decrypt data using the user session key + + Copyright (C) Andrew Tridgell 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" +#include "libcli/auth/libcli_auth.h" + +/* + encrypt or decrypt a blob of data using the user session key + as used in lsa_SetSecret + + before calling, the out blob must be initialised to be the same size + as the in blob +*/ +void sess_crypt_blob(DATA_BLOB *out, const DATA_BLOB *in, const DATA_BLOB *session_key, + bool forward) +{ + int i, k; + + for (i=0,k=0; + i<in->length; + i += 8, k += 7) { + uint8_t 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)); + } +} + + +/* + a convenient wrapper around sess_crypt_blob() for strings, using the LSA convention + + note that we round the length to a multiple of 8. This seems to be needed for + compatibility with windows + + caller should free using data_blob_free() +*/ +DATA_BLOB sess_encrypt_string(const char *str, const DATA_BLOB *session_key) +{ + DATA_BLOB ret, src; + int slen = strlen(str); + int dlen = (slen+7) & ~7; + + src = data_blob(NULL, 8+dlen); + if (!src.data) { + return data_blob(NULL, 0); + } + + ret = data_blob(NULL, 8+dlen); + if (!ret.data) { + data_blob_free(&src); + return data_blob(NULL, 0); + } + + SIVAL(src.data, 0, slen); + SIVAL(src.data, 4, 1); + memset(src.data+8, 0, dlen); + memcpy(src.data+8, str, slen); + + sess_crypt_blob(&ret, &src, session_key, true); + + data_blob_free(&src); + + return ret; +} + +/* + a convenient wrapper around sess_crypt_blob() for strings, using the LSA convention + + caller should free the returned string +*/ +char *sess_decrypt_string(TALLOC_CTX *mem_ctx, + DATA_BLOB *blob, const DATA_BLOB *session_key) +{ + DATA_BLOB out; + int slen; + char *ret; + + if (blob->length < 8) { + return NULL; + } + + out = data_blob_talloc(mem_ctx, NULL, blob->length); + if (!out.data) { + return NULL; + } + + sess_crypt_blob(&out, blob, session_key, false); + + if (IVAL(out.data, 4) != 1) { + DEBUG(0,("Unexpected revision number %d in session crypted string\n", + IVAL(out.data, 4))); + data_blob_free(&out); + return NULL; + } + + slen = IVAL(out.data, 0); + if (slen > blob->length - 8) { + DEBUG(0,("Invalid crypt length %d\n", slen)); + data_blob_free(&out); + return NULL; + } + + ret = talloc_strndup(mem_ctx, (const char *)(out.data+8), slen); + + data_blob_free(&out); + + DEBUG(0,("decrypted string '%s' of length %d\n", ret, slen)); + + return ret; +} + +/* + a convenient wrapper around sess_crypt_blob() for DATA_BLOBs, using the LSA convention + + note that we round the length to a multiple of 8. This seems to be needed for + compatibility with windows + + caller should free using data_blob_free() +*/ +DATA_BLOB sess_encrypt_blob(TALLOC_CTX *mem_ctx, DATA_BLOB *blob_in, const DATA_BLOB *session_key) +{ + DATA_BLOB ret, src; + int dlen = (blob_in->length+7) & ~7; + + src = data_blob_talloc(mem_ctx, NULL, 8+dlen); + if (!src.data) { + return data_blob(NULL, 0); + } + + ret = data_blob_talloc(mem_ctx, NULL, 8+dlen); + if (!ret.data) { + data_blob_free(&src); + return data_blob(NULL, 0); + } + + SIVAL(src.data, 0, blob_in->length); + SIVAL(src.data, 4, 1); + memset(src.data+8, 0, dlen); + memcpy(src.data+8, blob_in->data, blob_in->length); + + sess_crypt_blob(&ret, &src, session_key, true); + + data_blob_free(&src); + + return ret; +} + +/* + Decrypt a DATA_BLOB using the LSA convention +*/ +NTSTATUS sess_decrypt_blob(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, const DATA_BLOB *session_key, + DATA_BLOB *ret) +{ + DATA_BLOB out; + int slen; + + if (blob->length < 8) { + DEBUG(0, ("Unexpected length %d in session crypted secret (BLOB)\n", + (int)blob->length)); + return NT_STATUS_INVALID_PARAMETER; + } + + out = data_blob_talloc(mem_ctx, NULL, blob->length); + if (!out.data) { + return NT_STATUS_NO_MEMORY; + } + + sess_crypt_blob(&out, blob, session_key, false); + + if (IVAL(out.data, 4) != 1) { + DEBUG(2,("Unexpected revision number %d in session crypted secret (BLOB)\n", + IVAL(out.data, 4))); + return NT_STATUS_UNKNOWN_REVISION; + } + + slen = IVAL(out.data, 0); + if (slen > blob->length - 8) { + DEBUG(0,("Invalid crypt length %d in session crypted secret (BLOB)\n", slen)); + return NT_STATUS_WRONG_PASSWORD; + } + + *ret = data_blob_talloc(mem_ctx, out.data+8, slen); + if (slen && !ret->data) { + return NT_STATUS_NO_MEMORY; + } + + data_blob_free(&out); + + return NT_STATUS_OK; +} diff --git a/source4/libcli/auth/smbdes.c b/source4/libcli/auth/smbdes.c new file mode 100644 index 0000000000..32e65e779d --- /dev/null +++ b/source4/libcli/auth/smbdes.c @@ -0,0 +1,381 @@ +/* + 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" +#include "libcli/auth/libcli_auth.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) +*/ + + +static const uint8_t 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 uint8_t 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 uint8_t 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 uint8_t 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 uint8_t 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 uint8_t 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 uint8_t sc[16] = {1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1}; + +static const uint8_t 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 uint8_t *p, int n) +{ + int i; + for (i=0;i<n;i++) + out[i] = in[p[i]-1]; +} + +static void lshift(char *d, int count, int n) +{ + char out[64]; + int i; + for (i=0;i<n;i++) + out[i] = d[(i+count)%n]; + for (i=0;i<n;i++) + d[i] = out[i]; +} + +static void concat(char *out, char *in1, char *in2, int l1, int l2) +{ + while (l1--) + *out++ = *in1++; + while (l2--) + *out++ = *in2++; +} + +static void xor(char *out, char *in1, char *in2, int n) +{ + int i; + for (i=0;i<n;i++) + out[i] = in1[i] ^ in2[i]; +} + +static void dohash(char *out, char *in, char *key, int forw) +{ + int i, j, k; + char pk1[56]; + char c[28]; + char d[28]; + char cd[56]; + char ki[16][48]; + char pd1[64]; + char l[32], r[32]; + char rl[64]; + + permute(pk1, key, perm1, 56); + + for (i=0;i<28;i++) + c[i] = pk1[i]; + for (i=0;i<28;i++) + d[i] = pk1[i+28]; + + for (i=0;i<16;i++) { + lshift(c, sc[i], 28); + lshift(d, sc[i], 28); + + concat(cd, c, d, 28, 28); + permute(ki[i], cd, perm2, 48); + } + + permute(pd1, in, perm3, 64); + + for (j=0;j<32;j++) { + l[j] = pd1[j]; + r[j] = pd1[j+32]; + } + + for (i=0;i<16;i++) { + char er[48]; + char erk[48]; + char b[8][6]; + char cb[32]; + char pcb[32]; + char r2[32]; + + permute(er, r, perm4, 48); + + xor(erk, er, ki[forw ? i : 15 - i], 48); + + for (j=0;j<8;j++) + for (k=0;k<6;k++) + b[j][k] = erk[j*6 + k]; + + for (j=0;j<8;j++) { + int m, n; + m = (b[j][0]<<1) | b[j][5]; + + n = (b[j][1]<<3) | (b[j][2]<<2) | (b[j][3]<<1) | b[j][4]; + + for (k=0;k<4;k++) + b[j][k] = (sbox[j][m][n] & (1<<(3-k)))?1:0; + } + + for (j=0;j<8;j++) + for (k=0;k<4;k++) + cb[j*4+k] = b[j][k]; + permute(pcb, cb, perm5, 32); + + xor(r2, l, pcb, 32); + + for (j=0;j<32;j++) + l[j] = r[j]; + + for (j=0;j<32;j++) + r[j] = r2[j]; + } + + concat(rl, r, l, 32, 32); + + permute(out, rl, perm6, 64); +} + +static void str_to_key(const uint8_t *str,uint8_t *key) +{ + int i; + + key[0] = str[0]>>1; + key[1] = ((str[0]&0x01)<<6) | (str[1]>>2); + key[2] = ((str[1]&0x03)<<5) | (str[2]>>3); + key[3] = ((str[2]&0x07)<<4) | (str[3]>>4); + key[4] = ((str[3]&0x0F)<<3) | (str[4]>>5); + key[5] = ((str[4]&0x1F)<<2) | (str[5]>>6); + key[6] = ((str[5]&0x3F)<<1) | (str[6]>>7); + key[7] = str[6]&0x7F; + for (i=0;i<8;i++) { + key[i] = (key[i]<<1); + } +} + +/* + basic des crypt using a 56 bit (7 byte) key +*/ +void des_crypt56(uint8_t out[8], const uint8_t in[8], const uint8_t key[7], int forw) +{ + int i; + char outb[64]; + char inb[64]; + char keyb[64]; + uint8_t 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 uint8_t *p14,uint8_t *p16) +{ + const uint8_t 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 uint8_t *p21, const uint8_t *c8, uint8_t *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 uint8_t *p14, const uint8_t *in, uint8_t *out) +{ + des_crypt56(out, in, p14, 0); + des_crypt56(out+8, in+8, p14+7, 0); +} + +void E_old_pw_hash( uint8_t *p14, const uint8_t *in, uint8_t *out) +{ + des_crypt56(out, in, p14, 1); + des_crypt56(out+8, in+8, p14+7, 1); +} + +/* des encryption with a 128 bit key */ +void des_crypt128(uint8_t out[8], const uint8_t in[8], const uint8_t key[16]) +{ + uint8_t buf[8]; + des_crypt56(buf, in, key, 1); + des_crypt56(out, buf, key+9, 1); +} + +/* des encryption with a 64 bit key */ +void des_crypt64(uint8_t out[8], const uint8_t in[8], const uint8_t key[8], int forw) +{ + uint8_t buf[8]; + uint8_t key2[8]; + ZERO_STRUCT(key2); + des_crypt56(buf, in, key, forw); + key2[0] = key[7]; + des_crypt56(out, buf, key2, forw); +} + +/* des encryption with a 112 bit (14 byte) key */ +void des_crypt112(uint8_t out[8], const uint8_t in[8], const uint8_t key[14], int forw) +{ + uint8_t buf[8]; + des_crypt56(buf, in, key, forw); + des_crypt56(out, buf, key+7, forw); +} + +/* des encryption of a 16 byte lump of data with a 112 bit key */ +void des_crypt112_16(uint8_t out[16], uint8_t in[16], const uint8_t key[14], int forw) +{ + des_crypt56(out, in, key, forw); + des_crypt56(out + 8, in + 8, key+7, forw); +} + +/* 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_rid_crypt(uint_t rid, const uint8_t *in, uint8_t *out, int forw) +{ + uint8_t s[14]; + + s[0] = s[4] = s[8] = s[12] = (uint8_t)(rid & 0xFF); + s[1] = s[5] = s[9] = s[13] = (uint8_t)((rid >> 8) & 0xFF); + s[2] = s[6] = s[10] = (uint8_t)((rid >> 16) & 0xFF); + s[3] = s[7] = s[11] = (uint8_t)((rid >> 24) & 0xFF); + + des_crypt56(out, in, s, forw); + des_crypt56(out+8, in+8, s+7, forw); +} diff --git a/source4/libcli/auth/smbencrypt.c b/source4/libcli/auth/smbencrypt.c new file mode 100644 index 0000000000..c6118c6568 --- /dev/null +++ b/source4/libcli/auth/smbencrypt.c @@ -0,0 +1,595 @@ +/* + 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 "system/time.h" +#include "auth/ntlmssp/ntlmssp.h" +#include "auth/ntlmssp/msrpc_parse.h" +#include "../lib/crypto/crypto.h" +#include "libcli/auth/libcli_auth.h" + +/* + 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 uint8_t *c8, uint8_t p24[24]) +{ + bool ret; + uint8_t p21[21]; + + memset(p21,'\0',21); + ret = E_deshash(passwd, p21); + + SMBOWFencrypt(p21, c8, p24); + +#ifdef DEBUG_PASSWORD + DEBUG(100,("SMBencrypt: lm#, challenge, response\n")); + dump_data(100, p21, 16); + dump_data(100, c8, 8); + dump_data(100, p24, 24); +#endif + + 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 + */ + +bool E_md4hash(const char *passwd, uint8_t p16[16]) +{ + size_t len; + smb_ucs2_t *wpwd; + bool ret; + + ret = push_ucs2_talloc(NULL, &wpwd, passwd, &len); + if (!ret || len < 2) { + /* We don't want to return fixed data, as most callers + * don't check */ + mdfour(p16, (const uint8_t *)passwd, strlen(passwd)); + return false; + } + + len -= 2; + mdfour(p16, (const uint8_t *)wpwd, len); + + talloc_free(wpwd); + return true; +} + +/** + * 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, uint8_t p16[16]) +{ + bool ret = true; + char dospwd[256]; + ZERO_STRUCT(dospwd); + + /* Password must be converted to DOS charset - null terminated, uppercase. */ + push_string(dospwd, passwd, sizeof(dospwd), STR_ASCII|STR_UPPER|STR_TERMINATE); + + /* Only the first 14 chars are considered, password need not be null terminated. */ + E_P16((const uint8_t *)dospwd, p16); + + if (strlen(dospwd) > 14) { + ret = false; + } + + ZERO_STRUCT(dospwd); + + return ret; +} + +/* Does both the NTLMv2 owfs of a user's password */ +bool ntv2_owf_gen(const uint8_t owf[16], + const char *user_in, const char *domain_in, + bool upper_case_domain, /* Transform the domain into UPPER case */ + uint8_t kr_buf[16]) +{ + smb_ucs2_t *user; + smb_ucs2_t *domain; + size_t user_byte_len; + size_t domain_byte_len; + bool ret; + + HMACMD5Context ctx; + TALLOC_CTX *mem_ctx = talloc_init("ntv2_owf_gen for %s\\%s", domain_in, user_in); + + if (!mem_ctx) { + return false; + } + + if (!user_in) { + user_in = ""; + } + + if (!domain_in) { + domain_in = ""; + } + + user_in = strupper_talloc(mem_ctx, user_in); + if (user_in == NULL) { + talloc_free(mem_ctx); + return false; + } + + if (upper_case_domain) { + domain_in = strupper_talloc(mem_ctx, domain_in); + if (domain_in == NULL) { + talloc_free(mem_ctx); + return false; + } + } + + ret = push_ucs2_talloc(mem_ctx, &user, user_in, &user_byte_len ); + if (!ret) { + DEBUG(0, ("push_uss2_talloc() for user returned -1 (probably talloc() failure)\n")); + talloc_free(mem_ctx); + return false; + } + + ret = push_ucs2_talloc(mem_ctx, &domain, domain_in, &domain_byte_len); + if (!ret) { + DEBUG(0, ("push_ucs2_talloc() for domain returned -1 (probably talloc() failure)\n")); + talloc_free(mem_ctx); + return false; + } + + 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 void *)user, user_byte_len, &ctx); + hmac_md5_update((const void *)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, (const void *)user, user_byte_len); + dump_data(100, (const void *)domain, domain_byte_len); + dump_data(100, owf, 16); + dump_data(100, kr_buf, 16); +#endif + + talloc_free(mem_ctx); + return true; +} + +/* Does the des encryption from the NT or LM MD4 hash. */ +void SMBOWFencrypt(const uint8_t passwd[16], const uint8_t *c8, uint8_t p24[24]) +{ + uint8_t p21[21]; + + ZERO_STRUCT(p21); + + memcpy(p21, passwd, 16); + E_P24(p21, c8, p24); +} + +/* Does the NT MD4 hash then des encryption. */ + +void SMBNTencrypt(const char *passwd, uint8_t *c8, uint8_t *p24) +{ + uint8_t p21[21]; + + memset(p21,'\0',21); + + E_md4hash(passwd, p21); + 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 md5 encryption from the Key Response for NTLMv2. */ +void SMBOWFencrypt_ntv2(const uint8_t kr[16], + const DATA_BLOB *srv_chal, + const DATA_BLOB *smbcli_chal, + uint8_t 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(smbcli_chal->data, smbcli_chal->length, &ctx); + hmac_md5_final(resp_buf, &ctx); + +#ifdef DEBUG_PASSWORD + DEBUG(100, ("SMBOWFencrypt_ntv2: srv_chal, smbcli_chal, resp_buf\n")); + dump_data(100, srv_chal->data, srv_chal->length); + dump_data(100, smbcli_chal->data, smbcli_chal->length); + dump_data(100, resp_buf, 16); +#endif +} + +void SMBsesskeygen_ntv2(const uint8_t kr[16], + const uint8_t * nt_resp, uint8_t 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((uint8_t *)sess_key, &ctx); + +#ifdef DEBUG_PASSWORD + DEBUG(100, ("SMBsesskeygen_ntv2:\n")); + dump_data(100, sess_key, 16); +#endif +} + +void SMBsesskeygen_ntv1(const uint8_t kr[16], uint8_t sess_key[16]) +{ + /* yes, this session key does not change - yes, this + is a problem - but it is 128 bits */ + + mdfour((uint8_t *)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 uint8_t lm_hash[16], + const uint8_t lm_resp[24], /* only uses 8 */ + uint8_t sess_key[16]) +{ + /* Calculate the LM session key (effective length 40 bits, + but changes with each session) */ + uint8_t p24[24]; + uint8_t partial_lm_hash[14]; + + memcpy(partial_lm_hash, lm_hash, 8); + memset(partial_lm_hash + 8, 0xbd, 6); + + des_crypt56(p24, lm_resp, partial_lm_hash, 1); + des_crypt56(p24+8, lm_resp, partial_lm_hash + 7, 1); + + memcpy(sess_key, p24, 16); + +#ifdef DEBUG_PASSWORD + DEBUG(100, ("SMBsesskeygen_lm_sess_key: \n")); + dump_data(100, sess_key, 16); +#endif +} + +DATA_BLOB NTLMv2_generate_names_blob(TALLOC_CTX *mem_ctx, + const char *hostname, + const char *domain) +{ + DATA_BLOB names_blob = data_blob_talloc(mem_ctx, NULL, 0); + + msrpc_gen(mem_ctx, &names_blob, + "aaa", + NTLMSSP_NAME_TYPE_DOMAIN, domain, + NTLMSSP_NAME_TYPE_SERVER, hostname, + 0, ""); + return names_blob; +} + +static DATA_BLOB NTLMv2_generate_client_data(TALLOC_CTX *mem_ctx, const DATA_BLOB *names_blob) +{ + uint8_t client_chal[8]; + DATA_BLOB response = data_blob(NULL, 0); + uint8_t long_date[8]; + NTTIME nttime; + + unix_to_nt_time(&nttime, time(NULL)); + + generate_random_buffer(client_chal, sizeof(client_chal)); + + push_nttime(long_date, 0, nttime); + + /* See http://www.ubiqx.org/cifs/SMB.html#SMB.8.5 */ + + msrpc_gen(mem_ctx, &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(TALLOC_CTX *out_mem_ctx, + const uint8_t ntlm_v2_hash[16], + const DATA_BLOB *server_chal, + const DATA_BLOB *names_blob) +{ + uint8_t ntlmv2_response[16]; + DATA_BLOB ntlmv2_client_data; + DATA_BLOB final_response; + + TALLOC_CTX *mem_ctx = talloc_named(out_mem_ctx, 0, + "NTLMv2_generate_response internal context"); + + if (!mem_ctx) { + return data_blob(NULL, 0); + } + + /* 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(mem_ctx, 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_talloc(out_mem_ctx, 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); + + talloc_free(mem_ctx); + + return final_response; +} + +static DATA_BLOB LMv2_generate_response(TALLOC_CTX *mem_ctx, + const uint8_t ntlm_v2_hash[16], + const DATA_BLOB *server_chal) +{ + uint8_t lmv2_response[16]; + DATA_BLOB lmv2_client_data = data_blob_talloc(mem_ctx, NULL, 8); + DATA_BLOB final_response = data_blob_talloc(mem_ctx, 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(TALLOC_CTX *mem_ctx, + const char *user, const char *domain, const uint8_t nt_hash[16], + const DATA_BLOB *server_chal, + const DATA_BLOB *names_blob, + DATA_BLOB *lm_response, DATA_BLOB *nt_response, + DATA_BLOB *lm_session_key, DATA_BLOB *user_session_key) +{ + uint8_t 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, true, ntlm_v2_hash)) { + return false; + } + + if (nt_response) { + *nt_response = NTLMv2_generate_response(mem_ctx, + ntlm_v2_hash, server_chal, + names_blob); + if (user_session_key) { + *user_session_key = data_blob_talloc(mem_ctx, 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(mem_ctx, + ntlm_v2_hash, server_chal); + if (lm_session_key) { + *lm_session_key = data_blob_talloc(mem_ctx, NULL, 16); + + /* The NTLMv2 calculations also provide a session key, for signing etc later */ + /* use only the first 16 bytes of lm_response for session key */ + SMBsesskeygen_ntv2(ntlm_v2_hash, lm_response->data, lm_session_key->data); + } + } + + return true; +} + +bool SMBNTLMv2encrypt(TALLOC_CTX *mem_ctx, + 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 *lm_session_key, DATA_BLOB *user_session_key) +{ + uint8_t nt_hash[16]; + E_md4hash(password, nt_hash); + + return SMBNTLMv2encrypt_hash(mem_ctx, + user, domain, nt_hash, server_chal, names_blob, + lm_response, nt_response, lm_session_key, 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_t buffer[516], const char *password, int string_flags) +{ + uint8_t new_pw[512]; + size_t new_pw_len; + + /* the incoming buffer can be any alignment. */ + string_flags |= STR_NOALIGN; + + new_pw_len = push_string(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(uint8_t in_buffer[516], char *new_pwrd, + int new_pwrd_size, int string_flags) +{ + int byte_len=0; + ssize_t converted_pw_len; + + /* 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)) { + return false; + } + + /* decode into the return buffer. Buffer length supplied */ + converted_pw_len = pull_string(new_pwrd, &in_buffer[512 - byte_len], new_pwrd_size, + byte_len, string_flags); + + if (converted_pw_len == -1) { + return false; + } + +#ifdef DEBUG_PASSWORD + DEBUG(100,("decode_pw_buffer: new_pwrd: ")); + dump_data(100, (const uint8_t *)new_pwrd, converted_pw_len); + DEBUG(100,("multibyte len:%d\n", (int)converted_pw_len)); + DEBUG(100,("original char len:%d\n", byte_len/2)); +#endif + + return true; +} + +/*********************************************************** + encode a password buffer with an already unicode password. The + rest of the buffer is filled with random data to make it harder to attack. +************************************************************/ +bool set_pw_in_buffer(uint8_t buffer[516], DATA_BLOB *password) +{ + if (password->length > 512) { + return false; + } + + memcpy(&buffer[512 - password->length], password->data, password->length); + + generate_random_buffer(buffer, 512 - password->length); + + /* + * The length of the new password is in the last 4 bytes of + * the data buffer. + */ + SIVAL(buffer, 512, password->length); + return true; +} + +/*********************************************************** + decode a password buffer + *new_pw_size is the length in bytes of the extracted unicode password +************************************************************/ +bool extract_pw_from_buffer(TALLOC_CTX *mem_ctx, + uint8_t in_buffer[516], DATA_BLOB *new_pass) +{ + int byte_len=0; + + /* 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)) { + return false; + } + + *new_pass = data_blob_talloc(mem_ctx, &in_buffer[512 - byte_len], byte_len); + + if (!new_pass->data) { + return false; + } + + return true; +} diff --git a/source4/libcli/cldap/cldap.c b/source4/libcli/cldap/cldap.c new file mode 100644 index 0000000000..b18ba12b1f --- /dev/null +++ b/source4/libcli/cldap/cldap.c @@ -0,0 +1,738 @@ +/* + Unix SMB/CIFS implementation. + + cldap client library + + Copyright (C) Andrew Tridgell 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/>. +*/ + +/* + see RFC1798 for details of CLDAP + + basic properties + - carried over UDP on port 389 + - request and response matched by message ID + - request consists of only a single searchRequest element + - response can be in one of two forms + - a single searchResponse, followed by a searchResult + - a single searchResult +*/ + +#include "includes.h" +#include "lib/events/events.h" +#include "../lib/util/dlinklist.h" +#include "libcli/ldap/ldap.h" +#include "libcli/ldap/ldap_ndr.h" +#include "libcli/cldap/cldap.h" +#include "lib/socket/socket.h" +#include "libcli/security/security.h" +#include "librpc/gen_ndr/ndr_nbt.h" + +/* + destroy a pending request +*/ +static int cldap_request_destructor(struct cldap_request *req) +{ + if (req->state == CLDAP_REQUEST_SEND) { + DLIST_REMOVE(req->cldap->send_queue, req); + } + if (!req->is_reply && req->message_id != 0) { + idr_remove(req->cldap->idr, req->message_id); + req->message_id = 0; + } + return 0; +} + +/* + handle recv events on a cldap socket +*/ +static void cldap_socket_recv(struct cldap_socket *cldap) +{ + TALLOC_CTX *tmp_ctx = talloc_new(cldap); + NTSTATUS status; + struct socket_address *src; + DATA_BLOB blob; + size_t nread, dsize; + struct asn1_data *asn1 = asn1_init(tmp_ctx); + struct ldap_message *ldap_msg; + struct cldap_request *req; + + if (!asn1) return; + + status = socket_pending(cldap->sock, &dsize); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(tmp_ctx); + return; + } + + blob = data_blob_talloc(tmp_ctx, NULL, dsize); + if (blob.data == NULL) { + talloc_free(tmp_ctx); + return; + } + + status = socket_recvfrom(cldap->sock, blob.data, blob.length, &nread, + tmp_ctx, &src); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(tmp_ctx); + return; + } + blob.length = nread; + + DEBUG(2,("Received cldap packet of length %d from %s:%d\n", + (int)blob.length, src->addr, src->port)); + + if (!asn1_load(asn1, blob)) { + DEBUG(2,("Failed to setup for asn.1 decode\n")); + talloc_free(tmp_ctx); + return; + } + + ldap_msg = talloc(tmp_ctx, struct ldap_message); + if (ldap_msg == NULL) { + talloc_free(tmp_ctx); + return; + } + + /* this initial decode is used to find the message id */ + status = ldap_decode(asn1, NULL, ldap_msg); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(2,("Failed to decode ldap message: %s\n", nt_errstr(status))); + talloc_free(tmp_ctx); + return; + } + + /* find the pending request */ + req = idr_find(cldap->idr, ldap_msg->messageid); + if (req == NULL) { + if (cldap->incoming.handler) { + cldap->incoming.handler(cldap, ldap_msg, src); + } else { + DEBUG(2,("Mismatched cldap reply %u from %s:%d\n", + ldap_msg->messageid, src->addr, src->port)); + } + talloc_free(tmp_ctx); + return; + } + + req->asn1 = talloc_steal(req, asn1); + req->asn1->ofs = 0; + + req->state = CLDAP_REQUEST_DONE; + talloc_free(req->te); + + talloc_free(tmp_ctx); + + if (req->async.fn) { + req->async.fn(req); + } +} + +/* + handle request timeouts +*/ +static void cldap_request_timeout(struct tevent_context *event_ctx, + struct tevent_timer *te, struct timeval t, + void *private_data) +{ + struct cldap_request *req = talloc_get_type(private_data, struct cldap_request); + + /* possibly try again */ + if (req->num_retries != 0) { + size_t len = req->encoded.length; + + req->num_retries--; + + socket_sendto(req->cldap->sock, &req->encoded, &len, + req->dest); + + req->te = event_add_timed(req->cldap->event_ctx, req, + timeval_current_ofs(req->timeout, 0), + cldap_request_timeout, req); + return; + } + + req->state = CLDAP_REQUEST_ERROR; + req->status = NT_STATUS_IO_TIMEOUT; + if (req->async.fn) { + req->async.fn(req); + } +} + +/* + handle send events on a cldap socket +*/ +static void cldap_socket_send(struct cldap_socket *cldap) +{ + struct cldap_request *req; + NTSTATUS status; + + while ((req = cldap->send_queue)) { + size_t len; + + len = req->encoded.length; + status = socket_sendto(cldap->sock, &req->encoded, &len, + req->dest); + if (NT_STATUS_IS_ERR(status)) { + DEBUG(0,("Failed to send cldap request of length %u to %s:%d\n", + (unsigned)req->encoded.length, req->dest->addr, req->dest->port)); + DLIST_REMOVE(cldap->send_queue, req); + req->state = CLDAP_REQUEST_ERROR; + req->status = status; + if (req->async.fn) { + req->async.fn(req); + } + continue; + } + + if (!NT_STATUS_IS_OK(status)) return; + + DLIST_REMOVE(cldap->send_queue, req); + + if (req->is_reply) { + talloc_free(req); + } else { + req->state = CLDAP_REQUEST_WAIT; + + req->te = event_add_timed(cldap->event_ctx, req, + timeval_current_ofs(req->timeout, 0), + cldap_request_timeout, req); + + EVENT_FD_READABLE(cldap->fde); + } + } + + EVENT_FD_NOT_WRITEABLE(cldap->fde); + return; +} + + +/* + handle fd events on a cldap_socket +*/ +static void cldap_socket_handler(struct tevent_context *ev, struct tevent_fd *fde, + uint16_t flags, void *private_data) +{ + struct cldap_socket *cldap = talloc_get_type(private_data, struct cldap_socket); + if (flags & EVENT_FD_WRITE) { + cldap_socket_send(cldap); + } + if (flags & EVENT_FD_READ) { + cldap_socket_recv(cldap); + } +} + +/* + initialise a cldap_socket. The event_ctx is optional, if provided + then operations will use that event context +*/ +struct cldap_socket *cldap_socket_init(TALLOC_CTX *mem_ctx, + struct tevent_context *event_ctx, + struct smb_iconv_convenience *iconv_convenience) +{ + struct cldap_socket *cldap; + NTSTATUS status; + + cldap = talloc(mem_ctx, struct cldap_socket); + if (cldap == NULL) goto failed; + + cldap->event_ctx = talloc_reference(cldap, event_ctx); + if (cldap->event_ctx == NULL) goto failed; + + cldap->idr = idr_init(cldap); + if (cldap->idr == NULL) goto failed; + + status = socket_create("ip", SOCKET_TYPE_DGRAM, &cldap->sock, 0); + if (!NT_STATUS_IS_OK(status)) goto failed; + + talloc_steal(cldap, cldap->sock); + + cldap->fde = event_add_fd(cldap->event_ctx, cldap, + socket_get_fd(cldap->sock), 0, + cldap_socket_handler, cldap); + + cldap->send_queue = NULL; + cldap->incoming.handler = NULL; + cldap->iconv_convenience = iconv_convenience; + + return cldap; + +failed: + talloc_free(cldap); + return NULL; +} + + +/* + setup a handler for incoming requests +*/ +NTSTATUS cldap_set_incoming_handler(struct cldap_socket *cldap, + void (*handler)(struct cldap_socket *, struct ldap_message *, + struct socket_address *), + void *private_data) +{ + cldap->incoming.handler = handler; + cldap->incoming.private_data = private_data; + EVENT_FD_READABLE(cldap->fde); + return NT_STATUS_OK; +} + +/* + queue a cldap request for send +*/ +struct cldap_request *cldap_search_send(struct cldap_socket *cldap, + struct cldap_search *io) +{ + struct ldap_message *msg; + struct cldap_request *req; + struct ldap_SearchRequest *search; + + req = talloc_zero(cldap, struct cldap_request); + if (req == NULL) goto failed; + + req->cldap = cldap; + req->state = CLDAP_REQUEST_SEND; + req->timeout = io->in.timeout; + req->num_retries = io->in.retries; + req->is_reply = false; + req->asn1 = asn1_init(req); + if (!req->asn1) { + goto failed; + } + + req->dest = socket_address_from_strings(req, cldap->sock->backend_name, + io->in.dest_address, + io->in.dest_port); + if (!req->dest) goto failed; + + req->message_id = idr_get_new_random(cldap->idr, req, UINT16_MAX); + if (req->message_id == -1) goto failed; + + talloc_set_destructor(req, cldap_request_destructor); + + msg = talloc(req, struct ldap_message); + if (msg == NULL) goto failed; + msg->messageid = req->message_id; + msg->type = LDAP_TAG_SearchRequest; + msg->controls = NULL; + search = &msg->r.SearchRequest; + + search->basedn = ""; + search->scope = LDAP_SEARCH_SCOPE_BASE; + search->deref = LDAP_DEREFERENCE_NEVER; + search->timelimit = 0; + search->sizelimit = 0; + search->attributesonly = false; + search->num_attributes = str_list_length(io->in.attributes); + search->attributes = io->in.attributes; + search->tree = ldb_parse_tree(req, io->in.filter); + if (search->tree == NULL) { + goto failed; + } + + if (!ldap_encode(msg, NULL, &req->encoded, req)) { + DEBUG(0,("Failed to encode cldap message to %s:%d\n", + req->dest->addr, req->dest->port)); + goto failed; + } + + DLIST_ADD_END(cldap->send_queue, req, struct cldap_request *); + + EVENT_FD_WRITEABLE(cldap->fde); + + return req; + +failed: + talloc_free(req); + return NULL; +} + + +/* + queue a cldap reply for send +*/ +NTSTATUS cldap_reply_send(struct cldap_socket *cldap, struct cldap_reply *io) +{ + struct ldap_message *msg; + struct cldap_request *req; + DATA_BLOB blob1, blob2; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + req = talloc_zero(cldap, struct cldap_request); + if (req == NULL) goto failed; + + req->cldap = cldap; + req->state = CLDAP_REQUEST_SEND; + req->is_reply = true; + req->asn1 = asn1_init(req); + if (!req->asn1) { + goto failed; + } + + req->dest = io->dest; + if (talloc_reference(req, io->dest) == NULL) goto failed; + + talloc_set_destructor(req, cldap_request_destructor); + + msg = talloc(req, struct ldap_message); + if (msg == NULL) goto failed; + msg->messageid = io->messageid; + msg->controls = NULL; + + if (io->response) { + msg->type = LDAP_TAG_SearchResultEntry; + msg->r.SearchResultEntry = *io->response; + + if (!ldap_encode(msg, NULL, &blob1, req)) { + DEBUG(0,("Failed to encode cldap message to %s:%d\n", + req->dest->addr, req->dest->port)); + status = NT_STATUS_INVALID_PARAMETER; + goto failed; + } + } else { + blob1 = data_blob(NULL, 0); + } + + msg->type = LDAP_TAG_SearchResultDone; + msg->r.SearchResultDone = *io->result; + + if (!ldap_encode(msg, NULL, &blob2, req)) { + DEBUG(0,("Failed to encode cldap message to %s:%d\n", + req->dest->addr, req->dest->port)); + status = NT_STATUS_INVALID_PARAMETER; + goto failed; + } + + req->encoded = data_blob_talloc(req, NULL, blob1.length + blob2.length); + if (req->encoded.data == NULL) goto failed; + + memcpy(req->encoded.data, blob1.data, blob1.length); + memcpy(req->encoded.data+blob1.length, blob2.data, blob2.length); + + DLIST_ADD_END(cldap->send_queue, req, struct cldap_request *); + + EVENT_FD_WRITEABLE(cldap->fde); + + return NT_STATUS_OK; + +failed: + talloc_free(req); + return status; +} + +/* + receive a cldap reply +*/ +NTSTATUS cldap_search_recv(struct cldap_request *req, + TALLOC_CTX *mem_ctx, + struct cldap_search *io) +{ + struct ldap_message *ldap_msg; + NTSTATUS status; + + if (req == NULL) { + return NT_STATUS_NO_MEMORY; + } + + while (req->state < CLDAP_REQUEST_DONE) { + if (event_loop_once(req->cldap->event_ctx) != 0) { + talloc_free(req); + return NT_STATUS_UNEXPECTED_NETWORK_ERROR; + } + } + + if (req->state == CLDAP_REQUEST_ERROR) { + status = req->status; + talloc_free(req); + return status; + } + + ldap_msg = talloc(mem_ctx, struct ldap_message); + NT_STATUS_HAVE_NO_MEMORY(ldap_msg); + + status = ldap_decode(req->asn1, NULL, ldap_msg); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(2,("Failed to decode cldap search reply: %s\n", nt_errstr(status))); + talloc_free(req); + return status; + } + + ZERO_STRUCT(io->out); + + /* the first possible form has a search result in first place */ + if (ldap_msg->type == LDAP_TAG_SearchResultEntry) { + io->out.response = talloc(mem_ctx, struct ldap_SearchResEntry); + NT_STATUS_HAVE_NO_MEMORY(io->out.response); + *io->out.response = ldap_msg->r.SearchResultEntry; + + /* decode the 2nd part */ + status = ldap_decode(req->asn1, NULL, ldap_msg); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(2,("Failed to decode cldap search result entry: %s\n", nt_errstr(status))); + talloc_free(req); + return status; + } + } + + if (ldap_msg->type != LDAP_TAG_SearchResultDone) { + talloc_free(req); + return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR); + } + + io->out.result = talloc(mem_ctx, struct ldap_Result); + NT_STATUS_HAVE_NO_MEMORY(io->out.result); + *io->out.result = ldap_msg->r.SearchResultDone; + + talloc_free(req); + + if (io->out.result->resultcode != LDAP_SUCCESS) { + return NT_STATUS_LDAP(io->out.result->resultcode); + } + return NT_STATUS_OK; +} + + +/* + synchronous cldap search +*/ +NTSTATUS cldap_search(struct cldap_socket *cldap, + TALLOC_CTX *mem_ctx, + struct cldap_search *io) +{ + struct cldap_request *req = cldap_search_send(cldap, io); + return cldap_search_recv(req, mem_ctx, io); +} + + + +/* + queue a cldap netlogon for send +*/ +struct cldap_request *cldap_netlogon_send(struct cldap_socket *cldap, + struct cldap_netlogon *io) +{ + struct cldap_search search; + char *filter; + struct cldap_request *req; + const char *attr[] = { "NetLogon", NULL }; + TALLOC_CTX *tmp_ctx = talloc_new(cldap); + + filter = talloc_asprintf(tmp_ctx, "(&(NtVer=%s)", + ldap_encode_ndr_uint32(tmp_ctx, io->in.version)); + if (filter == NULL) goto failed; + if (io->in.user) { + filter = talloc_asprintf_append_buffer(filter, "(User=%s)", io->in.user); + if (filter == NULL) goto failed; + } + if (io->in.host) { + filter = talloc_asprintf_append_buffer(filter, "(Host=%s)", io->in.host); + if (filter == NULL) goto failed; + } + if (io->in.realm) { + filter = talloc_asprintf_append_buffer(filter, "(DnsDomain=%s)", io->in.realm); + if (filter == NULL) goto failed; + } + if (io->in.acct_control != -1) { + filter = talloc_asprintf_append_buffer(filter, "(AAC=%s)", + ldap_encode_ndr_uint32(tmp_ctx, io->in.acct_control)); + if (filter == NULL) goto failed; + } + if (io->in.domain_sid) { + struct dom_sid *sid = dom_sid_parse_talloc(tmp_ctx, io->in.domain_sid); + if (sid == NULL) goto failed; + filter = talloc_asprintf_append_buffer(filter, "(domainSid=%s)", + ldap_encode_ndr_dom_sid(tmp_ctx, sid)); + if (filter == NULL) goto failed; + } + if (io->in.domain_guid) { + struct GUID guid; + NTSTATUS status; + status = GUID_from_string(io->in.domain_guid, &guid); + if (!NT_STATUS_IS_OK(status)) goto failed; + filter = talloc_asprintf_append_buffer(filter, "(DomainGuid=%s)", + ldap_encode_ndr_GUID(tmp_ctx, &guid)); + if (filter == NULL) goto failed; + } + filter = talloc_asprintf_append_buffer(filter, ")"); + if (filter == NULL) goto failed; + + search.in.dest_address = io->in.dest_address; + search.in.dest_port = io->in.dest_port; + search.in.filter = filter; + search.in.attributes = attr; + search.in.timeout = 2; + search.in.retries = 2; + + req = cldap_search_send(cldap, &search); + + talloc_free(tmp_ctx); + return req; +failed: + talloc_free(tmp_ctx); + return NULL; +} + + +/* + receive a cldap netlogon reply +*/ +NTSTATUS cldap_netlogon_recv(struct cldap_request *req, + TALLOC_CTX *mem_ctx, + struct cldap_netlogon *io) +{ + NTSTATUS status; + struct cldap_search search; + struct cldap_socket *cldap; + DATA_BLOB *data; + + cldap = req->cldap; + + status = cldap_search_recv(req, mem_ctx, &search); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (search.out.response == NULL) { + return NT_STATUS_NOT_FOUND; + } + + if (search.out.response->num_attributes != 1 || + strcasecmp(search.out.response->attributes[0].name, "netlogon") != 0 || + search.out.response->attributes[0].num_values != 1 || + search.out.response->attributes[0].values->length < 2) { + return NT_STATUS_UNEXPECTED_NETWORK_ERROR; + } + data = search.out.response->attributes[0].values; + + status = pull_netlogon_samlogon_response(data, mem_ctx, req->cldap->iconv_convenience, + &io->out.netlogon); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (io->in.map_response) { + map_netlogon_samlogon_response(&io->out.netlogon); + } + return NT_STATUS_OK; +} + +/* + sync cldap netlogon search +*/ +NTSTATUS cldap_netlogon(struct cldap_socket *cldap, + TALLOC_CTX *mem_ctx, struct cldap_netlogon *io) +{ + struct cldap_request *req = cldap_netlogon_send(cldap, io); + return cldap_netlogon_recv(req, mem_ctx, io); +} + + +/* + send an empty reply (used on any error, so the client doesn't keep waiting + or send the bad request again) +*/ +NTSTATUS cldap_empty_reply(struct cldap_socket *cldap, + uint32_t message_id, + struct socket_address *src) +{ + NTSTATUS status; + struct cldap_reply reply; + struct ldap_Result result; + + reply.messageid = message_id; + reply.dest = src; + reply.response = NULL; + reply.result = &result; + + ZERO_STRUCT(result); + + status = cldap_reply_send(cldap, &reply); + + return status; +} + +/* + send an error reply (used on any error, so the client doesn't keep waiting + or send the bad request again) +*/ +NTSTATUS cldap_error_reply(struct cldap_socket *cldap, + uint32_t message_id, + struct socket_address *src, + int resultcode, + const char *errormessage) +{ + NTSTATUS status; + struct cldap_reply reply; + struct ldap_Result result; + + reply.messageid = message_id; + reply.dest = src; + reply.response = NULL; + reply.result = &result; + + ZERO_STRUCT(result); + result.resultcode = resultcode; + result.errormessage = errormessage; + + status = cldap_reply_send(cldap, &reply); + + return status; +} + + +/* + send a netlogon reply +*/ +NTSTATUS cldap_netlogon_reply(struct cldap_socket *cldap, + uint32_t message_id, + struct socket_address *src, + uint32_t version, + struct netlogon_samlogon_response *netlogon) +{ + NTSTATUS status; + struct cldap_reply reply; + struct ldap_SearchResEntry response; + struct ldap_Result result; + TALLOC_CTX *tmp_ctx = talloc_new(cldap); + DATA_BLOB blob; + + status = push_netlogon_samlogon_response(&blob, tmp_ctx, cldap->iconv_convenience, + netlogon); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + reply.messageid = message_id; + reply.dest = src; + reply.response = &response; + reply.result = &result; + + ZERO_STRUCT(result); + + response.dn = ""; + response.num_attributes = 1; + response.attributes = talloc(tmp_ctx, struct ldb_message_element); + NT_STATUS_HAVE_NO_MEMORY(response.attributes); + response.attributes->name = "netlogon"; + response.attributes->num_values = 1; + response.attributes->values = &blob; + + status = cldap_reply_send(cldap, &reply); + + talloc_free(tmp_ctx); + + return status; +} + + diff --git a/source4/libcli/cldap/cldap.h b/source4/libcli/cldap/cldap.h new file mode 100644 index 0000000000..8951daa775 --- /dev/null +++ b/source4/libcli/cldap/cldap.h @@ -0,0 +1,182 @@ +/* + Unix SMB/CIFS implementation. + + a async CLDAP library + + Copyright (C) Andrew Tridgell 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 "../lib/util/asn1.h" +#include "../libcli/netlogon.h" + +struct ldap_message; + +enum cldap_request_state {CLDAP_REQUEST_SEND, + CLDAP_REQUEST_WAIT, + CLDAP_REQUEST_DONE, + CLDAP_REQUEST_ERROR}; + +/* + a cldap request packet +*/ +struct cldap_request { + struct cldap_request *next, *prev; + + struct cldap_socket *cldap; + + enum cldap_request_state state; + NTSTATUS status; + + /* where to send the request */ + struct socket_address *dest; + + /* timeout between retries (seconds) */ + int timeout; + int num_retries; + + bool is_reply; + + /* the ldap message_id */ + int message_id; + + struct tevent_timer *te; + + /* the encoded request */ + DATA_BLOB encoded; + + /* the reply data */ + struct asn1_data *asn1; + + /* information on what to do on completion */ + struct { + void (*fn)(struct cldap_request *); + void *private_data; + } async; +}; + +/* + context structure for operations on cldap packets +*/ +struct cldap_socket { + struct socket_context *sock; + struct tevent_context *event_ctx; + struct smb_iconv_convenience *iconv_convenience; + + /* the fd event */ + struct tevent_fd *fde; + + /* a queue of outgoing requests */ + struct cldap_request *send_queue; + + /* mapping from message_id to pending request */ + struct idr_context *idr; + + /* what to do with incoming request packets */ + struct { + void (*handler)(struct cldap_socket *, struct ldap_message *, + struct socket_address *); + void *private_data; + } incoming; +}; + + +/* + a general cldap search request +*/ +struct cldap_search { + struct { + const char *dest_address; + uint16_t dest_port; + const char *filter; + const char **attributes; + int timeout; + int retries; + } in; + struct { + struct ldap_SearchResEntry *response; + struct ldap_Result *result; + } out; +}; + +struct cldap_socket *cldap_socket_init(TALLOC_CTX *mem_ctx, + struct tevent_context *event_ctx, + struct smb_iconv_convenience *iconv_convenience); +NTSTATUS cldap_set_incoming_handler(struct cldap_socket *cldap, + void (*handler)(struct cldap_socket *, struct ldap_message *, + struct socket_address *), + void *private_data); +struct cldap_request *cldap_search_send(struct cldap_socket *cldap, + struct cldap_search *io); +NTSTATUS cldap_search_recv(struct cldap_request *req, TALLOC_CTX *mem_ctx, + struct cldap_search *io); +NTSTATUS cldap_search(struct cldap_socket *cldap, TALLOC_CTX *mem_ctx, + struct cldap_search *io); + + +/* + a general cldap reply +*/ +struct cldap_reply { + uint32_t messageid; + struct socket_address *dest; + struct ldap_SearchResEntry *response; + struct ldap_Result *result; +}; + +NTSTATUS cldap_reply_send(struct cldap_socket *cldap, struct cldap_reply *io); + +NTSTATUS cldap_empty_reply(struct cldap_socket *cldap, + uint32_t message_id, + struct socket_address *src); +NTSTATUS cldap_error_reply(struct cldap_socket *cldap, + uint32_t message_id, + struct socket_address *src, + int resultcode, + const char *errormessage); + +/* + a netlogon cldap request +*/ +struct cldap_netlogon { + struct { + const char *dest_address; + uint16_t dest_port; + const char *realm; + const char *host; + const char *user; + const char *domain_guid; + const char *domain_sid; + int acct_control; + uint32_t version; + bool map_response; + } in; + struct { + struct netlogon_samlogon_response netlogon; + } out; +}; + +struct cldap_request *cldap_netlogon_send(struct cldap_socket *cldap, + struct cldap_netlogon *io); +NTSTATUS cldap_netlogon_recv(struct cldap_request *req, + TALLOC_CTX *mem_ctx, + struct cldap_netlogon *io); +NTSTATUS cldap_netlogon(struct cldap_socket *cldap, + TALLOC_CTX *mem_ctx, struct cldap_netlogon *io); +NTSTATUS cldap_netlogon_reply(struct cldap_socket *cldap, + uint32_t message_id, + struct socket_address *src, + uint32_t version, + struct netlogon_samlogon_response *netlogon); diff --git a/source4/libcli/cliconnect.c b/source4/libcli/cliconnect.c new file mode 100644 index 0000000000..14935dadba --- /dev/null +++ b/source4/libcli/cliconnect.c @@ -0,0 +1,267 @@ +/* + Unix SMB/CIFS implementation. + + client connect/disconnect routines + + Copyright (C) Andrew Tridgell 2003-2005 + Copyright (C) James Peach 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/libcli.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/auth/libcli_auth.h" +#include "libcli/smb_composite/smb_composite.h" + +/* + wrapper around smbcli_sock_connect() +*/ +bool smbcli_socket_connect(struct smbcli_state *cli, const char *server, + const char **ports, + struct tevent_context *ev_ctx, + struct resolve_context *resolve_ctx, + struct smbcli_options *options, + struct smb_iconv_convenience *iconv_convenience, + const char *socket_options) +{ + struct smbcli_socket *sock; + + sock = smbcli_sock_connect_byname(server, ports, NULL, + resolve_ctx, ev_ctx, + socket_options); + + if (sock == NULL) return false; + + cli->transport = smbcli_transport_init(sock, cli, true, options, + iconv_convenience); + if (!cli->transport) { + return false; + } + + return true; +} + +/* wrapper around smbcli_transport_connect() */ +bool smbcli_transport_establish(struct smbcli_state *cli, + struct nbt_name *calling, + struct nbt_name *called) +{ + return smbcli_transport_connect(cli->transport, calling, called); +} + +/* wrapper around smb_raw_negotiate() */ +NTSTATUS smbcli_negprot(struct smbcli_state *cli, bool unicode, int maxprotocol) +{ + return smb_raw_negotiate(cli->transport, unicode, maxprotocol); +} + +/* wrapper around smb_raw_sesssetup() */ +NTSTATUS smbcli_session_setup(struct smbcli_state *cli, + struct cli_credentials *credentials, + const char *workgroup, + struct smbcli_session_options options, + struct gensec_settings *gensec_settings) +{ + struct smb_composite_sesssetup setup; + NTSTATUS status; + + cli->session = smbcli_session_init(cli->transport, cli, true, + options); + if (!cli->session) return NT_STATUS_UNSUCCESSFUL; + + setup.in.sesskey = cli->transport->negotiate.sesskey; + setup.in.capabilities = cli->transport->negotiate.capabilities; + setup.in.credentials = credentials; + setup.in.workgroup = workgroup; + setup.in.gensec_settings = gensec_settings; + + status = smb_composite_sesssetup(cli->session, &setup); + + cli->session->vuid = setup.out.vuid; + + return status; +} + +/* wrapper around smb_raw_tcon() */ +NTSTATUS smbcli_tconX(struct smbcli_state *cli, const char *sharename, + const char *devtype, const char *password) +{ + union smb_tcon tcon; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + + cli->tree = smbcli_tree_init(cli->session, cli, true); + if (!cli->tree) return NT_STATUS_UNSUCCESSFUL; + + mem_ctx = talloc_init("tcon"); + if (!mem_ctx) { + return NT_STATUS_NO_MEMORY; + } + + /* setup a tree connect */ + tcon.generic.level = RAW_TCON_TCONX; + tcon.tconx.in.flags = 0; + if (cli->transport->negotiate.sec_mode & NEGOTIATE_SECURITY_USER_LEVEL) { + tcon.tconx.in.password = data_blob(NULL, 0); + } else if (cli->transport->negotiate.sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) { + tcon.tconx.in.password = data_blob_talloc(mem_ctx, NULL, 24); + if (cli->transport->negotiate.secblob.length < 8) { + return NT_STATUS_INVALID_PARAMETER; + } + SMBencrypt(password, cli->transport->negotiate.secblob.data, tcon.tconx.in.password.data); + } else { + tcon.tconx.in.password = data_blob_talloc(mem_ctx, password, strlen(password)+1); + } + tcon.tconx.in.path = sharename; + tcon.tconx.in.device = devtype; + + status = smb_raw_tcon(cli->tree, mem_ctx, &tcon); + + cli->tree->tid = tcon.tconx.out.tid; + + talloc_free(mem_ctx); + + return status; +} + + +/* + easy way to get to a fully connected smbcli_state in one call +*/ +NTSTATUS smbcli_full_connection(TALLOC_CTX *parent_ctx, + struct smbcli_state **ret_cli, + const char *host, + const char **ports, + const char *sharename, + const char *devtype, + const char *socket_options, + struct cli_credentials *credentials, + struct resolve_context *resolve_ctx, + struct tevent_context *ev, + struct smbcli_options *options, + struct smbcli_session_options *session_options, + struct smb_iconv_convenience *iconv_convenience, + struct gensec_settings *gensec_settings) +{ + struct smbcli_tree *tree; + NTSTATUS status; + + *ret_cli = NULL; + + status = smbcli_tree_full_connection(parent_ctx, + &tree, host, ports, + sharename, devtype, + socket_options, + credentials, resolve_ctx, ev, + options, + session_options, + iconv_convenience, + gensec_settings); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + (*ret_cli) = smbcli_state_init(parent_ctx); + + (*ret_cli)->tree = tree; + (*ret_cli)->session = tree->session; + (*ret_cli)->transport = tree->session->transport; + + talloc_steal(*ret_cli, tree); + +done: + return status; +} + + +/* + disconnect the tree +*/ +NTSTATUS smbcli_tdis(struct smbcli_state *cli) +{ + return smb_tree_disconnect(cli->tree); +} + +/**************************************************************************** + Initialise a client state structure. +****************************************************************************/ +struct smbcli_state *smbcli_state_init(TALLOC_CTX *mem_ctx) +{ + return talloc_zero(mem_ctx, struct smbcli_state); +} + +/* Insert a NULL at the first separator of the given path and return a pointer + * to the remainder of the string. + */ +static char * +terminate_path_at_separator(char * path) +{ + char * p; + + if (!path) { + return NULL; + } + + if ((p = strchr_m(path, '/'))) { + *p = '\0'; + return p + 1; + } + + if ((p = strchr_m(path, '\\'))) { + *p = '\0'; + return p + 1; + } + + /* No separator. */ + return NULL; +} + +/* + parse a //server/share type UNC name +*/ +bool smbcli_parse_unc(const char *unc_name, TALLOC_CTX *mem_ctx, + char **hostname, char **sharename) +{ + char *p; + + *hostname = *sharename = NULL; + + if (strncmp(unc_name, "\\\\", 2) && + strncmp(unc_name, "//", 2)) { + return false; + } + + *hostname = talloc_strdup(mem_ctx, &unc_name[2]); + p = terminate_path_at_separator(*hostname); + + if (p != NULL && *p) { + *sharename = talloc_strdup(mem_ctx, p); + terminate_path_at_separator(*sharename); + } + + if (*hostname && *sharename) { + return true; + } + + talloc_free(*hostname); + talloc_free(*sharename); + *hostname = *sharename = NULL; + return false; +} + + + diff --git a/source4/libcli/clideltree.c b/source4/libcli/clideltree.c new file mode 100644 index 0000000000..d947ac3547 --- /dev/null +++ b/source4/libcli/clideltree.c @@ -0,0 +1,137 @@ +/* + Unix SMB/CIFS implementation. + useful function for deleting a whole directory tree + Copyright (C) Andrew Tridgell 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 "libcli/raw/libcliraw.h" +#include "libcli/libcli.h" +#include "system/dir.h" + +struct delete_state { + struct smbcli_tree *tree; + int total_deleted; + bool failed; +}; + +/* + callback function for torture_deltree() +*/ +static void delete_fn(struct clilist_file_info *finfo, const char *name, void *state) +{ + struct delete_state *dstate = (struct delete_state *)state; + char *s, *n; + if (ISDOT(finfo->name) || ISDOTDOT(finfo->name)) { + return; + } + + n = strdup(name); + n[strlen(n)-1] = 0; + asprintf(&s, "%s%s", n, finfo->name); + + if (finfo->attrib & FILE_ATTRIBUTE_READONLY) { + if (NT_STATUS_IS_ERR(smbcli_setatr(dstate->tree, s, 0, 0))) { + DEBUG(2,("Failed to remove READONLY on %s - %s\n", + s, smbcli_errstr(dstate->tree))); + } + } + + if (finfo->attrib & FILE_ATTRIBUTE_DIRECTORY) { + char *s2; + asprintf(&s2, "%s\\*", s); + smbcli_unlink(dstate->tree, s2); + smbcli_list(dstate->tree, s2, + FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM, + delete_fn, state); + free(s2); + if (NT_STATUS_IS_ERR(smbcli_rmdir(dstate->tree, s))) { + DEBUG(2,("Failed to delete %s - %s\n", + s, smbcli_errstr(dstate->tree))); + dstate->failed = true; + } + dstate->total_deleted++; + } else { + if (NT_STATUS_IS_ERR(smbcli_unlink(dstate->tree, s))) { + DEBUG(2,("Failed to delete %s - %s\n", + s, smbcli_errstr(dstate->tree))); + dstate->failed = true; + } + dstate->total_deleted++; + } + free(s); + free(n); +} + +/* + recursively descend a tree deleting all files + returns the number of files deleted, or -1 on error +*/ +int smbcli_deltree(struct smbcli_tree *tree, const char *dname) +{ + char *mask; + struct delete_state dstate; + NTSTATUS status; + + dstate.tree = tree; + dstate.total_deleted = 0; + dstate.failed = false; + + /* it might be a file */ + status = smbcli_unlink(tree, dname); + if (NT_STATUS_IS_OK(smbcli_unlink(tree, dname))) { + return 1; + } + if (NT_STATUS_EQUAL(smbcli_nt_error(tree), NT_STATUS_OBJECT_NAME_NOT_FOUND) || + NT_STATUS_EQUAL(smbcli_nt_error(tree), NT_STATUS_OBJECT_PATH_NOT_FOUND) || + NT_STATUS_EQUAL(smbcli_nt_error(tree), NT_STATUS_NO_SUCH_FILE) || + NT_STATUS_EQUAL(smbcli_nt_error(tree), NT_STATUS_DOS(ERRDOS, ERRbadfile))) { + return 0; + } + if (NT_STATUS_EQUAL(status, NT_STATUS_CANNOT_DELETE)) { + /* it could be read-only */ + status = smbcli_setatr(tree, dname, FILE_ATTRIBUTE_NORMAL, 0); + if (NT_STATUS_IS_OK(smbcli_unlink(tree, dname))) { + return 1; + } + } + + asprintf(&mask, "%s\\*", dname); + smbcli_unlink(dstate.tree, mask); + smbcli_list(dstate.tree, mask, + FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM, + delete_fn, &dstate); + free(mask); + + status = smbcli_rmdir(dstate.tree, dname); + if (NT_STATUS_EQUAL(status, NT_STATUS_CANNOT_DELETE)) { + /* it could be read-only */ + status = smbcli_setatr(dstate.tree, dname, FILE_ATTRIBUTE_NORMAL, 0); + status = smbcli_rmdir(dstate.tree, dname); + } + if (NT_STATUS_IS_ERR(status)) { + DEBUG(2,("Failed to delete %s - %s\n", + dname, smbcli_errstr(dstate.tree))); + return -1; + } + dstate.total_deleted++; + + if (dstate.failed) { + return -1; + } + + return dstate.total_deleted; +} diff --git a/source4/libcli/clifile.c b/source4/libcli/clifile.c new file mode 100644 index 0000000000..2cf174060b --- /dev/null +++ b/source4/libcli/clifile.c @@ -0,0 +1,702 @@ +/* + Unix SMB/CIFS implementation. + client file operations + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) Jeremy Allison 2001-2002 + Copyright (C) James Myers 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 "system/filesys.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/libcli.h" + +/**************************************************************************** + Hard/Symlink a file (UNIX extensions). +****************************************************************************/ + +static NTSTATUS smbcli_link_internal(struct smbcli_tree *tree, + const char *fname_src, + const char *fname_dst, bool hard_link) +{ + union smb_setfileinfo parms; + NTSTATUS status; + + if (hard_link) { + parms.generic.level = RAW_SFILEINFO_UNIX_HLINK; + parms.unix_hlink.in.file.path = fname_src; + parms.unix_hlink.in.link_dest = fname_dst; + } else { + parms.generic.level = RAW_SFILEINFO_UNIX_LINK; + parms.unix_link.in.file.path = fname_src; + parms.unix_link.in.link_dest = fname_dst; + } + + status = smb_raw_setpathinfo(tree, &parms); + + return status; +} + +/**************************************************************************** + Map standard UNIX permissions onto wire representations. +****************************************************************************/ +uint32_t unix_perms_to_wire(mode_t perms) +{ + uint_t 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; +} + +/**************************************************************************** + Symlink a file (UNIX extensions). +****************************************************************************/ +NTSTATUS smbcli_unix_symlink(struct smbcli_tree *tree, const char *fname_src, + const char *fname_dst) +{ + return smbcli_link_internal(tree, fname_src, fname_dst, false); +} + +/**************************************************************************** + Hard a file (UNIX extensions). +****************************************************************************/ +NTSTATUS smbcli_unix_hardlink(struct smbcli_tree *tree, const char *fname_src, + const char *fname_dst) +{ + return smbcli_link_internal(tree, fname_src, fname_dst, true); +} + + +/**************************************************************************** + Chmod or chown a file internal (UNIX extensions). +****************************************************************************/ +static NTSTATUS smbcli_unix_chmod_chown_internal(struct smbcli_tree *tree, + const char *fname, + uint32_t mode, uint32_t uid, + uint32_t gid) +{ + union smb_setfileinfo parms; + NTSTATUS status; + + parms.generic.level = SMB_SFILEINFO_UNIX_BASIC; + parms.unix_basic.in.file.path = fname; + parms.unix_basic.in.uid = uid; + parms.unix_basic.in.gid = gid; + parms.unix_basic.in.mode = mode; + + status = smb_raw_setpathinfo(tree, &parms); + + return status; +} + +/**************************************************************************** + chmod a file (UNIX extensions). +****************************************************************************/ + +NTSTATUS smbcli_unix_chmod(struct smbcli_tree *tree, const char *fname, mode_t mode) +{ + return smbcli_unix_chmod_chown_internal(tree, fname, + unix_perms_to_wire(mode), + SMB_UID_NO_CHANGE, + SMB_GID_NO_CHANGE); +} + +/**************************************************************************** + chown a file (UNIX extensions). +****************************************************************************/ +NTSTATUS smbcli_unix_chown(struct smbcli_tree *tree, const char *fname, uid_t uid, + gid_t gid) +{ + return smbcli_unix_chmod_chown_internal(tree, fname, SMB_MODE_NO_CHANGE, + (uint32_t)uid, (uint32_t)gid); +} + + +/**************************************************************************** + Rename a file. +****************************************************************************/ +NTSTATUS smbcli_rename(struct smbcli_tree *tree, const char *fname_src, + const char *fname_dst) +{ + union smb_rename parms; + + parms.generic.level = RAW_RENAME_RENAME; + parms.rename.in.attrib = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY; + parms.rename.in.pattern1 = fname_src; + parms.rename.in.pattern2 = fname_dst; + + return smb_raw_rename(tree, &parms); +} + + +/**************************************************************************** + Delete a file. +****************************************************************************/ +NTSTATUS smbcli_unlink(struct smbcli_tree *tree, const char *fname) +{ + union smb_unlink parms; + + parms.unlink.in.pattern = fname; + if (strchr(fname, '*')) { + parms.unlink.in.attrib = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN; + } else { + parms.unlink.in.attrib = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY; + } + + return smb_raw_unlink(tree, &parms); +} + +/**************************************************************************** + Create a directory. +****************************************************************************/ +NTSTATUS smbcli_mkdir(struct smbcli_tree *tree, const char *dname) +{ + union smb_mkdir parms; + + parms.mkdir.level = RAW_MKDIR_MKDIR; + parms.mkdir.in.path = dname; + + return smb_raw_mkdir(tree, &parms); +} + + +/**************************************************************************** + Remove a directory. +****************************************************************************/ +NTSTATUS smbcli_rmdir(struct smbcli_tree *tree, const char *dname) +{ + struct smb_rmdir parms; + + parms.in.path = dname; + + return smb_raw_rmdir(tree, &parms); +} + + +/**************************************************************************** + Set or clear the delete on close flag. +****************************************************************************/ +NTSTATUS smbcli_nt_delete_on_close(struct smbcli_tree *tree, int fnum, + bool flag) +{ + union smb_setfileinfo parms; + NTSTATUS status; + + parms.disposition_info.level = RAW_SFILEINFO_DISPOSITION_INFO; + parms.disposition_info.in.file.fnum = fnum; + parms.disposition_info.in.delete_on_close = flag; + + status = smb_raw_setfileinfo(tree, &parms); + + return status; +} + + +/**************************************************************************** + Create/open a file - exposing the full horror of the NT API :-). + Used in CIFS-on-CIFS NTVFS. +****************************************************************************/ +int smbcli_nt_create_full(struct smbcli_tree *tree, const char *fname, + uint32_t CreatFlags, uint32_t DesiredAccess, + uint32_t FileAttributes, uint32_t ShareAccess, + uint32_t CreateDisposition, uint32_t CreateOptions, + uint8_t SecurityFlags) +{ + union smb_open open_parms; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + + mem_ctx = talloc_init("raw_open"); + if (!mem_ctx) return -1; + + open_parms.ntcreatex.level = RAW_OPEN_NTCREATEX; + open_parms.ntcreatex.in.flags = CreatFlags; + open_parms.ntcreatex.in.root_fid = 0; + open_parms.ntcreatex.in.access_mask = DesiredAccess; + open_parms.ntcreatex.in.file_attr = FileAttributes; + open_parms.ntcreatex.in.alloc_size = 0; + open_parms.ntcreatex.in.share_access = ShareAccess; + open_parms.ntcreatex.in.open_disposition = CreateDisposition; + open_parms.ntcreatex.in.create_options = CreateOptions; + open_parms.ntcreatex.in.impersonation = 0; + open_parms.ntcreatex.in.security_flags = SecurityFlags; + open_parms.ntcreatex.in.fname = fname; + + status = smb_raw_open(tree, mem_ctx, &open_parms); + talloc_free(mem_ctx); + + if (NT_STATUS_IS_OK(status)) { + return open_parms.ntcreatex.out.file.fnum; + } + + return -1; +} + + +/**************************************************************************** + Open a file (using SMBopenx) + WARNING: if you open with O_WRONLY then getattrE won't work! +****************************************************************************/ +int smbcli_open(struct smbcli_tree *tree, const char *fname, int flags, + int share_mode) +{ + union smb_open open_parms; + uint_t openfn=0; + uint_t accessmode=0; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + + mem_ctx = talloc_init("raw_open"); + if (!mem_ctx) return -1; + + if (flags & O_CREAT) { + openfn |= OPENX_OPEN_FUNC_CREATE; + } + if (!(flags & O_EXCL)) { + if (flags & O_TRUNC) { + openfn |= OPENX_OPEN_FUNC_TRUNC; + } else { + openfn |= OPENX_OPEN_FUNC_OPEN; + } + } + + accessmode = (share_mode<<OPENX_MODE_DENY_SHIFT); + + if ((flags & O_ACCMODE) == O_RDWR) { + accessmode |= OPENX_MODE_ACCESS_RDWR; + } else if ((flags & O_ACCMODE) == O_WRONLY) { + accessmode |= OPENX_MODE_ACCESS_WRITE; + } + +#if defined(O_SYNC) + if ((flags & O_SYNC) == O_SYNC) { + accessmode |= OPENX_MODE_WRITE_THRU; + } +#endif + + if (share_mode == DENY_FCB) { + accessmode = OPENX_MODE_ACCESS_FCB | OPENX_MODE_DENY_FCB; + } + + open_parms.openx.level = RAW_OPEN_OPENX; + open_parms.openx.in.flags = 0; + open_parms.openx.in.open_mode = accessmode; + open_parms.openx.in.search_attrs = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN; + open_parms.openx.in.file_attrs = 0; + open_parms.openx.in.write_time = 0; + open_parms.openx.in.open_func = openfn; + open_parms.openx.in.size = 0; + open_parms.openx.in.timeout = 0; + open_parms.openx.in.fname = fname; + + status = smb_raw_open(tree, mem_ctx, &open_parms); + talloc_free(mem_ctx); + + if (NT_STATUS_IS_OK(status)) { + return open_parms.openx.out.file.fnum; + } + + return -1; +} + + +/**************************************************************************** + Close a file. +****************************************************************************/ +NTSTATUS smbcli_close(struct smbcli_tree *tree, int fnum) +{ + union smb_close close_parms; + NTSTATUS status; + + close_parms.close.level = RAW_CLOSE_CLOSE; + close_parms.close.in.file.fnum = fnum; + close_parms.close.in.write_time = 0; + status = smb_raw_close(tree, &close_parms); + return status; +} + +/**************************************************************************** + send a lock with a specified locktype + this is used for testing LOCKING_ANDX_CANCEL_LOCK +****************************************************************************/ +NTSTATUS smbcli_locktype(struct smbcli_tree *tree, int fnum, + uint32_t offset, uint32_t len, int timeout, + uint8_t locktype) +{ + union smb_lock parms; + struct smb_lock_entry lock[1]; + NTSTATUS status; + + parms.lockx.level = RAW_LOCK_LOCKX; + parms.lockx.in.file.fnum = fnum; + parms.lockx.in.mode = locktype; + parms.lockx.in.timeout = timeout; + parms.lockx.in.ulock_cnt = 0; + parms.lockx.in.lock_cnt = 1; + lock[0].pid = tree->session->pid; + lock[0].offset = offset; + lock[0].count = len; + parms.lockx.in.locks = &lock[0]; + + status = smb_raw_lock(tree, &parms); + + return status; +} + + +/**************************************************************************** + Lock a file. +****************************************************************************/ +NTSTATUS smbcli_lock(struct smbcli_tree *tree, int fnum, + uint32_t offset, uint32_t len, int timeout, + enum brl_type lock_type) +{ + union smb_lock parms; + struct smb_lock_entry lock[1]; + NTSTATUS status; + + parms.lockx.level = RAW_LOCK_LOCKX; + parms.lockx.in.file.fnum = fnum; + parms.lockx.in.mode = (lock_type == READ_LOCK? 1 : 0); + parms.lockx.in.timeout = timeout; + parms.lockx.in.ulock_cnt = 0; + parms.lockx.in.lock_cnt = 1; + lock[0].pid = tree->session->pid; + lock[0].offset = offset; + lock[0].count = len; + parms.lockx.in.locks = &lock[0]; + + status = smb_raw_lock(tree, &parms); + + return status; +} + + +/**************************************************************************** + Unlock a file. +****************************************************************************/ +NTSTATUS smbcli_unlock(struct smbcli_tree *tree, int fnum, uint32_t offset, uint32_t len) +{ + union smb_lock parms; + struct smb_lock_entry lock[1]; + NTSTATUS status; + + parms.lockx.level = RAW_LOCK_LOCKX; + parms.lockx.in.file.fnum = fnum; + parms.lockx.in.mode = 0; + parms.lockx.in.timeout = 0; + parms.lockx.in.ulock_cnt = 1; + parms.lockx.in.lock_cnt = 0; + lock[0].pid = tree->session->pid; + lock[0].offset = offset; + lock[0].count = len; + parms.lockx.in.locks = &lock[0]; + + status = smb_raw_lock(tree, &parms); + return status; +} + + +/**************************************************************************** + Lock a file with 64 bit offsets. +****************************************************************************/ +NTSTATUS smbcli_lock64(struct smbcli_tree *tree, int fnum, + off_t offset, off_t len, int timeout, + enum brl_type lock_type) +{ + union smb_lock parms; + int ltype; + struct smb_lock_entry lock[1]; + NTSTATUS status; + + if (!(tree->session->transport->negotiate.capabilities & CAP_LARGE_FILES)) { + return smbcli_lock(tree, fnum, offset, len, timeout, lock_type); + } + + parms.lockx.level = RAW_LOCK_LOCKX; + parms.lockx.in.file.fnum = fnum; + + ltype = (lock_type == READ_LOCK? 1 : 0); + ltype |= LOCKING_ANDX_LARGE_FILES; + parms.lockx.in.mode = ltype; + parms.lockx.in.timeout = timeout; + parms.lockx.in.ulock_cnt = 0; + parms.lockx.in.lock_cnt = 1; + lock[0].pid = tree->session->pid; + lock[0].offset = offset; + lock[0].count = len; + parms.lockx.in.locks = &lock[0]; + + status = smb_raw_lock(tree, &parms); + + return status; +} + + +/**************************************************************************** + Unlock a file with 64 bit offsets. +****************************************************************************/ +NTSTATUS smbcli_unlock64(struct smbcli_tree *tree, int fnum, off_t offset, + off_t len) +{ + union smb_lock parms; + struct smb_lock_entry lock[1]; + NTSTATUS status; + + if (!(tree->session->transport->negotiate.capabilities & CAP_LARGE_FILES)) { + return smbcli_unlock(tree, fnum, offset, len); + } + + parms.lockx.level = RAW_LOCK_LOCKX; + parms.lockx.in.file.fnum = fnum; + parms.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; + parms.lockx.in.timeout = 0; + parms.lockx.in.ulock_cnt = 1; + parms.lockx.in.lock_cnt = 0; + lock[0].pid = tree->session->pid; + lock[0].offset = offset; + lock[0].count = len; + parms.lockx.in.locks = &lock[0]; + + status = smb_raw_lock(tree, &parms); + + return status; +} + + +/**************************************************************************** + Do a SMBgetattrE call. +****************************************************************************/ +NTSTATUS smbcli_getattrE(struct smbcli_tree *tree, int fnum, + uint16_t *attr, size_t *size, + time_t *c_time, time_t *a_time, time_t *m_time) +{ + union smb_fileinfo parms; + NTSTATUS status; + + parms.getattre.level = RAW_FILEINFO_GETATTRE; + parms.getattre.in.file.fnum = fnum; + + status = smb_raw_fileinfo(tree, NULL, &parms); + + if (!NT_STATUS_IS_OK(status)) + return status; + + if (size) { + *size = parms.getattre.out.size; + } + + if (attr) { + *attr = parms.getattre.out.attrib; + } + + if (c_time) { + *c_time = parms.getattre.out.create_time; + } + + if (a_time) { + *a_time = parms.getattre.out.access_time; + } + + if (m_time) { + *m_time = parms.getattre.out.write_time; + } + + return status; +} + +/**************************************************************************** + Do a SMBgetatr call +****************************************************************************/ +NTSTATUS smbcli_getatr(struct smbcli_tree *tree, const char *fname, + uint16_t *attr, size_t *size, time_t *t) +{ + union smb_fileinfo parms; + NTSTATUS status; + + parms.getattr.level = RAW_FILEINFO_GETATTR; + parms.getattr.in.file.path = fname; + + status = smb_raw_pathinfo(tree, NULL, &parms); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (size) { + *size = parms.getattr.out.size; + } + + if (t) { + *t = parms.getattr.out.write_time; + } + + if (attr) { + *attr = parms.getattr.out.attrib; + } + + return status; +} + + +/**************************************************************************** + Do a SMBsetatr call. +****************************************************************************/ +NTSTATUS smbcli_setatr(struct smbcli_tree *tree, const char *fname, uint16_t mode, + time_t t) +{ + union smb_setfileinfo parms; + + parms.setattr.level = RAW_SFILEINFO_SETATTR; + parms.setattr.in.file.path = fname; + parms.setattr.in.attrib = mode; + parms.setattr.in.write_time = t; + + return smb_raw_setpathinfo(tree, &parms); +} + +/**************************************************************************** + Do a setfileinfo basic_info call. +****************************************************************************/ +NTSTATUS smbcli_fsetatr(struct smbcli_tree *tree, int fnum, uint16_t mode, + NTTIME create_time, NTTIME access_time, + NTTIME write_time, NTTIME change_time) +{ + union smb_setfileinfo parms; + + parms.basic_info.level = RAW_SFILEINFO_BASIC_INFO; + parms.basic_info.in.file.fnum = fnum; + parms.basic_info.in.attrib = mode; + parms.basic_info.in.create_time = create_time; + parms.basic_info.in.access_time = access_time; + parms.basic_info.in.write_time = write_time; + parms.basic_info.in.change_time = change_time; + + return smb_raw_setfileinfo(tree, &parms); +} + + +/**************************************************************************** + truncate a file to a given size +****************************************************************************/ +NTSTATUS smbcli_ftruncate(struct smbcli_tree *tree, int fnum, uint64_t size) +{ + union smb_setfileinfo parms; + + parms.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFO; + parms.end_of_file_info.in.file.fnum = fnum; + parms.end_of_file_info.in.size = size; + + return smb_raw_setfileinfo(tree, &parms); +} + + +/**************************************************************************** + Check for existence of a dir. +****************************************************************************/ +NTSTATUS smbcli_chkpath(struct smbcli_tree *tree, const char *path) +{ + union smb_chkpath parms; + char *path2; + NTSTATUS status; + + path2 = strdup(path); + trim_string(path2,NULL,"\\"); + if (!*path2) { + free(path2); + path2 = strdup("\\"); + } + + parms.chkpath.in.path = path2; + + status = smb_raw_chkpath(tree, &parms); + + free(path2); + + return status; +} + + +/**************************************************************************** + Query disk space. +****************************************************************************/ +NTSTATUS smbcli_dskattr(struct smbcli_tree *tree, uint32_t *bsize, + uint64_t *total, uint64_t *avail) +{ + union smb_fsinfo fsinfo_parms; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + + mem_ctx = talloc_init("smbcli_dskattr"); + + fsinfo_parms.dskattr.level = RAW_QFS_SIZE_INFO; + status = smb_raw_fsinfo(tree, mem_ctx, &fsinfo_parms); + if (NT_STATUS_IS_OK(status)) { + *bsize = fsinfo_parms.size_info.out.bytes_per_sector * fsinfo_parms.size_info.out.sectors_per_unit; + *total = fsinfo_parms.size_info.out.total_alloc_units; + *avail = fsinfo_parms.size_info.out.avail_alloc_units; + } + + talloc_free(mem_ctx); + + return status; +} + + +/**************************************************************************** + Create and open a temporary file. +****************************************************************************/ +int smbcli_ctemp(struct smbcli_tree *tree, const char *path, char **tmp_path) +{ + union smb_open open_parms; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + + mem_ctx = talloc_init("raw_open"); + if (!mem_ctx) return -1; + + open_parms.openx.level = RAW_OPEN_CTEMP; + open_parms.ctemp.in.attrib = 0; + open_parms.ctemp.in.directory = path; + open_parms.ctemp.in.write_time = 0; + + status = smb_raw_open(tree, mem_ctx, &open_parms); + if (tmp_path) { + *tmp_path = strdup(open_parms.ctemp.out.name); + } + talloc_free(mem_ctx); + if (NT_STATUS_IS_OK(status)) { + return open_parms.ctemp.out.file.fnum; + } + return -1; +} diff --git a/source4/libcli/clilist.c b/source4/libcli/clilist.c new file mode 100644 index 0000000000..65ee0a8303 --- /dev/null +++ b/source4/libcli/clilist.c @@ -0,0 +1,356 @@ +/* + Unix SMB/CIFS implementation. + client directory list routines + Copyright (C) Andrew Tridgell 1994-2003 + Copyright (C) James Myers 2003 <myersjj@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/>. +*/ + +#include "includes.h" +#include "libcli/libcli.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" + +struct search_private { + struct clilist_file_info *dirlist; + TALLOC_CTX *mem_ctx; + int dirlist_len; + int ff_searchcount; /* total received in 1 server trip */ + int total_received; /* total received all together */ + enum smb_search_data_level data_level; + const char *last_name; /* used to continue trans2 search */ + struct smb_search_id id; /* used for old-style search */ +}; + + +/**************************************************************************** + Interpret a long filename structure. +****************************************************************************/ +static bool interpret_long_filename(enum smb_search_data_level level, + const union smb_search_data *info, + struct clilist_file_info *finfo) +{ + struct clilist_file_info finfo2; + + if (!finfo) finfo = &finfo2; + ZERO_STRUCTP(finfo); + + switch (level) { + case RAW_SEARCH_DATA_STANDARD: + finfo->size = info->standard.size; + finfo->mtime = info->standard.write_time; + finfo->attrib = info->standard.attrib; + finfo->name = info->standard.name.s; + finfo->short_name = info->standard.name.s; + break; + + case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO: + finfo->size = info->both_directory_info.size; + finfo->mtime = nt_time_to_unix(info->both_directory_info.write_time); + finfo->attrib = info->both_directory_info.attrib; + finfo->short_name = info->both_directory_info.short_name.s; + finfo->name = info->both_directory_info.name.s; + break; + + default: + DEBUG(0,("Unhandled level %d in interpret_long_filename\n", (int)level)); + return false; + } + + return true; +} + +/* callback function used for trans2 search */ +static bool smbcli_list_new_callback(void *private_data, const union smb_search_data *file) +{ + struct search_private *state = (struct search_private*) private_data; + struct clilist_file_info *tdl; + + /* add file info to the dirlist pool */ + tdl = talloc_realloc(state, + state->dirlist, + struct clilist_file_info, + state->dirlist_len + 1); + if (!tdl) { + return false; + } + state->dirlist = tdl; + state->dirlist_len++; + + interpret_long_filename(state->data_level, file, &state->dirlist[state->total_received]); + + state->last_name = state->dirlist[state->total_received].name; + state->total_received++; + state->ff_searchcount++; + + return true; +} + +int smbcli_list_new(struct smbcli_tree *tree, const char *Mask, uint16_t attribute, + enum smb_search_data_level level, + void (*fn)(struct clilist_file_info *, const char *, void *), + void *caller_state) +{ + union smb_search_first first_parms; + union smb_search_next next_parms; + struct search_private state; /* for callbacks */ + int received = 0; + bool first = true; + int num_received = 0; + int max_matches = 512; + char *mask; + int ff_eos = 0, i, ff_searchcount; + int ff_dir_handle=0; + + /* initialize state for search */ + state.mem_ctx = talloc_init("smbcli_list_new"); + state.dirlist_len = 0; + state.total_received = 0; + + state.dirlist = talloc_array(state.mem_ctx, + struct clilist_file_info, 0); + mask = talloc_strdup(state.mem_ctx, Mask); + + if (level == RAW_SEARCH_DATA_GENERIC) { + if (tree->session->transport->negotiate.capabilities & CAP_NT_SMBS) { + level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO; + } else { + level = RAW_SEARCH_DATA_STANDARD; + } + } + state.data_level = level; + + while (1) { + state.ff_searchcount = 0; + if (first) { + NTSTATUS status; + + first_parms.t2ffirst.level = RAW_SEARCH_TRANS2; + first_parms.t2ffirst.data_level = state.data_level; + first_parms.t2ffirst.in.max_count = max_matches; + first_parms.t2ffirst.in.search_attrib = attribute; + first_parms.t2ffirst.in.pattern = mask; + first_parms.t2ffirst.in.flags = FLAG_TRANS2_FIND_CLOSE_IF_END; + first_parms.t2ffirst.in.storage_type = 0; + + status = smb_raw_search_first(tree, + state.mem_ctx, &first_parms, + (void*)&state, smbcli_list_new_callback); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(state.mem_ctx); + return -1; + } + + ff_dir_handle = first_parms.t2ffirst.out.handle; + ff_searchcount = first_parms.t2ffirst.out.count; + ff_eos = first_parms.t2ffirst.out.end_of_search; + + received = first_parms.t2ffirst.out.count; + if (received <= 0) break; + if (ff_eos) break; + first = false; + } else { + NTSTATUS status; + + next_parms.t2fnext.level = RAW_SEARCH_TRANS2; + next_parms.t2fnext.data_level = state.data_level; + next_parms.t2fnext.in.max_count = max_matches; + next_parms.t2fnext.in.last_name = state.last_name; + next_parms.t2fnext.in.handle = ff_dir_handle; + next_parms.t2fnext.in.resume_key = 0; + next_parms.t2fnext.in.flags = FLAG_TRANS2_FIND_CLOSE_IF_END; + + status = smb_raw_search_next(tree, + state.mem_ctx, + &next_parms, + (void*)&state, + smbcli_list_new_callback); + + if (!NT_STATUS_IS_OK(status)) { + return -1; + } + ff_searchcount = next_parms.t2fnext.out.count; + ff_eos = next_parms.t2fnext.out.end_of_search; + received = next_parms.t2fnext.out.count; + if (received <= 0) break; + if (ff_eos) break; + } + + num_received += received; + } + + for (i=0;i<state.total_received;i++) { + fn(&state.dirlist[i], Mask, caller_state); + } + + talloc_free(state.mem_ctx); + + return state.total_received; +} + +/**************************************************************************** + Interpret a short filename structure. + The length of the structure is returned. +****************************************************************************/ +static bool interpret_short_filename(enum smb_search_data_level level, + const union smb_search_data *info, + struct clilist_file_info *finfo) +{ + struct clilist_file_info finfo2; + + if (!finfo) finfo = &finfo2; + ZERO_STRUCTP(finfo); + + switch (level) { + case RAW_SEARCH_DATA_SEARCH: + finfo->mtime = info->search.write_time; + finfo->size = info->search.size; + finfo->attrib = info->search.attrib; + finfo->name = info->search.name; + finfo->short_name = info->search.name; + break; + + default: + DEBUG(0,("Unhandled level %d in interpret_short_filename\n", (int)level)); + return false; + } + + return true; +} + +/* callback function used for smb_search */ +static bool smbcli_list_old_callback(void *private_data, const union smb_search_data *file) +{ + struct search_private *state = (struct search_private*) private_data; + struct clilist_file_info *tdl; + + /* add file info to the dirlist pool */ + tdl = talloc_realloc(state, + state->dirlist, + struct clilist_file_info, + state->dirlist_len + 1); + + if (!tdl) { + return false; + } + state->dirlist = tdl; + state->dirlist_len++; + + interpret_short_filename(state->data_level, file, &state->dirlist[state->total_received]); + + state->total_received++; + state->ff_searchcount++; + state->id = file->search.id; /* return resume info */ + + return true; +} + +int smbcli_list_old(struct smbcli_tree *tree, const char *Mask, uint16_t attribute, + void (*fn)(struct clilist_file_info *, const char *, void *), + void *caller_state) +{ + union smb_search_first first_parms; + union smb_search_next next_parms; + struct search_private state; /* for callbacks */ + const int num_asked = 500; + int received = 0; + bool first = true; + int num_received = 0; + char *mask; + int i; + + /* initialize state for search */ + state.mem_ctx = talloc_init("smbcli_list_old"); + state.dirlist_len = 0; + state.total_received = 0; + state.data_level = RAW_SEARCH_DATA_SEARCH; + + state.dirlist = talloc_array(state.mem_ctx, struct clilist_file_info, + 0); + mask = talloc_strdup(state.mem_ctx, Mask); + + while (1) { + state.ff_searchcount = 0; + if (first) { + NTSTATUS status; + + first_parms.search_first.level = RAW_SEARCH_SEARCH; + first_parms.search_first.data_level = RAW_SEARCH_DATA_SEARCH; + first_parms.search_first.in.max_count = num_asked; + first_parms.search_first.in.search_attrib = attribute; + first_parms.search_first.in.pattern = mask; + + status = smb_raw_search_first(tree, state.mem_ctx, + &first_parms, + (void*)&state, + smbcli_list_old_callback); + + if (!NT_STATUS_IS_OK(status)) { + talloc_free(state.mem_ctx); + return -1; + } + + received = first_parms.search_first.out.count; + if (received <= 0) break; + first = false; + } else { + NTSTATUS status; + + next_parms.search_next.level = RAW_SEARCH_SEARCH; + next_parms.search_next.data_level = RAW_SEARCH_DATA_SEARCH; + next_parms.search_next.in.max_count = num_asked; + next_parms.search_next.in.search_attrib = attribute; + next_parms.search_next.in.id = state.id; + + status = smb_raw_search_next(tree, state.mem_ctx, + &next_parms, + (void*)&state, + smbcli_list_old_callback); + + if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) { + break; + } + if (!NT_STATUS_IS_OK(status)) { + talloc_free(state.mem_ctx); + return -1; + } + received = next_parms.search_next.out.count; + if (received <= 0) break; + } + + num_received += received; + } + + for (i=0;i<state.total_received;i++) { + fn(&state.dirlist[i], Mask, caller_state); + } + + talloc_free(state.mem_ctx); + + return state.total_received; +} + +/**************************************************************************** + Do a directory listing, calling fn on each file found. + This auto-switches between old and new style. +****************************************************************************/ + +int smbcli_list(struct smbcli_tree *tree, const char *Mask,uint16_t attribute, + void (*fn)(struct clilist_file_info *, const char *, void *), void *state) +{ + if (tree->session->transport->negotiate.protocol <= PROTOCOL_LANMAN1) + return smbcli_list_old(tree, Mask, attribute, fn, state); + return smbcli_list_new(tree, Mask, attribute, RAW_SEARCH_DATA_GENERIC, fn, state); +} diff --git a/source4/libcli/climessage.c b/source4/libcli/climessage.c new file mode 100644 index 0000000000..5ed0e8e3cd --- /dev/null +++ b/source4/libcli/climessage.c @@ -0,0 +1,95 @@ +/* + Unix SMB/CIFS implementation. + client message handling routines + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) James J Myers 2003 <myersjj@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/>. +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/libcli.h" + + +/**************************************************************************** +start a message sequence +****************************************************************************/ +bool smbcli_message_start(struct smbcli_tree *tree, const char *host, const char *username, + int *grp) +{ + struct smbcli_request *req; + + req = smbcli_request_setup(tree, SMBsendstrt, 0, 0); + smbcli_req_append_string(req, username, STR_TERMINATE); + smbcli_req_append_string(req, host, STR_TERMINATE); + if (!smbcli_request_send(req) || + !smbcli_request_receive(req) || + smbcli_is_error(tree)) { + smbcli_request_destroy(req); + return false; + } + + *grp = SVAL(req->in.vwv, VWV(0)); + smbcli_request_destroy(req); + + return true; +} + + +/**************************************************************************** +send a message +****************************************************************************/ +bool smbcli_message_text(struct smbcli_tree *tree, char *msg, int len, int grp) +{ + struct smbcli_request *req; + + req = smbcli_request_setup(tree, SMBsendtxt, 1, 0); + SSVAL(req->out.vwv, VWV(0), grp); + + smbcli_req_append_bytes(req, (const uint8_t *)msg, len); + + if (!smbcli_request_send(req) || + !smbcli_request_receive(req) || + smbcli_is_error(tree)) { + smbcli_request_destroy(req); + return false; + } + + smbcli_request_destroy(req); + return true; +} + +/**************************************************************************** +end a message +****************************************************************************/ +bool smbcli_message_end(struct smbcli_tree *tree, int grp) +{ + struct smbcli_request *req; + + req = smbcli_request_setup(tree, SMBsendend, 1, 0); + SSVAL(req->out.vwv, VWV(0), grp); + + if (!smbcli_request_send(req) || + !smbcli_request_receive(req) || + smbcli_is_error(tree)) { + smbcli_request_destroy(req); + return false; + } + + smbcli_request_destroy(req); + return true; +} + diff --git a/source4/libcli/clireadwrite.c b/source4/libcli/clireadwrite.c new file mode 100644 index 0000000000..ae2367918c --- /dev/null +++ b/source4/libcli/clireadwrite.c @@ -0,0 +1,167 @@ +/* + Unix SMB/CIFS implementation. + client file read/write routines + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) James Myers 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 "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/libcli.h" + +/**************************************************************************** + Read size bytes at offset offset using SMBreadX. +****************************************************************************/ +ssize_t smbcli_read(struct smbcli_tree *tree, int fnum, void *_buf, off_t offset, + size_t size) +{ + uint8_t *buf = (uint8_t *)_buf; + union smb_read parms; + int readsize; + ssize_t total = 0; + + if (size == 0) { + return 0; + } + + parms.readx.level = RAW_READ_READX; + parms.readx.in.file.fnum = fnum; + + /* + * Set readsize to the maximum size we can handle in one readX, + * rounded down to a multiple of 1024. + */ + readsize = (tree->session->transport->negotiate.max_xmit - (MIN_SMB_SIZE+32)); + if (readsize > 0xFFFF) readsize = 0xFFFF; + + while (total < size) { + NTSTATUS status; + + readsize = MIN(readsize, size-total); + + parms.readx.in.offset = offset; + parms.readx.in.mincnt = readsize; + parms.readx.in.maxcnt = readsize; + parms.readx.in.remaining = size - total; + parms.readx.in.read_for_execute = false; + parms.readx.out.data = buf + total; + + status = smb_raw_read(tree, &parms); + + if (!NT_STATUS_IS_OK(status)) { + return -1; + } + + total += parms.readx.out.nread; + offset += parms.readx.out.nread; + + /* If the server returned less than we asked for we're at EOF */ + if (parms.readx.out.nread < readsize) + break; + } + + return total; +} + + +/**************************************************************************** + 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 smbcli_write(struct smbcli_tree *tree, + int fnum, uint16_t write_mode, + const void *_buf, off_t offset, size_t size) +{ + const uint8_t *buf = (const uint8_t *)_buf; + union smb_write parms; + int block = (tree->session->transport->negotiate.max_xmit - (MIN_SMB_SIZE+32)); + ssize_t total = 0; + + if (size == 0) { + return 0; + } + + if (block > 0xFFFF) block = 0xFFFF; + + + parms.writex.level = RAW_WRITE_WRITEX; + parms.writex.in.file.fnum = fnum; + parms.writex.in.wmode = write_mode; + parms.writex.in.remaining = 0; + + while (total < size) { + NTSTATUS status; + + block = MIN(block, size - total); + + parms.writex.in.offset = offset; + parms.writex.in.count = block; + parms.writex.in.data = buf; + + status = smb_raw_write(tree, &parms); + + if (!NT_STATUS_IS_OK(status)) { + return -1; + } + + offset += parms.writex.out.nwritten; + total += parms.writex.out.nwritten; + buf += parms.writex.out.nwritten; + } + + return total; +} + +/**************************************************************************** + write to a file using a SMBwrite and not bypassing 0 byte writes +****************************************************************************/ +ssize_t smbcli_smbwrite(struct smbcli_tree *tree, + int fnum, const void *_buf, off_t offset, size_t size1) +{ + const uint8_t *buf = (const uint8_t *)_buf; + union smb_write parms; + ssize_t total = 0; + + parms.write.level = RAW_WRITE_WRITE; + parms.write.in.remaining = 0; + + do { + size_t size = MIN(size1, tree->session->transport->negotiate.max_xmit - 48); + if (size > 0xFFFF) size = 0xFFFF; + + parms.write.in.file.fnum = fnum; + parms.write.in.offset = offset; + parms.write.in.count = size; + parms.write.in.data = buf + total; + + if (NT_STATUS_IS_ERR(smb_raw_write(tree, &parms))) + return -1; + + size = parms.write.out.nwritten; + if (size == 0) + break; + + size1 -= size; + total += size; + offset += size; + } while (size1); + + return total; +} diff --git a/source4/libcli/clitrans2.c b/source4/libcli/clitrans2.c new file mode 100644 index 0000000000..5c5ba6585f --- /dev/null +++ b/source4/libcli/clitrans2.c @@ -0,0 +1,224 @@ +/* + Unix SMB/CIFS implementation. + client trans2 calls + Copyright (C) James J Myers 2003 <myersjj@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/>. +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/libcli.h" + +/**************************************************************************** +send a qpathinfo call +****************************************************************************/ +NTSTATUS smbcli_qpathinfo(struct smbcli_tree *tree, const char *fname, + time_t *c_time, time_t *a_time, time_t *m_time, + size_t *size, uint16_t *mode) +{ + union smb_fileinfo parms; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + + mem_ctx = talloc_init("smbcli_qpathinfo"); + if (!mem_ctx) return NT_STATUS_NO_MEMORY; + + parms.standard.level = RAW_FILEINFO_STANDARD; + parms.standard.in.file.path = fname; + + status = smb_raw_pathinfo(tree, mem_ctx, &parms); + talloc_free(mem_ctx); + if (!NT_STATUS_IS_OK(status)) + return status; + + if (c_time) { + *c_time = parms.standard.out.create_time; + } + if (a_time) { + *a_time = parms.standard.out.access_time; + } + if (m_time) { + *m_time = parms.standard.out.write_time; + } + if (size) { + *size = parms.standard.out.size; + } + if (mode) { + *mode = parms.standard.out.attrib; + } + + return status; +} + +/**************************************************************************** +send a qpathinfo call with the SMB_QUERY_FILE_ALL_INFO info level +****************************************************************************/ +NTSTATUS smbcli_qpathinfo2(struct smbcli_tree *tree, const char *fname, + time_t *c_time, time_t *a_time, time_t *m_time, + time_t *w_time, size_t *size, uint16_t *mode, + ino_t *ino) +{ + union smb_fileinfo parms; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + + mem_ctx = talloc_init("smbcli_qfilename"); + if (!mem_ctx) return NT_STATUS_NO_MEMORY; + + parms.all_info.level = RAW_FILEINFO_ALL_INFO; + parms.all_info.in.file.path = fname; + + status = smb_raw_pathinfo(tree, mem_ctx, &parms); + talloc_free(mem_ctx); + if (!NT_STATUS_IS_OK(status)) + return status; + + if (c_time) { + *c_time = nt_time_to_unix(parms.all_info.out.create_time); + } + if (a_time) { + *a_time = nt_time_to_unix(parms.all_info.out.access_time); + } + if (m_time) { + *m_time = nt_time_to_unix(parms.all_info.out.change_time); + } + if (w_time) { + *w_time = nt_time_to_unix(parms.all_info.out.write_time); + } + if (size) { + *size = parms.all_info.out.size; + } + if (mode) { + *mode = parms.all_info.out.attrib; + } + + return status; +} + + +/**************************************************************************** +send a qfileinfo QUERY_FILE_NAME_INFO call +****************************************************************************/ +NTSTATUS smbcli_qfilename(struct smbcli_tree *tree, int fnum, const char **name) +{ + union smb_fileinfo parms; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + + mem_ctx = talloc_init("smbcli_qfilename"); + if (!mem_ctx) return NT_STATUS_NO_MEMORY; + + parms.name_info.level = RAW_FILEINFO_NAME_INFO; + parms.name_info.in.file.fnum = fnum; + + status = smb_raw_fileinfo(tree, mem_ctx, &parms); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + *name = NULL; + return status; + } + + *name = strdup(parms.name_info.out.fname.s); + + talloc_free(mem_ctx); + + return status; +} + + +/**************************************************************************** +send a qfileinfo call +****************************************************************************/ +NTSTATUS smbcli_qfileinfo(struct smbcli_tree *tree, int fnum, + uint16_t *mode, size_t *size, + time_t *c_time, time_t *a_time, time_t *m_time, + time_t *w_time, ino_t *ino) +{ + union smb_fileinfo parms; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + + mem_ctx = talloc_init("smbcli_qfileinfo"); + if (!mem_ctx) + return NT_STATUS_NO_MEMORY; + + parms.all_info.level = RAW_FILEINFO_ALL_INFO; + parms.all_info.in.file.fnum = fnum; + + status = smb_raw_fileinfo(tree, mem_ctx, &parms); + talloc_free(mem_ctx); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (c_time) { + *c_time = nt_time_to_unix(parms.all_info.out.create_time); + } + if (a_time) { + *a_time = nt_time_to_unix(parms.all_info.out.access_time); + } + if (m_time) { + *m_time = nt_time_to_unix(parms.all_info.out.change_time); + } + if (w_time) { + *w_time = nt_time_to_unix(parms.all_info.out.write_time); + } + if (mode) { + *mode = parms.all_info.out.attrib; + } + if (size) { + *size = (size_t)parms.all_info.out.size; + } + if (ino) { + *ino = 0; + } + + return status; +} + + +/**************************************************************************** +send a qpathinfo SMB_QUERY_FILE_ALT_NAME_INFO call +****************************************************************************/ +NTSTATUS smbcli_qpathinfo_alt_name(struct smbcli_tree *tree, const char *fname, + const char **alt_name) +{ + union smb_fileinfo parms; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + + parms.alt_name_info.level = RAW_FILEINFO_ALT_NAME_INFO; + parms.alt_name_info.in.file.path = fname; + + mem_ctx = talloc_init("smbcli_qpathinfo_alt_name"); + if (!mem_ctx) return NT_STATUS_NO_MEMORY; + + status = smb_raw_pathinfo(tree, mem_ctx, &parms); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + *alt_name = NULL; + return smbcli_nt_error(tree); + } + + if (!parms.alt_name_info.out.fname.s) { + *alt_name = strdup(""); + } else { + *alt_name = strdup(parms.alt_name_info.out.fname.s); + } + + talloc_free(mem_ctx); + + return NT_STATUS_OK; +} diff --git a/source4/libcli/composite/composite.c b/source4/libcli/composite/composite.c new file mode 100644 index 0000000000..ab32175d00 --- /dev/null +++ b/source4/libcli/composite/composite.c @@ -0,0 +1,218 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Volker Lendecke 2005 + Copyright (C) Andrew Tridgell 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/>. +*/ +/* + composite API helper functions +*/ + +#include "includes.h" +#include "lib/events/events.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/smb2/smb2.h" +#include "libcli/composite/composite.h" +#include "lib/messaging/irpc.h" +#include "librpc/rpc/dcerpc.h" +#include "../libcli/nbt/libnbt.h" + +/* + create a new composite_context structure + and initialize it +*/ +_PUBLIC_ struct composite_context *composite_create(TALLOC_CTX *mem_ctx, + struct tevent_context *ev) +{ + struct composite_context *c; + + c = talloc_zero(mem_ctx, struct composite_context); + if (!c) return NULL; + c->state = COMPOSITE_STATE_IN_PROGRESS; + c->event_ctx = talloc_reference(c, ev); + if (!c->event_ctx) { + talloc_free(c); + return NULL; + } + + return c; +} + +/* + block until a composite function has completed, then return the status +*/ +_PUBLIC_ NTSTATUS composite_wait(struct composite_context *c) +{ + if (c == NULL) return NT_STATUS_NO_MEMORY; + + c->used_wait = true; + + while (c->state < COMPOSITE_STATE_DONE) { + if (event_loop_once(c->event_ctx) != 0) { + return NT_STATUS_UNSUCCESSFUL; + } + } + + return c->status; +} + +/* + block until a composite function has completed, then return the status. + Free the composite context before returning +*/ +_PUBLIC_ NTSTATUS composite_wait_free(struct composite_context *c) +{ + NTSTATUS status = composite_wait(c); + talloc_free(c); + return status; +} + +/* + callback from composite_done() and composite_error() + + this is used to allow for a composite function to complete without + going through any state transitions. When that happens the caller + has had no opportunity to fill in the async callback fields + (ctx->async.fn and ctx->async.private_data) which means the usual way of + dealing with composite functions doesn't work. To cope with this, + we trigger a timer event that will happen then the event loop is + re-entered. This gives the caller a chance to setup the callback, + and allows the caller to ignore the fact that the composite + function completed early +*/ +static void composite_trigger(struct tevent_context *ev, struct tevent_timer *te, + struct timeval t, void *ptr) +{ + struct composite_context *c = talloc_get_type(ptr, struct composite_context); + if (c->async.fn) { + c->async.fn(c); + } +} + + +_PUBLIC_ void composite_error(struct composite_context *ctx, NTSTATUS status) +{ + /* you are allowed to pass NT_STATUS_OK to composite_error(), in which + case it is equivalent to composite_done() */ + if (NT_STATUS_IS_OK(status)) { + composite_done(ctx); + return; + } + if (!ctx->used_wait && !ctx->async.fn) { + event_add_timed(ctx->event_ctx, ctx, timeval_zero(), composite_trigger, ctx); + } + ctx->status = status; + ctx->state = COMPOSITE_STATE_ERROR; + if (ctx->async.fn != NULL) { + ctx->async.fn(ctx); + } +} + +_PUBLIC_ bool composite_nomem(const void *p, struct composite_context *ctx) +{ + if (p != NULL) { + return false; + } + composite_error(ctx, NT_STATUS_NO_MEMORY); + return true; +} + +_PUBLIC_ bool composite_is_ok(struct composite_context *ctx) +{ + if (NT_STATUS_IS_OK(ctx->status)) { + return true; + } + composite_error(ctx, ctx->status); + return false; +} + +_PUBLIC_ void composite_done(struct composite_context *ctx) +{ + if (!ctx->used_wait && !ctx->async.fn) { + event_add_timed(ctx->event_ctx, ctx, timeval_zero(), composite_trigger, ctx); + } + ctx->state = COMPOSITE_STATE_DONE; + if (ctx->async.fn != NULL) { + ctx->async.fn(ctx); + } +} + +_PUBLIC_ void composite_continue(struct composite_context *ctx, + struct composite_context *new_ctx, + void (*continuation)(struct composite_context *), + void *private_data) +{ + if (composite_nomem(new_ctx, ctx)) return; + new_ctx->async.fn = continuation; + new_ctx->async.private_data = private_data; + + /* if we are setting up a continuation, and the context has + already finished, then we should run the callback with an + immediate event, otherwise we can be stuck forever */ + if (new_ctx->state >= COMPOSITE_STATE_DONE && continuation) { + event_add_timed(new_ctx->event_ctx, new_ctx, timeval_zero(), composite_trigger, new_ctx); + } +} + +_PUBLIC_ void composite_continue_rpc(struct composite_context *ctx, + struct rpc_request *new_req, + void (*continuation)(struct rpc_request *), + void *private_data) +{ + if (composite_nomem(new_req, ctx)) return; + new_req->async.callback = continuation; + new_req->async.private_data = private_data; +} + +_PUBLIC_ void composite_continue_irpc(struct composite_context *ctx, + struct irpc_request *new_req, + void (*continuation)(struct irpc_request *), + void *private_data) +{ + if (composite_nomem(new_req, ctx)) return; + new_req->async.fn = continuation; + new_req->async.private_data = private_data; +} + +_PUBLIC_ void composite_continue_smb(struct composite_context *ctx, + struct smbcli_request *new_req, + void (*continuation)(struct smbcli_request *), + void *private_data) +{ + if (composite_nomem(new_req, ctx)) return; + new_req->async.fn = continuation; + new_req->async.private_data = private_data; +} + +_PUBLIC_ void composite_continue_smb2(struct composite_context *ctx, + struct smb2_request *new_req, + void (*continuation)(struct smb2_request *), + void *private_data) +{ + if (composite_nomem(new_req, ctx)) return; + new_req->async.fn = continuation; + new_req->async.private_data = private_data; +} + +_PUBLIC_ void composite_continue_nbt(struct composite_context *ctx, + struct nbt_name_request *new_req, + void (*continuation)(struct nbt_name_request *), + void *private_data) +{ + if (composite_nomem(new_req, ctx)) return; + new_req->async.fn = continuation; + new_req->async.private_data = private_data; +} diff --git a/source4/libcli/composite/composite.h b/source4/libcli/composite/composite.h new file mode 100644 index 0000000000..a99a762c5f --- /dev/null +++ b/source4/libcli/composite/composite.h @@ -0,0 +1,109 @@ +/* + Unix SMB/CIFS implementation. + + composite request interfaces + + Copyright (C) Andrew Tridgell 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/>. +*/ + +#ifndef __COMPOSITE_H__ +#define __COMPOSITE_H__ + +#include "libcli/raw/interfaces.h" + +struct tevent_context; + +/* + this defines the structures associated with "composite" + requests. Composite requests are libcli requests that are internally + implemented as multiple async calls, but can be treated as a + single call via these composite calls. The composite calls are + particularly designed to be used in async applications. + you can also stack multiple level of composite call +*/ + +/* + a composite call moves between the following 3 states. +*/ +enum composite_state { COMPOSITE_STATE_INIT, /* we are creating the request */ + COMPOSITE_STATE_IN_PROGRESS, /* the request is in the outgoing socket Q */ + COMPOSITE_STATE_DONE, /* the request is received by the caller finished */ + COMPOSITE_STATE_ERROR }; /* a packet or transport level error has occurred */ + +/* the context of one "composite" call */ +struct composite_context { + /* the external state - will be queried by the caller */ + enum composite_state state; + + /* a private pointer for use by the composite function + implementation */ + void *private_data; + + /* status code when finished */ + NTSTATUS status; + + /* the event context we are using */ + struct tevent_context *event_ctx; + + /* information on what to do on completion */ + struct { + void (*fn)(struct composite_context *); + void *private_data; + } async; + + bool used_wait; +}; + +struct irpc_request; +struct smbcli_request; +struct smb2_request; +struct rpc_request; +struct nbt_name_request; + +struct composite_context *composite_create(TALLOC_CTX *mem_ctx, struct tevent_context *ev); +bool composite_nomem(const void *p, struct composite_context *ctx); +void composite_continue(struct composite_context *ctx, + struct composite_context *new_ctx, + void (*continuation)(struct composite_context *), + void *private_data); +void composite_continue_rpc(struct composite_context *ctx, + struct rpc_request *new_req, + void (*continuation)(struct rpc_request *), + void *private_data); +void composite_continue_irpc(struct composite_context *ctx, + struct irpc_request *new_req, + void (*continuation)(struct irpc_request *), + void *private_data); +void composite_continue_smb(struct composite_context *ctx, + struct smbcli_request *new_req, + void (*continuation)(struct smbcli_request *), + void *private_data); +void composite_continue_smb2(struct composite_context *ctx, + struct smb2_request *new_req, + void (*continuation)(struct smb2_request *), + void *private_data); +void composite_continue_nbt(struct composite_context *ctx, + struct nbt_name_request *new_req, + void (*continuation)(struct nbt_name_request *), + void *private_data); +bool composite_is_ok(struct composite_context *ctx); +void composite_done(struct composite_context *ctx); +void composite_error(struct composite_context *ctx, NTSTATUS status); +NTSTATUS composite_wait(struct composite_context *c); +NTSTATUS composite_wait_free(struct composite_context *c); + + +#endif /* __COMPOSITE_H__ */ diff --git a/source4/libcli/config.mk b/source4/libcli/config.mk new file mode 100644 index 0000000000..dc3431ab9f --- /dev/null +++ b/source4/libcli/config.mk @@ -0,0 +1,169 @@ +mkinclude auth/config.mk +mkinclude ldap/config.mk +mkinclude security/config.mk +mkinclude wbclient/config.mk + +[SUBSYSTEM::LIBSAMBA-ERRORS] + +LIBSAMBA-ERRORS_OBJ_FILES = $(addprefix ../libcli/util/, doserr.o ) $(libclisrcdir)/util/errormap.o $(libclisrcdir)/util/nterr.o + +PUBLIC_HEADERS += $(addprefix ../libcli/util/, error.h ntstatus.h doserr.h werror.h) + +[SUBSYSTEM::LIBCLI_LSA] +PUBLIC_DEPENDENCIES = RPC_NDR_LSA +PRIVATE_DEPENDENCIES = LIBSECURITY + +LIBCLI_LSA_OBJ_FILES = $(libclisrcdir)/util/clilsa.o + +$(eval $(call proto_header_template,$(libclisrcdir)/util/clilsa.h,$(LIBCLI_LSA_OBJ_FILES:.o=.c))) + +[SUBSYSTEM::LIBCLI_COMPOSITE] +PUBLIC_DEPENDENCIES = LIBEVENTS + +LIBCLI_COMPOSITE_OBJ_FILES = $(libclisrcdir)/composite/composite.o +$(eval $(call proto_header_template,$(libclisrcdir)/composite/proto.h,$(LIBCLI_COMPOSITE_OBJ_FILES:.o=.c))) + +[SUBSYSTEM::LIBCLI_SMB_COMPOSITE] +PUBLIC_DEPENDENCIES = LIBCLI_COMPOSITE CREDENTIALS gensec LIBCLI_RESOLVE + +LIBCLI_SMB_COMPOSITE_OBJ_FILES = $(addprefix $(libclisrcdir)/smb_composite/, \ + loadfile.o \ + savefile.o \ + connect.o \ + sesssetup.o \ + fetchfile.o \ + appendacl.o \ + fsinfo.o \ + smb2.o) + +$(eval $(call proto_header_template,$(libclisrcdir)/smb_composite/proto.h,$(LIBCLI_SMB_COMPOSITE_OBJ_FILES:.o=.c))) + +[SUBSYSTEM::NDR_NBT_BUF] + +NDR_NBT_BUF_OBJ_FILES = $(libclinbtsrcdir)/nbtname.o + +$(eval $(call proto_header_template,$(libclinbtsrcdir)/nbtname.h,$(NDR_NBT_BUF_OBJ_FILES:.o=.c))) + +[SUBSYSTEM::LIBCLI_NBT] +PUBLIC_DEPENDENCIES = LIBNDR NDR_NBT LIBCLI_COMPOSITE LIBEVENTS \ + NDR_SECURITY samba_socket LIBSAMBA-UTIL + +LIBCLI_NBT_OBJ_FILES = $(addprefix $(libclinbtsrcdir)/, \ + nbtsocket.o \ + namequery.o \ + nameregister.o \ + namerefresh.o \ + namerelease.o) + +[BINARY::nmblookup] +INSTALLDIR = BINDIR +PRIVATE_DEPENDENCIES = \ + LIBSAMBA-HOSTCONFIG \ + LIBSAMBA-UTIL \ + LIBCLI_NBT \ + LIBPOPT \ + POPT_SAMBA \ + LIBNETIF \ + LIBCLI_RESOLVE + +nmblookup_OBJ_FILES = $(libclinbtsrcdir)/tools/nmblookup.o +MANPAGES += $(libclinbtsrcdir)/man/nmblookup.1 + +[SUBSYSTEM::LIBCLI_NDR_NETLOGON] +PUBLIC_DEPENDENCIES = LIBNDR \ + NDR_SECURITY + +LIBCLI_NDR_NETLOGON_OBJ_FILES = $(addprefix $(libclinbtsrcdir)/../, ndr_netlogon.o) + +[SUBSYSTEM::LIBCLI_NETLOGON] +PUBLIC_DEPENDENCIES = LIBSAMBA-UTIL LIBCLI_NDR_NETLOGON + +LIBCLI_NETLOGON_OBJ_FILES = $(addprefix $(libclinbtsrcdir)/, \ + ../netlogon.o) + +[PYTHON::python_netbios] +LIBRARY_REALNAME = samba/netbios.$(SHLIBEXT) +PUBLIC_DEPENDENCIES = LIBCLI_NBT DYNCONFIG LIBSAMBA-HOSTCONFIG + +python_netbios_OBJ_FILES = $(libclinbtsrcdir)/pynbt.o + +[SUBSYSTEM::LIBCLI_DGRAM] +PUBLIC_DEPENDENCIES = LIBCLI_NBT LIBNDR LIBCLI_RESOLVE LIBCLI_NETLOGON + +LIBCLI_DGRAM_OBJ_FILES = $(addprefix $(libclisrcdir)/dgram/, \ + dgramsocket.o \ + mailslot.o \ + netlogon.o \ + browse.o) + +[SUBSYSTEM::LIBCLI_CLDAP] +PUBLIC_DEPENDENCIES = LIBCLI_LDAP +PRIVATE_DEPENDENCIES = LIBSAMBA-UTIL LIBLDB LIBCLI_NETLOGON + +LIBCLI_CLDAP_OBJ_FILES = $(libclisrcdir)/cldap/cldap.o +# PUBLIC_HEADERS += $(libclisrcdir)/cldap/cldap.h + +[SUBSYSTEM::LIBCLI_WREPL] +PUBLIC_DEPENDENCIES = NDR_WINSREPL samba_socket LIBEVENTS LIBPACKET + +LIBCLI_WREPL_OBJ_FILES = $(libclisrcdir)/wrepl/winsrepl.o + +$(eval $(call proto_header_template,$(libclisrcdir)/wrepl/winsrepl_proto.h,$(LIBCLI_WREPL_OBJ_FILES:.o=.c))) + +[SUBSYSTEM::LIBCLI_RESOLVE] +PUBLIC_DEPENDENCIES = NDR_NBT + +LIBCLI_RESOLVE_OBJ_FILES = $(libclisrcdir)/resolve/resolve.o + +$(eval $(call proto_header_template,$(libclisrcdir)/resolve/proto.h,$(LIBCLI_RESOLVE_OBJ_FILES:.o=.c))) + +[SUBSYSTEM::LP_RESOLVE] +PRIVATE_DEPENDENCIES = LIBCLI_NBT LIBSAMBA-HOSTCONFIG LIBNETIF + +LP_RESOLVE_OBJ_FILES = $(addprefix $(libclisrcdir)/resolve/, \ + bcast.o nbtlist.o wins.o \ + dns_ex.o \ + host.o resolve_lp.o) + +$(eval $(call proto_header_template,$(libclisrcdir)/resolve/lp_proto.h,$(LP_RESOLVE_OBJ_FILES:.o=.c))) + +[SUBSYSTEM::LIBCLI_FINDDCS] +PUBLIC_DEPENDENCIES = LIBCLI_NBT MESSAGING + +LIBCLI_FINDDCS_OBJ_FILES = $(libclisrcdir)/finddcs.o + +$(eval $(call proto_header_template,$(libclisrcdir)/finddcs.h,$(LIBCLI_FINDDCS_OBJ_FILES:.o=.c))) + +[SUBSYSTEM::LIBCLI_SMB] +PUBLIC_DEPENDENCIES = LIBCLI_RAW LIBSAMBA-ERRORS LIBCLI_AUTH \ + LIBCLI_SMB_COMPOSITE LIBCLI_NBT LIBSECURITY LIBCLI_RESOLVE \ + LIBCLI_DGRAM LIBCLI_SMB2 LIBCLI_FINDDCS samba_socket + +LIBCLI_SMB_OBJ_FILES = $(addprefix $(libclisrcdir)/, \ + clireadwrite.o \ + cliconnect.o \ + clifile.o \ + clilist.o \ + clitrans2.o \ + climessage.o \ + clideltree.o) + +$(eval $(call proto_header_template,$(libclisrcdir)/libcli_proto.h,$(LIBCLI_SMB_OBJ_FILES:.o=.c))) + +# PUBLIC_HEADERS += $(libclisrcdir)/libcli.h + +[SUBSYSTEM::LIBCLI_RAW] +PRIVATE_DEPENDENCIES = LIBCLI_COMPOSITE LP_RESOLVE gensec LIBCLI_RESOLVE LIBSECURITY LIBNDR +#LDFLAGS = $(LIBCLI_SMB_COMPOSITE_OUTPUT) +PUBLIC_DEPENDENCIES = samba_socket LIBPACKET gensec LIBCRYPTO CREDENTIALS + +LIBCLI_RAW_OBJ_FILES = $(addprefix $(libclisrcdir)/raw/, rawfile.o smb_signing.o clisocket.o \ + clitransport.o clisession.o clitree.o clierror.o rawrequest.o \ + rawreadwrite.o rawsearch.o rawsetfileinfo.o raweas.o rawtrans.o \ + clioplock.o rawnegotiate.o rawfsinfo.o rawfileinfo.o rawnotify.o \ + rawioctl.o rawacl.o rawdate.o rawlpq.o rawshadow.o) + + +$(eval $(call proto_header_template,$(libclisrcdir)/raw/raw_proto.h,$(LIBCLI_RAW_OBJ_FILES:.o=.c))) + +mkinclude smb2/config.mk diff --git a/source4/libcli/dgram/browse.c b/source4/libcli/dgram/browse.c new file mode 100644 index 0000000000..14d8278635 --- /dev/null +++ b/source4/libcli/dgram/browse.c @@ -0,0 +1,114 @@ +/* + Unix SMB/CIFS implementation. + + handling for browsing dgram requests + + Copyright (C) Jelmer Vernooij 2005 + Heavily based on ntlogon.c + + 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 "libcli/dgram/libdgram.h" +#include "lib/socket/socket.h" +#include "libcli/resolve/resolve.h" +#include "librpc/gen_ndr/ndr_nbt.h" +#include "param/param.h" + +NTSTATUS dgram_mailslot_browse_send(struct nbt_dgram_socket *dgmsock, + struct nbt_name *dest_name, + struct socket_address *dest, + struct nbt_name *src_name, + struct nbt_browse_packet *request) +{ + NTSTATUS status; + enum ndr_err_code ndr_err; + DATA_BLOB blob; + TALLOC_CTX *tmp_ctx = talloc_new(dgmsock); + + ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, dgmsock->iconv_convenience, request, + (ndr_push_flags_fn_t)ndr_push_nbt_browse_packet); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + talloc_free(tmp_ctx); + return ndr_map_error2ntstatus(ndr_err); + } + + status = dgram_mailslot_send(dgmsock, DGRAM_DIRECT_UNIQUE, + NBT_MAILSLOT_BROWSE, + dest_name, dest, + src_name, &blob); + talloc_free(tmp_ctx); + return status; +} + +NTSTATUS dgram_mailslot_browse_reply(struct nbt_dgram_socket *dgmsock, + struct nbt_dgram_packet *request, + const char *mailslot_name, + const char *my_netbios_name, + struct nbt_browse_packet *reply) +{ + NTSTATUS status; + enum ndr_err_code ndr_err; + DATA_BLOB blob; + TALLOC_CTX *tmp_ctx = talloc_new(dgmsock); + struct nbt_name myname; + struct socket_address *dest; + + ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, dgmsock->iconv_convenience, reply, + (ndr_push_flags_fn_t)ndr_push_nbt_browse_packet); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + talloc_free(tmp_ctx); + return ndr_map_error2ntstatus(ndr_err); + } + + make_nbt_name_client(&myname, my_netbios_name); + + dest = socket_address_from_strings(tmp_ctx, dgmsock->sock->backend_name, + request->src_addr, request->src_port); + if (!dest) { + talloc_free(tmp_ctx); + return NT_STATUS_NO_MEMORY; + } + + status = dgram_mailslot_send(dgmsock, DGRAM_DIRECT_UNIQUE, + mailslot_name, + &request->data.msg.source_name, + dest, + &myname, &blob); + talloc_free(tmp_ctx); + return status; +} + +NTSTATUS dgram_mailslot_browse_parse(struct dgram_mailslot_handler *dgmslot, + TALLOC_CTX *mem_ctx, + struct nbt_dgram_packet *dgram, + struct nbt_browse_packet *pkt) +{ + DATA_BLOB data = dgram_mailslot_data(dgram); + enum ndr_err_code ndr_err; + + ndr_err = ndr_pull_struct_blob(&data, mem_ctx, dgmslot->dgmsock->iconv_convenience, pkt, + (ndr_pull_flags_fn_t)ndr_pull_nbt_browse_packet); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + NTSTATUS status = ndr_map_error2ntstatus(ndr_err); + DEBUG(0,("Failed to parse browse packet of length %d: %s\n", + (int)data.length, nt_errstr(status))); + if (DEBUGLVL(10)) { + file_save("browse.dat", data.data, data.length); + } + return status; + } + return NT_STATUS_OK; +} diff --git a/source4/libcli/dgram/dgramsocket.c b/source4/libcli/dgram/dgramsocket.c new file mode 100644 index 0000000000..751706d2c5 --- /dev/null +++ b/source4/libcli/dgram/dgramsocket.c @@ -0,0 +1,245 @@ +/* + Unix SMB/CIFS implementation. + + low level socket handling for nbt dgram requests (UDP138) + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/events/events.h" +#include "../lib/util/dlinklist.h" +#include "libcli/dgram/libdgram.h" +#include "lib/socket/socket.h" +#include "librpc/gen_ndr/ndr_nbt.h" + + +/* + handle recv events on a nbt dgram socket +*/ +static void dgm_socket_recv(struct nbt_dgram_socket *dgmsock) +{ + TALLOC_CTX *tmp_ctx = talloc_new(dgmsock); + NTSTATUS status; + struct socket_address *src; + DATA_BLOB blob; + size_t nread, dsize; + struct nbt_dgram_packet *packet; + const char *mailslot_name; + enum ndr_err_code ndr_err; + + status = socket_pending(dgmsock->sock, &dsize); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(tmp_ctx); + return; + } + + blob = data_blob_talloc(tmp_ctx, NULL, dsize); + if (blob.data == NULL) { + talloc_free(tmp_ctx); + return; + } + + status = socket_recvfrom(dgmsock->sock, blob.data, blob.length, &nread, + tmp_ctx, &src); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(tmp_ctx); + return; + } + blob.length = nread; + + DEBUG(2,("Received dgram packet of length %d from %s:%d\n", + (int)blob.length, src->addr, src->port)); + + packet = talloc(tmp_ctx, struct nbt_dgram_packet); + if (packet == NULL) { + talloc_free(tmp_ctx); + return; + } + + /* parse the request */ + ndr_err = ndr_pull_struct_blob(&blob, packet, dgmsock->iconv_convenience, packet, + (ndr_pull_flags_fn_t)ndr_pull_nbt_dgram_packet); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + status = ndr_map_error2ntstatus(ndr_err); + DEBUG(2,("Failed to parse incoming NBT DGRAM packet - %s\n", + nt_errstr(status))); + talloc_free(tmp_ctx); + return; + } + + /* if this is a mailslot message, then see if we can dispatch it to a handler */ + mailslot_name = dgram_mailslot_name(packet); + if (mailslot_name) { + struct dgram_mailslot_handler *dgmslot; + dgmslot = dgram_mailslot_find(dgmsock, mailslot_name); + if (dgmslot) { + dgmslot->handler(dgmslot, packet, src); + } else { + DEBUG(2,("No mailslot handler for '%s'\n", mailslot_name)); + } + } else { + /* dispatch if there is a general handler */ + if (dgmsock->incoming.handler) { + dgmsock->incoming.handler(dgmsock, packet, src); + } + } + + talloc_free(tmp_ctx); +} + + +/* + handle send events on a nbt dgram socket +*/ +static void dgm_socket_send(struct nbt_dgram_socket *dgmsock) +{ + struct nbt_dgram_request *req; + NTSTATUS status; + + while ((req = dgmsock->send_queue)) { + size_t len; + + len = req->encoded.length; + status = socket_sendto(dgmsock->sock, &req->encoded, &len, + req->dest); + if (NT_STATUS_IS_ERR(status)) { + DEBUG(3,("Failed to send datagram of length %u to %s:%d: %s\n", + (unsigned)req->encoded.length, req->dest->addr, req->dest->port, + nt_errstr(status))); + DLIST_REMOVE(dgmsock->send_queue, req); + talloc_free(req); + continue; + } + + if (!NT_STATUS_IS_OK(status)) return; + + DLIST_REMOVE(dgmsock->send_queue, req); + talloc_free(req); + } + + EVENT_FD_NOT_WRITEABLE(dgmsock->fde); + return; +} + + +/* + handle fd events on a nbt_dgram_socket +*/ +static void dgm_socket_handler(struct tevent_context *ev, struct tevent_fd *fde, + uint16_t flags, void *private_data) +{ + struct nbt_dgram_socket *dgmsock = talloc_get_type(private_data, + struct nbt_dgram_socket); + if (flags & EVENT_FD_WRITE) { + dgm_socket_send(dgmsock); + } + if (flags & EVENT_FD_READ) { + dgm_socket_recv(dgmsock); + } +} + +/* + initialise a nbt_dgram_socket. The event_ctx is optional, if provided + then operations will use that event context +*/ +struct nbt_dgram_socket *nbt_dgram_socket_init(TALLOC_CTX *mem_ctx, + struct tevent_context *event_ctx, + struct smb_iconv_convenience *iconv_convenience) +{ + struct nbt_dgram_socket *dgmsock; + NTSTATUS status; + + dgmsock = talloc(mem_ctx, struct nbt_dgram_socket); + if (dgmsock == NULL) goto failed; + + dgmsock->event_ctx = talloc_reference(dgmsock, event_ctx); + if (dgmsock->event_ctx == NULL) goto failed; + + status = socket_create("ip", SOCKET_TYPE_DGRAM, &dgmsock->sock, 0); + if (!NT_STATUS_IS_OK(status)) goto failed; + + socket_set_option(dgmsock->sock, "SO_BROADCAST", "1"); + + talloc_steal(dgmsock, dgmsock->sock); + + dgmsock->fde = event_add_fd(dgmsock->event_ctx, dgmsock, + socket_get_fd(dgmsock->sock), 0, + dgm_socket_handler, dgmsock); + + dgmsock->send_queue = NULL; + dgmsock->incoming.handler = NULL; + dgmsock->mailslot_handlers = NULL; + dgmsock->iconv_convenience = iconv_convenience; + + return dgmsock; + +failed: + talloc_free(dgmsock); + return NULL; +} + + +/* + setup a handler for generic incoming requests +*/ +NTSTATUS dgram_set_incoming_handler(struct nbt_dgram_socket *dgmsock, + void (*handler)(struct nbt_dgram_socket *, + struct nbt_dgram_packet *, + struct socket_address *), + void *private_data) +{ + dgmsock->incoming.handler = handler; + dgmsock->incoming.private_data = private_data; + EVENT_FD_READABLE(dgmsock->fde); + return NT_STATUS_OK; +} + + +/* + queue a datagram for send +*/ +NTSTATUS nbt_dgram_send(struct nbt_dgram_socket *dgmsock, + struct nbt_dgram_packet *packet, + struct socket_address *dest) +{ + struct nbt_dgram_request *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + enum ndr_err_code ndr_err; + + req = talloc(dgmsock, struct nbt_dgram_request); + if (req == NULL) goto failed; + + req->dest = dest; + if (talloc_reference(req, dest) == NULL) goto failed; + + ndr_err = ndr_push_struct_blob(&req->encoded, req, dgmsock->iconv_convenience, packet, + (ndr_push_flags_fn_t)ndr_push_nbt_dgram_packet); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + status = ndr_map_error2ntstatus(ndr_err); + goto failed; + } + + DLIST_ADD_END(dgmsock->send_queue, req, struct nbt_dgram_request *); + + EVENT_FD_WRITEABLE(dgmsock->fde); + + return NT_STATUS_OK; + +failed: + talloc_free(req); + return status; +} diff --git a/source4/libcli/dgram/libdgram.h b/source4/libcli/dgram/libdgram.h new file mode 100644 index 0000000000..a17a6042d9 --- /dev/null +++ b/source4/libcli/dgram/libdgram.h @@ -0,0 +1,157 @@ +/* + Unix SMB/CIFS implementation. + + a raw async NBT DGRAM library + + Copyright (C) Andrew Tridgell 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 "../libcli/netlogon.h" + +/* + a datagram name request +*/ +struct nbt_dgram_request { + struct nbt_dgram_request *next, *prev; + + /* where to send the request */ + struct socket_address *dest; + + /* the encoded request */ + DATA_BLOB encoded; +}; + +/* + context structure for operations on dgram packets +*/ +struct nbt_dgram_socket { + struct socket_context *sock; + struct tevent_context *event_ctx; + struct smb_iconv_convenience *iconv_convenience; + + /* the fd event */ + struct tevent_fd *fde; + + /* a queue of outgoing requests */ + struct nbt_dgram_request *send_queue; + + /* a list of mailslot handlers */ + struct dgram_mailslot_handler *mailslot_handlers; + + /* what to do with incoming request packets */ + struct { + void (*handler)(struct nbt_dgram_socket *, struct nbt_dgram_packet *, + struct socket_address *src); + void *private_data; + } incoming; +}; + + +/* + the mailslot code keeps a list of mailslot handlers. A mailslot + handler is a function that receives incoming packets for a specific + mailslot name. When a caller needs to send a mailslot and wants to + get a reply then it needs to register itself as listening for + incoming packets on the reply mailslot +*/ + +typedef void (*dgram_mailslot_handler_t)(struct dgram_mailslot_handler *, + struct nbt_dgram_packet *, + struct socket_address *src); + +struct dgram_mailslot_handler { + struct dgram_mailslot_handler *next, *prev; + + struct nbt_dgram_socket *dgmsock; + const char *mailslot_name; + + dgram_mailslot_handler_t handler; + void *private_data; +}; + + +/* prototypes */ +NTSTATUS nbt_dgram_send(struct nbt_dgram_socket *dgmsock, + struct nbt_dgram_packet *packet, + struct socket_address *dest); +NTSTATUS dgram_set_incoming_handler(struct nbt_dgram_socket *dgmsock, + void (*handler)(struct nbt_dgram_socket *, + struct nbt_dgram_packet *, + struct socket_address *), + void *private_data); +struct nbt_dgram_socket *nbt_dgram_socket_init(TALLOC_CTX *mem_ctx, + struct tevent_context *event_ctx, + struct smb_iconv_convenience *); + +const char *dgram_mailslot_name(struct nbt_dgram_packet *packet); +struct dgram_mailslot_handler *dgram_mailslot_find(struct nbt_dgram_socket *dgmsock, + const char *mailslot_name); +struct dgram_mailslot_handler *dgram_mailslot_listen(struct nbt_dgram_socket *dgmsock, + const char *mailslot_name, + dgram_mailslot_handler_t handler, + void *private_data); +struct dgram_mailslot_handler *dgram_mailslot_temp(struct nbt_dgram_socket *dgmsock, + const char *mailslot_name, + dgram_mailslot_handler_t handler, + void *private_data); +DATA_BLOB dgram_mailslot_data(struct nbt_dgram_packet *dgram); + + +NTSTATUS dgram_mailslot_send(struct nbt_dgram_socket *dgmsock, + enum dgram_msg_type msg_type, + const char *mailslot_name, + struct nbt_name *dest_name, + struct socket_address *dest, + struct nbt_name *src_name, + DATA_BLOB *request); + +NTSTATUS dgram_mailslot_netlogon_send(struct nbt_dgram_socket *dgmsock, + struct nbt_name *dest_name, + struct socket_address *dest, + const char *mailslot_name, + struct nbt_name *src_name, + struct nbt_netlogon_packet *request); +NTSTATUS dgram_mailslot_netlogon_reply(struct nbt_dgram_socket *dgmsock, + struct nbt_dgram_packet *request, + const char *my_netbios_name, + const char *mailslot_name, + struct nbt_netlogon_response *reply); +NTSTATUS dgram_mailslot_netlogon_parse_request(struct dgram_mailslot_handler *dgmslot, + TALLOC_CTX *mem_ctx, + struct nbt_dgram_packet *dgram, + struct nbt_netlogon_packet *netlogon); + +NTSTATUS dgram_mailslot_netlogon_parse_response(struct dgram_mailslot_handler *dgmslot, + TALLOC_CTX *mem_ctx, + struct nbt_dgram_packet *dgram, + struct nbt_netlogon_response *netlogon); + +NTSTATUS dgram_mailslot_browse_send(struct nbt_dgram_socket *dgmsock, + struct nbt_name *dest_name, + struct socket_address *dest, + struct nbt_name *src_name, + struct nbt_browse_packet *request); + +NTSTATUS dgram_mailslot_browse_reply(struct nbt_dgram_socket *dgmsock, + struct nbt_dgram_packet *request, + const char *mailslot_name, + const char *my_netbios_name, + struct nbt_browse_packet *reply); + +NTSTATUS dgram_mailslot_browse_parse(struct dgram_mailslot_handler *dgmslot, + TALLOC_CTX *mem_ctx, + struct nbt_dgram_packet *dgram, + struct nbt_browse_packet *pkt); diff --git a/source4/libcli/dgram/mailslot.c b/source4/libcli/dgram/mailslot.c new file mode 100644 index 0000000000..261946e458 --- /dev/null +++ b/source4/libcli/dgram/mailslot.c @@ -0,0 +1,226 @@ +/* + Unix SMB/CIFS implementation. + + packet handling for mailslot requests. + + Copyright (C) Andrew Tridgell 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/>. +*/ + +/* + This implements "Class 2 mailslots", i.e. the communication mechanism + used for all mailslot packets smaller than 425 bytes. + + "Class 1 mailslots" (which use SMB) are used for messages larger + than 426 bytes and are supported on some systems. These are not implemented + in Samba4 yet, as there don't appear to be any core services that use + them. + + 425 and 426-byte sized messages are not supported at all. +*/ + +#include "includes.h" +#include "lib/events/events.h" +#include "../lib/util/dlinklist.h" +#include "libcli/dgram/libdgram.h" +#include "lib/socket/socket.h" + +/* + destroy a mailslot handler +*/ +static int dgram_mailslot_destructor(struct dgram_mailslot_handler *dgmslot) +{ + DLIST_REMOVE(dgmslot->dgmsock->mailslot_handlers, dgmslot); + return 0; +} + +/* + start listening on a mailslot. talloc_free() the handle to stop listening +*/ +struct dgram_mailslot_handler *dgram_mailslot_listen(struct nbt_dgram_socket *dgmsock, + const char *mailslot_name, + dgram_mailslot_handler_t handler, + void *private_data) +{ + struct dgram_mailslot_handler *dgmslot; + + dgmslot = talloc(dgmsock, struct dgram_mailslot_handler); + if (dgmslot == NULL) return NULL; + + dgmslot->dgmsock = dgmsock; + dgmslot->mailslot_name = talloc_strdup(dgmslot, mailslot_name); + if (dgmslot->mailslot_name == NULL) { + talloc_free(dgmslot); + return NULL; + } + dgmslot->handler = handler; + dgmslot->private_data = private_data; + + DLIST_ADD(dgmsock->mailslot_handlers, dgmslot); + talloc_set_destructor(dgmslot, dgram_mailslot_destructor); + + EVENT_FD_READABLE(dgmsock->fde); + + return dgmslot; +} + +/* + find the handler for a specific mailslot name +*/ +struct dgram_mailslot_handler *dgram_mailslot_find(struct nbt_dgram_socket *dgmsock, + const char *mailslot_name) +{ + struct dgram_mailslot_handler *h; + for (h=dgmsock->mailslot_handlers;h;h=h->next) { + if (strcasecmp(h->mailslot_name, mailslot_name) == 0) { + return h; + } + } + return NULL; +} + +/* + check that a datagram packet is a valid mailslot request, and return the + mailslot name if it is, otherwise return NULL +*/ +const char *dgram_mailslot_name(struct nbt_dgram_packet *packet) +{ + if (packet->msg_type != DGRAM_DIRECT_UNIQUE && + packet->msg_type != DGRAM_DIRECT_GROUP && + packet->msg_type != DGRAM_BCAST) { + return NULL; + } + if (packet->data.msg.dgram_body_type != DGRAM_SMB) return NULL; + if (packet->data.msg.body.smb.smb_command != SMB_TRANSACTION) return NULL; + return packet->data.msg.body.smb.body.trans.mailslot_name; +} + + +/* + create a temporary mailslot handler for a reply mailslot, allocating + a new mailslot name using the given base name and a random integer extension +*/ +struct dgram_mailslot_handler *dgram_mailslot_temp(struct nbt_dgram_socket *dgmsock, + const char *mailslot_name, + dgram_mailslot_handler_t handler, + void *private_data) +{ + char *name; + int i; + struct dgram_mailslot_handler *dgmslot; + + /* try a 100 times at most */ + for (i=0;i<100;i++) { + name = talloc_asprintf(dgmsock, "%s%03u", + mailslot_name, + generate_random() % 1000); + if (name == NULL) return NULL; + if (dgram_mailslot_find(dgmsock, name)) { + talloc_free(name); + return NULL; + } + dgmslot = dgram_mailslot_listen(dgmsock, name, handler, private_data); + talloc_free(name); + if (dgmslot != NULL) { + return dgmslot; + } + } + DEBUG(2,("Unable to create temporary mailslot from %s\n", mailslot_name)); + return NULL; +} + + +/* + send a mailslot request +*/ +NTSTATUS dgram_mailslot_send(struct nbt_dgram_socket *dgmsock, + enum dgram_msg_type msg_type, + const char *mailslot_name, + struct nbt_name *dest_name, + struct socket_address *dest, + struct nbt_name *src_name, + DATA_BLOB *request) +{ + TALLOC_CTX *tmp_ctx = talloc_new(dgmsock); + struct nbt_dgram_packet packet; + struct dgram_message *msg; + struct dgram_smb_packet *smb; + struct smb_trans_body *trans; + struct socket_address *src; + NTSTATUS status; + + if (dest->port == 0) { + return NT_STATUS_INVALID_PARAMETER; + } + + ZERO_STRUCT(packet); + packet.msg_type = msg_type; + packet.flags = DGRAM_FLAG_FIRST | DGRAM_NODE_NBDD; + packet.dgram_id = generate_random() % UINT16_MAX; + src = socket_get_my_addr(dgmsock->sock, tmp_ctx); + if (!src) { + return NT_STATUS_NO_MEMORY; + } + packet.src_addr = src->addr; + packet.src_port = src->port; + + msg = &packet.data.msg; + /* this length calculation is very crude - it should be based on gensize + calls */ + msg->length = 138 + strlen(mailslot_name) + request->length; + msg->offset = 0; + + msg->source_name = *src_name; + msg->dest_name = *dest_name; + msg->dgram_body_type = DGRAM_SMB; + + smb = &msg->body.smb; + smb->smb_command = SMB_TRANSACTION; + + trans = &smb->body.trans; + trans->total_data_count = request->length; + trans->timeout = 1000; + trans->data_count = request->length; + trans->data_offset = 70 + strlen(mailslot_name); + trans->opcode = 1; /* write mail slot */ + trans->priority = 1; + trans->_class = 2; + trans->mailslot_name = mailslot_name; + trans->data = *request; + + status = nbt_dgram_send(dgmsock, &packet, dest); + + talloc_free(tmp_ctx); + + return status; +} + +/* + return the mailslot data portion from a mailslot packet +*/ +DATA_BLOB dgram_mailslot_data(struct nbt_dgram_packet *dgram) +{ + struct smb_trans_body *trans = &dgram->data.msg.body.smb.body.trans; + DATA_BLOB ret = trans->data; + int pad = trans->data_offset - (70 + strlen(trans->mailslot_name)); + + if (pad < 0 || pad > ret.length) { + DEBUG(2,("Badly formatted data in mailslot - pad = %d\n", pad)); + return data_blob(NULL, 0); + } + ret.data += pad; + ret.length -= pad; + return ret; +} diff --git a/source4/libcli/dgram/netlogon.c b/source4/libcli/dgram/netlogon.c new file mode 100644 index 0000000000..26b00bdafd --- /dev/null +++ b/source4/libcli/dgram/netlogon.c @@ -0,0 +1,145 @@ +/* + Unix SMB/CIFS implementation. + + handling for netlogon dgram requests + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/dgram/libdgram.h" +#include "lib/socket/socket.h" +#include "libcli/resolve/resolve.h" +#include "librpc/gen_ndr/ndr_nbt.h" + +/* + send a netlogon mailslot request +*/ +NTSTATUS dgram_mailslot_netlogon_send(struct nbt_dgram_socket *dgmsock, + struct nbt_name *dest_name, + struct socket_address *dest, + const char *mailslot, + struct nbt_name *src_name, + struct nbt_netlogon_packet *request) +{ + NTSTATUS status; + enum ndr_err_code ndr_err; + DATA_BLOB blob; + TALLOC_CTX *tmp_ctx = talloc_new(dgmsock); + + ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, + dgmsock->iconv_convenience, + request, + (ndr_push_flags_fn_t)ndr_push_nbt_netlogon_packet); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + talloc_free(tmp_ctx); + return ndr_map_error2ntstatus(ndr_err); + } + + + status = dgram_mailslot_send(dgmsock, DGRAM_DIRECT_UNIQUE, + mailslot, + dest_name, dest, + src_name, &blob); + talloc_free(tmp_ctx); + return status; +} + + +/* + send a netlogon mailslot reply +*/ +NTSTATUS dgram_mailslot_netlogon_reply(struct nbt_dgram_socket *dgmsock, + struct nbt_dgram_packet *request, + const char *my_netbios_name, + const char *mailslot_name, + struct nbt_netlogon_response *reply) +{ + NTSTATUS status; + DATA_BLOB blob; + TALLOC_CTX *tmp_ctx = talloc_new(dgmsock); + struct nbt_name myname; + struct socket_address *dest; + + status = push_nbt_netlogon_response(&blob, tmp_ctx, dgmsock->iconv_convenience, + reply); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + make_nbt_name_client(&myname, my_netbios_name); + + dest = socket_address_from_strings(tmp_ctx, dgmsock->sock->backend_name, + request->src_addr, request->src_port); + if (!dest) { + talloc_free(tmp_ctx); + return NT_STATUS_NO_MEMORY; + } + + status = dgram_mailslot_send(dgmsock, DGRAM_DIRECT_UNIQUE, + mailslot_name, + &request->data.msg.source_name, + dest, + &myname, &blob); + talloc_free(tmp_ctx); + return status; +} + + +/* + parse a netlogon response. The packet must be a valid mailslot packet +*/ +NTSTATUS dgram_mailslot_netlogon_parse_request(struct dgram_mailslot_handler *dgmslot, + TALLOC_CTX *mem_ctx, + struct nbt_dgram_packet *dgram, + struct nbt_netlogon_packet *netlogon) +{ + DATA_BLOB data = dgram_mailslot_data(dgram); + enum ndr_err_code ndr_err; + + ndr_err = ndr_pull_struct_blob(&data, mem_ctx, dgmslot->dgmsock->iconv_convenience, netlogon, + (ndr_pull_flags_fn_t)ndr_pull_nbt_netlogon_packet); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + NTSTATUS status = ndr_map_error2ntstatus(ndr_err); + DEBUG(0,("Failed to parse netlogon packet of length %d: %s\n", + (int)data.length, nt_errstr(status))); + if (DEBUGLVL(10)) { + file_save("netlogon.dat", data.data, data.length); + } + return status; + } + return NT_STATUS_OK; +} + +/* + parse a netlogon response. The packet must be a valid mailslot packet +*/ +NTSTATUS dgram_mailslot_netlogon_parse_response(struct dgram_mailslot_handler *dgmslot, + TALLOC_CTX *mem_ctx, + struct nbt_dgram_packet *dgram, + struct nbt_netlogon_response *netlogon) +{ + NTSTATUS status; + DATA_BLOB data = dgram_mailslot_data(dgram); + + status = pull_nbt_netlogon_response(&data, mem_ctx, dgmslot->dgmsock->iconv_convenience, netlogon); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return NT_STATUS_OK; +} + diff --git a/source4/libcli/finddcs.c b/source4/libcli/finddcs.c new file mode 100644 index 0000000000..2e4fad9332 --- /dev/null +++ b/source4/libcli/finddcs.c @@ -0,0 +1,276 @@ +/* + Unix SMB/CIFS implementation. + + a composite API for finding a DC and its name + + Copyright (C) Volker Lendecke 2005 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 "include/includes.h" +#include "lib/messaging/irpc.h" +#include "librpc/gen_ndr/ndr_irpc.h" +#include "librpc/gen_ndr/samr.h" +#include "libcli/composite/composite.h" +#include "libcli/libcli.h" +#include "libcli/resolve/resolve.h" +#include "libcli/finddcs.h" + +struct finddcs_state { + struct composite_context *ctx; + struct messaging_context *msg_ctx; + + const char *my_netbios_name; + const char *domain_name; + struct dom_sid *domain_sid; + + struct nbtd_getdcname r; + struct nbt_name_status node_status; + + struct smb_iconv_convenience *iconv_convenience; + + int num_dcs; + struct nbt_dc_name *dcs; + uint16_t nbt_port; +}; + +static void finddcs_name_resolved(struct composite_context *ctx); +static void finddcs_getdc_replied(struct irpc_request *ireq); +static void fallback_node_status(struct finddcs_state *state); +static void fallback_node_status_replied(struct nbt_name_request *name_req); + +/* + * Setup and send off the a normal name resolution for the target name. + * + * The domain_sid parameter is optional, and is used in the subsequent getdc request. + * + * This will try a GetDC request, but this may not work. It will try + * a node status as a fallback, then return no name (but still include + * the IP) + */ + +struct composite_context *finddcs_send(TALLOC_CTX *mem_ctx, + const char *my_netbios_name, + uint16_t nbt_port, + const char *domain_name, + int name_type, + struct dom_sid *domain_sid, + struct smb_iconv_convenience *iconv_convenience, + struct resolve_context *resolve_ctx, + struct tevent_context *event_ctx, + struct messaging_context *msg_ctx) +{ + struct composite_context *c, *creq; + struct finddcs_state *state; + struct nbt_name name; + + c = composite_create(mem_ctx, event_ctx); + if (c == NULL) return NULL; + + state = talloc(c, struct finddcs_state); + if (composite_nomem(state, c)) return c; + c->private_data = state; + + state->ctx = c; + + state->nbt_port = nbt_port; + state->my_netbios_name = talloc_strdup(state, my_netbios_name); + state->domain_name = talloc_strdup(state, domain_name); + state->iconv_convenience = iconv_convenience; + if (composite_nomem(state->domain_name, c)) return c; + + if (domain_sid) { + state->domain_sid = talloc_reference(state, domain_sid); + if (composite_nomem(state->domain_sid, c)) return c; + } else { + state->domain_sid = NULL; + } + + state->msg_ctx = msg_ctx; + + make_nbt_name(&name, state->domain_name, name_type); + creq = resolve_name_send(resolve_ctx, &name, event_ctx); + composite_continue(c, creq, finddcs_name_resolved, state); + return c; +} + +/* Having got an name query answer, fire off a GetDC request, so we + * can find the target's all-important name. (Kerberos and some + * netlogon operations are quite picky about names) + * + * The name is a courtesy, if we don't find it, don't completely fail. + * + * However, if the nbt server is down, fall back to a node status + * request + */ +static void finddcs_name_resolved(struct composite_context *ctx) +{ + struct finddcs_state *state = + talloc_get_type(ctx->async.private_data, struct finddcs_state); + struct irpc_request *ireq; + struct server_id *nbt_servers; + const char *address; + + state->ctx->status = resolve_name_recv(ctx, state, &address); + if (!composite_is_ok(state->ctx)) return; + + /* TODO: This should try and find all the DCs, and give the + * caller them in the order they responded */ + + state->num_dcs = 1; + state->dcs = talloc_array(state, struct nbt_dc_name, state->num_dcs); + if (composite_nomem(state->dcs, state->ctx)) return; + + state->dcs[0].address = talloc_steal(state->dcs, address); + + /* Try and find the nbt server. Fallback to a node status + * request if we can't make this happen The nbt server just + * might not be running, or we may not have a messaging + * context (not root etc) */ + if (!state->msg_ctx) { + fallback_node_status(state); + return; + } + + nbt_servers = irpc_servers_byname(state->msg_ctx, state, "nbt_server"); + if ((nbt_servers == NULL) || (nbt_servers[0].id == 0)) { + fallback_node_status(state); + return; + } + + state->r.in.domainname = state->domain_name; + state->r.in.ip_address = state->dcs[0].address; + state->r.in.my_computername = state->my_netbios_name; + state->r.in.my_accountname = talloc_asprintf(state, "%s$", state->my_netbios_name); + if (composite_nomem(state->r.in.my_accountname, state->ctx)) return; + state->r.in.account_control = ACB_WSTRUST; + state->r.in.domain_sid = state->domain_sid; + + ireq = irpc_call_send(state->msg_ctx, nbt_servers[0], + &ndr_table_irpc, NDR_NBTD_GETDCNAME, + &state->r, state); + if (!ireq) { + fallback_node_status(state); + return; + } + + composite_continue_irpc(state->ctx, ireq, finddcs_getdc_replied, state); +} + +/* Called when the GetDC request returns */ +static void finddcs_getdc_replied(struct irpc_request *ireq) +{ + struct finddcs_state *state = + talloc_get_type(ireq->async.private_data, struct finddcs_state); + + state->ctx->status = irpc_call_recv(ireq); + if (!composite_is_ok(state->ctx)) return; + + state->dcs[0].name = talloc_steal(state->dcs, state->r.out.dcname); + composite_done(state->ctx); +} + +/* The GetDC request might not be available (such as occours when the + * NBT server is down). Fallback to a node status. It is the best + * hope we have... */ +static void fallback_node_status(struct finddcs_state *state) +{ + struct nbt_name_socket *nbtsock; + struct nbt_name_request *name_req; + + state->node_status.in.name.name = "*"; + state->node_status.in.name.type = NBT_NAME_CLIENT; + state->node_status.in.name.scope = NULL; + state->node_status.in.dest_addr = state->dcs[0].address; + state->node_status.in.dest_port = state->nbt_port; + state->node_status.in.timeout = 1; + state->node_status.in.retries = 2; + + nbtsock = nbt_name_socket_init(state, state->ctx->event_ctx, + state->iconv_convenience); + if (composite_nomem(nbtsock, state->ctx)) return; + + name_req = nbt_name_status_send(nbtsock, &state->node_status); + if (composite_nomem(name_req, state->ctx)) return; + + composite_continue_nbt(state->ctx, + name_req, + fallback_node_status_replied, + state); +} + +/* We have a node status reply (or perhaps a timeout) */ +static void fallback_node_status_replied(struct nbt_name_request *name_req) +{ + int i; + struct finddcs_state *state = talloc_get_type(name_req->async.private_data, struct finddcs_state); + state->ctx->status = nbt_name_status_recv(name_req, state, &state->node_status); + if (!composite_is_ok(state->ctx)) return; + + for (i=0; i < state->node_status.out.status.num_names; i++) { + int j; + if (state->node_status.out.status.names[i].type == NBT_NAME_SERVER) { + char *name = talloc_strndup(state->dcs, state->node_status.out.status.names[0].name, 15); + /* Strip space padding */ + if (name) { + j = MIN(strlen(name), 15); + for (; j > 0 && name[j - 1] == ' '; j--) { + name[j - 1] = '\0'; + } + } + state->dcs[0].name = name; + composite_done(state->ctx); + return; + } + } + composite_error(state->ctx, NT_STATUS_NO_LOGON_SERVERS); +} + +NTSTATUS finddcs_recv(struct composite_context *c, TALLOC_CTX *mem_ctx, + int *num_dcs, struct nbt_dc_name **dcs) +{ + NTSTATUS status = composite_wait(c); + if (NT_STATUS_IS_OK(status)) { + struct finddcs_state *state = + talloc_get_type(c->private_data, struct finddcs_state); + *num_dcs = state->num_dcs; + *dcs = talloc_steal(mem_ctx, state->dcs); + } + talloc_free(c); + return status; +} + +NTSTATUS finddcs(TALLOC_CTX *mem_ctx, + const char *my_netbios_name, + uint16_t nbt_port, + const char *domain_name, int name_type, + struct dom_sid *domain_sid, + struct smb_iconv_convenience *iconv_convenience, + struct resolve_context *resolve_ctx, + struct tevent_context *event_ctx, + struct messaging_context *msg_ctx, + int *num_dcs, struct nbt_dc_name **dcs) +{ + struct composite_context *c = finddcs_send(mem_ctx, + my_netbios_name, + nbt_port, + domain_name, name_type, + domain_sid, + iconv_convenience, + resolve_ctx, + event_ctx, msg_ctx); + return finddcs_recv(c, mem_ctx, num_dcs, dcs); +} diff --git a/source4/libcli/ldap/config.mk b/source4/libcli/ldap/config.mk new file mode 100644 index 0000000000..f0c0f5295d --- /dev/null +++ b/source4/libcli/ldap/config.mk @@ -0,0 +1,12 @@ +[SUBSYSTEM::LIBCLI_LDAP] +PUBLIC_DEPENDENCIES = LIBSAMBA-ERRORS LIBTEVENT LIBPACKET +PRIVATE_DEPENDENCIES = LIBCLI_COMPOSITE samba_socket NDR_SAMR LIBTLS \ + LIBCLI_LDAP_NDR LIBNDR LP_RESOLVE gensec LIBCLI_LDAP_MESSAGE + +LIBCLI_LDAP_OBJ_FILES = $(addprefix $(libclisrcdir)/ldap/, \ + ldap_client.o ldap_bind.o \ + ldap_ildap.o ldap_controls.o) +PUBLIC_HEADERS += $(libclisrcdir)/ldap/ldap.h + +$(eval $(call proto_header_template,$(libclisrcdir)/ldap/ldap_proto.h,$(LIBCLI_LDAP_OBJ_FILES:.o=.c))) + diff --git a/source4/libcli/ldap/ldap.h b/source4/libcli/ldap/ldap.h new file mode 100644 index 0000000000..79cfef2128 --- /dev/null +++ b/source4/libcli/ldap/ldap.h @@ -0,0 +1,31 @@ +/* + Unix SMB/CIFS Implementation. + LDAP protocol helper functions for SAMBA + Copyright (C) Volker Lendecke 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/>. + +*/ + +#ifndef _SMB_LDAP_H_ +#define _SMB_LDAP_H_ + +#include "../libcli/ldap/ldap_message.h" +#include "librpc/gen_ndr/misc.h" + +struct tevent_context; +struct cli_credentials; +struct dom_sid; + +#endif diff --git a/source4/libcli/ldap/ldap_bind.c b/source4/libcli/ldap/ldap_bind.c new file mode 100644 index 0000000000..5e6a5faafa --- /dev/null +++ b/source4/libcli/ldap/ldap_bind.c @@ -0,0 +1,413 @@ +/* + Unix SMB/CIFS mplementation. + + LDAP bind calls + + Copyright (C) Andrew Tridgell 2005 + Copyright (C) Volker Lendecke 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" +#include "libcli/ldap/ldap.h" +#include "libcli/ldap/ldap_proto.h" +#include "libcli/ldap/ldap_client.h" +#include "lib/tls/tls.h" +#include "auth/gensec/gensec.h" +#include "auth/credentials/credentials.h" +#include "lib/stream/packet.h" +#include "param/param.h" + +struct ldap_simple_creds { + const char *dn; + const char *pw; +}; + +_PUBLIC_ NTSTATUS ldap_rebind(struct ldap_connection *conn) +{ + NTSTATUS status; + struct ldap_simple_creds *creds; + + switch (conn->bind.type) { + case LDAP_BIND_SASL: + status = ldap_bind_sasl(conn, (struct cli_credentials *)conn->bind.creds, + conn->lp_ctx); + break; + + case LDAP_BIND_SIMPLE: + creds = (struct ldap_simple_creds *)conn->bind.creds; + + if (creds == NULL) { + return NT_STATUS_UNSUCCESSFUL; + } + + status = ldap_bind_simple(conn, creds->dn, creds->pw); + break; + + default: + return NT_STATUS_UNSUCCESSFUL; + } + + return status; +} + + +static struct ldap_message *new_ldap_simple_bind_msg(struct ldap_connection *conn, + const char *dn, const char *pw) +{ + struct ldap_message *res; + + res = new_ldap_message(conn); + if (!res) { + return NULL; + } + + res->type = LDAP_TAG_BindRequest; + res->r.BindRequest.version = 3; + res->r.BindRequest.dn = talloc_strdup(res, dn); + res->r.BindRequest.mechanism = LDAP_AUTH_MECH_SIMPLE; + res->r.BindRequest.creds.password = talloc_strdup(res, pw); + res->controls = NULL; + + return res; +} + + +/* + perform a simple username/password bind +*/ +_PUBLIC_ NTSTATUS ldap_bind_simple(struct ldap_connection *conn, + const char *userdn, const char *password) +{ + struct ldap_request *req; + struct ldap_message *msg; + const char *dn, *pw; + NTSTATUS status; + + if (conn == NULL) { + return NT_STATUS_INVALID_CONNECTION; + } + + if (userdn) { + dn = userdn; + } else { + if (conn->auth_dn) { + dn = conn->auth_dn; + } else { + dn = ""; + } + } + + if (password) { + pw = password; + } else { + if (conn->simple_pw) { + pw = conn->simple_pw; + } else { + pw = ""; + } + } + + msg = new_ldap_simple_bind_msg(conn, dn, pw); + NT_STATUS_HAVE_NO_MEMORY(msg); + + /* send the request */ + req = ldap_request_send(conn, msg); + talloc_free(msg); + NT_STATUS_HAVE_NO_MEMORY(req); + + /* wait for replies */ + status = ldap_request_wait(req); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return status; + } + + /* check its a valid reply */ + msg = req->replies[0]; + if (msg->type != LDAP_TAG_BindResponse) { + talloc_free(req); + return NT_STATUS_UNEXPECTED_NETWORK_ERROR; + } + + status = ldap_check_response(conn, &msg->r.BindResponse.response); + + talloc_free(req); + + if (NT_STATUS_IS_OK(status)) { + struct ldap_simple_creds *creds = talloc(conn, struct ldap_simple_creds); + if (creds == NULL) { + return NT_STATUS_NO_MEMORY; + } + creds->dn = talloc_strdup(creds, dn); + creds->pw = talloc_strdup(creds, pw); + if (creds->dn == NULL || creds->pw == NULL) { + return NT_STATUS_NO_MEMORY; + } + conn->bind.type = LDAP_BIND_SIMPLE; + conn->bind.creds = creds; + } + + return status; +} + + +static struct ldap_message *new_ldap_sasl_bind_msg(struct ldap_connection *conn, + const char *sasl_mechanism, + DATA_BLOB *secblob) +{ + struct ldap_message *res; + + res = new_ldap_message(conn); + if (!res) { + return NULL; + } + + res->type = LDAP_TAG_BindRequest; + res->r.BindRequest.version = 3; + res->r.BindRequest.dn = ""; + res->r.BindRequest.mechanism = LDAP_AUTH_MECH_SASL; + res->r.BindRequest.creds.SASL.mechanism = talloc_strdup(res, sasl_mechanism); + if (secblob) { + res->r.BindRequest.creds.SASL.secblob = talloc(res, DATA_BLOB); + if (!res->r.BindRequest.creds.SASL.secblob) { + talloc_free(res); + return NULL; + } + *res->r.BindRequest.creds.SASL.secblob = *secblob; + } else { + res->r.BindRequest.creds.SASL.secblob = NULL; + } + res->controls = NULL; + + return res; +} + + +/* + perform a sasl bind using the given credentials +*/ +_PUBLIC_ NTSTATUS ldap_bind_sasl(struct ldap_connection *conn, + struct cli_credentials *creds, + struct loadparm_context *lp_ctx) +{ + NTSTATUS status; + TALLOC_CTX *tmp_ctx = NULL; + + DATA_BLOB input = data_blob(NULL, 0); + DATA_BLOB output = data_blob(NULL, 0); + + struct ldap_message **sasl_mechs_msgs; + struct ldap_SearchResEntry *search; + int count, i; + + const char **sasl_names; + uint32_t old_gensec_features; + static const char *supported_sasl_mech_attrs[] = { + "supportedSASLMechanisms", + NULL + }; + + gensec_init(lp_ctx); + + status = gensec_client_start(conn, &conn->gensec, + conn->event.event_ctx, + lp_gensec_settings(conn, lp_ctx)); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("Failed to start GENSEC engine (%s)\n", nt_errstr(status))); + goto failed; + } + + /* require Kerberos SIGN/SEAL only if we don't use SSL + * Windows seem not to like double encryption */ + old_gensec_features = cli_credentials_get_gensec_features(creds); + if (tls_enabled(conn->sock)) { + cli_credentials_set_gensec_features(creds, old_gensec_features & ~(GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL)); + } + + /* this call also sets the gensec_want_features */ + status = gensec_set_credentials(conn->gensec, creds); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to set GENSEC creds: %s\n", + nt_errstr(status))); + goto failed; + } + + /* reset the original gensec_features (on the credentials + * context, so we don't tatoo it ) */ + cli_credentials_set_gensec_features(creds, old_gensec_features); + + if (conn->host) { + status = gensec_set_target_hostname(conn->gensec, conn->host); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to set GENSEC target hostname: %s\n", + nt_errstr(status))); + goto failed; + } + } + + status = gensec_set_target_service(conn->gensec, "ldap"); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to set GENSEC target service: %s\n", + nt_errstr(status))); + goto failed; + } + + status = ildap_search(conn, "", LDAP_SEARCH_SCOPE_BASE, "", supported_sasl_mech_attrs, + false, NULL, NULL, &sasl_mechs_msgs); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to inquire of target's available sasl mechs in rootdse search: %s\n", + nt_errstr(status))); + goto failed; + } + + count = ildap_count_entries(conn, sasl_mechs_msgs); + if (count != 1) { + DEBUG(1, ("Failed to inquire of target's available sasl mechs in rootdse search: wrong number of replies: %d\n", + count)); + goto failed; + } + + tmp_ctx = talloc_new(conn); + if (tmp_ctx == NULL) goto failed; + + search = &sasl_mechs_msgs[0]->r.SearchResultEntry; + if (search->num_attributes != 1) { + DEBUG(1, ("Failed to inquire of target's available sasl mechs in rootdse search: wrong number of attributes: %d != 1\n", + search->num_attributes)); + goto failed; + } + + sasl_names = talloc_array(tmp_ctx, const char *, search->attributes[0].num_values + 1); + if (!sasl_names) { + DEBUG(1, ("talloc_arry(char *, %d) failed\n", + count)); + goto failed; + } + + for (i=0; i<search->attributes[0].num_values; i++) { + sasl_names[i] = (const char *)search->attributes[0].values[i].data; + } + sasl_names[i] = NULL; + + status = gensec_start_mech_by_sasl_list(conn->gensec, sasl_names); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("None of the %d proposed SASL mechs were acceptable: %s\n", + count, nt_errstr(status))); + goto failed; + } + + while (1) { + NTSTATUS gensec_status; + struct ldap_message *response; + struct ldap_message *msg; + struct ldap_request *req; + int result = LDAP_OTHER; + + status = gensec_update(conn->gensec, tmp_ctx, + input, + &output); + /* The status value here, from GENSEC is vital to the security + * of the system. Even if the other end accepts, if GENSEC + * claims 'MORE_PROCESSING_REQUIRED' then you must keep + * feeding it blobs, or else the remote host/attacker might + * avoid mutal authentication requirements. + * + * Likewise, you must not feed GENSEC too much (after the OK), + * it doesn't like that either + */ + + gensec_status = status; + + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && + !NT_STATUS_IS_OK(status)) { + break; + } + if (NT_STATUS_IS_OK(status) && output.length == 0) { + break; + } + + /* Perhaps we should make gensec_start_mech_by_sasl_list() return the name we got? */ + msg = new_ldap_sasl_bind_msg(tmp_ctx, conn->gensec->ops->sasl_name, (output.data?&output:NULL)); + if (msg == NULL) { + status = NT_STATUS_NO_MEMORY; + goto failed; + } + + req = ldap_request_send(conn, msg); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto failed; + } + talloc_steal(tmp_ctx, req); + + status = ldap_result_n(req, 0, &response); + if (!NT_STATUS_IS_OK(status)) { + goto failed; + } + + if (response->type != LDAP_TAG_BindResponse) { + status = NT_STATUS_UNEXPECTED_NETWORK_ERROR; + goto failed; + } + + result = response->r.BindResponse.response.resultcode; + + if (result != LDAP_SUCCESS && result != LDAP_SASL_BIND_IN_PROGRESS) { + status = ldap_check_response(conn, + &response->r.BindResponse.response); + break; + } + + /* This is where we check if GENSEC wanted to be fed more data */ + if (!NT_STATUS_EQUAL(gensec_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + break; + } + if (response->r.BindResponse.SASL.secblob) { + input = *response->r.BindResponse.SASL.secblob; + } else { + input = data_blob(NULL, 0); + } + } + + talloc_free(tmp_ctx); + + if (NT_STATUS_IS_OK(status)) { + struct socket_context *sasl_socket; + status = gensec_socket_init(conn->gensec, + conn, + conn->sock, + conn->event.event_ctx, + ldap_read_io_handler, + conn, + &sasl_socket); + if (!NT_STATUS_IS_OK(status)) goto failed; + + conn->sock = sasl_socket; + packet_set_socket(conn->packet, conn->sock); + + conn->bind.type = LDAP_BIND_SASL; + conn->bind.creds = creds; + } + + return status; + +failed: + talloc_free(tmp_ctx); + talloc_free(conn->gensec); + conn->gensec = NULL; + return status; +} diff --git a/source4/libcli/ldap/ldap_client.c b/source4/libcli/ldap/ldap_client.c new file mode 100644 index 0000000000..304a2e1253 --- /dev/null +++ b/source4/libcli/ldap/ldap_client.c @@ -0,0 +1,832 @@ +/* + Unix SMB/CIFS mplementation. + LDAP protocol helper functions for SAMBA + + Copyright (C) Andrew Tridgell 2004 + Copyright (C) Volker Lendecke 2004 + Copyright (C) Stefan Metzmacher 2004 + Copyright (C) Simo Sorce 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" +#include <tevent.h> +#include "lib/socket/socket.h" +#include "../lib/util/asn1.h" +#include "../lib/util/dlinklist.h" +#include "libcli/ldap/ldap.h" +#include "libcli/ldap/ldap_proto.h" +#include "libcli/ldap/ldap_client.h" +#include "libcli/composite/composite.h" +#include "lib/stream/packet.h" +#include "lib/tls/tls.h" +#include "auth/gensec/gensec.h" +#include "system/time.h" +#include "param/param.h" +#include "libcli/resolve/resolve.h" + +/** + create a new ldap_connection stucture. The event context is optional +*/ +_PUBLIC_ struct ldap_connection *ldap4_new_connection(TALLOC_CTX *mem_ctx, + struct loadparm_context *lp_ctx, + struct tevent_context *ev) +{ + struct ldap_connection *conn; + + if (ev == NULL) { + return NULL; + } + + conn = talloc_zero(mem_ctx, struct ldap_connection); + if (conn == NULL) { + return NULL; + } + + conn->next_messageid = 1; + conn->event.event_ctx = ev; + + conn->lp_ctx = lp_ctx; + + /* set a reasonable request timeout */ + conn->timeout = 60; + + /* explicitly avoid reconnections by default */ + conn->reconnect.max_retries = 0; + + return conn; +} + +/* + the connection is dead +*/ +static void ldap_connection_dead(struct ldap_connection *conn) +{ + struct ldap_request *req; + + talloc_free(conn->sock); /* this will also free event.fde */ + talloc_free(conn->packet); + conn->sock = NULL; + conn->event.fde = NULL; + conn->packet = NULL; + + /* return an error for any pending request ... */ + while (conn->pending) { + req = conn->pending; + DLIST_REMOVE(req->conn->pending, req); + req->state = LDAP_REQUEST_DONE; + req->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR; + if (req->async.fn) { + req->async.fn(req); + } + } +} + +static void ldap_reconnect(struct ldap_connection *conn); + +/* + handle packet errors +*/ +static void ldap_error_handler(void *private_data, NTSTATUS status) +{ + struct ldap_connection *conn = talloc_get_type(private_data, + struct ldap_connection); + ldap_connection_dead(conn); + + /* but try to reconnect so that the ldb client can go on */ + ldap_reconnect(conn); +} + + +/* + match up with a pending message, adding to the replies list +*/ +static void ldap_match_message(struct ldap_connection *conn, struct ldap_message *msg) +{ + struct ldap_request *req; + int i; + + for (req=conn->pending; req; req=req->next) { + if (req->messageid == msg->messageid) break; + } + /* match a zero message id to the last request sent. + It seems that servers send 0 if unable to parse */ + if (req == NULL && msg->messageid == 0) { + req = conn->pending; + } + if (req == NULL) { + DEBUG(0,("ldap: no matching message id for %u\n", + msg->messageid)); + talloc_free(msg); + return; + } + + /* Check for undecoded critical extensions */ + for (i=0; msg->controls && msg->controls[i]; i++) { + if (!msg->controls_decoded[i] && + msg->controls[i]->critical) { + req->status = NT_STATUS_LDAP(LDAP_UNAVAILABLE_CRITICAL_EXTENSION); + req->state = LDAP_REQUEST_DONE; + DLIST_REMOVE(conn->pending, req); + if (req->async.fn) { + req->async.fn(req); + } + return; + } + } + + /* add to the list of replies received */ + talloc_steal(req, msg); + req->replies = talloc_realloc(req, req->replies, + struct ldap_message *, req->num_replies+1); + if (req->replies == NULL) { + req->status = NT_STATUS_NO_MEMORY; + req->state = LDAP_REQUEST_DONE; + DLIST_REMOVE(conn->pending, req); + if (req->async.fn) { + req->async.fn(req); + } + return; + } + + req->replies[req->num_replies] = talloc_steal(req->replies, msg); + req->num_replies++; + + if (msg->type != LDAP_TAG_SearchResultEntry && + msg->type != LDAP_TAG_SearchResultReference) { + /* currently only search results expect multiple + replies */ + req->state = LDAP_REQUEST_DONE; + DLIST_REMOVE(conn->pending, req); + } + + if (req->async.fn) { + req->async.fn(req); + } +} + + +/* + decode/process LDAP data +*/ +static NTSTATUS ldap_recv_handler(void *private_data, DATA_BLOB blob) +{ + NTSTATUS status; + struct ldap_connection *conn = talloc_get_type(private_data, + struct ldap_connection); + struct ldap_message *msg = talloc(conn, struct ldap_message); + struct asn1_data *asn1 = asn1_init(conn); + + if (asn1 == NULL || msg == NULL) { + return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR); + } + + if (!asn1_load(asn1, blob)) { + talloc_free(msg); + talloc_free(asn1); + return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR); + } + + status = ldap_decode(asn1, samba_ldap_control_handlers(), msg); + if (!NT_STATUS_IS_OK(status)) { + asn1_free(asn1); + return status; + } + + ldap_match_message(conn, msg); + + data_blob_free(&blob); + asn1_free(asn1); + return NT_STATUS_OK; +} + +/* Handle read events, from the GENSEC socket callback, or real events */ +void ldap_read_io_handler(void *private_data, uint16_t flags) +{ + struct ldap_connection *conn = talloc_get_type(private_data, + struct ldap_connection); + packet_recv(conn->packet); +} + +/* + handle ldap socket events +*/ +static void ldap_io_handler(struct tevent_context *ev, struct tevent_fd *fde, + uint16_t flags, void *private_data) +{ + struct ldap_connection *conn = talloc_get_type(private_data, + struct ldap_connection); + if (flags & TEVENT_FD_WRITE) { + packet_queue_run(conn->packet); + if (!tls_enabled(conn->sock)) return; + } + if (flags & TEVENT_FD_READ) { + ldap_read_io_handler(private_data, flags); + } +} + +/* + parse a ldap URL +*/ +static NTSTATUS ldap_parse_basic_url(TALLOC_CTX *mem_ctx, const char *url, + char **host, uint16_t *port, bool *ldaps) +{ + int tmp_port = 0; + char protocol[11]; + char tmp_host[1025]; + int ret; + + /* Paranoia check */ + SMB_ASSERT(sizeof(protocol)>10 && sizeof(tmp_host)>254); + + ret = sscanf(url, "%10[^:]://%254[^:/]:%d", protocol, tmp_host, &tmp_port); + if (ret < 2) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (strequal(protocol, "ldap")) { + *port = 389; + *ldaps = false; + } else if (strequal(protocol, "ldaps")) { + *port = 636; + *ldaps = true; + } else { + DEBUG(0, ("unrecognised ldap protocol (%s)!\n", protocol)); + return NT_STATUS_PROTOCOL_UNREACHABLE; + } + + if (tmp_port != 0) + *port = tmp_port; + + *host = talloc_strdup(mem_ctx, tmp_host); + NT_STATUS_HAVE_NO_MEMORY(*host); + + return NT_STATUS_OK; +} + +/* + connect to a ldap server +*/ + +struct ldap_connect_state { + struct composite_context *ctx; + struct ldap_connection *conn; +}; + +static void ldap_connect_recv_unix_conn(struct composite_context *ctx); +static void ldap_connect_recv_tcp_conn(struct composite_context *ctx); + +_PUBLIC_ struct composite_context *ldap_connect_send(struct ldap_connection *conn, + const char *url) +{ + struct composite_context *result, *ctx; + struct ldap_connect_state *state; + char protocol[11]; + int ret; + + result = talloc_zero(conn, struct composite_context); + if (result == NULL) goto failed; + result->state = COMPOSITE_STATE_IN_PROGRESS; + result->async.fn = NULL; + result->event_ctx = conn->event.event_ctx; + + state = talloc(result, struct ldap_connect_state); + if (state == NULL) goto failed; + state->ctx = result; + result->private_data = state; + + state->conn = conn; + + if (conn->reconnect.url == NULL) { + conn->reconnect.url = talloc_strdup(conn, url); + if (conn->reconnect.url == NULL) goto failed; + } + + /* Paranoia check */ + SMB_ASSERT(sizeof(protocol)>10); + + ret = sscanf(url, "%10[^:]://", protocol); + if (ret < 1) { + return NULL; + } + + if (strequal(protocol, "ldapi")) { + struct socket_address *unix_addr; + char path[1025]; + + NTSTATUS status = socket_create("unix", SOCKET_TYPE_STREAM, &conn->sock, 0); + if (!NT_STATUS_IS_OK(status)) { + return NULL; + } + talloc_steal(conn, conn->sock); + SMB_ASSERT(sizeof(protocol)>10); + SMB_ASSERT(sizeof(path)>1024); + + /* LDAPI connections are to localhost, so give the local host name as the target for gensec */ + conn->host = talloc_asprintf(conn, "%s.%s", lp_netbios_name(conn->lp_ctx), lp_realm(conn->lp_ctx)); + if (composite_nomem(conn->host, state->ctx)) { + return result; + } + + /* The %c specifier doesn't null terminate :-( */ + ZERO_STRUCT(path); + ret = sscanf(url, "%10[^:]://%1025c", protocol, path); + if (ret < 2) { + composite_error(state->ctx, NT_STATUS_INVALID_PARAMETER); + return result; + } + + rfc1738_unescape(path); + + unix_addr = socket_address_from_strings(conn, conn->sock->backend_name, + path, 0); + if (!unix_addr) { + return NULL; + } + + ctx = socket_connect_send(conn->sock, NULL, unix_addr, + 0, conn->event.event_ctx); + ctx->async.fn = ldap_connect_recv_unix_conn; + ctx->async.private_data = state; + return result; + } else { + NTSTATUS status = ldap_parse_basic_url(conn, url, &conn->host, + &conn->port, &conn->ldaps); + if (!NT_STATUS_IS_OK(state->ctx->status)) { + composite_error(state->ctx, status); + return result; + } + + ctx = socket_connect_multi_send(state, conn->host, 1, &conn->port, + lp_resolve_context(conn->lp_ctx), conn->event.event_ctx); + if (ctx == NULL) goto failed; + + ctx->async.fn = ldap_connect_recv_tcp_conn; + ctx->async.private_data = state; + return result; + } + failed: + talloc_free(result); + return NULL; +} + +static void ldap_connect_got_sock(struct composite_context *ctx, + struct ldap_connection *conn) +{ + /* setup a handler for events on this socket */ + conn->event.fde = tevent_add_fd(conn->event.event_ctx, conn->sock, + socket_get_fd(conn->sock), + TEVENT_FD_READ, ldap_io_handler, conn); + if (conn->event.fde == NULL) { + composite_error(ctx, NT_STATUS_INTERNAL_ERROR); + return; + } + + tevent_fd_set_close_fn(conn->event.fde, socket_tevent_fd_close_fn); + socket_set_flags(conn->sock, SOCKET_FLAG_NOCLOSE); + + talloc_steal(conn, conn->sock); + if (conn->ldaps) { + struct socket_context *tls_socket; + struct socket_context *tmp_socket; + char *cafile = lp_tls_cafile(conn->sock, conn->lp_ctx); + + if (!cafile || !*cafile) { + talloc_free(conn->sock); + return; + } + + tls_socket = tls_init_client(conn->sock, conn->event.fde, cafile); + talloc_free(cafile); + + if (tls_socket == NULL) { + talloc_free(conn->sock); + return; + } + + /* the original socket, must become a child of the tls socket */ + tmp_socket = conn->sock; + conn->sock = talloc_steal(conn, tls_socket); + talloc_steal(conn->sock, tmp_socket); + } + + conn->packet = packet_init(conn); + if (conn->packet == NULL) { + talloc_free(conn->sock); + return; + } + + packet_set_private(conn->packet, conn); + packet_set_socket(conn->packet, conn->sock); + packet_set_callback(conn->packet, ldap_recv_handler); + packet_set_full_request(conn->packet, ldap_full_packet); + packet_set_error_handler(conn->packet, ldap_error_handler); + packet_set_event_context(conn->packet, conn->event.event_ctx); + packet_set_fde(conn->packet, conn->event.fde); +/* packet_set_serialise(conn->packet); */ + + if (conn->ldaps) { + packet_set_unreliable_select(conn->packet); + } + + composite_done(ctx); +} + +static void ldap_connect_recv_tcp_conn(struct composite_context *ctx) +{ + struct ldap_connect_state *state = + talloc_get_type(ctx->async.private_data, + struct ldap_connect_state); + struct ldap_connection *conn = state->conn; + uint16_t port; + NTSTATUS status = socket_connect_multi_recv(ctx, state, &conn->sock, + &port); + if (!NT_STATUS_IS_OK(status)) { + composite_error(state->ctx, status); + return; + } + + ldap_connect_got_sock(state->ctx, conn); +} + +static void ldap_connect_recv_unix_conn(struct composite_context *ctx) +{ + struct ldap_connect_state *state = + talloc_get_type(ctx->async.private_data, + struct ldap_connect_state); + struct ldap_connection *conn = state->conn; + + NTSTATUS status = socket_connect_recv(ctx); + + if (!NT_STATUS_IS_OK(state->ctx->status)) { + composite_error(state->ctx, status); + return; + } + + ldap_connect_got_sock(state->ctx, conn); +} + +_PUBLIC_ NTSTATUS ldap_connect_recv(struct composite_context *ctx) +{ + NTSTATUS status = composite_wait(ctx); + talloc_free(ctx); + return status; +} + +_PUBLIC_ NTSTATUS ldap_connect(struct ldap_connection *conn, const char *url) +{ + struct composite_context *ctx = ldap_connect_send(conn, url); + return ldap_connect_recv(ctx); +} + +/* set reconnect parameters */ + +_PUBLIC_ void ldap_set_reconn_params(struct ldap_connection *conn, int max_retries) +{ + if (conn) { + conn->reconnect.max_retries = max_retries; + conn->reconnect.retries = 0; + conn->reconnect.previous = time(NULL); + } +} + +/* Actually this function is NOT ASYNC safe, FIXME? */ +static void ldap_reconnect(struct ldap_connection *conn) +{ + NTSTATUS status; + time_t now = time(NULL); + + /* do we have set up reconnect ? */ + if (conn->reconnect.max_retries == 0) return; + + /* is the retry time expired ? */ + if (now > conn->reconnect.previous + 30) { + conn->reconnect.retries = 0; + conn->reconnect.previous = now; + } + + /* are we reconnectind too often and too fast? */ + if (conn->reconnect.retries > conn->reconnect.max_retries) return; + + /* keep track of the number of reconnections */ + conn->reconnect.retries++; + + /* reconnect */ + status = ldap_connect(conn, conn->reconnect.url); + if ( ! NT_STATUS_IS_OK(status)) { + return; + } + + /* rebind */ + status = ldap_rebind(conn); + if ( ! NT_STATUS_IS_OK(status)) { + ldap_connection_dead(conn); + } +} + +/* destroy an open ldap request */ +static int ldap_request_destructor(struct ldap_request *req) +{ + if (req->state == LDAP_REQUEST_PENDING) { + DLIST_REMOVE(req->conn->pending, req); + } + return 0; +} + +/* + called on timeout of a ldap request +*/ +static void ldap_request_timeout(struct tevent_context *ev, struct tevent_timer *te, + struct timeval t, void *private_data) +{ + struct ldap_request *req = talloc_get_type(private_data, struct ldap_request); + req->status = NT_STATUS_IO_TIMEOUT; + if (req->state == LDAP_REQUEST_PENDING) { + DLIST_REMOVE(req->conn->pending, req); + } + req->state = LDAP_REQUEST_DONE; + if (req->async.fn) { + req->async.fn(req); + } +} + + +/* + called on completion of a one-way ldap request +*/ +static void ldap_request_complete(struct tevent_context *ev, struct tevent_timer *te, + struct timeval t, void *private_data) +{ + struct ldap_request *req = talloc_get_type(private_data, struct ldap_request); + if (req->async.fn) { + req->async.fn(req); + } +} + +/* + send a ldap message - async interface +*/ +_PUBLIC_ struct ldap_request *ldap_request_send(struct ldap_connection *conn, + struct ldap_message *msg) +{ + struct ldap_request *req; + NTSTATUS status = NT_STATUS_UNSUCCESSFUL; + + req = talloc_zero(conn, struct ldap_request); + if (req == NULL) return NULL; + + if (conn->sock == NULL) { + status = NT_STATUS_INVALID_CONNECTION; + goto failed; + } + + req->state = LDAP_REQUEST_SEND; + req->conn = conn; + req->messageid = conn->next_messageid++; + if (conn->next_messageid == 0) { + conn->next_messageid = 1; + } + req->type = msg->type; + if (req->messageid == -1) { + goto failed; + } + + talloc_set_destructor(req, ldap_request_destructor); + + msg->messageid = req->messageid; + + if (!ldap_encode(msg, samba_ldap_control_handlers(), &req->data, req)) { + status = NT_STATUS_INTERNAL_ERROR; + goto failed; + } + + status = packet_send(conn->packet, req->data); + if (!NT_STATUS_IS_OK(status)) { + goto failed; + } + + /* some requests don't expect a reply, so don't add those to the + pending queue */ + if (req->type == LDAP_TAG_AbandonRequest || + req->type == LDAP_TAG_UnbindRequest) { + req->status = NT_STATUS_OK; + req->state = LDAP_REQUEST_DONE; + /* we can't call the async callback now, as it isn't setup, so + call it as next event */ + tevent_add_timer(conn->event.event_ctx, req, timeval_zero(), + ldap_request_complete, req); + return req; + } + + req->state = LDAP_REQUEST_PENDING; + DLIST_ADD(conn->pending, req); + + /* put a timeout on the request */ + req->time_event = tevent_add_timer(conn->event.event_ctx, req, + timeval_current_ofs(conn->timeout, 0), + ldap_request_timeout, req); + + return req; + +failed: + req->status = status; + req->state = LDAP_REQUEST_ERROR; + tevent_add_timer(conn->event.event_ctx, req, timeval_zero(), + ldap_request_complete, req); + + return req; +} + + +/* + wait for a request to complete + note that this does not destroy the request +*/ +_PUBLIC_ NTSTATUS ldap_request_wait(struct ldap_request *req) +{ + while (req->state < LDAP_REQUEST_DONE) { + if (tevent_loop_once(req->conn->event.event_ctx) != 0) { + req->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR; + break; + } + } + return req->status; +} + + +/* + a mapping of ldap response code to strings +*/ +static const struct { + enum ldap_result_code code; + const char *str; +} ldap_code_map[] = { +#define _LDAP_MAP_CODE(c) { c, #c } + _LDAP_MAP_CODE(LDAP_SUCCESS), + _LDAP_MAP_CODE(LDAP_OPERATIONS_ERROR), + _LDAP_MAP_CODE(LDAP_PROTOCOL_ERROR), + _LDAP_MAP_CODE(LDAP_TIME_LIMIT_EXCEEDED), + _LDAP_MAP_CODE(LDAP_SIZE_LIMIT_EXCEEDED), + _LDAP_MAP_CODE(LDAP_COMPARE_FALSE), + _LDAP_MAP_CODE(LDAP_COMPARE_TRUE), + _LDAP_MAP_CODE(LDAP_AUTH_METHOD_NOT_SUPPORTED), + _LDAP_MAP_CODE(LDAP_STRONG_AUTH_REQUIRED), + _LDAP_MAP_CODE(LDAP_REFERRAL), + _LDAP_MAP_CODE(LDAP_ADMIN_LIMIT_EXCEEDED), + _LDAP_MAP_CODE(LDAP_UNAVAILABLE_CRITICAL_EXTENSION), + _LDAP_MAP_CODE(LDAP_CONFIDENTIALITY_REQUIRED), + _LDAP_MAP_CODE(LDAP_SASL_BIND_IN_PROGRESS), + _LDAP_MAP_CODE(LDAP_NO_SUCH_ATTRIBUTE), + _LDAP_MAP_CODE(LDAP_UNDEFINED_ATTRIBUTE_TYPE), + _LDAP_MAP_CODE(LDAP_INAPPROPRIATE_MATCHING), + _LDAP_MAP_CODE(LDAP_CONSTRAINT_VIOLATION), + _LDAP_MAP_CODE(LDAP_ATTRIBUTE_OR_VALUE_EXISTS), + _LDAP_MAP_CODE(LDAP_INVALID_ATTRIBUTE_SYNTAX), + _LDAP_MAP_CODE(LDAP_NO_SUCH_OBJECT), + _LDAP_MAP_CODE(LDAP_ALIAS_PROBLEM), + _LDAP_MAP_CODE(LDAP_INVALID_DN_SYNTAX), + _LDAP_MAP_CODE(LDAP_ALIAS_DEREFERENCING_PROBLEM), + _LDAP_MAP_CODE(LDAP_INAPPROPRIATE_AUTHENTICATION), + _LDAP_MAP_CODE(LDAP_INVALID_CREDENTIALS), + _LDAP_MAP_CODE(LDAP_INSUFFICIENT_ACCESS_RIGHTS), + _LDAP_MAP_CODE(LDAP_BUSY), + _LDAP_MAP_CODE(LDAP_UNAVAILABLE), + _LDAP_MAP_CODE(LDAP_UNWILLING_TO_PERFORM), + _LDAP_MAP_CODE(LDAP_LOOP_DETECT), + _LDAP_MAP_CODE(LDAP_NAMING_VIOLATION), + _LDAP_MAP_CODE(LDAP_OBJECT_CLASS_VIOLATION), + _LDAP_MAP_CODE(LDAP_NOT_ALLOWED_ON_NON_LEAF), + _LDAP_MAP_CODE(LDAP_NOT_ALLOWED_ON_RDN), + _LDAP_MAP_CODE(LDAP_ENTRY_ALREADY_EXISTS), + _LDAP_MAP_CODE(LDAP_OBJECT_CLASS_MODS_PROHIBITED), + _LDAP_MAP_CODE(LDAP_AFFECTS_MULTIPLE_DSAS), + _LDAP_MAP_CODE(LDAP_OTHER) +}; + +/* + used to setup the status code from a ldap response +*/ +_PUBLIC_ NTSTATUS ldap_check_response(struct ldap_connection *conn, struct ldap_Result *r) +{ + int i; + const char *codename = "unknown"; + + if (r->resultcode == LDAP_SUCCESS) { + return NT_STATUS_OK; + } + + if (conn->last_error) { + talloc_free(conn->last_error); + } + + for (i=0;i<ARRAY_SIZE(ldap_code_map);i++) { + if (r->resultcode == ldap_code_map[i].code) { + codename = ldap_code_map[i].str; + break; + } + } + + conn->last_error = talloc_asprintf(conn, "LDAP error %u %s - %s <%s> <%s>", + r->resultcode, + codename, + r->dn?r->dn:"(NULL)", + r->errormessage?r->errormessage:"", + r->referral?r->referral:""); + + return NT_STATUS_LDAP(r->resultcode); +} + +/* + return error string representing the last error +*/ +_PUBLIC_ const char *ldap_errstr(struct ldap_connection *conn, + TALLOC_CTX *mem_ctx, + NTSTATUS status) +{ + if (NT_STATUS_IS_LDAP(status) && conn->last_error != NULL) { + return talloc_strdup(mem_ctx, conn->last_error); + } + return talloc_asprintf(mem_ctx, "LDAP client internal error: %s", nt_errstr(status)); +} + + +/* + return the Nth result message, waiting if necessary +*/ +_PUBLIC_ NTSTATUS ldap_result_n(struct ldap_request *req, int n, struct ldap_message **msg) +{ + *msg = NULL; + + NT_STATUS_HAVE_NO_MEMORY(req); + + while (req->state < LDAP_REQUEST_DONE && n >= req->num_replies) { + if (tevent_loop_once(req->conn->event.event_ctx) != 0) { + return NT_STATUS_UNEXPECTED_NETWORK_ERROR; + } + } + + if (n < req->num_replies) { + *msg = req->replies[n]; + return NT_STATUS_OK; + } + + if (!NT_STATUS_IS_OK(req->status)) { + return req->status; + } + + return NT_STATUS_NO_MORE_ENTRIES; +} + + +/* + return a single result message, checking if it is of the expected LDAP type +*/ +_PUBLIC_ NTSTATUS ldap_result_one(struct ldap_request *req, struct ldap_message **msg, int type) +{ + NTSTATUS status; + status = ldap_result_n(req, 0, msg); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if ((*msg)->type != type) { + *msg = NULL; + return NT_STATUS_UNEXPECTED_NETWORK_ERROR; + } + return status; +} + +/* + a simple ldap transaction, for single result requests that only need a status code + this relies on single valued requests having the response type == request type + 1 +*/ +_PUBLIC_ NTSTATUS ldap_transaction(struct ldap_connection *conn, struct ldap_message *msg) +{ + struct ldap_request *req = ldap_request_send(conn, msg); + struct ldap_message *res; + NTSTATUS status; + status = ldap_result_n(req, 0, &res); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return status; + } + if (res->type != msg->type + 1) { + talloc_free(req); + return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR); + } + status = ldap_check_response(conn, &res->r.GeneralResult); + talloc_free(req); + return status; +} diff --git a/source4/libcli/ldap/ldap_client.h b/source4/libcli/ldap/ldap_client.h new file mode 100644 index 0000000000..084de2e6dc --- /dev/null +++ b/source4/libcli/ldap/ldap_client.h @@ -0,0 +1,140 @@ +/* + Unix SMB/CIFS Implementation. + + ldap client side header + + Copyright (C) Andrew Tridgell 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 "libcli/ldap/ldap.h" + +enum ldap_request_state { LDAP_REQUEST_SEND=1, LDAP_REQUEST_PENDING=2, LDAP_REQUEST_DONE=3, LDAP_REQUEST_ERROR=4 }; + +/* this is the handle that the caller gets when an async ldap message + is sent */ +struct ldap_request { + struct ldap_request *next, *prev; + struct ldap_connection *conn; + + enum ldap_request_tag type; + int messageid; + enum ldap_request_state state; + + int num_replies; + struct ldap_message **replies; + + NTSTATUS status; + DATA_BLOB data; + struct { + void (*fn)(struct ldap_request *); + void *private_data; + } async; + + struct tevent_timer *time_event; +}; + + +/* main context for a ldap client connection */ +struct ldap_connection { + struct socket_context *sock; + struct loadparm_context *lp_ctx; + + char *host; + uint16_t port; + bool ldaps; + + const char *auth_dn; + const char *simple_pw; + + struct { + char *url; + int max_retries; + int retries; + time_t previous; + } reconnect; + + struct { + enum { LDAP_BIND_SIMPLE, LDAP_BIND_SASL } type; + void *creds; + } bind; + + /* next message id to assign */ + unsigned next_messageid; + + /* Outstanding LDAP requests that have not yet been replied to */ + struct ldap_request *pending; + + /* Let's support SASL */ + struct gensec_security *gensec; + + /* the default timeout for messages */ + int timeout; + + /* last error message */ + char *last_error; + + struct { + struct tevent_context *event_ctx; + struct tevent_fd *fde; + } event; + + struct packet_context *packet; +}; + +struct ldap_connection *ldap4_new_connection(TALLOC_CTX *mem_ctx, + struct loadparm_context *lp_ctx, + struct tevent_context *ev); + +NTSTATUS ldap_connect(struct ldap_connection *conn, const char *url); +struct composite_context *ldap_connect_send(struct ldap_connection *conn, + const char *url); + +NTSTATUS ldap_rebind(struct ldap_connection *conn); +NTSTATUS ldap_bind_simple(struct ldap_connection *conn, + const char *userdn, const char *password); +NTSTATUS ldap_bind_sasl(struct ldap_connection *conn, + struct cli_credentials *creds, + struct loadparm_context *lp_ctx); +struct ldap_request *ldap_request_send(struct ldap_connection *conn, + struct ldap_message *msg); +NTSTATUS ldap_request_wait(struct ldap_request *req); +struct composite_context; +NTSTATUS ldap_connect_recv(struct composite_context *ctx); +NTSTATUS ldap_result_n(struct ldap_request *req, int n, struct ldap_message **msg); +NTSTATUS ldap_result_one(struct ldap_request *req, struct ldap_message **msg, int type); +NTSTATUS ldap_transaction(struct ldap_connection *conn, struct ldap_message *msg); +const char *ldap_errstr(struct ldap_connection *conn, + TALLOC_CTX *mem_ctx, + NTSTATUS status); +NTSTATUS ldap_check_response(struct ldap_connection *conn, struct ldap_Result *r); +void ldap_set_reconn_params(struct ldap_connection *conn, int max_retries); +int ildap_count_entries(struct ldap_connection *conn, struct ldap_message **res); +NTSTATUS ildap_search_bytree(struct ldap_connection *conn, const char *basedn, + int scope, struct ldb_parse_tree *tree, + const char * const *attrs, bool attributesonly, + struct ldb_control **control_req, + struct ldb_control ***control_res, + struct ldap_message ***results); +NTSTATUS ildap_search(struct ldap_connection *conn, const char *basedn, + int scope, const char *expression, + const char * const *attrs, bool attributesonly, + struct ldb_control **control_req, + struct ldb_control ***control_res, + struct ldap_message ***results); + + + diff --git a/source4/libcli/ldap/ldap_controls.c b/source4/libcli/ldap/ldap_controls.c new file mode 100644 index 0000000000..7949758a80 --- /dev/null +++ b/source4/libcli/ldap/ldap_controls.c @@ -0,0 +1,1237 @@ +/* + Unix SMB/CIFS mplementation. + LDAP protocol helper functions for SAMBA + + Copyright (C) Simo Sorce 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "includes.h" +#include "../lib/util/asn1.h" +#include "libcli/ldap/ldap.h" +#include "lib/ldb/include/ldb.h" +#include "libcli/ldap/ldap_proto.h" +#include "dsdb/samdb/samdb.h" + +static bool decode_server_sort_response(void *mem_ctx, DATA_BLOB in, void *_out) +{ + void **out = (void **)_out; + DATA_BLOB attr; + struct asn1_data *data = asn1_init(mem_ctx); + struct ldb_sort_resp_control *lsrc; + + if (!data) return false; + + if (!asn1_load(data, in)) { + return false; + } + + lsrc = talloc(mem_ctx, struct ldb_sort_resp_control); + if (!lsrc) { + return false; + } + + if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + + if (!asn1_read_enumerated(data, &(lsrc->result))) { + return false; + } + + lsrc->attr_desc = NULL; + if (asn1_peek_tag(data, ASN1_OCTET_STRING)) { + if (!asn1_read_OctetString(data, mem_ctx, &attr)) { + return false; + } + lsrc->attr_desc = talloc_strndup(lsrc, (const char *)attr.data, attr.length); + if (!lsrc->attr_desc) { + return false; + } + } + + if (!asn1_end_tag(data)) { + return false; + } + + *out = lsrc; + + return true; +} + +static bool decode_server_sort_request(void *mem_ctx, DATA_BLOB in, void *_out) +{ + void **out = (void **)_out; + DATA_BLOB attr; + DATA_BLOB rule; + struct asn1_data *data = asn1_init(mem_ctx); + struct ldb_server_sort_control **lssc; + int num; + + if (!data) return false; + + if (!asn1_load(data, in)) { + return false; + } + + if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + + lssc = NULL; + + for (num = 0; asn1_peek_tag(data, ASN1_SEQUENCE(0)); num++) { + lssc = talloc_realloc(mem_ctx, lssc, struct ldb_server_sort_control *, num + 2); + if (!lssc) { + return false; + } + lssc[num] = talloc_zero(lssc, struct ldb_server_sort_control); + if (!lssc[num]) { + return false; + } + + if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + + if (!asn1_read_OctetString(data, mem_ctx, &attr)) { + return false; + } + + lssc[num]->attributeName = talloc_strndup(lssc[num], (const char *)attr.data, attr.length); + if (!lssc [num]->attributeName) { + return false; + } + + if (asn1_peek_tag(data, ASN1_OCTET_STRING)) { + if (!asn1_read_OctetString(data, mem_ctx, &rule)) { + return false; + } + lssc[num]->orderingRule = talloc_strndup(lssc[num], (const char *)rule.data, rule.length); + if (!lssc[num]->orderingRule) { + return false; + } + } + + if (asn1_peek_tag(data, ASN1_BOOLEAN)) { + bool reverse; + if (!asn1_read_BOOLEAN(data, &reverse)) { + return false; + } + lssc[num]->reverse = reverse; + } + + if (!asn1_end_tag(data)) { + return false; + } + } + + if (lssc != NULL) { + lssc[num] = NULL; + } + + if (!asn1_end_tag(data)) { + return false; + } + + *out = lssc; + + return true; +} + +static bool decode_extended_dn_request(void *mem_ctx, DATA_BLOB in, void *_out) +{ + void **out = (void **)_out; + struct asn1_data *data; + struct ldb_extended_dn_control *ledc; + + /* The content of this control is optional */ + if (in.length == 0) { + *out = NULL; + return true; + } + + data = asn1_init(mem_ctx); + if (!data) return false; + + if (!asn1_load(data, in)) { + return false; + } + + ledc = talloc(mem_ctx, struct ldb_extended_dn_control); + if (!ledc) { + return false; + } + + if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + + if (!asn1_read_Integer(data, &(ledc->type))) { + return false; + } + + if (!asn1_end_tag(data)) { + return false; + } + + *out = ledc; + + return true; +} + +static bool decode_sd_flags_request(void *mem_ctx, DATA_BLOB in, void *_out) +{ + void **out = (void **)_out; + struct asn1_data *data = asn1_init(mem_ctx); + struct ldb_sd_flags_control *lsdfc; + + if (!data) return false; + + if (!asn1_load(data, in)) { + return false; + } + + lsdfc = talloc(mem_ctx, struct ldb_sd_flags_control); + if (!lsdfc) { + return false; + } + + if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + + if (!asn1_read_Integer(data, &(lsdfc->secinfo_flags))) { + return false; + } + + if (!asn1_end_tag(data)) { + return false; + } + + *out = lsdfc; + + return true; +} + +static bool decode_search_options_request(void *mem_ctx, DATA_BLOB in, void *_out) +{ + void **out = (void **)_out; + struct asn1_data *data = asn1_init(mem_ctx); + struct ldb_search_options_control *lsoc; + + if (!data) return false; + + if (!asn1_load(data, in)) { + return false; + } + + lsoc = talloc(mem_ctx, struct ldb_search_options_control); + if (!lsoc) { + return false; + } + + if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + + if (!asn1_read_Integer(data, &(lsoc->search_options))) { + return false; + } + + if (!asn1_end_tag(data)) { + return false; + } + + *out = lsoc; + + return true; +} + +static bool decode_paged_results_request(void *mem_ctx, DATA_BLOB in, void *_out) +{ + void **out = (void **)_out; + DATA_BLOB cookie; + struct asn1_data *data = asn1_init(mem_ctx); + struct ldb_paged_control *lprc; + + if (!data) return false; + + if (!asn1_load(data, in)) { + return false; + } + + lprc = talloc(mem_ctx, struct ldb_paged_control); + if (!lprc) { + return false; + } + + if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + + if (!asn1_read_Integer(data, &(lprc->size))) { + return false; + } + + if (!asn1_read_OctetString(data, mem_ctx, &cookie)) { + return false; + } + lprc->cookie_len = cookie.length; + if (lprc->cookie_len) { + lprc->cookie = talloc_memdup(lprc, cookie.data, cookie.length); + + if (!(lprc->cookie)) { + return false; + } + } else { + lprc->cookie = NULL; + } + + if (!asn1_end_tag(data)) { + return false; + } + + *out = lprc; + + return true; +} + +static bool decode_dirsync_request(void *mem_ctx, DATA_BLOB in, void *_out) +{ + void **out = (void **)_out; + DATA_BLOB cookie; + struct asn1_data *data = asn1_init(mem_ctx); + struct ldb_dirsync_control *ldc; + + if (!data) return false; + + if (!asn1_load(data, in)) { + return false; + } + + ldc = talloc(mem_ctx, struct ldb_dirsync_control); + if (!ldc) { + return false; + } + + if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + + if (!asn1_read_Integer(data, &(ldc->flags))) { + return false; + } + + if (!asn1_read_Integer(data, &(ldc->max_attributes))) { + return false; + } + + if (!asn1_read_OctetString(data, mem_ctx, &cookie)) { + return false; + } + ldc->cookie_len = cookie.length; + if (ldc->cookie_len) { + ldc->cookie = talloc_memdup(ldc, cookie.data, cookie.length); + + if (!(ldc->cookie)) { + return false; + } + } else { + ldc->cookie = NULL; + } + + if (!asn1_end_tag(data)) { + return false; + } + + *out = ldc; + + return true; +} + +/* seem that this controls has 2 forms one in case it is used with + * a Search Request and another when used ina Search Response + */ +static bool decode_asq_control(void *mem_ctx, DATA_BLOB in, void *_out) +{ + void **out = (void **)_out; + DATA_BLOB source_attribute; + struct asn1_data *data = asn1_init(mem_ctx); + struct ldb_asq_control *lac; + + if (!data) return false; + + if (!asn1_load(data, in)) { + return false; + } + + lac = talloc(mem_ctx, struct ldb_asq_control); + if (!lac) { + return false; + } + + if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + + if (asn1_peek_tag(data, ASN1_OCTET_STRING)) { + + if (!asn1_read_OctetString(data, mem_ctx, &source_attribute)) { + return false; + } + lac->src_attr_len = source_attribute.length; + if (lac->src_attr_len) { + lac->source_attribute = talloc_strndup(lac, (const char *)source_attribute.data, source_attribute.length); + + if (!(lac->source_attribute)) { + return false; + } + } else { + lac->source_attribute = NULL; + } + + lac->request = 1; + + } else if (asn1_peek_tag(data, ASN1_ENUMERATED)) { + + if (!asn1_read_enumerated(data, &(lac->result))) { + return false; + } + + lac->request = 0; + + } else { + return false; + } + + if (!asn1_end_tag(data)) { + return false; + } + + *out = lac; + + return true; +} + +static bool decode_domain_scope_request(void *mem_ctx, DATA_BLOB in, void *_out) +{ + if (in.length != 0) { + return false; + } + + return true; +} + +static bool decode_notification_request(void *mem_ctx, DATA_BLOB in, void *_out) +{ + if (in.length != 0) { + return false; + } + + return true; +} + +static bool decode_show_deleted_request(void *mem_ctx, DATA_BLOB in, void *_out) +{ + if (in.length != 0) { + return false; + } + + return true; +} + +static bool decode_permissive_modify_request(void *mem_ctx, DATA_BLOB in, void *_out) +{ + if (in.length != 0) { + return false; + } + + return true; +} + +static bool decode_manageDSAIT_request(void *mem_ctx, DATA_BLOB in, void *_out) +{ + if (in.length != 0) { + return false; + } + + return true; +} + +static bool decode_vlv_request(void *mem_ctx, DATA_BLOB in, void *_out) +{ + void **out = (void **)_out; + DATA_BLOB assertion_value, context_id; + struct asn1_data *data = asn1_init(mem_ctx); + struct ldb_vlv_req_control *lvrc; + + if (!data) return false; + + if (!asn1_load(data, in)) { + return false; + } + + lvrc = talloc(mem_ctx, struct ldb_vlv_req_control); + if (!lvrc) { + return false; + } + + if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + + if (!asn1_read_Integer(data, &(lvrc->beforeCount))) { + return false; + } + + if (!asn1_read_Integer(data, &(lvrc->afterCount))) { + return false; + } + + if (asn1_peek_tag(data, ASN1_CONTEXT(0))) { + + lvrc->type = 0; + + if (!asn1_start_tag(data, ASN1_CONTEXT(0))) { + return false; + } + + if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + + if (!asn1_read_Integer(data, &(lvrc->match.byOffset.offset))) { + return false; + } + + if (!asn1_read_Integer(data, &(lvrc->match.byOffset.contentCount))) { + return false; + } + + if (!asn1_end_tag(data)) { /*SEQUENCE*/ + return false; + } + + if (!asn1_end_tag(data)) { /*CONTEXT*/ + return false; + } + + } else { + + lvrc->type = 1; + + if (!asn1_start_tag(data, ASN1_CONTEXT(1))) { + return false; + } + + if (!asn1_read_OctetString(data, mem_ctx, &assertion_value)) { + return false; + } + lvrc->match.gtOrEq.value_len = assertion_value.length; + if (lvrc->match.gtOrEq.value_len) { + lvrc->match.gtOrEq.value = talloc_memdup(lvrc, assertion_value.data, assertion_value.length); + + if (!(lvrc->match.gtOrEq.value)) { + return false; + } + } else { + lvrc->match.gtOrEq.value = NULL; + } + + if (!asn1_end_tag(data)) { /*CONTEXT*/ + return false; + } + } + + if (asn1_peek_tag(data, ASN1_OCTET_STRING)) { + if (!asn1_read_OctetString(data, mem_ctx, &context_id)) { + return false; + } + lvrc->ctxid_len = context_id.length; + if (lvrc->ctxid_len) { + lvrc->contextId = talloc_memdup(lvrc, context_id.data, context_id.length); + + if (!(lvrc->contextId)) { + return false; + } + } else { + lvrc->contextId = NULL; + } + } else { + lvrc->contextId = NULL; + lvrc->ctxid_len = 0; + } + + if (!asn1_end_tag(data)) { + return false; + } + + *out = lvrc; + + return true; +} + +static bool decode_vlv_response(void *mem_ctx, DATA_BLOB in, void *_out) +{ + void **out = (void **)_out; + DATA_BLOB context_id; + struct asn1_data *data = asn1_init(mem_ctx); + struct ldb_vlv_resp_control *lvrc; + + if (!data) return false; + + if (!asn1_load(data, in)) { + return false; + } + + lvrc = talloc(mem_ctx, struct ldb_vlv_resp_control); + if (!lvrc) { + return false; + } + + if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + + if (!asn1_read_Integer(data, &(lvrc->targetPosition))) { + return false; + } + + if (!asn1_read_Integer(data, &(lvrc->contentCount))) { + return false; + } + + if (!asn1_read_enumerated(data, &(lvrc->vlv_result))) { + return false; + } + + if (asn1_peek_tag(data, ASN1_OCTET_STRING)) { + if (!asn1_read_OctetString(data, mem_ctx, &context_id)) { + return false; + } + lvrc->contextId = talloc_strndup(lvrc, (const char *)context_id.data, context_id.length); + if (!lvrc->contextId) { + return false; + } + lvrc->ctxid_len = context_id.length; + } else { + lvrc->contextId = NULL; + lvrc->ctxid_len = 0; + } + + if (!asn1_end_tag(data)) { + return false; + } + + *out = lvrc; + + return true; +} + +static bool encode_server_sort_response(void *mem_ctx, void *in, DATA_BLOB *out) +{ + struct ldb_sort_resp_control *lsrc = talloc_get_type(in, struct ldb_sort_resp_control); + struct asn1_data *data = asn1_init(mem_ctx); + + if (!data) return false; + + if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + + if (!asn1_write_enumerated(data, lsrc->result)) { + return false; + } + + if (lsrc->attr_desc) { + if (!asn1_write_OctetString(data, lsrc->attr_desc, strlen(lsrc->attr_desc))) { + return false; + } + } + + if (!asn1_pop_tag(data)) { + return false; + } + + *out = data_blob_talloc(mem_ctx, data->data, data->length); + if (out->data == NULL) { + return false; + } + talloc_free(data); + + return true; +} + +static bool encode_server_sort_request(void *mem_ctx, void *in, DATA_BLOB *out) +{ + struct ldb_server_sort_control **lssc = talloc_get_type(in, struct ldb_server_sort_control *); + struct asn1_data *data = asn1_init(mem_ctx); + int num; + + if (!data) return false; + + if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + + for (num = 0; lssc[num]; num++) { + if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + + if (!asn1_write_OctetString(data, lssc[num]->attributeName, strlen(lssc[num]->attributeName))) { + return false; + } + + if (lssc[num]->orderingRule) { + if (!asn1_write_OctetString(data, lssc[num]->orderingRule, strlen(lssc[num]->orderingRule))) { + return false; + } + } + + if (lssc[num]->reverse) { + if (!asn1_write_BOOLEAN(data, lssc[num]->reverse)) { + return false; + } + } + + if (!asn1_pop_tag(data)) { + return false; + } + } + + if (!asn1_pop_tag(data)) { + return false; + } + + *out = data_blob_talloc(mem_ctx, data->data, data->length); + if (out->data == NULL) { + return false; + } + talloc_free(data); + + return true; +} + +static bool encode_extended_dn_request(void *mem_ctx, void *in, DATA_BLOB *out) +{ + struct ldb_extended_dn_control *ledc = talloc_get_type(in, struct ldb_extended_dn_control); + struct asn1_data *data; + + if (!in) { + *out = data_blob(NULL, 0); + return true; + } + + data = asn1_init(mem_ctx); + + if (!data) return false; + + if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + + if (!asn1_write_Integer(data, ledc->type)) { + return false; + } + + if (!asn1_pop_tag(data)) { + return false; + } + + *out = data_blob_talloc(mem_ctx, data->data, data->length); + if (out->data == NULL) { + return false; + } + talloc_free(data); + + return true; +} + +static bool encode_sd_flags_request(void *mem_ctx, void *in, DATA_BLOB *out) +{ + struct ldb_sd_flags_control *lsdfc = talloc_get_type(in, struct ldb_sd_flags_control); + struct asn1_data *data = asn1_init(mem_ctx); + + if (!data) return false; + + if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + + if (!asn1_write_Integer(data, lsdfc->secinfo_flags)) { + return false; + } + + if (!asn1_pop_tag(data)) { + return false; + } + + *out = data_blob_talloc(mem_ctx, data->data, data->length); + if (out->data == NULL) { + return false; + } + talloc_free(data); + + return true; +} + +static bool encode_search_options_request(void *mem_ctx, void *in, DATA_BLOB *out) +{ + struct ldb_search_options_control *lsoc = talloc_get_type(in, struct ldb_search_options_control); + struct asn1_data *data = asn1_init(mem_ctx); + + if (!data) return false; + + if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + + if (!asn1_write_Integer(data, lsoc->search_options)) { + return false; + } + + if (!asn1_pop_tag(data)) { + return false; + } + + *out = data_blob_talloc(mem_ctx, data->data, data->length); + if (out->data == NULL) { + return false; + } + talloc_free(data); + + return true; +} + +static bool encode_paged_results_request(void *mem_ctx, void *in, DATA_BLOB *out) +{ + struct ldb_paged_control *lprc = talloc_get_type(in, struct ldb_paged_control); + struct asn1_data *data = asn1_init(mem_ctx); + + if (!data) return false; + + if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + + if (!asn1_write_Integer(data, lprc->size)) { + return false; + } + + if (!asn1_write_OctetString(data, lprc->cookie, lprc->cookie_len)) { + return false; + } + + if (!asn1_pop_tag(data)) { + return false; + } + + *out = data_blob_talloc(mem_ctx, data->data, data->length); + if (out->data == NULL) { + return false; + } + talloc_free(data); + + return true; +} + +/* seem that this controls has 2 forms one in case it is used with + * a Search Request and another when used ina Search Response + */ +static bool encode_asq_control(void *mem_ctx, void *in, DATA_BLOB *out) +{ + struct ldb_asq_control *lac = talloc_get_type(in, struct ldb_asq_control); + struct asn1_data *data = asn1_init(mem_ctx); + + if (!data) return false; + + if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + + if (lac->request) { + + if (!asn1_write_OctetString(data, lac->source_attribute, lac->src_attr_len)) { + return false; + } + } else { + if (!asn1_write_enumerated(data, lac->result)) { + return false; + } + } + + if (!asn1_pop_tag(data)) { + return false; + } + + *out = data_blob_talloc(mem_ctx, data->data, data->length); + if (out->data == NULL) { + return false; + } + talloc_free(data); + + return true; +} + +static bool encode_dirsync_request(void *mem_ctx, void *in, DATA_BLOB *out) +{ + struct ldb_dirsync_control *ldc = talloc_get_type(in, struct ldb_dirsync_control); + struct asn1_data *data = asn1_init(mem_ctx); + + if (!data) return false; + + if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + + if (!asn1_write_Integer(data, ldc->flags)) { + return false; + } + + if (!asn1_write_Integer(data, ldc->max_attributes)) { + return false; + } + + if (!asn1_write_OctetString(data, ldc->cookie, ldc->cookie_len)) { + return false; + } + + if (!asn1_pop_tag(data)) { + return false; + } + + *out = data_blob_talloc(mem_ctx, data->data, data->length); + if (out->data == NULL) { + return false; + } + talloc_free(data); + + return true; +} + +static bool encode_domain_scope_request(void *mem_ctx, void *in, DATA_BLOB *out) +{ + if (in) { + return false; + } + + *out = data_blob(NULL, 0); + return true; +} + +static bool encode_notification_request(void *mem_ctx, void *in, DATA_BLOB *out) +{ + if (in) { + return false; + } + + *out = data_blob(NULL, 0); + return true; +} + +static bool encode_show_deleted_request(void *mem_ctx, void *in, DATA_BLOB *out) +{ + if (in) { + return false; + } + + *out = data_blob(NULL, 0); + return true; +} + +static bool encode_permissive_modify_request(void *mem_ctx, void *in, DATA_BLOB *out) +{ + if (in) { + return false; + } + + *out = data_blob(NULL, 0); + return true; +} + +static bool encode_manageDSAIT_request(void *mem_ctx, void *in, DATA_BLOB *out) +{ + if (in) { + return false; + } + + *out = data_blob(NULL, 0); + return true; +} + +static bool encode_vlv_request(void *mem_ctx, void *in, DATA_BLOB *out) +{ + struct ldb_vlv_req_control *lvrc = talloc_get_type(in, struct ldb_vlv_req_control); + struct asn1_data *data = asn1_init(mem_ctx); + + if (!data) return false; + + if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + + if (!asn1_write_Integer(data, lvrc->beforeCount)) { + return false; + } + + if (!asn1_write_Integer(data, lvrc->afterCount)) { + return false; + } + + if (lvrc->type == 0) { + if (!asn1_push_tag(data, ASN1_CONTEXT(0))) { + return false; + } + + if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + + if (!asn1_write_Integer(data, lvrc->match.byOffset.offset)) { + return false; + } + + if (!asn1_write_Integer(data, lvrc->match.byOffset.contentCount)) { + return false; + } + + if (!asn1_pop_tag(data)) { /*SEQUENCE*/ + return false; + } + + if (!asn1_pop_tag(data)) { /*CONTEXT*/ + return false; + } + } else { + if (!asn1_push_tag(data, ASN1_CONTEXT(1))) { + return false; + } + + if (!asn1_write_OctetString(data, lvrc->match.gtOrEq.value, lvrc->match.gtOrEq.value_len)) { + return false; + } + + if (!asn1_pop_tag(data)) { /*CONTEXT*/ + return false; + } + } + + if (lvrc->ctxid_len) { + if (!asn1_write_OctetString(data, lvrc->contextId, lvrc->ctxid_len)) { + return false; + } + } + + if (!asn1_pop_tag(data)) { + return false; + } + + *out = data_blob_talloc(mem_ctx, data->data, data->length); + if (out->data == NULL) { + return false; + } + talloc_free(data); + + return true; +} + +static bool encode_vlv_response(void *mem_ctx, void *in, DATA_BLOB *out) +{ + struct ldb_vlv_resp_control *lvrc = talloc_get_type(in, struct ldb_vlv_resp_control); + struct asn1_data *data = asn1_init(mem_ctx); + + if (!data) return false; + + if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + + if (!asn1_write_Integer(data, lvrc->targetPosition)) { + return false; + } + + if (!asn1_write_Integer(data, lvrc->contentCount)) { + return false; + } + + if (!asn1_write_enumerated(data, lvrc->vlv_result)) { + return false; + } + + if (lvrc->ctxid_len) { + if (!asn1_write_OctetString(data, lvrc->contextId, lvrc->ctxid_len)) { + return false; + } + } + + if (!asn1_pop_tag(data)) { + return false; + } + + *out = data_blob_talloc(mem_ctx, data->data, data->length); + if (out->data == NULL) { + return false; + } + talloc_free(data); + + return true; +} + +static bool encode_openldap_dereference(void *mem_ctx, void *in, DATA_BLOB *out) +{ + struct dsdb_openldap_dereference_control *control = talloc_get_type(in, struct dsdb_openldap_dereference_control); + int i,j; + struct asn1_data *data = asn1_init(mem_ctx); + + if (!data) return false; + + if (!control) return false; + + if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + + for (i=0; control->dereference && control->dereference[i]; i++) { + if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + if (!asn1_write_OctetString(data, control->dereference[i]->source_attribute, strlen(control->dereference[i]->source_attribute))) { + return false; + } + if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + for (j=0; control->dereference && control->dereference[i]->dereference_attribute[j]; j++) { + if (!asn1_write_OctetString(data, control->dereference[i]->dereference_attribute[j], + strlen(control->dereference[i]->dereference_attribute[j]))) { + return false; + } + } + + asn1_pop_tag(data); + asn1_pop_tag(data); + } + asn1_pop_tag(data); + + *out = data_blob_talloc(mem_ctx, data->data, data->length); + if (out->data == NULL) { + return false; + } + talloc_free(data); + return true; +} + +static bool decode_openldap_dereference(void *mem_ctx, DATA_BLOB in, void *_out) +{ + void **out = (void **)_out; + struct asn1_data *data = asn1_init(mem_ctx); + struct dsdb_openldap_dereference_result_control *control; + struct dsdb_openldap_dereference_result **r = NULL; + int i = 0; + if (!data) return false; + + control = talloc(mem_ctx, struct dsdb_openldap_dereference_result_control); + if (!control) return false; + + if (!asn1_load(data, in)) { + return false; + } + + control = talloc(mem_ctx, struct dsdb_openldap_dereference_result_control); + if (!control) { + return false; + } + + if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + + while (asn1_tag_remaining(data) > 0) { + r = talloc_realloc(control, r, struct dsdb_openldap_dereference_result *, i + 2); + if (!r) { + return false; + } + r[i] = talloc_zero(r, struct dsdb_openldap_dereference_result); + if (!r[i]) { + return false; + } + + if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) { + return false; + } + + asn1_read_OctetString_talloc(r[i], data, &r[i]->source_attribute); + asn1_read_OctetString_talloc(r[i], data, &r[i]->dereferenced_dn); + if (asn1_peek_tag(data, ASN1_CONTEXT(0))) { + if (!asn1_start_tag(data, ASN1_CONTEXT(0))) { + return false; + } + + ldap_decode_attribs_bare(r, data, &r[i]->attributes, + &r[i]->num_attributes); + + if (!asn1_end_tag(data)) { + return false; + } + } + if (!asn1_end_tag(data)) { + return false; + } + i++; + r[i] = NULL; + } + + if (!asn1_end_tag(data)) { + return false; + } + + control->attributes = r; + *out = control; + + return true; +} + +static const struct ldap_control_handler ldap_known_controls[] = { + { "1.2.840.113556.1.4.319", decode_paged_results_request, encode_paged_results_request }, + { "1.2.840.113556.1.4.529", decode_extended_dn_request, encode_extended_dn_request }, + { "1.2.840.113556.1.4.473", decode_server_sort_request, encode_server_sort_request }, + { "1.2.840.113556.1.4.474", decode_server_sort_response, encode_server_sort_response }, + { "1.2.840.113556.1.4.1504", decode_asq_control, encode_asq_control }, + { "1.2.840.113556.1.4.841", decode_dirsync_request, encode_dirsync_request }, + { "1.2.840.113556.1.4.528", decode_notification_request, encode_notification_request }, + { "1.2.840.113556.1.4.417", decode_show_deleted_request, encode_show_deleted_request }, + { "1.2.840.113556.1.4.1413", decode_permissive_modify_request, encode_permissive_modify_request }, + { "1.2.840.113556.1.4.801", decode_sd_flags_request, encode_sd_flags_request }, + { "1.2.840.113556.1.4.1339", decode_domain_scope_request, encode_domain_scope_request }, + { "1.2.840.113556.1.4.1340", decode_search_options_request, encode_search_options_request }, + { "2.16.840.1.113730.3.4.2", decode_manageDSAIT_request, encode_manageDSAIT_request }, + { "2.16.840.1.113730.3.4.9", decode_vlv_request, encode_vlv_request }, + { "2.16.840.1.113730.3.4.10", decode_vlv_response, encode_vlv_response }, +/* DSDB_CONTROL_CURRENT_PARTITION_OID is internal only, and has no network representation */ + { "1.3.6.1.4.1.7165.4.3.2", NULL, NULL }, +/* DSDB_EXTENDED_REPLICATED_OBJECTS_OID is internal only, and has no network representation */ + { "1.3.6.1.4.1.7165.4.4.1", NULL, NULL }, + { DSDB_OPENLDAP_DEREFERENCE_CONTROL, decode_openldap_dereference, encode_openldap_dereference}, + { NULL, NULL, NULL } +}; + +const struct ldap_control_handler *samba_ldap_control_handlers(void) +{ + return ldap_known_controls; +} + diff --git a/source4/libcli/ldap/ldap_ildap.c b/source4/libcli/ldap/ldap_ildap.c new file mode 100644 index 0000000000..f059e3882a --- /dev/null +++ b/source4/libcli/ldap/ldap_ildap.c @@ -0,0 +1,129 @@ +/* + Unix SMB/CIFS mplementation. + + ildap api - an api similar to the traditional ldap api + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "includes.h" +#include "libcli/ldap/ldap.h" +#include "libcli/ldap/ldap_client.h" + + +/* + count the returned search entries +*/ +_PUBLIC_ int ildap_count_entries(struct ldap_connection *conn, struct ldap_message **res) +{ + int i; + for (i=0;res && res[i];i++) /* noop */ ; + return i; +} + + +/* + perform a synchronous ldap search +*/ +_PUBLIC_ NTSTATUS ildap_search_bytree(struct ldap_connection *conn, const char *basedn, + int scope, struct ldb_parse_tree *tree, + const char * const *attrs, bool attributesonly, + struct ldb_control **control_req, + struct ldb_control ***control_res, + struct ldap_message ***results) +{ + struct ldap_message *msg; + int n, i; + NTSTATUS status; + struct ldap_request *req; + + if (control_res) + *control_res = NULL; + *results = NULL; + + msg = new_ldap_message(conn); + NT_STATUS_HAVE_NO_MEMORY(msg); + + for (n=0;attrs && attrs[n];n++) /* noop */ ; + + msg->type = LDAP_TAG_SearchRequest; + msg->r.SearchRequest.basedn = basedn; + msg->r.SearchRequest.scope = scope; + msg->r.SearchRequest.deref = LDAP_DEREFERENCE_NEVER; + msg->r.SearchRequest.timelimit = 0; + msg->r.SearchRequest.sizelimit = 0; + msg->r.SearchRequest.attributesonly = attributesonly; + msg->r.SearchRequest.tree = tree; + msg->r.SearchRequest.num_attributes = n; + msg->r.SearchRequest.attributes = attrs; + msg->controls = control_req; + + req = ldap_request_send(conn, msg); + talloc_steal(msg, req); + + for (i=n=0;true;i++) { + struct ldap_message *res; + status = ldap_result_n(req, i, &res); + if (!NT_STATUS_IS_OK(status)) break; + + if (res->type == LDAP_TAG_SearchResultDone) { + status = ldap_check_response(conn, &res->r.GeneralResult); + if (control_res) { + *control_res = talloc_steal(conn, res->controls); + } + break; + } + + if (res->type != LDAP_TAG_SearchResultEntry && + res->type != LDAP_TAG_SearchResultReference) + continue; + + (*results) = talloc_realloc(conn, *results, struct ldap_message *, n+2); + if (*results == NULL) { + talloc_free(msg); + return NT_STATUS_NO_MEMORY; + } + (*results)[n] = talloc_steal(*results, res); + (*results)[n+1] = NULL; + n++; + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) { + status = NT_STATUS_OK; + } + + return status; +} + +/* + perform a ldap search +*/ +_PUBLIC_ NTSTATUS ildap_search(struct ldap_connection *conn, const char *basedn, + int scope, const char *expression, + const char * const *attrs, bool attributesonly, + struct ldb_control **control_req, + struct ldb_control ***control_res, + struct ldap_message ***results) +{ + struct ldb_parse_tree *tree = ldb_parse_tree(conn, expression); + NTSTATUS status; + status = ildap_search_bytree(conn, basedn, scope, tree, attrs, + attributesonly, control_req, + control_res, results); + talloc_free(tree); + return status; +} diff --git a/source4/libcli/libcli.h b/source4/libcli/libcli.h new file mode 100644 index 0000000000..e729e12721 --- /dev/null +++ b/source4/libcli/libcli.h @@ -0,0 +1,70 @@ +/* + Unix SMB/CIFS implementation. + SMB parameters and setup + Copyright (C) Andrew Tridgell 2004 + Copyright (C) James Myers 2003 <myersjj@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/>. +*/ + +#ifndef __LIBCLI_H__ +#define __LIBCLI_H__ + +#include "librpc/gen_ndr/nbt.h" + +struct substitute_context; + +/* + smbcli_state: internal state used in libcli library for single-threaded callers, + i.e. a single session on a single socket. + */ +struct smbcli_state { + struct smbcli_transport *transport; + struct smbcli_session *session; + struct smbcli_tree *tree; + struct substitute_context *substitute; + struct smblsa_state *lsa; +}; + +struct clilist_file_info { + uint64_t size; + uint16_t attrib; + time_t mtime; + const char *name; + const char *short_name; +}; + +struct nbt_dc_name { + const char *address; + const char *name; +}; + +struct cli_credentials; +struct tevent_context; + +/* passed to br lock code. */ +enum brl_type { + READ_LOCK, + WRITE_LOCK, + PENDING_READ_LOCK, + PENDING_WRITE_LOCK +}; + + + +#include "libcli/raw/libcliraw.h" +struct gensec_settings; +#include "libcli/libcli_proto.h" + +#endif /* __LIBCLI_H__ */ diff --git a/source4/libcli/rap/rap.h b/source4/libcli/rap/rap.h new file mode 100644 index 0000000000..6dcaa9bc83 --- /dev/null +++ b/source4/libcli/rap/rap.h @@ -0,0 +1,358 @@ +/* + Unix SMB/CIFS implementation. + RAP operations + Copyright (C) Volker Lendecke 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/>. +*/ + +#define RAP_WshareEnum 0 +#define RAP_WshareGetInfo 1 +#define RAP_WshareSetInfo 2 +#define RAP_WshareAdd 3 +#define RAP_WshareDel 4 +#define RAP_NetShareCheck 5 +#define RAP_WsessionEnum 6 +#define RAP_WsessionGetInfo 7 +#define RAP_WsessionDel 8 +#define RAP_WconnectionEnum 9 +#define RAP_WfileEnum 10 +#define RAP_WfileGetInfo 11 +#define RAP_WfileClose 12 +#define RAP_WserverGetInfo 13 +#define RAP_WserverSetInfo 14 +#define RAP_WserverDiskEnum 15 +#define RAP_WserverAdminCommand 16 +#define RAP_NetAuditOpen 17 +#define RAP_WauditClear 18 +#define RAP_NetErrorLogOpen 19 +#define RAP_WerrorLogClear 20 +#define RAP_NetCharDevEnum 21 +#define RAP_NetCharDevGetInfo 22 +#define RAP_WCharDevControl 23 +#define RAP_NetCharDevQEnum 24 +#define RAP_NetCharDevQGetInfo 25 +#define RAP_WCharDevQSetInfo 26 +#define RAP_WCharDevQPurge 27 +#define RAP_WCharDevQPurgeSelf 28 +#define RAP_WMessageNameEnum 29 +#define RAP_WMessageNameGetInfo 30 +#define RAP_WMessageNameAdd 31 +#define RAP_WMessageNameDel 32 +#define RAP_WMessageNameFwd 33 +#define RAP_WMessageNameUnFwd 34 +#define RAP_WMessageBufferSend 35 +#define RAP_WMessageFileSend 36 +#define RAP_WMessageLogFileSet 37 +#define RAP_WMessageLogFileGet 38 +#define RAP_WServiceEnum 39 +#define RAP_WServiceInstall 40 +#define RAP_WServiceControl 41 +#define RAP_WAccessEnum 42 +#define RAP_WAccessGetInfo 43 +#define RAP_WAccessSetInfo 44 +#define RAP_WAccessAdd 45 +#define RAP_WAccessDel 46 +#define RAP_WGroupEnum 47 +#define RAP_WGroupAdd 48 +#define RAP_WGroupDel 49 +#define RAP_WGroupAddUser 50 +#define RAP_WGroupDelUser 51 +#define RAP_WGroupGetUsers 52 +#define RAP_WUserEnum 53 +#define RAP_WUserAdd 54 +#define RAP_WUserDel 55 +#define RAP_WUserGetInfo 56 +#define RAP_WUserSetInfo 57 +#define RAP_WUserPasswordSet 58 +#define RAP_WUserGetGroups 59 +#define RAP_WWkstaSetUID 62 +#define RAP_WWkstaGetInfo 63 +#define RAP_WWkstaSetInfo 64 +#define RAP_WUseEnum 65 +#define RAP_WUseAdd 66 +#define RAP_WUseDel 67 +#define RAP_WUseGetInfo 68 +#define RAP_WPrintQEnum 69 +#define RAP_WPrintQGetInfo 70 +#define RAP_WPrintQSetInfo 71 +#define RAP_WPrintQAdd 72 +#define RAP_WPrintQDel 73 +#define RAP_WPrintQPause 74 +#define RAP_WPrintQContinue 75 +#define RAP_WPrintJobEnum 76 +#define RAP_WPrintJobGetInfo 77 +#define RAP_WPrintJobSetInfo_OLD 78 +#define RAP_WPrintJobDel 81 +#define RAP_WPrintJobPause 82 +#define RAP_WPrintJobContinue 83 +#define RAP_WPrintDestEnum 84 +#define RAP_WPrintDestGetInfo 85 +#define RAP_WPrintDestControl 86 +#define RAP_WProfileSave 87 +#define RAP_WProfileLoad 88 +#define RAP_WStatisticsGet 89 +#define RAP_WStatisticsClear 90 +#define RAP_NetRemoteTOD 91 +#define RAP_WNetBiosEnum 92 +#define RAP_WNetBiosGetInfo 93 +#define RAP_NetServerEnum 94 +#define RAP_I_NetServerEnum 95 +#define RAP_WServiceGetInfo 96 +#define RAP_WPrintQPurge 103 +#define RAP_NetServerEnum2 104 +#define RAP_WAccessGetUserPerms 105 +#define RAP_WGroupGetInfo 106 +#define RAP_WGroupSetInfo 107 +#define RAP_WGroupSetUsers 108 +#define RAP_WUserSetGroups 109 +#define RAP_WUserModalsGet 110 +#define RAP_WUserModalsSet 111 +#define RAP_WFileEnum2 112 +#define RAP_WUserAdd2 113 +#define RAP_WUserSetInfo2 114 +#define RAP_WUserPasswordSet2 115 +#define RAP_I_NetServerEnum2 116 +#define RAP_WConfigGet2 117 +#define RAP_WConfigGetAll2 118 +#define RAP_WGetDCName 119 +#define RAP_NetHandleGetInfo 120 +#define RAP_NetHandleSetInfo 121 +#define RAP_WStatisticsGet2 122 +#define RAP_WBuildGetInfo 123 +#define RAP_WFileGetInfo2 124 +#define RAP_WFileClose2 125 +#define RAP_WNetServerReqChallenge 126 +#define RAP_WNetServerAuthenticate 127 +#define RAP_WNetServerPasswordSet 128 +#define RAP_WNetAccountDeltas 129 +#define RAP_WNetAccountSync 130 +#define RAP_WUserEnum2 131 +#define RAP_WWkstaUserLogon 132 +#define RAP_WWkstaUserLogoff 133 +#define RAP_WLogonEnum 134 +#define RAP_WErrorLogRead 135 +#define RAP_NetPathType 136 +#define RAP_NetPathCanonicalize 137 +#define RAP_NetPathCompare 138 +#define RAP_NetNameValidate 139 +#define RAP_NetNameCanonicalize 140 +#define RAP_NetNameCompare 141 +#define RAP_WAuditRead 142 +#define RAP_WPrintDestAdd 143 +#define RAP_WPrintDestSetInfo 144 +#define RAP_WPrintDestDel 145 +#define RAP_WUserValidate2 146 +#define RAP_WPrintJobSetInfo 147 +#define RAP_TI_NetServerDiskEnum 148 +#define RAP_TI_NetServerDiskGetInfo 149 +#define RAP_TI_FTVerifyMirror 150 +#define RAP_TI_FTAbortVerify 151 +#define RAP_TI_FTGetInfo 152 +#define RAP_TI_FTSetInfo 153 +#define RAP_TI_FTLockDisk 154 +#define RAP_TI_FTFixError 155 +#define RAP_TI_FTAbortFix 156 +#define RAP_TI_FTDiagnoseError 157 +#define RAP_TI_FTGetDriveStats 158 +#define RAP_TI_FTErrorGetInfo 160 +#define RAP_NetAccessCheck 163 +#define RAP_NetAlertRaise 164 +#define RAP_NetAlertStart 165 +#define RAP_NetAlertStop 166 +#define RAP_NetAuditWrite 167 +#define RAP_NetIRemoteAPI 168 +#define RAP_NetServiceStatus 169 +#define RAP_NetServerRegister 170 +#define RAP_NetServerDeregister 171 +#define RAP_NetSessionEntryMake 172 +#define RAP_NetSessionEntryClear 173 +#define RAP_NetSessionEntryGetInfo 174 +#define RAP_NetSessionEntrySetInfo 175 +#define RAP_NetConnectionEntryMake 176 +#define RAP_NetConnectionEntryClear 177 +#define RAP_NetConnectionEntrySetInfo 178 +#define RAP_NetConnectionEntryGetInfo 179 +#define RAP_NetFileEntryMake 180 +#define RAP_NetFileEntryClear 181 +#define RAP_NetFileEntrySetInfo 182 +#define RAP_NetFileEntryGetInfo 183 +#define RAP_AltSrvMessageBufferSend 184 +#define RAP_AltSrvMessageFileSend 185 +#define RAP_wI_NetRplWkstaEnum 186 +#define RAP_wI_NetRplWkstaGetInfo 187 +#define RAP_wI_NetRplWkstaSetInfo 188 +#define RAP_wI_NetRplWkstaAdd 189 +#define RAP_wI_NetRplWkstaDel 190 +#define RAP_wI_NetRplProfileEnum 191 +#define RAP_wI_NetRplProfileGetInfo 192 +#define RAP_wI_NetRplProfileSetInfo 193 +#define RAP_wI_NetRplProfileAdd 194 +#define RAP_wI_NetRplProfileDel 195 +#define RAP_wI_NetRplProfileClone 196 +#define RAP_wI_NetRplBaseProfileEnum 197 +#define RAP_WIServerSetInfo 201 +#define RAP_WPrintDriverEnum 205 +#define RAP_WPrintQProcessorEnum 206 +#define RAP_WPrintPortEnum 207 +#define RAP_WNetWriteUpdateLog 208 +#define RAP_WNetAccountUpdate 209 +#define RAP_WNetAccountConfirmUpdate 210 +#define RAP_WConfigSet 211 +#define RAP_WAccountsReplicate 212 +#define RAP_SamOEMChgPasswordUser2_P 214 +#define RAP_NetServerEnum3 215 +#define RAP_WprintDriverGetInfo 250 +#define RAP_WprintDriverSetInfo 251 +#define RAP_WaliasAdd 252 +#define RAP_WaliasDel 253 +#define RAP_WaliasGetInfo 254 +#define RAP_WaliasSetInfo 255 +#define RAP_WaliasEnum 256 +#define RAP_WuserGetLogonAsn 257 +#define RAP_WuserSetLogonAsn 258 +#define RAP_WuserGetAppSel 259 +#define RAP_WuserSetAppSel 260 +#define RAP_WappAdd 261 +#define RAP_WappDel 262 +#define RAP_WappGetInfo 263 +#define RAP_WappSetInfo 264 +#define RAP_WappEnum 265 +#define RAP_WUserDCDBInit 266 +#define RAP_WDASDAdd 267 +#define RAP_WDASDDel 268 +#define RAP_WDASDGetInfo 269 +#define RAP_WDASDSetInfo 270 +#define RAP_WDASDEnum 271 +#define RAP_WDASDCheck 272 +#define RAP_WDASDCtl 273 +#define RAP_WuserRemoteLogonCheck 274 +#define RAP_WUserPasswordSet3 275 +#define RAP_WCreateRIPLMachine 276 +#define RAP_WDeleteRIPLMachine 277 +#define RAP_WGetRIPLMachineInfo 278 +#define RAP_WSetRIPLMachineInfo 279 +#define RAP_WEnumRIPLMachine 280 +#define RAP_I_ShareAdd 281 +#define RAP_AliasEnum 282 +#define RAP_WaccessApply 283 +#define RAP_WPrt16Query 284 +#define RAP_WPrt16Set 285 +#define RAP_WUserDel100 286 +#define RAP_WUserRemoteLogonCheck2 287 +#define RAP_WRemoteTODSet 294 +#define RAP_WprintJobMoveAll 295 +#define RAP_W16AppParmAdd 296 +#define RAP_W16AppParmDel 297 +#define RAP_W16AppParmGet 298 +#define RAP_W16AppParmSet 299 +#define RAP_W16RIPLMachineCreate 300 +#define RAP_W16RIPLMachineGetInfo 301 +#define RAP_W16RIPLMachineSetInfo 302 +#define RAP_W16RIPLMachineEnum 303 +#define RAP_W16RIPLMachineListParmEnum 304 +#define RAP_W16RIPLMachClassGetInfo 305 +#define RAP_W16RIPLMachClassEnum 306 +#define RAP_W16RIPLMachClassCreate 307 +#define RAP_W16RIPLMachClassSetInfo 308 +#define RAP_W16RIPLMachClassDelete 309 +#define RAP_W16RIPLMachClassLPEnum 310 +#define RAP_W16RIPLMachineDelete 311 +#define RAP_W16WSLevelGetInfo 312 +#define RAP_WserverNameAdd 313 +#define RAP_WserverNameDel 314 +#define RAP_WserverNameEnum 315 +#define RAP_I_WDASDEnum 316 +#define RAP_WDASDEnumTerminate 317 +#define RAP_WDASDSetInfo2 318 +#define MAX_API 318 + +struct rap_shareenum_info_0 { + char name[13]; +}; + +struct rap_shareenum_info_1 { + char name[13]; + char pad; + uint16_t type; + char *comment; +}; + +union rap_shareenum_info { + struct rap_shareenum_info_0 info0; + struct rap_shareenum_info_1 info1; +}; + +struct rap_NetShareEnum { + struct { + uint16_t level; + uint16_t bufsize; + } in; + + struct { + uint16_t status; + uint16_t convert; + uint16_t count; + uint16_t available; + union rap_shareenum_info *info; + } out; +}; + +struct rap_server_info_0 { + char name[16]; +}; + +struct rap_server_info_1 { + char name[16]; + uint8_t version_major; + uint8_t version_minor; + uint32_t servertype; + char *comment; +}; + +union rap_server_info { + struct rap_server_info_0 info0; + struct rap_server_info_1 info1; +}; + +struct rap_NetServerEnum2 { + struct { + uint16_t level; + uint16_t bufsize; + uint32_t servertype; + const char *domain; + } in; + + struct { + uint16_t status; + uint16_t convert; + uint16_t count; + uint16_t available; + union rap_server_info *info; + } out; +}; + +struct rap_WserverGetInfo { + struct { + uint16_t level; + uint16_t bufsize; + } in; + + struct { + uint16_t status; + uint16_t convert; + uint16_t available; + union rap_server_info info; + } out; +}; diff --git a/source4/libcli/raw/README b/source4/libcli/raw/README new file mode 100644 index 0000000000..cb3e507e3a --- /dev/null +++ b/source4/libcli/raw/README @@ -0,0 +1,5 @@ +Design notes for client library restructure: + +1 - no references to cli_state should exist in libcli/raw. +2 - all interfaces to functions in this directory should use cli_session or cli_tree as + the primary context structure
\ No newline at end of file diff --git a/source4/libcli/raw/clierror.c b/source4/libcli/raw/clierror.c new file mode 100644 index 0000000000..a41748640b --- /dev/null +++ b/source4/libcli/raw/clierror.c @@ -0,0 +1,73 @@ +/* + Unix SMB/CIFS implementation. + client error handling routines + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) James Myers 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 "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" + + +/*************************************************************************** + Return an error message from the last response +****************************************************************************/ +_PUBLIC_ const char *smbcli_errstr(struct smbcli_tree *tree) +{ + switch (tree->session->transport->error.etype) { + case ETYPE_SMB: + return nt_errstr(tree->session->transport->error.e.nt_status); + + case ETYPE_SOCKET: + return "socket_error"; + + case ETYPE_NBT: + return "nbt_error"; + + case ETYPE_NONE: + return "no_error"; + } + return NULL; +} + + +/* Return the 32-bit NT status code from the last packet */ +_PUBLIC_ NTSTATUS smbcli_nt_error(struct smbcli_tree *tree) +{ + switch (tree->session->transport->error.etype) { + case ETYPE_SMB: + return tree->session->transport->error.e.nt_status; + + case ETYPE_SOCKET: + return NT_STATUS_UNSUCCESSFUL; + + case ETYPE_NBT: + return NT_STATUS_UNSUCCESSFUL; + + case ETYPE_NONE: + return NT_STATUS_OK; + } + + return NT_STATUS_UNSUCCESSFUL; +} + + +/* Return true if the last packet was an error */ +bool smbcli_is_error(struct smbcli_tree *tree) +{ + return NT_STATUS_IS_ERR(smbcli_nt_error(tree)); +} diff --git a/source4/libcli/raw/clioplock.c b/source4/libcli/raw/clioplock.c new file mode 100644 index 0000000000..42ac6b517b --- /dev/null +++ b/source4/libcli/raw/clioplock.c @@ -0,0 +1,62 @@ +/* + 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" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" + +/**************************************************************************** +send an ack for an oplock break request +****************************************************************************/ +_PUBLIC_ bool smbcli_oplock_ack(struct smbcli_tree *tree, uint16_t fnum, uint16_t ack_level) +{ + bool ret; + struct smbcli_request *req; + + req = smbcli_request_setup(tree, SMBlockingX, 8, 0); + + SSVAL(req->out.vwv,VWV(0),0xFF); + SSVAL(req->out.vwv,VWV(1),0); + SSVAL(req->out.vwv,VWV(2),fnum); + SCVAL(req->out.vwv,VWV(3),LOCKING_ANDX_OPLOCK_RELEASE); + SCVAL(req->out.vwv,VWV(3)+1,ack_level); + SIVAL(req->out.vwv,VWV(4),0); + SSVAL(req->out.vwv,VWV(6),0); + SSVAL(req->out.vwv,VWV(7),0); + + /* this request does not expect a reply, so tell the signing + subsystem not to allocate an id for a reply */ + req->one_way_request = 1; + + ret = smbcli_request_send(req); + + return ret; +} + + +/**************************************************************************** +set the oplock handler for a connection +****************************************************************************/ +_PUBLIC_ void smbcli_oplock_handler(struct smbcli_transport *transport, + bool (*handler)(struct smbcli_transport *, uint16_t, uint16_t, uint8_t, void *), + void *private_data) +{ + transport->oplock.handler = handler; + transport->oplock.private_data = private_data; +} diff --git a/source4/libcli/raw/clisession.c b/source4/libcli/raw/clisession.c new file mode 100644 index 0000000000..41765bfb2b --- /dev/null +++ b/source4/libcli/raw/clisession.c @@ -0,0 +1,297 @@ +/* + Unix SMB/CIFS implementation. + SMB client session context management functions + + Copyright (C) Andrew Tridgell 1994-2005 + Copyright (C) James Myers 2003 <myersjj@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/>. +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "system/filesys.h" + +#define SETUP_REQUEST_SESSION(cmd, wct, buflen) do { \ + req = smbcli_request_setup_session(session, cmd, wct, buflen); \ + if (!req) return NULL; \ +} while (0) + + +/**************************************************************************** + Initialize the session context +****************************************************************************/ +struct smbcli_session *smbcli_session_init(struct smbcli_transport *transport, + TALLOC_CTX *parent_ctx, bool primary, + struct smbcli_session_options options) +{ + struct smbcli_session *session; + uint16_t flags2; + uint32_t capabilities; + + session = talloc_zero(parent_ctx, struct smbcli_session); + if (!session) { + return NULL; + } + + if (primary) { + session->transport = talloc_steal(session, transport); + } else { + session->transport = talloc_reference(session, transport); + } + session->pid = (uint16_t)getpid(); + session->vuid = UID_FIELD_INVALID; + session->options = options; + + capabilities = transport->negotiate.capabilities; + + flags2 = FLAGS2_LONG_PATH_COMPONENTS | FLAGS2_EXTENDED_ATTRIBUTES; + + if (capabilities & CAP_UNICODE) { + flags2 |= FLAGS2_UNICODE_STRINGS; + } + if (capabilities & CAP_STATUS32) { + flags2 |= FLAGS2_32_BIT_ERROR_CODES; + } + if (capabilities & CAP_EXTENDED_SECURITY) { + flags2 |= FLAGS2_EXTENDED_SECURITY; + } + if (session->transport->negotiate.sign_info.doing_signing) { + flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES; + } + + session->flags2 = flags2; + + return session; +} + +/**************************************************************************** + Perform a session setup (async send) +****************************************************************************/ +struct smbcli_request *smb_raw_sesssetup_send(struct smbcli_session *session, + union smb_sesssetup *parms) +{ + struct smbcli_request *req = NULL; + + switch (parms->old.level) { + case RAW_SESSSETUP_OLD: + SETUP_REQUEST_SESSION(SMBsesssetupX, 10, 0); + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv,VWV(2),parms->old.in.bufsize); + SSVAL(req->out.vwv,VWV(3),parms->old.in.mpx_max); + SSVAL(req->out.vwv,VWV(4),parms->old.in.vc_num); + SIVAL(req->out.vwv,VWV(5),parms->old.in.sesskey); + SSVAL(req->out.vwv,VWV(7),parms->old.in.password.length); + SIVAL(req->out.vwv,VWV(8), 0); /* reserved */ + smbcli_req_append_blob(req, &parms->old.in.password); + smbcli_req_append_string(req, parms->old.in.user, STR_TERMINATE); + smbcli_req_append_string(req, parms->old.in.domain, STR_TERMINATE|STR_UPPER); + smbcli_req_append_string(req, parms->old.in.os, STR_TERMINATE); + smbcli_req_append_string(req, parms->old.in.lanman, STR_TERMINATE); + break; + + case RAW_SESSSETUP_NT1: + SETUP_REQUEST_SESSION(SMBsesssetupX, 13, 0); + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), parms->nt1.in.bufsize); + SSVAL(req->out.vwv, VWV(3), parms->nt1.in.mpx_max); + SSVAL(req->out.vwv, VWV(4), parms->nt1.in.vc_num); + SIVAL(req->out.vwv, VWV(5), parms->nt1.in.sesskey); + SSVAL(req->out.vwv, VWV(7), parms->nt1.in.password1.length); + SSVAL(req->out.vwv, VWV(8), parms->nt1.in.password2.length); + SIVAL(req->out.vwv, VWV(9), 0); /* reserved */ + SIVAL(req->out.vwv, VWV(11), parms->nt1.in.capabilities); + smbcli_req_append_blob(req, &parms->nt1.in.password1); + smbcli_req_append_blob(req, &parms->nt1.in.password2); + smbcli_req_append_string(req, parms->nt1.in.user, STR_TERMINATE); + smbcli_req_append_string(req, parms->nt1.in.domain, STR_TERMINATE|STR_UPPER); + smbcli_req_append_string(req, parms->nt1.in.os, STR_TERMINATE); + smbcli_req_append_string(req, parms->nt1.in.lanman, STR_TERMINATE); + break; + + case RAW_SESSSETUP_SPNEGO: + SETUP_REQUEST_SESSION(SMBsesssetupX, 12, 0); + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), parms->spnego.in.bufsize); + SSVAL(req->out.vwv, VWV(3), parms->spnego.in.mpx_max); + SSVAL(req->out.vwv, VWV(4), parms->spnego.in.vc_num); + SIVAL(req->out.vwv, VWV(5), parms->spnego.in.sesskey); + SSVAL(req->out.vwv, VWV(7), parms->spnego.in.secblob.length); + SIVAL(req->out.vwv, VWV(8), 0); /* reserved */ + SIVAL(req->out.vwv, VWV(10), parms->spnego.in.capabilities); + smbcli_req_append_blob(req, &parms->spnego.in.secblob); + smbcli_req_append_string(req, parms->spnego.in.os, STR_TERMINATE); + smbcli_req_append_string(req, parms->spnego.in.lanman, STR_TERMINATE); + smbcli_req_append_string(req, parms->spnego.in.workgroup, STR_TERMINATE); + break; + + case RAW_SESSSETUP_SMB2: + return NULL; + } + + if (!smbcli_request_send(req)) { + smbcli_request_destroy(req); + return NULL; + } + + return req; +} + + +/**************************************************************************** + Perform a session setup (async recv) +****************************************************************************/ +NTSTATUS smb_raw_sesssetup_recv(struct smbcli_request *req, + TALLOC_CTX *mem_ctx, + union smb_sesssetup *parms) +{ + uint16_t len; + uint8_t *p; + + if (!smbcli_request_receive(req)) { + return smbcli_request_destroy(req); + } + + if (!NT_STATUS_IS_OK(req->status) && + !NT_STATUS_EQUAL(req->status,NT_STATUS_MORE_PROCESSING_REQUIRED)) { + return smbcli_request_destroy(req); + } + + switch (parms->old.level) { + case RAW_SESSSETUP_OLD: + SMBCLI_CHECK_WCT(req, 3); + ZERO_STRUCT(parms->old.out); + parms->old.out.vuid = SVAL(req->in.hdr, HDR_UID); + parms->old.out.action = SVAL(req->in.vwv, VWV(2)); + p = req->in.data; + if (p) { + p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->old.out.os, p, -1, STR_TERMINATE); + p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->old.out.lanman, p, -1, STR_TERMINATE); + p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->old.out.domain, p, -1, STR_TERMINATE); + } + break; + + case RAW_SESSSETUP_NT1: + SMBCLI_CHECK_WCT(req, 3); + ZERO_STRUCT(parms->nt1.out); + parms->nt1.out.vuid = SVAL(req->in.hdr, HDR_UID); + parms->nt1.out.action = SVAL(req->in.vwv, VWV(2)); + p = req->in.data; + if (p) { + p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->nt1.out.os, p, -1, STR_TERMINATE); + p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->nt1.out.lanman, p, -1, STR_TERMINATE); + if (p < (req->in.data + req->in.data_size)) { + p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->nt1.out.domain, p, -1, STR_TERMINATE); + } + } + break; + + case RAW_SESSSETUP_SPNEGO: + SMBCLI_CHECK_WCT(req, 4); + ZERO_STRUCT(parms->spnego.out); + parms->spnego.out.vuid = SVAL(req->in.hdr, HDR_UID); + parms->spnego.out.action = SVAL(req->in.vwv, VWV(2)); + len = SVAL(req->in.vwv, VWV(3)); + p = req->in.data; + if (!p) { + break; + } + + parms->spnego.out.secblob = smbcli_req_pull_blob(&req->in.bufinfo, mem_ctx, p, len); + p += parms->spnego.out.secblob.length; + p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->spnego.out.os, p, -1, STR_TERMINATE); + p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->spnego.out.lanman, p, -1, STR_TERMINATE); + p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->spnego.out.workgroup, p, -1, STR_TERMINATE); + break; + + case RAW_SESSSETUP_SMB2: + req->status = NT_STATUS_INTERNAL_ERROR; + break; + } + +failed: + return smbcli_request_destroy(req); +} + + +/* + Perform a session setup (sync interface) +*/ +NTSTATUS smb_raw_sesssetup(struct smbcli_session *session, + TALLOC_CTX *mem_ctx, union smb_sesssetup *parms) +{ + struct smbcli_request *req = smb_raw_sesssetup_send(session, parms); + return smb_raw_sesssetup_recv(req, mem_ctx, parms); +} + + +/**************************************************************************** + Send a ulogoff (async send) +*****************************************************************************/ +struct smbcli_request *smb_raw_ulogoff_send(struct smbcli_session *session) +{ + struct smbcli_request *req; + + SETUP_REQUEST_SESSION(SMBulogoffX, 2, 0); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + + if (!smbcli_request_send(req)) { + smbcli_request_destroy(req); + return NULL; + } + + return req; +} + +/**************************************************************************** + Send a ulogoff (sync interface) +*****************************************************************************/ +NTSTATUS smb_raw_ulogoff(struct smbcli_session *session) +{ + struct smbcli_request *req = smb_raw_ulogoff_send(session); + return smbcli_request_simple_recv(req); +} + + +/**************************************************************************** + Send a exit (async send) +*****************************************************************************/ +struct smbcli_request *smb_raw_exit_send(struct smbcli_session *session) +{ + struct smbcli_request *req; + + SETUP_REQUEST_SESSION(SMBexit, 0, 0); + + if (!smbcli_request_send(req)) { + smbcli_request_destroy(req); + return NULL; + } + + return req; +} + +/**************************************************************************** + Send a exit (sync interface) +*****************************************************************************/ +_PUBLIC_ NTSTATUS smb_raw_exit(struct smbcli_session *session) +{ + struct smbcli_request *req = smb_raw_exit_send(session); + return smbcli_request_simple_recv(req); +} diff --git a/source4/libcli/raw/clisocket.c b/source4/libcli/raw/clisocket.c new file mode 100644 index 0000000000..b9e83218dd --- /dev/null +++ b/source4/libcli/raw/clisocket.c @@ -0,0 +1,249 @@ +/* + Unix SMB/CIFS implementation. + + SMB client socket context management functions + + Copyright (C) Andrew Tridgell 1994-2005 + Copyright (C) James Myers 2003 <myersjj@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/>. +*/ + +#include "includes.h" +#include "lib/events/events.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/composite/composite.h" +#include "lib/socket/socket.h" +#include "libcli/resolve/resolve.h" +#include "param/param.h" +#include "libcli/raw/raw_proto.h" + +struct sock_connect_state { + struct composite_context *ctx; + const char *host_name; + int num_ports; + uint16_t *ports; + const char *socket_options; + struct smbcli_socket *result; +}; + +/* + connect a smbcli_socket context to an IP/port pair + if port is 0 then choose 445 then 139 +*/ + +static void smbcli_sock_connect_recv_conn(struct composite_context *ctx); + +struct composite_context *smbcli_sock_connect_send(TALLOC_CTX *mem_ctx, + const char *host_addr, + const char **ports, + const char *host_name, + struct resolve_context *resolve_ctx, + struct tevent_context *event_ctx, + const char *socket_options) +{ + struct composite_context *result, *ctx; + struct sock_connect_state *state; + int i; + + result = talloc_zero(mem_ctx, struct composite_context); + if (result == NULL) goto failed; + result->state = COMPOSITE_STATE_IN_PROGRESS; + + result->event_ctx = talloc_reference(result, event_ctx); + if (result->event_ctx == NULL) goto failed; + + state = talloc(result, struct sock_connect_state); + if (state == NULL) goto failed; + state->ctx = result; + result->private_data = state; + + state->host_name = talloc_strdup(state, host_name); + if (state->host_name == NULL) goto failed; + + state->num_ports = str_list_length(ports); + state->ports = talloc_array(state, uint16_t, state->num_ports); + if (state->ports == NULL) goto failed; + for (i=0;ports[i];i++) { + state->ports[i] = atoi(ports[i]); + } + state->socket_options = talloc_reference(state, socket_options); + + ctx = socket_connect_multi_send(state, host_addr, + state->num_ports, state->ports, + resolve_ctx, + state->ctx->event_ctx); + if (ctx == NULL) goto failed; + ctx->async.fn = smbcli_sock_connect_recv_conn; + ctx->async.private_data = state; + return result; + +failed: + talloc_free(result); + return NULL; +} + +static void smbcli_sock_connect_recv_conn(struct composite_context *ctx) +{ + struct sock_connect_state *state = + talloc_get_type(ctx->async.private_data, + struct sock_connect_state); + struct socket_context *sock; + uint16_t port; + + state->ctx->status = socket_connect_multi_recv(ctx, state, &sock, + &port); + if (!composite_is_ok(state->ctx)) return; + + state->ctx->status = + socket_set_option(sock, state->socket_options, NULL); + if (!composite_is_ok(state->ctx)) return; + + + state->result = talloc_zero(state, struct smbcli_socket); + if (composite_nomem(state->result, state->ctx)) return; + + state->result->sock = talloc_steal(state->result, sock); + state->result->port = port; + state->result->hostname = talloc_steal(sock, state->host_name); + + state->result->event.ctx = + talloc_reference(state->result, state->ctx->event_ctx); + if (composite_nomem(state->result->event.ctx, state->ctx)) return; + + composite_done(state->ctx); +} + +/* + finish a smbcli_sock_connect_send() operation +*/ +NTSTATUS smbcli_sock_connect_recv(struct composite_context *c, + TALLOC_CTX *mem_ctx, + struct smbcli_socket **result) +{ + NTSTATUS status = composite_wait(c); + if (NT_STATUS_IS_OK(status)) { + struct sock_connect_state *state = + talloc_get_type(c->private_data, + struct sock_connect_state); + *result = talloc_steal(mem_ctx, state->result); + } + talloc_free(c); + return status; +} + +/* + connect a smbcli_socket context to an IP/port pair + if port is 0 then choose the ports listed in smb.conf (normally 445 then 139) + + sync version of the function +*/ +NTSTATUS smbcli_sock_connect(TALLOC_CTX *mem_ctx, + const char *host_addr, const char **ports, + const char *host_name, + struct resolve_context *resolve_ctx, + struct tevent_context *event_ctx, + const char *socket_options, + struct smbcli_socket **result) +{ + struct composite_context *c = + smbcli_sock_connect_send(mem_ctx, host_addr, ports, host_name, + resolve_ctx, + event_ctx, socket_options); + return smbcli_sock_connect_recv(c, mem_ctx, result); +} + + +/**************************************************************************** + mark the socket as dead +****************************************************************************/ +_PUBLIC_ void smbcli_sock_dead(struct smbcli_socket *sock) +{ + talloc_free(sock->event.fde); + sock->event.fde = NULL; + talloc_free(sock->sock); + sock->sock = NULL; +} + +/**************************************************************************** + Set socket options on a open connection. +****************************************************************************/ +void smbcli_sock_set_options(struct smbcli_socket *sock, const char *options) +{ + socket_set_option(sock->sock, options, NULL); +} + +/**************************************************************************** +resolve a hostname and connect +****************************************************************************/ +_PUBLIC_ struct smbcli_socket *smbcli_sock_connect_byname(const char *host, const char **ports, + TALLOC_CTX *mem_ctx, + struct resolve_context *resolve_ctx, + struct tevent_context *event_ctx, + const char *socket_options) +{ + int name_type = NBT_NAME_SERVER; + const char *address; + NTSTATUS status; + struct nbt_name nbt_name; + char *name, *p; + TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); + struct smbcli_socket *result; + + if (event_ctx == NULL) { + DEBUG(0, ("Invalid NULL event context passed in as parameter\n")); + return NULL; + } + + if (tmp_ctx == NULL) { + DEBUG(0, ("talloc_new failed\n")); + return NULL; + } + + name = talloc_strdup(tmp_ctx, host); + if (name == NULL) { + DEBUG(0, ("talloc_strdup failed\n")); + talloc_free(tmp_ctx); + return NULL; + } + + /* allow hostnames of the form NAME#xx and do a netbios lookup */ + if ((p = strchr(name, '#'))) { + name_type = strtol(p+1, NULL, 16); + *p = 0; + } + + make_nbt_name(&nbt_name, host, name_type); + + status = resolve_name(resolve_ctx, &nbt_name, tmp_ctx, &address, event_ctx); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(tmp_ctx); + return NULL; + } + + status = smbcli_sock_connect(mem_ctx, address, ports, name, resolve_ctx, + event_ctx, + socket_options, &result); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(9, ("smbcli_sock_connect failed: %s\n", + nt_errstr(status))); + talloc_free(tmp_ctx); + return NULL; + } + + talloc_free(tmp_ctx); + + return result; +} diff --git a/source4/libcli/raw/clitransport.c b/source4/libcli/raw/clitransport.c new file mode 100644 index 0000000000..5cf0272e88 --- /dev/null +++ b/source4/libcli/raw/clitransport.c @@ -0,0 +1,680 @@ +/* + Unix SMB/CIFS implementation. + SMB client transport context management functions + + Copyright (C) Andrew Tridgell 1994-2005 + Copyright (C) James Myers 2003 <myersjj@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/>. +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "lib/socket/socket.h" +#include "../lib/util/dlinklist.h" +#include "lib/events/events.h" +#include "lib/stream/packet.h" +#include "librpc/gen_ndr/ndr_nbt.h" +#include "../libcli/nbt/libnbt.h" + + +/* + an event has happened on the socket +*/ +static void smbcli_transport_event_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, void *private_data) +{ + struct smbcli_transport *transport = talloc_get_type(private_data, + struct smbcli_transport); + if (flags & EVENT_FD_READ) { + packet_recv(transport->packet); + return; + } + if (flags & EVENT_FD_WRITE) { + packet_queue_run(transport->packet); + } +} + +/* + destroy a transport + */ +static int transport_destructor(struct smbcli_transport *transport) +{ + smbcli_transport_dead(transport, NT_STATUS_LOCAL_DISCONNECT); + return 0; +} + + +/* + handle receive errors +*/ +static void smbcli_transport_error(void *private_data, NTSTATUS status) +{ + struct smbcli_transport *transport = talloc_get_type(private_data, struct smbcli_transport); + smbcli_transport_dead(transport, status); +} + +static NTSTATUS smbcli_transport_finish_recv(void *private_data, DATA_BLOB blob); + +/* + create a transport structure based on an established socket +*/ +struct smbcli_transport *smbcli_transport_init(struct smbcli_socket *sock, + TALLOC_CTX *parent_ctx, + bool primary, + struct smbcli_options *options, + struct smb_iconv_convenience *iconv_convenience) +{ + struct smbcli_transport *transport; + + transport = talloc_zero(parent_ctx, struct smbcli_transport); + if (!transport) return NULL; + + if (primary) { + transport->socket = talloc_steal(transport, sock); + } else { + transport->socket = talloc_reference(transport, sock); + } + transport->negotiate.protocol = PROTOCOL_NT1; + transport->options = *options; + transport->negotiate.max_xmit = transport->options.max_xmit; + transport->iconv_convenience = iconv_convenience; + + /* setup the stream -> packet parser */ + transport->packet = packet_init(transport); + if (transport->packet == NULL) { + talloc_free(transport); + return NULL; + } + packet_set_private(transport->packet, transport); + packet_set_socket(transport->packet, transport->socket->sock); + packet_set_callback(transport->packet, smbcli_transport_finish_recv); + packet_set_full_request(transport->packet, packet_full_request_nbt); + packet_set_error_handler(transport->packet, smbcli_transport_error); + packet_set_event_context(transport->packet, transport->socket->event.ctx); + packet_set_nofree(transport->packet); + + smbcli_init_signing(transport); + + ZERO_STRUCT(transport->called); + + /* take over event handling from the socket layer - it only + handles events up until we are connected */ + talloc_free(transport->socket->event.fde); + transport->socket->event.fde = event_add_fd(transport->socket->event.ctx, + transport->socket->sock, + socket_get_fd(transport->socket->sock), + EVENT_FD_READ, + smbcli_transport_event_handler, + transport); + + packet_set_fde(transport->packet, transport->socket->event.fde); + packet_set_serialise(transport->packet); + talloc_set_destructor(transport, transport_destructor); + + return transport; +} + +/* + mark the transport as dead +*/ +void smbcli_transport_dead(struct smbcli_transport *transport, NTSTATUS status) +{ + smbcli_sock_dead(transport->socket); + + if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) { + status = NT_STATUS_UNEXPECTED_NETWORK_ERROR; + } + + /* kill only the first pending receive - this is so that if + that async function frees the connection we don't die trying + to use old memory. The caller has to cope with only one + network error */ + if (transport->pending_recv) { + struct smbcli_request *req = transport->pending_recv; + req->state = SMBCLI_REQUEST_ERROR; + req->status = status; + DLIST_REMOVE(transport->pending_recv, req); + if (req->async.fn) { + req->async.fn(req); + } + } +} + + +/* + send a session request +*/ +struct smbcli_request *smbcli_transport_connect_send(struct smbcli_transport *transport, + struct nbt_name *calling, + struct nbt_name *called) +{ + uint8_t *p; + struct smbcli_request *req; + DATA_BLOB calling_blob, called_blob; + TALLOC_CTX *tmp_ctx = talloc_new(transport); + NTSTATUS status; + + status = nbt_name_dup(transport, called, &transport->called); + if (!NT_STATUS_IS_OK(status)) goto failed; + + status = nbt_name_to_blob(tmp_ctx, transport->iconv_convenience, &calling_blob, calling); + if (!NT_STATUS_IS_OK(status)) goto failed; + + status = nbt_name_to_blob(tmp_ctx, transport->iconv_convenience, &called_blob, called); + if (!NT_STATUS_IS_OK(status)) goto failed; + + /* allocate output buffer */ + req = smbcli_request_setup_nonsmb(transport, + NBT_HDR_SIZE + + calling_blob.length + called_blob.length); + if (req == NULL) goto failed; + + /* put in the destination name */ + p = req->out.buffer + NBT_HDR_SIZE; + memcpy(p, called_blob.data, called_blob.length); + p += called_blob.length; + + memcpy(p, calling_blob.data, calling_blob.length); + p += calling_blob.length; + + _smb_setlen(req->out.buffer, PTR_DIFF(p, req->out.buffer) - NBT_HDR_SIZE); + SCVAL(req->out.buffer,0,0x81); + + if (!smbcli_request_send(req)) { + smbcli_request_destroy(req); + goto failed; + } + + talloc_free(tmp_ctx); + return req; + +failed: + talloc_free(tmp_ctx); + return NULL; +} + +/* + map a session request error to a NTSTATUS + */ +static NTSTATUS map_session_refused_error(uint8_t error) +{ + switch (error) { + case 0x80: + case 0x81: + return NT_STATUS_REMOTE_NOT_LISTENING; + case 0x82: + return NT_STATUS_RESOURCE_NAME_NOT_FOUND; + case 0x83: + return NT_STATUS_REMOTE_RESOURCES; + } + return NT_STATUS_UNEXPECTED_IO_ERROR; +} + + +/* + finish a smbcli_transport_connect() +*/ +NTSTATUS smbcli_transport_connect_recv(struct smbcli_request *req) +{ + NTSTATUS status; + + if (!smbcli_request_receive(req)) { + smbcli_request_destroy(req); + return NT_STATUS_UNEXPECTED_NETWORK_ERROR; + } + + switch (CVAL(req->in.buffer,0)) { + case 0x82: + status = NT_STATUS_OK; + break; + case 0x83: + status = map_session_refused_error(CVAL(req->in.buffer,4)); + break; + case 0x84: + DEBUG(1,("Warning: session retarget not supported\n")); + status = NT_STATUS_NOT_SUPPORTED; + break; + default: + status = NT_STATUS_UNEXPECTED_IO_ERROR; + break; + } + + smbcli_request_destroy(req); + return status; +} + + +/* + send a session request (if needed) +*/ +bool smbcli_transport_connect(struct smbcli_transport *transport, + struct nbt_name *calling, + struct nbt_name *called) +{ + struct smbcli_request *req; + NTSTATUS status; + + if (transport->socket->port == 445) { + return true; + } + + req = smbcli_transport_connect_send(transport, + calling, called); + status = smbcli_transport_connect_recv(req); + return NT_STATUS_IS_OK(status); +} + +/**************************************************************************** +get next mid in sequence +****************************************************************************/ +uint16_t smbcli_transport_next_mid(struct smbcli_transport *transport) +{ + uint16_t mid; + struct smbcli_request *req; + + mid = transport->next_mid; + +again: + /* now check to see if this mid is being used by one of the + pending requests. This is quite efficient because the list is + usually very short */ + + /* the zero mid is reserved for requests that don't have a mid */ + if (mid == 0) mid = 1; + + for (req=transport->pending_recv; req; req=req->next) { + if (req->mid == mid) { + mid++; + goto again; + } + } + + transport->next_mid = mid+1; + return mid; +} + +static void idle_handler(struct tevent_context *ev, + struct tevent_timer *te, struct timeval t, void *private_data) +{ + struct smbcli_transport *transport = talloc_get_type(private_data, + struct smbcli_transport); + struct timeval next = timeval_add(&t, 0, transport->idle.period); + transport->socket->event.te = event_add_timed(transport->socket->event.ctx, + transport, + next, + idle_handler, transport); + transport->idle.func(transport, transport->idle.private_data); +} + +/* + setup the idle handler for a transport + the period is in microseconds +*/ +_PUBLIC_ void smbcli_transport_idle_handler(struct smbcli_transport *transport, + void (*idle_func)(struct smbcli_transport *, void *), + uint64_t period, + void *private_data) +{ + transport->idle.func = idle_func; + transport->idle.private_data = private_data; + transport->idle.period = period; + + if (transport->socket->event.te != NULL) { + talloc_free(transport->socket->event.te); + } + + transport->socket->event.te = event_add_timed(transport->socket->event.ctx, + transport, + timeval_current_ofs(0, period), + idle_handler, transport); +} + +/* + we have a full request in our receive buffer - match it to a pending request + and process + */ +static NTSTATUS smbcli_transport_finish_recv(void *private_data, DATA_BLOB blob) +{ + struct smbcli_transport *transport = talloc_get_type(private_data, + struct smbcli_transport); + uint8_t *buffer, *hdr, *vwv; + int len; + uint16_t wct=0, mid = 0, op = 0; + struct smbcli_request *req = NULL; + + buffer = blob.data; + len = blob.length; + + hdr = buffer+NBT_HDR_SIZE; + vwv = hdr + HDR_VWV; + + /* see if it could be an oplock break request */ + if (smbcli_handle_oplock_break(transport, len, hdr, vwv)) { + talloc_free(buffer); + return NT_STATUS_OK; + } + + /* at this point we need to check for a readbraw reply, as + these can be any length */ + if (transport->readbraw_pending) { + transport->readbraw_pending = 0; + + /* it must match the first entry in the pending queue + as the client is not allowed to have outstanding + readbraw requests */ + req = transport->pending_recv; + if (!req) goto error; + + req->in.buffer = buffer; + talloc_steal(req, buffer); + req->in.size = len; + req->in.allocated = req->in.size; + goto async; + } + + if (len >= MIN_SMB_SIZE) { + /* extract the mid for matching to pending requests */ + mid = SVAL(hdr, HDR_MID); + wct = CVAL(hdr, HDR_WCT); + op = CVAL(hdr, HDR_COM); + } + + /* match the incoming request against the list of pending requests */ + for (req=transport->pending_recv; req; req=req->next) { + if (req->mid == mid) break; + } + + /* see if it's a ntcancel reply for the current MID */ + req = smbcli_handle_ntcancel_reply(req, len, hdr); + + if (!req) { + DEBUG(1,("Discarding unmatched reply with mid %d op %d\n", mid, op)); + goto error; + } + + /* fill in the 'in' portion of the matching request */ + req->in.buffer = buffer; + talloc_steal(req, buffer); + req->in.size = len; + req->in.allocated = req->in.size; + + /* handle NBT session replies */ + if (req->in.size >= 4 && req->in.buffer[0] != 0) { + req->status = NT_STATUS_OK; + goto async; + } + + /* handle non-SMB replies */ + if (req->in.size < NBT_HDR_SIZE + MIN_SMB_SIZE) { + req->state = SMBCLI_REQUEST_ERROR; + goto error; + } + + if (req->in.size < NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct)) { + DEBUG(2,("bad reply size for mid %d\n", mid)); + req->status = NT_STATUS_UNSUCCESSFUL; + req->state = SMBCLI_REQUEST_ERROR; + goto error; + } + + req->in.hdr = hdr; + req->in.vwv = vwv; + req->in.wct = wct; + if (req->in.size >= NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct)) { + req->in.data = req->in.vwv + VWV(wct) + 2; + req->in.data_size = SVAL(req->in.vwv, VWV(wct)); + if (req->in.size < NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct) + req->in.data_size) { + DEBUG(3,("bad data size for mid %d\n", mid)); + /* blergh - w2k3 gives a bogus data size values in some + openX replies */ + req->in.data_size = req->in.size - (NBT_HDR_SIZE + MIN_SMB_SIZE + VWV(wct)); + } + } + req->in.ptr = req->in.data; + req->flags2 = SVAL(req->in.hdr, HDR_FLG2); + + smb_setup_bufinfo(req); + + if (!(req->flags2 & FLAGS2_32_BIT_ERROR_CODES)) { + int eclass = CVAL(req->in.hdr,HDR_RCLS); + int code = SVAL(req->in.hdr,HDR_ERR); + if (eclass == 0 && code == 0) { + transport->error.e.nt_status = NT_STATUS_OK; + } else { + transport->error.e.nt_status = NT_STATUS_DOS(eclass, code); + } + } else { + transport->error.e.nt_status = NT_STATUS(IVAL(req->in.hdr, HDR_RCLS)); + } + + req->status = transport->error.e.nt_status; + if (NT_STATUS_IS_OK(req->status)) { + transport->error.etype = ETYPE_NONE; + } else { + transport->error.etype = ETYPE_SMB; + } + + if (!smbcli_request_check_sign_mac(req)) { + transport->error.etype = ETYPE_SOCKET; + transport->error.e.socket_error = SOCKET_READ_BAD_SIG; + req->state = SMBCLI_REQUEST_ERROR; + req->status = NT_STATUS_ACCESS_DENIED; + goto error; + }; + +async: + /* if this request has an async handler then call that to + notify that the reply has been received. This might destroy + the request so it must happen last */ + + req->state = SMBCLI_REQUEST_DONE; + + if (req->recv_helper.fn) { + /* + * let the recv helper decide in + * what state the request really is + */ + req->state = req->recv_helper.fn(req); + + /* if more parts are needed, wait for them */ + if (req->state <= SMBCLI_REQUEST_RECV) { + return NT_STATUS_OK; + } + } + DLIST_REMOVE(transport->pending_recv, req); + if (req->async.fn) { + req->async.fn(req); + } + return NT_STATUS_OK; + +error: + if (req) { + DLIST_REMOVE(transport->pending_recv, req); + req->state = SMBCLI_REQUEST_ERROR; + if (req->async.fn) { + req->async.fn(req); + } + } else { + talloc_free(buffer); + } + return NT_STATUS_OK; +} + +/* + process some read/write requests that are pending + return false if the socket is dead +*/ +_PUBLIC_ bool smbcli_transport_process(struct smbcli_transport *transport) +{ + NTSTATUS status; + size_t npending; + + packet_queue_run(transport->packet); + if (transport->socket->sock == NULL) { + return false; + } + + status = socket_pending(transport->socket->sock, &npending); + if (NT_STATUS_IS_OK(status) && npending > 0) { + packet_recv(transport->packet); + } + if (transport->socket->sock == NULL) { + return false; + } + return true; +} + +/* + handle timeouts of individual smb requests +*/ +static void smbcli_timeout_handler(struct tevent_context *ev, struct tevent_timer *te, + struct timeval t, void *private_data) +{ + struct smbcli_request *req = talloc_get_type(private_data, struct smbcli_request); + + if (req->state == SMBCLI_REQUEST_RECV) { + DLIST_REMOVE(req->transport->pending_recv, req); + } + req->status = NT_STATUS_IO_TIMEOUT; + req->state = SMBCLI_REQUEST_ERROR; + if (req->async.fn) { + req->async.fn(req); + } +} + + +/* + destroy a request +*/ +static int smbcli_request_destructor(struct smbcli_request *req) +{ + if (req->state == SMBCLI_REQUEST_RECV) { + DLIST_REMOVE(req->transport->pending_recv, req); + } + return 0; +} + + +/* + put a request into the send queue +*/ +void smbcli_transport_send(struct smbcli_request *req) +{ + DATA_BLOB blob; + NTSTATUS status; + + /* check if the transport is dead */ + if (req->transport->socket->sock == NULL) { + req->state = SMBCLI_REQUEST_ERROR; + req->status = NT_STATUS_NET_WRITE_FAULT; + return; + } + + blob = data_blob_const(req->out.buffer, req->out.size); + status = packet_send(req->transport->packet, blob); + if (!NT_STATUS_IS_OK(status)) { + req->state = SMBCLI_REQUEST_ERROR; + req->status = status; + return; + } + + if (req->one_way_request) { + req->state = SMBCLI_REQUEST_DONE; + smbcli_request_destroy(req); + return; + } + + req->state = SMBCLI_REQUEST_RECV; + DLIST_ADD(req->transport->pending_recv, req); + + /* add a timeout */ + if (req->transport->options.request_timeout) { + event_add_timed(req->transport->socket->event.ctx, req, + timeval_current_ofs(req->transport->options.request_timeout, 0), + smbcli_timeout_handler, req); + } + + talloc_set_destructor(req, smbcli_request_destructor); +} + + +/**************************************************************************** + Send an SMBecho (async send) +*****************************************************************************/ +_PUBLIC_ struct smbcli_request *smb_raw_echo_send(struct smbcli_transport *transport, + struct smb_echo *p) +{ + struct smbcli_request *req; + + req = smbcli_request_setup_transport(transport, SMBecho, 1, p->in.size); + if (!req) return NULL; + + SSVAL(req->out.vwv, VWV(0), p->in.repeat_count); + + memcpy(req->out.data, p->in.data, p->in.size); + + ZERO_STRUCT(p->out); + + if (!smbcli_request_send(req)) { + smbcli_request_destroy(req); + return NULL; + } + + return req; +} + +/**************************************************************************** + raw echo interface (async recv) +****************************************************************************/ +NTSTATUS smb_raw_echo_recv(struct smbcli_request *req, TALLOC_CTX *mem_ctx, + struct smb_echo *p) +{ + if (!smbcli_request_receive(req) || + smbcli_request_is_error(req)) { + goto failed; + } + + SMBCLI_CHECK_WCT(req, 1); + p->out.count++; + p->out.sequence_number = SVAL(req->in.vwv, VWV(0)); + p->out.size = req->in.data_size; + talloc_free(p->out.data); + p->out.data = talloc_array(mem_ctx, uint8_t, p->out.size); + NT_STATUS_HAVE_NO_MEMORY(p->out.data); + + if (!smbcli_raw_pull_data(&req->in.bufinfo, req->in.data, p->out.size, p->out.data)) { + req->status = NT_STATUS_BUFFER_TOO_SMALL; + } + + if (p->out.count == p->in.repeat_count) { + return smbcli_request_destroy(req); + } + + return NT_STATUS_OK; + +failed: + return smbcli_request_destroy(req); +} + +/**************************************************************************** + Send a echo (sync interface) +*****************************************************************************/ +NTSTATUS smb_raw_echo(struct smbcli_transport *transport, struct smb_echo *p) +{ + struct smbcli_request *req = smb_raw_echo_send(transport, p); + return smbcli_request_simple_recv(req); +} diff --git a/source4/libcli/raw/clitree.c b/source4/libcli/raw/clitree.c new file mode 100644 index 0000000000..a083396f35 --- /dev/null +++ b/source4/libcli/raw/clitree.c @@ -0,0 +1,216 @@ +/* + Unix SMB/CIFS implementation. + + SMB client tree context management functions + + Copyright (C) Andrew Tridgell 1994-2005 + Copyright (C) James Myers 2003 <myersjj@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/>. +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/smb_composite/smb_composite.h" + +#define SETUP_REQUEST_TREE(cmd, wct, buflen) do { \ + req = smbcli_request_setup(tree, cmd, wct, buflen); \ + if (!req) return NULL; \ +} while (0) + +/**************************************************************************** + Initialize the tree context +****************************************************************************/ +_PUBLIC_ struct smbcli_tree *smbcli_tree_init(struct smbcli_session *session, + TALLOC_CTX *parent_ctx, bool primary) +{ + struct smbcli_tree *tree; + + tree = talloc_zero(parent_ctx, struct smbcli_tree); + if (!tree) { + return NULL; + } + + if (primary) { + tree->session = talloc_steal(tree, session); + } else { + tree->session = talloc_reference(tree, session); + } + + + return tree; +} + +/**************************************************************************** + Send a tconX (async send) +****************************************************************************/ +struct smbcli_request *smb_raw_tcon_send(struct smbcli_tree *tree, + union smb_tcon *parms) +{ + struct smbcli_request *req = NULL; + + switch (parms->tcon.level) { + case RAW_TCON_TCON: + SETUP_REQUEST_TREE(SMBtcon, 0, 0); + smbcli_req_append_ascii4(req, parms->tcon.in.service, STR_ASCII); + smbcli_req_append_ascii4(req, parms->tcon.in.password,STR_ASCII); + smbcli_req_append_ascii4(req, parms->tcon.in.dev, STR_ASCII); + break; + + case RAW_TCON_TCONX: + SETUP_REQUEST_TREE(SMBtconX, 4, 0); + SSVAL(req->out.vwv, VWV(0), 0xFF); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), parms->tconx.in.flags); + SSVAL(req->out.vwv, VWV(3), parms->tconx.in.password.length); + smbcli_req_append_blob(req, &parms->tconx.in.password); + smbcli_req_append_string(req, parms->tconx.in.path, STR_TERMINATE | STR_UPPER); + smbcli_req_append_string(req, parms->tconx.in.device, STR_TERMINATE | STR_ASCII); + break; + + case RAW_TCON_SMB2: + return NULL; + } + + if (!smbcli_request_send(req)) { + smbcli_request_destroy(req); + return NULL; + } + + return req; +} + +/**************************************************************************** + Send a tconX (async recv) +****************************************************************************/ +NTSTATUS smb_raw_tcon_recv(struct smbcli_request *req, TALLOC_CTX *mem_ctx, + union smb_tcon *parms) +{ + uint8_t *p; + + if (!smbcli_request_receive(req) || + smbcli_request_is_error(req)) { + goto failed; + } + + switch (parms->tcon.level) { + case RAW_TCON_TCON: + SMBCLI_CHECK_WCT(req, 2); + parms->tcon.out.max_xmit = SVAL(req->in.vwv, VWV(0)); + parms->tcon.out.tid = SVAL(req->in.vwv, VWV(1)); + break; + + case RAW_TCON_TCONX: + ZERO_STRUCT(parms->tconx.out); + parms->tconx.out.tid = SVAL(req->in.hdr, HDR_TID); + if (req->in.wct >= 4) { + parms->tconx.out.options = SVAL(req->in.vwv, VWV(3)); + } + + /* output is actual service name */ + p = req->in.data; + if (!p) break; + + p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->tconx.out.dev_type, + p, -1, STR_ASCII | STR_TERMINATE); + p += smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->tconx.out.fs_type, + p, -1, STR_TERMINATE); + break; + + case RAW_TCON_SMB2: + req->status = NT_STATUS_INTERNAL_ERROR; + break; + } + +failed: + return smbcli_request_destroy(req); +} + +/**************************************************************************** + Send a tconX (sync interface) +****************************************************************************/ +_PUBLIC_ NTSTATUS smb_raw_tcon(struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, + union smb_tcon *parms) +{ + struct smbcli_request *req = smb_raw_tcon_send(tree, parms); + return smb_raw_tcon_recv(req, mem_ctx, parms); +} + + +/**************************************************************************** + Send a tree disconnect. +****************************************************************************/ +_PUBLIC_ NTSTATUS smb_tree_disconnect(struct smbcli_tree *tree) +{ + struct smbcli_request *req; + + if (!tree) return NT_STATUS_OK; + req = smbcli_request_setup(tree, SMBtdis, 0, 0); + + if (smbcli_request_send(req)) { + (void) smbcli_request_receive(req); + } + return smbcli_request_destroy(req); +} + + +/* + a convenient function to establish a smbcli_tree from scratch +*/ +NTSTATUS smbcli_tree_full_connection(TALLOC_CTX *parent_ctx, + struct smbcli_tree **ret_tree, + const char *dest_host, const char **dest_ports, + const char *service, const char *service_type, + const char *socket_options, + struct cli_credentials *credentials, + struct resolve_context *resolve_ctx, + struct tevent_context *ev, + struct smbcli_options *options, + struct smbcli_session_options *session_options, + struct smb_iconv_convenience *iconv_convenience, + struct gensec_settings *gensec_settings) +{ + struct smb_composite_connect io; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(parent_ctx); + if (!tmp_ctx) { + return NT_STATUS_NO_MEMORY; + } + + io.in.dest_host = dest_host; + io.in.dest_ports = dest_ports; + io.in.socket_options = socket_options; + io.in.called_name = strupper_talloc(tmp_ctx, dest_host); + io.in.service = service; + io.in.service_type = service_type; + io.in.credentials = credentials; + io.in.gensec_settings = gensec_settings; + io.in.fallback_to_anonymous = false; + + /* This workgroup gets sent out by the SPNEGO session setup. + * I don't know of any servers that look at it, so we + * hardcode it to "". */ + io.in.workgroup = ""; + io.in.options = *options; + io.in.session_options = *session_options; + io.in.iconv_convenience = iconv_convenience; + + status = smb_composite_connect(&io, parent_ctx, resolve_ctx, ev); + if (NT_STATUS_IS_OK(status)) { + *ret_tree = io.out.tree; + } + talloc_free(tmp_ctx); + return status; +} diff --git a/source4/libcli/raw/interfaces.h b/source4/libcli/raw/interfaces.h new file mode 100644 index 0000000000..a0584c0aa4 --- /dev/null +++ b/source4/libcli/raw/interfaces.h @@ -0,0 +1,2766 @@ +/* + Unix SMB/CIFS implementation. + SMB request interface structures + Copyright (C) Andrew Tridgell 2003 + Copyright (C) James J Myers 2003 <myersjj@samba.org> + 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/>. +*/ + +#ifndef __LIBCLI_RAW_INTERFACES_H__ +#define __LIBCLI_RAW_INTERFACES_H__ + +#include "smb.h" +#include "librpc/gen_ndr/misc.h" /* for struct GUID */ + +/* this structure is just a wrapper for a string, the only reason we + bother with this is that it allows us to check the length provided + on the wire in testsuite test code to ensure that we are + terminating names in the same way that win2003 is. The *ONLY* time + you should ever look at the 'private_length' field in this + structure is inside compliance test code, in all other cases just + use the null terminated char* as the definitive definition of the + string + + also note that this structure is only used in packets where there + is an explicit length provided on the wire (hence the name). That + length is placed in 'private_length'. For packets where the length + is always determined by NULL or packet termination a normal char* + is used in the structure definition. + */ +struct smb_wire_string { + uint32_t private_length; + const char *s; +}; + +/* + * SMB2 uses a 16Byte handle, + * (we can maybe use struct GUID later) + */ +struct smb2_handle { + uint64_t data[2]; +}; + +struct ntvfs_handle; + +/* + * a generic container for file handles or file pathes + * for qfileinfo/setfileinfo and qpathinfo/setpathinfo +*/ +union smb_handle_or_path { + /* + * this is used for + * the qpathinfo and setpathinfo + * calls + */ + const char *path; + /* + * this is used as file handle in SMB + */ + uint16_t fnum; + + /* + * this is used as file handle in SMB2 + */ + struct smb2_handle handle; + + /* + * this is used as generic file handle for the NTVFS layer + */ + struct ntvfs_handle *ntvfs; +}; + +/* + a generic container for file handles +*/ +union smb_handle { + /* + * this is used as file handle in SMB + */ + uint16_t fnum; + + /* + * this is used as file handle in SMB2 + */ + struct smb2_handle handle; + + /* + * this is used as generic file handle for the NTVFS layer + */ + struct ntvfs_handle *ntvfs; +}; + +/* + this header defines the structures and unions used between the SMB + parser and the backends. +*/ + +/* struct used for SMBlseek call */ +union smb_seek { + struct { + struct { + union smb_handle file; + uint16_t mode; + int32_t offset; /* signed */ + } in; + struct { + int32_t offset; + } out; + } lseek, generic; +}; + +/* struct used in unlink() call */ +union smb_unlink { + struct { + struct { + const char *pattern; + uint16_t attrib; + } in; + } unlink; +}; + + +/* struct used in chkpath() call */ +union smb_chkpath { + struct { + struct { + const char *path; + } in; + } chkpath; +}; + +enum smb_mkdir_level {RAW_MKDIR_GENERIC, RAW_MKDIR_MKDIR, RAW_MKDIR_T2MKDIR}; + +/* union used in mkdir() call */ +union smb_mkdir { + /* generic level */ + struct { + enum smb_mkdir_level level; + } generic; + + struct { + enum smb_mkdir_level level; + struct { + const char *path; + } in; + } mkdir; + + struct { + enum smb_mkdir_level level; + struct { + const char *path; + uint_t num_eas; + struct ea_struct *eas; + } in; + } t2mkdir; +}; + +/* struct used in rmdir() call */ +struct smb_rmdir { + struct { + const char *path; + } in; +}; + +/* struct used in rename() call */ +enum smb_rename_level {RAW_RENAME_RENAME, RAW_RENAME_NTRENAME, RAW_RENAME_NTTRANS}; + +union smb_rename { + struct { + enum smb_rename_level level; + } generic; + + /* SMBrename interface */ + struct { + enum smb_rename_level level; + + struct { + const char *pattern1; + const char *pattern2; + uint16_t attrib; + } in; + } rename; + + + /* SMBntrename interface */ + struct { + enum smb_rename_level level; + + struct { + uint16_t attrib; + uint16_t flags; /* see RENAME_FLAG_* */ + uint32_t cluster_size; + const char *old_name; + const char *new_name; + } in; + } ntrename; + + /* NT TRANS rename interface */ + struct { + enum smb_rename_level level; + + struct { + union smb_handle file; + uint16_t flags;/* see RENAME_REPLACE_IF_EXISTS */ + const char *new_name; + } in; + } nttrans; +}; + +enum smb_tcon_level { + RAW_TCON_TCON, + RAW_TCON_TCONX, + RAW_TCON_SMB2 +}; + +/* union used in tree connect call */ +union smb_tcon { + /* generic interface */ + struct { + enum smb_tcon_level level; + } generic; + + /* SMBtcon interface */ + struct { + enum smb_tcon_level level; + + struct { + const char *service; + const char *password; + const char *dev; + } in; + struct { + uint16_t max_xmit; + uint16_t tid; + } out; + } tcon; + + /* SMBtconX interface */ + struct { + enum smb_tcon_level level; + + struct { + uint16_t flags; + DATA_BLOB password; + const char *path; + const char *device; + } in; + struct { + uint16_t options; + char *dev_type; + char *fs_type; + uint16_t tid; + } out; + } tconx; + + /* SMB2 TreeConnect */ + struct smb2_tree_connect { + enum smb_tcon_level level; + + struct { + /* static body buffer 8 (0x08) bytes */ + uint16_t reserved; + /* uint16_t path_ofs */ + /* uint16_t path_size */ + /* dynamic body */ + const char *path; /* as non-terminated UTF-16 on the wire */ + } in; + struct { + /* static body buffer 16 (0x10) bytes */ + /* uint16_t buffer_code; 0x10 */ + uint8_t share_type; + uint8_t reserved; + uint32_t flags; + uint32_t capabilities; + uint32_t access_mask; + + /* extracted from the SMB2 header */ + uint32_t tid; + } out; + } smb2; +}; + + +enum smb_sesssetup_level { + RAW_SESSSETUP_OLD, + RAW_SESSSETUP_NT1, + RAW_SESSSETUP_SPNEGO, + RAW_SESSSETUP_SMB2 +}; + +/* union used in session_setup call */ +union smb_sesssetup { + /* the pre-NT1 interface */ + struct { + enum smb_sesssetup_level level; + + struct { + uint16_t bufsize; + uint16_t mpx_max; + uint16_t vc_num; + uint32_t sesskey; + DATA_BLOB password; + const char *user; + const char *domain; + const char *os; + const char *lanman; + } in; + struct { + uint16_t action; + uint16_t vuid; + char *os; + char *lanman; + char *domain; + } out; + } old; + + /* the NT1 interface */ + struct { + enum smb_sesssetup_level level; + + struct { + uint16_t bufsize; + uint16_t mpx_max; + uint16_t vc_num; + uint32_t sesskey; + uint32_t capabilities; + DATA_BLOB password1; + DATA_BLOB password2; + const char *user; + const char *domain; + const char *os; + const char *lanman; + } in; + struct { + uint16_t action; + uint16_t vuid; + char *os; + char *lanman; + char *domain; + } out; + } nt1; + + + /* the SPNEGO interface */ + struct { + enum smb_sesssetup_level level; + + struct { + uint16_t bufsize; + uint16_t mpx_max; + uint16_t vc_num; + uint32_t sesskey; + uint32_t capabilities; + DATA_BLOB secblob; + const char *os; + const char *lanman; + const char *workgroup; + } in; + struct { + uint16_t action; + DATA_BLOB secblob; + char *os; + char *lanman; + char *workgroup; + uint16_t vuid; + } out; + } spnego; + + /* SMB2 SessionSetup */ + struct smb2_session_setup { + enum smb_sesssetup_level level; + + struct { + /* static body 24 (0x18) bytes */ + uint8_t vc_number; + uint8_t security_mode; + uint32_t capabilities; + uint32_t channel; + /* uint16_t secblob_ofs */ + /* uint16_t secblob_size */ + uint64_t previous_sessionid; + /* dynamic body */ + DATA_BLOB secblob; + } in; + struct { + /* body buffer 8 (0x08) bytes */ + uint16_t session_flags; + /* uint16_t secblob_ofs */ + /* uint16_t secblob_size */ + /* dynamic body */ + DATA_BLOB secblob; + + /* extracted from the SMB2 header */ + uint64_t uid; + } out; + } smb2; +}; + +/* Note that the specified enum values are identical to the actual info-levels used + * on the wire. + */ +enum smb_fileinfo_level { + RAW_FILEINFO_GENERIC = 0xF000, + RAW_FILEINFO_GETATTR, /* SMBgetatr */ + RAW_FILEINFO_GETATTRE, /* SMBgetattrE */ + RAW_FILEINFO_SEC_DESC, /* NT_TRANSACT_QUERY_SECURITY_DESC */ + RAW_FILEINFO_STANDARD = SMB_QFILEINFO_STANDARD, + RAW_FILEINFO_EA_SIZE = SMB_QFILEINFO_EA_SIZE, + RAW_FILEINFO_EA_LIST = SMB_QFILEINFO_EA_LIST, + RAW_FILEINFO_ALL_EAS = SMB_QFILEINFO_ALL_EAS, + RAW_FILEINFO_IS_NAME_VALID = SMB_QFILEINFO_IS_NAME_VALID, + RAW_FILEINFO_BASIC_INFO = SMB_QFILEINFO_BASIC_INFO, + RAW_FILEINFO_STANDARD_INFO = SMB_QFILEINFO_STANDARD_INFO, + RAW_FILEINFO_EA_INFO = SMB_QFILEINFO_EA_INFO, + RAW_FILEINFO_NAME_INFO = SMB_QFILEINFO_NAME_INFO, + RAW_FILEINFO_ALL_INFO = SMB_QFILEINFO_ALL_INFO, + RAW_FILEINFO_ALT_NAME_INFO = SMB_QFILEINFO_ALT_NAME_INFO, + RAW_FILEINFO_STREAM_INFO = SMB_QFILEINFO_STREAM_INFO, + RAW_FILEINFO_COMPRESSION_INFO = SMB_QFILEINFO_COMPRESSION_INFO, + RAW_FILEINFO_UNIX_BASIC = SMB_QFILEINFO_UNIX_BASIC, + RAW_FILEINFO_UNIX_INFO2 = SMB_QFILEINFO_UNIX_INFO2, + RAW_FILEINFO_UNIX_LINK = SMB_QFILEINFO_UNIX_LINK, + RAW_FILEINFO_BASIC_INFORMATION = SMB_QFILEINFO_BASIC_INFORMATION, + RAW_FILEINFO_STANDARD_INFORMATION = SMB_QFILEINFO_STANDARD_INFORMATION, + RAW_FILEINFO_INTERNAL_INFORMATION = SMB_QFILEINFO_INTERNAL_INFORMATION, + RAW_FILEINFO_EA_INFORMATION = SMB_QFILEINFO_EA_INFORMATION, + RAW_FILEINFO_ACCESS_INFORMATION = SMB_QFILEINFO_ACCESS_INFORMATION, + RAW_FILEINFO_NAME_INFORMATION = SMB_QFILEINFO_NAME_INFORMATION, + RAW_FILEINFO_POSITION_INFORMATION = SMB_QFILEINFO_POSITION_INFORMATION, + RAW_FILEINFO_MODE_INFORMATION = SMB_QFILEINFO_MODE_INFORMATION, + RAW_FILEINFO_ALIGNMENT_INFORMATION = SMB_QFILEINFO_ALIGNMENT_INFORMATION, + RAW_FILEINFO_ALL_INFORMATION = SMB_QFILEINFO_ALL_INFORMATION, + RAW_FILEINFO_ALT_NAME_INFORMATION = SMB_QFILEINFO_ALT_NAME_INFORMATION, + RAW_FILEINFO_STREAM_INFORMATION = SMB_QFILEINFO_STREAM_INFORMATION, + RAW_FILEINFO_COMPRESSION_INFORMATION = SMB_QFILEINFO_COMPRESSION_INFORMATION, + RAW_FILEINFO_NETWORK_OPEN_INFORMATION = SMB_QFILEINFO_NETWORK_OPEN_INFORMATION, + RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION = SMB_QFILEINFO_ATTRIBUTE_TAG_INFORMATION, + /* SMB2 specific levels */ + RAW_FILEINFO_SMB2_ALL_EAS = 0x0f01, + RAW_FILEINFO_SMB2_ALL_INFORMATION = 0x1201 +}; + +/* union used in qfileinfo() and qpathinfo() backend calls */ +union smb_fileinfo { + /* generic interface: + * matches RAW_FILEINFO_GENERIC */ + struct { + enum smb_fileinfo_level level; + struct { + union smb_handle_or_path file; + } in; + struct { + uint32_t attrib; + uint32_t ea_size; + uint_t num_eas; + struct ea_struct { + uint8_t flags; + struct smb_wire_string name; + DATA_BLOB value; + } *eas; + NTTIME create_time; + NTTIME access_time; + NTTIME write_time; + NTTIME change_time; + uint64_t alloc_size; + uint64_t size; + uint32_t nlink; + struct smb_wire_string fname; + struct smb_wire_string alt_fname; + uint8_t delete_pending; + uint8_t directory; + uint64_t compressed_size; + uint16_t format; + uint8_t unit_shift; + uint8_t chunk_shift; + uint8_t cluster_shift; + uint64_t file_id; + uint32_t access_flags; /* seen 0x001f01ff from w2k3 */ + uint64_t position; + uint32_t mode; + uint32_t alignment_requirement; + uint32_t reparse_tag; + uint_t num_streams; + struct stream_struct { + uint64_t size; + uint64_t alloc_size; + struct smb_wire_string stream_name; + } *streams; + } out; + } generic; + + + /* SMBgetatr interface: + * matches RAW_FILEINFO_GETATTR */ + struct { + enum smb_fileinfo_level level; + struct { + union smb_handle_or_path file; + } in; + struct { + uint16_t attrib; + uint32_t size; + time_t write_time; + } out; + } getattr; + + /* SMBgetattrE and RAW_FILEINFO_STANDARD interface */ + struct { + enum smb_fileinfo_level level; + struct { + union smb_handle_or_path file; + } in; + struct { + time_t create_time; + time_t access_time; + time_t write_time; + uint32_t size; + uint32_t alloc_size; + uint16_t attrib; + } out; + } getattre, standard; + + /* trans2 RAW_FILEINFO_EA_SIZE interface */ + struct { + enum smb_fileinfo_level level; + struct { + union smb_handle_or_path file; + } in; + struct { + time_t create_time; + time_t access_time; + time_t write_time; + uint32_t size; + uint32_t alloc_size; + uint16_t attrib; + uint32_t ea_size; + } out; + } ea_size; + + /* trans2 RAW_FILEINFO_EA_LIST interface */ + struct { + enum smb_fileinfo_level level; + struct { + union smb_handle_or_path file; + uint_t num_names; + struct ea_name { + struct smb_wire_string name; + } *ea_names; + } in; + + struct smb_ea_list { + uint_t num_eas; + struct ea_struct *eas; + } out; + } ea_list; + + /* trans2 RAW_FILEINFO_ALL_EAS and RAW_FILEINFO_FULL_EA_INFORMATION interfaces */ + struct { + enum smb_fileinfo_level level; + struct { + union smb_handle_or_path file; + /* SMB2 only - SMB2_CONTINUE_FLAG_* */ + uint8_t continue_flags; + } in; + struct smb_ea_list out; + } all_eas; + + /* trans2 qpathinfo RAW_FILEINFO_IS_NAME_VALID interface + only valid for a QPATHNAME call - no returned data */ + struct { + enum smb_fileinfo_level level; + struct { + union smb_handle_or_path file; + } in; + } is_name_valid; + + /* RAW_FILEINFO_BASIC_INFO and RAW_FILEINFO_BASIC_INFORMATION interfaces */ + struct { + enum smb_fileinfo_level level; + struct { + union smb_handle_or_path file; + } in; + struct { + NTTIME create_time; + NTTIME access_time; + NTTIME write_time; + NTTIME change_time; + uint32_t attrib; + } out; + } basic_info; + + + /* RAW_FILEINFO_STANDARD_INFO and RAW_FILEINFO_STANDARD_INFORMATION interfaces */ + struct { + enum smb_fileinfo_level level; + struct { + union smb_handle_or_path file; + } in; + struct { + uint64_t alloc_size; + uint64_t size; + uint32_t nlink; + bool delete_pending; + bool directory; + } out; + } standard_info; + + /* RAW_FILEINFO_EA_INFO and RAW_FILEINFO_EA_INFORMATION interfaces */ + struct { + enum smb_fileinfo_level level; + struct { + union smb_handle_or_path file; + } in; + struct { + uint32_t ea_size; + } out; + } ea_info; + + /* RAW_FILEINFO_NAME_INFO and RAW_FILEINFO_NAME_INFORMATION interfaces */ + struct { + enum smb_fileinfo_level level; + struct { + union smb_handle_or_path file; + } in; + struct { + struct smb_wire_string fname; + } out; + } name_info; + + /* RAW_FILEINFO_ALL_INFO and RAW_FILEINFO_ALL_INFORMATION interfaces */ + struct { + enum smb_fileinfo_level level; + struct { + union smb_handle_or_path file; + } in; + struct { + NTTIME create_time; + NTTIME access_time; + NTTIME write_time; + NTTIME change_time; + uint32_t attrib; + uint64_t alloc_size; + uint64_t size; + uint32_t nlink; + uint8_t delete_pending; + uint8_t directory; + uint32_t ea_size; + struct smb_wire_string fname; + } out; + } all_info; + + /* RAW_FILEINFO_SMB2_ALL_INFORMATION interface */ + struct { + enum smb_fileinfo_level level; + struct { + union smb_handle_or_path file; + } in; + struct { + NTTIME create_time; + NTTIME access_time; + NTTIME write_time; + NTTIME change_time; + uint32_t attrib; + uint32_t unknown1; + uint64_t alloc_size; + uint64_t size; + uint32_t nlink; + uint8_t delete_pending; + uint8_t directory; + /* uint16_t _pad; */ + uint64_t file_id; + uint32_t ea_size; + uint32_t access_mask; + uint64_t position; + uint32_t mode; + uint32_t alignment_requirement; + struct smb_wire_string fname; + } out; + } all_info2; + + /* RAW_FILEINFO_ALT_NAME_INFO and RAW_FILEINFO_ALT_NAME_INFORMATION interfaces */ + struct { + enum smb_fileinfo_level level; + struct { + union smb_handle_or_path file; + } in; + struct { + struct smb_wire_string fname; + } out; + } alt_name_info; + + /* RAW_FILEINFO_STREAM_INFO and RAW_FILEINFO_STREAM_INFORMATION interfaces */ + struct { + enum smb_fileinfo_level level; + struct { + union smb_handle_or_path file; + } in; + struct stream_information { + uint_t num_streams; + struct stream_struct *streams; + } out; + } stream_info; + + /* RAW_FILEINFO_COMPRESSION_INFO and RAW_FILEINFO_COMPRESSION_INFORMATION interfaces */ + struct { + enum smb_fileinfo_level level; + struct { + union smb_handle_or_path file; + } in; + struct { + uint64_t compressed_size; + uint16_t format; + uint8_t unit_shift; + uint8_t chunk_shift; + uint8_t cluster_shift; + } out; + } compression_info; + + /* RAW_FILEINFO_UNIX_BASIC interface */ + struct { + enum smb_fileinfo_level level; + struct { + union smb_handle_or_path file; + } in; + struct { + uint64_t end_of_file; + uint64_t num_bytes; + NTTIME status_change_time; + NTTIME access_time; + NTTIME change_time; + uint64_t uid; + uint64_t gid; + uint32_t file_type; + uint64_t dev_major; + uint64_t dev_minor; + uint64_t unique_id; + uint64_t permissions; + uint64_t nlink; + } out; + } unix_basic_info; + + /* RAW_FILEINFO_UNIX_INFO2 interface */ + struct { + enum smb_fileinfo_level level; + struct { + union smb_handle_or_path file; + } in; + struct { + uint64_t end_of_file; + uint64_t num_bytes; + NTTIME status_change_time; + NTTIME access_time; + NTTIME change_time; + uint64_t uid; + uint64_t gid; + uint32_t file_type; + uint64_t dev_major; + uint64_t dev_minor; + uint64_t unique_id; + uint64_t permissions; + uint64_t nlink; + NTTIME create_time; + uint32_t file_flags; + uint32_t flags_mask; + } out; + } unix_info2; + + /* RAW_FILEINFO_UNIX_LINK interface */ + struct { + enum smb_fileinfo_level level; + struct { + union smb_handle_or_path file; + } in; + struct { + struct smb_wire_string link_dest; + } out; + } unix_link_info; + + /* RAW_FILEINFO_INTERNAL_INFORMATION interface */ + struct { + enum smb_fileinfo_level level; + struct { + union smb_handle_or_path file; + } in; + struct { + uint64_t file_id; + } out; + } internal_information; + + /* RAW_FILEINFO_ACCESS_INFORMATION interface */ + struct { + enum smb_fileinfo_level level; + struct { + union smb_handle_or_path file; + } in; + struct { + uint32_t access_flags; + } out; + } access_information; + + /* RAW_FILEINFO_POSITION_INFORMATION interface */ + struct { + enum smb_fileinfo_level level; + struct { + union smb_handle_or_path file; + } in; + struct { + uint64_t position; + } out; + } position_information; + + /* RAW_FILEINFO_MODE_INFORMATION interface */ + struct { + enum smb_fileinfo_level level; + struct { + union smb_handle_or_path file; + } in; + struct { + uint32_t mode; + } out; + } mode_information; + + /* RAW_FILEINFO_ALIGNMENT_INFORMATION interface */ + struct { + enum smb_fileinfo_level level; + struct { + union smb_handle_or_path file; + } in; + struct { + uint32_t alignment_requirement; + } out; + } alignment_information; + + /* RAW_FILEINFO_NETWORK_OPEN_INFORMATION interface */ + struct { + enum smb_fileinfo_level level; + struct { + union smb_handle_or_path file; + } in; + struct { + NTTIME create_time; + NTTIME access_time; + NTTIME write_time; + NTTIME change_time; + uint64_t alloc_size; + uint64_t size; + uint32_t attrib; + } out; + } network_open_information; + + + /* RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION interface */ + struct { + enum smb_fileinfo_level level; + struct { + union smb_handle_or_path file; + } in; + struct { + uint32_t attrib; + uint32_t reparse_tag; + } out; + } attribute_tag_information; + + /* RAW_FILEINFO_SEC_DESC */ + struct { + enum smb_fileinfo_level level; + struct { + union smb_handle_or_path file; + uint32_t secinfo_flags; + } in; + struct { + struct security_descriptor *sd; + } out; + } query_secdesc; +}; + + +enum smb_setfileinfo_level { + RAW_SFILEINFO_GENERIC = 0xF000, + RAW_SFILEINFO_SETATTR, /* SMBsetatr */ + RAW_SFILEINFO_SETATTRE, /* SMBsetattrE */ + RAW_SFILEINFO_SEC_DESC, /* NT_TRANSACT_SET_SECURITY_DESC */ + RAW_SFILEINFO_STANDARD = SMB_SFILEINFO_STANDARD, + RAW_SFILEINFO_EA_SET = SMB_SFILEINFO_EA_SET, + RAW_SFILEINFO_BASIC_INFO = SMB_SFILEINFO_BASIC_INFO, + RAW_SFILEINFO_DISPOSITION_INFO = SMB_SFILEINFO_DISPOSITION_INFO, + RAW_SFILEINFO_ALLOCATION_INFO = SMB_SFILEINFO_ALLOCATION_INFO, + RAW_SFILEINFO_END_OF_FILE_INFO = SMB_SFILEINFO_END_OF_FILE_INFO, + RAW_SFILEINFO_UNIX_BASIC = SMB_SFILEINFO_UNIX_BASIC, + RAW_SFILEINFO_UNIX_INFO2 = SMB_SFILEINFO_UNIX_INFO2, + RAW_SFILEINFO_UNIX_LINK = SMB_SFILEINFO_UNIX_LINK, + RAW_SFILEINFO_UNIX_HLINK = SMB_SFILEINFO_UNIX_HLINK, + RAW_SFILEINFO_BASIC_INFORMATION = SMB_SFILEINFO_BASIC_INFORMATION, + RAW_SFILEINFO_RENAME_INFORMATION = SMB_SFILEINFO_RENAME_INFORMATION, + RAW_SFILEINFO_LINK_INFORMATION = SMB_SFILEINFO_LINK_INFORMATION, + RAW_SFILEINFO_DISPOSITION_INFORMATION = SMB_SFILEINFO_DISPOSITION_INFORMATION, + RAW_SFILEINFO_POSITION_INFORMATION = SMB_SFILEINFO_POSITION_INFORMATION, + RAW_SFILEINFO_FULL_EA_INFORMATION = SMB_SFILEINFO_FULL_EA_INFORMATION, + RAW_SFILEINFO_MODE_INFORMATION = SMB_SFILEINFO_MODE_INFORMATION, + RAW_SFILEINFO_ALLOCATION_INFORMATION = SMB_SFILEINFO_ALLOCATION_INFORMATION, + RAW_SFILEINFO_END_OF_FILE_INFORMATION = SMB_SFILEINFO_END_OF_FILE_INFORMATION, + RAW_SFILEINFO_PIPE_INFORMATION = SMB_SFILEINFO_PIPE_INFORMATION, + RAW_SFILEINFO_VALID_DATA_INFORMATION = SMB_SFILEINFO_VALID_DATA_INFORMATION, + RAW_SFILEINFO_SHORT_NAME_INFORMATION = SMB_SFILEINFO_SHORT_NAME_INFORMATION, + RAW_SFILEINFO_1025 = SMB_SFILEINFO_1025, + RAW_SFILEINFO_1027 = SMB_SFILEINFO_1027, + RAW_SFILEINFO_1029 = SMB_SFILEINFO_1029, + RAW_SFILEINFO_1030 = SMB_SFILEINFO_1030, + RAW_SFILEINFO_1031 = SMB_SFILEINFO_1031, + RAW_SFILEINFO_1032 = SMB_SFILEINFO_1032, + RAW_SFILEINFO_1036 = SMB_SFILEINFO_1036, + RAW_SFILEINFO_1041 = SMB_SFILEINFO_1041, + RAW_SFILEINFO_1042 = SMB_SFILEINFO_1042, + RAW_SFILEINFO_1043 = SMB_SFILEINFO_1043, + RAW_SFILEINFO_1044 = SMB_SFILEINFO_1044, + + /* cope with breakage in SMB2 */ + RAW_SFILEINFO_RENAME_INFORMATION_SMB2 = SMB_SFILEINFO_RENAME_INFORMATION|0x80000000, +}; + +/* union used in setfileinfo() and setpathinfo() calls */ +union smb_setfileinfo { + /* generic interface */ + struct { + enum smb_setfileinfo_level level; + struct { + union smb_handle_or_path file; + } in; + } generic; + + /* RAW_SFILEINFO_SETATTR (SMBsetatr) interface - only via setpathinfo() */ + struct { + enum smb_setfileinfo_level level; + struct { + union smb_handle_or_path file; + uint16_t attrib; + time_t write_time; + } in; + } setattr; + + /* RAW_SFILEINFO_SETATTRE (SMBsetattrE) interface - only via setfileinfo() + also RAW_SFILEINFO_STANDARD */ + struct { + enum smb_setfileinfo_level level; + struct { + union smb_handle_or_path file; + time_t create_time; + time_t access_time; + time_t write_time; + /* notice that size, alloc_size and attrib are not settable, + unlike the corresponding qfileinfo level */ + } in; + } setattre, standard; + + /* RAW_SFILEINFO_EA_SET interface */ + struct { + enum smb_setfileinfo_level level; + struct { + union smb_handle_or_path file; + uint_t num_eas; + struct ea_struct *eas; + } in; + } ea_set; + + /* RAW_SFILEINFO_BASIC_INFO and + RAW_SFILEINFO_BASIC_INFORMATION interfaces */ + struct { + enum smb_setfileinfo_level level; + struct { + union smb_handle_or_path file; + NTTIME create_time; + NTTIME access_time; + NTTIME write_time; + NTTIME change_time; + uint32_t attrib; + uint32_t reserved; + } in; + } basic_info; + + /* RAW_SFILEINFO_DISPOSITION_INFO and + RAW_SFILEINFO_DISPOSITION_INFORMATION interfaces */ + struct { + enum smb_setfileinfo_level level; + struct { + union smb_handle_or_path file; + bool delete_on_close; + } in; + } disposition_info; + + /* RAW_SFILEINFO_ALLOCATION_INFO and + RAW_SFILEINFO_ALLOCATION_INFORMATION interfaces */ + struct { + enum smb_setfileinfo_level level; + struct { + union smb_handle_or_path file; + /* w2k3 rounds this up to nearest 4096 */ + uint64_t alloc_size; + } in; + } allocation_info; + + /* RAW_SFILEINFO_END_OF_FILE_INFO and + RAW_SFILEINFO_END_OF_FILE_INFORMATION interfaces */ + struct { + enum smb_setfileinfo_level level; + struct { + union smb_handle_or_path file; + uint64_t size; + } in; + } end_of_file_info; + + /* RAW_SFILEINFO_RENAME_INFORMATION interface */ + struct { + enum smb_setfileinfo_level level; + struct { + union smb_handle_or_path file; + uint8_t overwrite; + uint64_t root_fid; + const char *new_name; + } in; + } rename_information; + + /* RAW_SFILEINFO_LINK_INFORMATION interface */ + struct { + enum smb_setfileinfo_level level; + struct { + union smb_handle_or_path file; + uint8_t overwrite; + uint64_t root_fid; + const char *new_name; + } in; + } link_information; + + /* RAW_SFILEINFO_POSITION_INFORMATION interface */ + struct { + enum smb_setfileinfo_level level; + struct { + union smb_handle_or_path file; + uint64_t position; + } in; + } position_information; + + /* RAW_SFILEINFO_MODE_INFORMATION interface */ + struct { + enum smb_setfileinfo_level level; + struct { + union smb_handle_or_path file; + /* valid values seem to be 0, 2, 4 and 6 */ + uint32_t mode; + } in; + } mode_information; + + /* RAW_SFILEINFO_UNIX_BASIC interface */ + struct { + enum smb_setfileinfo_level level; + struct { + union smb_handle_or_path file; + uint32_t mode; /* yuck - this field remains to fix compile of libcli/clifile.c */ + uint64_t end_of_file; + uint64_t num_bytes; + NTTIME status_change_time; + NTTIME access_time; + NTTIME change_time; + uint64_t uid; + uint64_t gid; + uint32_t file_type; + uint64_t dev_major; + uint64_t dev_minor; + uint64_t unique_id; + uint64_t permissions; + uint64_t nlink; + } in; + } unix_basic; + + /* RAW_SFILEINFO_UNIX_INFO2 interface */ + struct { + enum smb_setfileinfo_level level; + struct { + union smb_handle_or_path file; + uint64_t end_of_file; + uint64_t num_bytes; + NTTIME status_change_time; + NTTIME access_time; + NTTIME change_time; + uint64_t uid; + uint64_t gid; + uint32_t file_type; + uint64_t dev_major; + uint64_t dev_minor; + uint64_t unique_id; + uint64_t permissions; + uint64_t nlink; + NTTIME create_time; + uint32_t file_flags; + uint32_t flags_mask; + } in; + } unix_info2; + + /* RAW_SFILEINFO_UNIX_LINK, RAW_SFILEINFO_UNIX_HLINK interface */ + struct { + enum smb_setfileinfo_level level; + struct { + union smb_handle_or_path file; + const char *link_dest; + } in; + } unix_link, unix_hlink; + + /* RAW_FILEINFO_SET_SEC_DESC */ + struct { + enum smb_setfileinfo_level level; + struct { + union smb_handle_or_path file; + uint32_t secinfo_flags; + struct security_descriptor *sd; + } in; + } set_secdesc; + + /* RAW_SFILEINFO_FULL_EA_INFORMATION */ + struct { + enum smb_setfileinfo_level level; + struct { + union smb_handle_or_path file; + struct smb_ea_list eas; + } in; + } full_ea_information; +}; + + +enum smb_fsinfo_level { + RAW_QFS_GENERIC = 0xF000, + RAW_QFS_DSKATTR, /* SMBdskattr */ + RAW_QFS_ALLOCATION = SMB_QFS_ALLOCATION, + RAW_QFS_VOLUME = SMB_QFS_VOLUME, + RAW_QFS_VOLUME_INFO = SMB_QFS_VOLUME_INFO, + RAW_QFS_SIZE_INFO = SMB_QFS_SIZE_INFO, + RAW_QFS_DEVICE_INFO = SMB_QFS_DEVICE_INFO, + RAW_QFS_ATTRIBUTE_INFO = SMB_QFS_ATTRIBUTE_INFO, + RAW_QFS_UNIX_INFO = SMB_QFS_UNIX_INFO, + RAW_QFS_VOLUME_INFORMATION = SMB_QFS_VOLUME_INFORMATION, + RAW_QFS_SIZE_INFORMATION = SMB_QFS_SIZE_INFORMATION, + RAW_QFS_DEVICE_INFORMATION = SMB_QFS_DEVICE_INFORMATION, + RAW_QFS_ATTRIBUTE_INFORMATION = SMB_QFS_ATTRIBUTE_INFORMATION, + RAW_QFS_QUOTA_INFORMATION = SMB_QFS_QUOTA_INFORMATION, + RAW_QFS_FULL_SIZE_INFORMATION = SMB_QFS_FULL_SIZE_INFORMATION, + RAW_QFS_OBJECTID_INFORMATION = SMB_QFS_OBJECTID_INFORMATION}; + + +/* union for fsinfo() backend call. Note that there are no in + structures, as this call only contains out parameters */ +union smb_fsinfo { + /* generic interface */ + struct { + enum smb_fsinfo_level level; + struct smb2_handle handle; /* only for smb2 */ + + struct { + uint32_t block_size; + uint64_t blocks_total; + uint64_t blocks_free; + uint32_t fs_id; + NTTIME create_time; + uint32_t serial_number; + uint32_t fs_attr; + uint32_t max_file_component_length; + uint32_t device_type; + uint32_t device_characteristics; + uint64_t quota_soft; + uint64_t quota_hard; + uint64_t quota_flags; + struct GUID guid; + char *volume_name; + char *fs_type; + } out; + } generic; + + /* SMBdskattr interface */ + struct { + enum smb_fsinfo_level level; + + struct { + uint16_t units_total; + uint16_t blocks_per_unit; + uint16_t block_size; + uint16_t units_free; + } out; + } dskattr; + + /* trans2 RAW_QFS_ALLOCATION interface */ + struct { + enum smb_fsinfo_level level; + + struct { + uint32_t fs_id; + uint32_t sectors_per_unit; + uint32_t total_alloc_units; + uint32_t avail_alloc_units; + uint16_t bytes_per_sector; + } out; + } allocation; + + /* TRANS2 RAW_QFS_VOLUME interface */ + struct { + enum smb_fsinfo_level level; + + struct { + uint32_t serial_number; + struct smb_wire_string volume_name; + } out; + } volume; + + /* TRANS2 RAW_QFS_VOLUME_INFO and RAW_QFS_VOLUME_INFORMATION interfaces */ + struct { + enum smb_fsinfo_level level; + struct smb2_handle handle; /* only for smb2 */ + + struct { + NTTIME create_time; + uint32_t serial_number; + struct smb_wire_string volume_name; + } out; + } volume_info; + + /* trans2 RAW_QFS_SIZE_INFO and RAW_QFS_SIZE_INFORMATION interfaces */ + struct { + enum smb_fsinfo_level level; + struct smb2_handle handle; /* only for smb2 */ + + struct { + uint64_t total_alloc_units; + uint64_t avail_alloc_units; /* maps to call_avail_alloc_units */ + uint32_t sectors_per_unit; + uint32_t bytes_per_sector; + } out; + } size_info; + + /* TRANS2 RAW_QFS_DEVICE_INFO and RAW_QFS_DEVICE_INFORMATION interfaces */ + struct { + enum smb_fsinfo_level level; + struct smb2_handle handle; /* only for smb2 */ + + struct { + uint32_t device_type; + uint32_t characteristics; + } out; + } device_info; + + + /* TRANS2 RAW_QFS_ATTRIBUTE_INFO and RAW_QFS_ATTRIBUTE_INFORMATION interfaces */ + struct { + enum smb_fsinfo_level level; + struct smb2_handle handle; /* only for smb2 */ + + struct { + uint32_t fs_attr; + uint32_t max_file_component_length; + struct smb_wire_string fs_type; + } out; + } attribute_info; + + + /* TRANS2 RAW_QFS_UNIX_INFO interface */ + struct { + enum smb_fsinfo_level level; + + struct { + uint16_t major_version; + uint16_t minor_version; + uint64_t capability; + } out; + } unix_info; + + /* trans2 RAW_QFS_QUOTA_INFORMATION interface */ + struct { + enum smb_fsinfo_level level; + struct smb2_handle handle; /* only for smb2 */ + + struct { + uint64_t unknown[3]; + uint64_t quota_soft; + uint64_t quota_hard; + uint64_t quota_flags; + } out; + } quota_information; + + /* trans2 RAW_QFS_FULL_SIZE_INFORMATION interface */ + struct { + enum smb_fsinfo_level level; + struct smb2_handle handle; /* only for smb2 */ + + struct { + uint64_t total_alloc_units; + uint64_t call_avail_alloc_units; + uint64_t actual_avail_alloc_units; + uint32_t sectors_per_unit; + uint32_t bytes_per_sector; + } out; + } full_size_information; + + /* trans2 RAW_QFS_OBJECTID_INFORMATION interface */ + struct { + enum smb_fsinfo_level level; + struct smb2_handle handle; /* only for smb2 */ + + struct { + struct GUID guid; + uint64_t unknown[6]; + } out; + } objectid_information; +}; + + + +enum smb_open_level { + RAW_OPEN_OPEN, + RAW_OPEN_OPENX, + RAW_OPEN_MKNEW, + RAW_OPEN_CREATE, + RAW_OPEN_CTEMP, + RAW_OPEN_SPLOPEN, + RAW_OPEN_NTCREATEX, + RAW_OPEN_T2OPEN, + RAW_OPEN_NTTRANS_CREATE, + RAW_OPEN_OPENX_READX, + RAW_OPEN_SMB2 +}; + +/* the generic interface is defined to be equal to the NTCREATEX interface */ +#define RAW_OPEN_GENERIC RAW_OPEN_NTCREATEX + +/* union for open() backend call */ +union smb_open { +/* + * because the *.out.file structs are not aligned to the same offset for each level + * we provide a hepler macro that should be used to find the current smb_handle structure + */ +#define SMB_OPEN_OUT_FILE(op, file) do { \ + switch (op->generic.level) { \ + case RAW_OPEN_OPEN: \ + file = &op->openold.out.file; \ + break; \ + case RAW_OPEN_OPENX: \ + file = &op->openx.out.file; \ + break; \ + case RAW_OPEN_MKNEW: \ + file = &op->mknew.out.file; \ + break; \ + case RAW_OPEN_CREATE: \ + file = &op->create.out.file; \ + break; \ + case RAW_OPEN_CTEMP: \ + file = &op->ctemp.out.file; \ + break; \ + case RAW_OPEN_SPLOPEN: \ + file = &op->splopen.out.file; \ + break; \ + case RAW_OPEN_NTCREATEX: \ + file = &op->ntcreatex.out.file; \ + break; \ + case RAW_OPEN_T2OPEN: \ + file = &op->t2open.out.file; \ + break; \ + case RAW_OPEN_NTTRANS_CREATE: \ + file = &op->nttrans.out.file; \ + break; \ + case RAW_OPEN_OPENX_READX: \ + file = &op->openxreadx.out.file; \ + break; \ + case RAW_OPEN_SMB2: \ + file = &op->smb2.out.file; \ + break; \ + default: \ + /* this must be a programmer error */ \ + file = NULL; \ + break; \ + } \ +} while (0) + /* SMBNTCreateX, nttrans and generic interface */ + struct { + enum smb_open_level level; + struct { + uint32_t flags; + uint32_t root_fid; + uint32_t access_mask; + uint64_t alloc_size; + uint32_t file_attr; + uint32_t share_access; + uint32_t open_disposition; + uint32_t create_options; + uint32_t impersonation; + uint8_t security_flags; + /* NOTE: fname can also be a pointer to a + uint64_t file_id if create_options has the + NTCREATEX_OPTIONS_OPEN_BY_FILE_ID flag set */ + const char *fname; + + /* these last 2 elements are only used in the + NTTRANS varient of the call */ + struct security_descriptor *sec_desc; + struct smb_ea_list *ea_list; + + /* some optional parameters from the SMB2 varient */ + bool query_maximal_access; + } in; + struct { + union smb_handle file; + uint8_t oplock_level; + uint32_t create_action; + NTTIME create_time; + NTTIME access_time; + NTTIME write_time; + NTTIME change_time; + uint32_t attrib; + uint64_t alloc_size; + uint64_t size; + uint16_t file_type; + uint16_t ipc_state; + uint8_t is_directory; + + /* optional return values matching SMB2 tagged + values in the call */ + uint32_t maximal_access; + } out; + } ntcreatex, nttrans, generic; + + /* TRANS2_OPEN interface */ + struct { + enum smb_open_level level; + struct { + uint16_t flags; + uint16_t open_mode; + uint16_t search_attrs; + uint16_t file_attrs; + time_t write_time; + uint16_t open_func; + uint32_t size; + uint32_t timeout; + const char *fname; + uint_t num_eas; + struct ea_struct *eas; + } in; + struct { + union smb_handle file; + uint16_t attrib; + time_t write_time; + uint32_t size; + uint16_t access; + uint16_t ftype; + uint16_t devstate; + uint16_t action; + uint32_t file_id; + } out; + } t2open; + + /* SMBopen interface */ + struct { + enum smb_open_level level; + struct { + uint16_t open_mode; + uint16_t search_attrs; + const char *fname; + } in; + struct { + union smb_handle file; + uint16_t attrib; + time_t write_time; + uint32_t size; + uint16_t rmode; + } out; + } openold; + + /* SMBopenX interface */ + struct { + enum smb_open_level level; + struct { + uint16_t flags; + uint16_t open_mode; + uint16_t search_attrs; /* not honoured by win2003 */ + uint16_t file_attrs; + time_t write_time; /* not honoured by win2003 */ + uint16_t open_func; + uint32_t size; /* note that this sets the + initial file size, not + just allocation size */ + uint32_t timeout; /* not honoured by win2003 */ + const char *fname; + } in; + struct { + union smb_handle file; + uint16_t attrib; + time_t write_time; + uint32_t size; + uint16_t access; + uint16_t ftype; + uint16_t devstate; + uint16_t action; + uint32_t unique_fid; + uint32_t access_mask; + uint32_t unknown; + } out; + } openx; + + /* SMBmknew interface */ + struct { + enum smb_open_level level; + struct { + uint16_t attrib; + time_t write_time; + const char *fname; + } in; + struct { + union smb_handle file; + } out; + } mknew, create; + + /* SMBctemp interface */ + struct { + enum smb_open_level level; + struct { + uint16_t attrib; + time_t write_time; + const char *directory; + } in; + struct { + union smb_handle file; + /* temp name, relative to directory */ + char *name; + } out; + } ctemp; + + /* SMBsplopen interface */ + struct { + enum smb_open_level level; + struct { + uint16_t setup_length; + uint16_t mode; + const char *ident; + } in; + struct { + union smb_handle file; + } out; + } splopen; + + + /* chained OpenX/ReadX interface */ + struct { + enum smb_open_level level; + struct { + uint16_t flags; + uint16_t open_mode; + uint16_t search_attrs; /* not honoured by win2003 */ + uint16_t file_attrs; + time_t write_time; /* not honoured by win2003 */ + uint16_t open_func; + uint32_t size; /* note that this sets the + initial file size, not + just allocation size */ + uint32_t timeout; /* not honoured by win2003 */ + const char *fname; + + /* readx part */ + uint64_t offset; + uint16_t mincnt; + uint32_t maxcnt; + uint16_t remaining; + } in; + struct { + union smb_handle file; + uint16_t attrib; + time_t write_time; + uint32_t size; + uint16_t access; + uint16_t ftype; + uint16_t devstate; + uint16_t action; + uint32_t unique_fid; + uint32_t access_mask; + uint32_t unknown; + + /* readx part */ + uint8_t *data; + uint16_t remaining; + uint16_t compaction_mode; + uint16_t nread; + } out; + } openxreadx; + +#define SMB2_CREATE_FLAG_REQUEST_OPLOCK 0x0100 +#define SMB2_CREATE_FLAG_REQUEST_EXCLUSIVE_OPLOCK 0x0800 +#define SMB2_CREATE_FLAG_GRANT_OPLOCK 0x0001 +#define SMB2_CREATE_FLAG_GRANT_EXCLUSIVE_OPLOCK 0x0080 + + /* SMB2 Create */ + struct smb2_create { + enum smb_open_level level; + struct { + /* static body buffer 56 (0x38) bytes */ + uint8_t security_flags; /* SMB2_SECURITY_* */ + uint8_t oplock_level; /* SMB2_OPLOCK_LEVEL_* */ + uint32_t impersonation_level; /* SMB2_IMPERSONATION_* */ + uint64_t create_flags; + uint64_t reserved; + uint32_t desired_access; + uint32_t file_attributes; + uint32_t share_access; /* NTCREATEX_SHARE_ACCESS_* */ + uint32_t create_disposition; /* NTCREATEX_DISP_* */ + uint32_t create_options; /* NTCREATEX_OPTIONS_* */ + + /* uint16_t fname_ofs */ + /* uint16_t fname_size */ + /* uint32_t blob_ofs; */ + /* uint32_t blob_size; */ + + /* dynamic body */ + const char *fname; + + /* now some optional parameters - encoded as tagged blobs */ + struct smb_ea_list eas; + uint64_t alloc_size; + struct security_descriptor *sec_desc; + bool durable_open; + struct smb2_handle *durable_handle; + bool query_maximal_access; + NTTIME timewarp; + bool query_on_disk_id; + + /* and any additional blobs the caller wants */ + struct smb2_create_blobs { + uint32_t num_blobs; + struct smb2_create_blob { + const char *tag; + DATA_BLOB data; + } *blobs; + } blobs; + } in; + struct { + union smb_handle file; + + /* static body buffer 88 (0x58) bytes */ + /* uint16_t buffer_code; 0x59 = 0x58 + 1 */ + uint8_t oplock_level; + uint8_t reserved; + uint32_t create_action; + NTTIME create_time; + NTTIME access_time; + NTTIME write_time; + NTTIME change_time; + uint64_t alloc_size; + uint64_t size; + uint32_t file_attr; + uint32_t reserved2; + /* struct smb2_handle handle;*/ + /* uint32_t blob_ofs; */ + /* uint32_t blob_size; */ + + /* optional return values matching tagged values in the call */ + uint32_t maximal_access; + uint8_t on_disk_id[32]; + + /* tagged blobs in the reply */ + struct smb2_create_blobs blobs; + } out; + } smb2; +}; + + + +enum smb_read_level { + RAW_READ_READBRAW, + RAW_READ_LOCKREAD, + RAW_READ_READ, + RAW_READ_READX, + RAW_READ_SMB2 +}; + +#define RAW_READ_GENERIC RAW_READ_READX + +/* union for read() backend call + + note that .infoX.out.data will be allocated before the backend is + called. It will be big enough to hold the maximum size asked for +*/ +union smb_read { + /* SMBreadX (and generic) interface */ + struct { + enum smb_read_level level; + struct { + union smb_handle file; + uint64_t offset; + uint32_t mincnt; /* enforced on SMB2, 16 bit on SMB */ + uint32_t maxcnt; + uint16_t remaining; + bool read_for_execute; + } in; + struct { + uint8_t *data; + uint16_t remaining; + uint16_t compaction_mode; + uint32_t nread; + } out; + } readx, generic; + + /* SMBreadbraw interface */ + struct { + enum smb_read_level level; + struct { + union smb_handle file; + uint64_t offset; + uint16_t maxcnt; + uint16_t mincnt; + uint32_t timeout; + } in; + struct { + uint8_t *data; + uint32_t nread; + } out; + } readbraw; + + + /* SMBlockandread interface */ + struct { + enum smb_read_level level; + struct { + union smb_handle file; + uint16_t count; + uint32_t offset; + uint16_t remaining; + } in; + struct { + uint8_t *data; + uint16_t nread; + } out; + } lockread; + + /* SMBread interface */ + struct { + enum smb_read_level level; + struct { + union smb_handle file; + uint16_t count; + uint32_t offset; + uint16_t remaining; + } in; + struct { + uint8_t *data; + uint16_t nread; + } out; + } read; + + /* SMB2 Read */ + struct smb2_read { + enum smb_read_level level; + struct { + union smb_handle file; + + /* static body buffer 48 (0x30) bytes */ + /* uint16_t buffer_code; 0x31 = 0x30 + 1 */ + uint8_t _pad; + uint8_t reserved; + uint32_t length; + uint64_t offset; + /* struct smb2_handle handle; */ + uint32_t min_count; + uint32_t channel; + uint32_t remaining; + /* the docs give no indication of what + these channel variables are for */ + uint16_t channel_offset; + uint16_t channel_length; + } in; + struct { + /* static body buffer 16 (0x10) bytes */ + /* uint16_t buffer_code; 0x11 = 0x10 + 1 */ + /* uint8_t data_ofs; */ + /* uint8_t reserved; */ + /* uint32_t data_size; */ + uint32_t remaining; + uint32_t reserved; + + /* dynamic body */ + DATA_BLOB data; + } out; + } smb2; +}; + + +enum smb_write_level { + RAW_WRITE_WRITEUNLOCK, + RAW_WRITE_WRITE, + RAW_WRITE_WRITEX, + RAW_WRITE_WRITECLOSE, + RAW_WRITE_SPLWRITE, + RAW_WRITE_SMB2 +}; + +#define RAW_WRITE_GENERIC RAW_WRITE_WRITEX + +/* union for write() backend call +*/ +union smb_write { + /* SMBwriteX interface */ + struct { + enum smb_write_level level; + struct { + union smb_handle file; + uint64_t offset; + uint16_t wmode; + uint16_t remaining; + uint32_t count; + const uint8_t *data; + } in; + struct { + uint32_t nwritten; + uint16_t remaining; + } out; + } writex, generic; + + /* SMBwriteunlock interface */ + struct { + enum smb_write_level level; + struct { + union smb_handle file; + uint16_t count; + uint32_t offset; + uint16_t remaining; + const uint8_t *data; + } in; + struct { + uint32_t nwritten; + } out; + } writeunlock; + + /* SMBwrite interface */ + struct { + enum smb_write_level level; + struct { + union smb_handle file; + uint16_t count; + uint32_t offset; + uint16_t remaining; + const uint8_t *data; + } in; + struct { + uint16_t nwritten; + } out; + } write; + + /* SMBwriteclose interface */ + struct { + enum smb_write_level level; + struct { + union smb_handle file; + uint16_t count; + uint32_t offset; + time_t mtime; + const uint8_t *data; + } in; + struct { + uint16_t nwritten; + } out; + } writeclose; + + /* SMBsplwrite interface */ + struct { + enum smb_write_level level; + struct { + union smb_handle file; + uint16_t count; + const uint8_t *data; + } in; + } splwrite; + + /* SMB2 Write */ + struct smb2_write { + enum smb_write_level level; + struct { + union smb_handle file; + + /* static body buffer 48 (0x30) bytes */ + /* uint16_t buffer_code; 0x31 = 0x30 + 1 */ + /* uint16_t data_ofs; */ + /* uint32_t data_size; */ + uint64_t offset; + /* struct smb2_handle handle; */ + uint64_t unknown1; /* 0xFFFFFFFFFFFFFFFF */ + uint64_t unknown2; /* 0xFFFFFFFFFFFFFFFF */ + + /* dynamic body */ + DATA_BLOB data; + } in; + struct { + /* static body buffer 17 (0x11) bytes */ + /* uint16_t buffer_code; 0x11 = 0x10 + 1*/ + uint16_t _pad; + uint32_t nwritten; + uint64_t unknown1; /* 0x0000000000000000 */ + } out; + } smb2; +}; + + +enum smb_lock_level { + RAW_LOCK_LOCK, + RAW_LOCK_UNLOCK, + RAW_LOCK_LOCKX, + RAW_LOCK_SMB2, + RAW_LOCK_SMB2_BREAK +}; + +#define RAW_LOCK_GENERIC RAW_LOCK_LOCKX + +/* union for lock() backend call +*/ +union smb_lock { + /* SMBlockingX and generic interface */ + struct { + enum smb_lock_level level; + struct { + union smb_handle file; + uint16_t mode; + uint32_t timeout; + uint16_t ulock_cnt; + uint16_t lock_cnt; + struct smb_lock_entry { + uint32_t pid; /* 16 bits in SMB1 */ + uint64_t offset; + uint64_t count; + } *locks; /* unlocks are first in the arrray */ + } in; + } generic, lockx; + + /* SMBlock and SMBunlock interface */ + struct { + enum smb_lock_level level; + struct { + union smb_handle file; + uint32_t count; + uint32_t offset; + } in; + } lock, unlock; + + /* SMB2 Lock */ + struct smb2_lock { + enum smb_lock_level level; + struct { + union smb_handle file; + + /* static body buffer 48 (0x30) bytes */ + /* uint16_t buffer_code; 0x30 */ + uint16_t lock_count; + uint32_t reserved; + /* struct smb2_handle handle; */ + struct smb2_lock_element { + uint64_t offset; + uint64_t length; +/* these flags are the same as the SMB2 lock flags */ +#define SMB2_LOCK_FLAG_NONE 0x00000000 +#define SMB2_LOCK_FLAG_SHARED 0x00000001 +#define SMB2_LOCK_FLAG_EXCLUSIVE 0x00000002 +#define SMB2_LOCK_FLAG_UNLOCK 0x00000004 +#define SMB2_LOCK_FLAG_FAIL_IMMEDIATELY 0x00000010 +#define SMB2_LOCK_FLAG_ALL_MASK 0x00000017 + uint32_t flags; + uint32_t reserved; + } *locks; + } in; + struct { + /* static body buffer 4 (0x04) bytes */ + /* uint16_t buffer_code; 0x04 */ + uint16_t reserved; + } out; + } smb2; + + /* SMB2 Break */ + struct smb2_break { + enum smb_lock_level level; + struct { + union smb_handle file; + + /* static body buffer 24 (0x18) bytes */ + uint8_t oplock_level; + uint8_t reserved; + uint32_t reserved2; + /* struct smb2_handle handle; */ + } in, out; + } smb2_break; +}; + + +enum smb_close_level { + RAW_CLOSE_CLOSE, + RAW_CLOSE_SPLCLOSE, + RAW_CLOSE_SMB2, + RAW_CLOSE_GENERIC, +}; + +/* + union for close() backend call +*/ +union smb_close { + /* generic interface */ + struct { + enum smb_close_level level; + struct { + union smb_handle file; + time_t write_time; +#define SMB2_CLOSE_FLAGS_FULL_INFORMATION (1<<0) + uint16_t flags; /* SMB2_CLOSE_FLAGS_* */ + } in; + struct { + uint16_t flags; + NTTIME create_time; + NTTIME access_time; + NTTIME write_time; + NTTIME change_time; + uint64_t alloc_size; + uint64_t size; + uint32_t file_attr; + } out; + } generic; + + /* SMBclose interface */ + struct { + enum smb_close_level level; + struct { + union smb_handle file; + time_t write_time; + } in; + } close; + + /* SMBsplclose interface - empty! */ + struct { + enum smb_close_level level; + struct { + union smb_handle file; + } in; + } splclose; + + /* SMB2 Close */ + struct smb2_close { + enum smb_close_level level; + struct { + union smb_handle file; + + /* static body buffer 24 (0x18) bytes */ + /* uint16_t buffer_code; 0x18 */ + uint16_t flags; /* SMB2_CLOSE_FLAGS_* */ + uint32_t _pad; + } in; + struct { + /* static body buffer 60 (0x3C) bytes */ + /* uint16_t buffer_code; 0x3C */ + uint16_t flags; + uint32_t _pad; + NTTIME create_time; + NTTIME access_time; + NTTIME write_time; + NTTIME change_time; + uint64_t alloc_size; + uint64_t size; + uint32_t file_attr; + } out; + } smb2; +}; + + +enum smb_lpq_level {RAW_LPQ_GENERIC, RAW_LPQ_RETQ}; + +/* + union for lpq() backend +*/ +union smb_lpq { + /* generic interface */ + struct { + enum smb_lpq_level level; + + } generic; + + + /* SMBsplretq interface */ + struct { + enum smb_lpq_level level; + + struct { + uint16_t maxcount; + uint16_t startidx; + } in; + struct { + uint16_t count; + uint16_t restart_idx; + struct { + time_t time; + uint8_t status; + uint16_t job; + uint32_t size; + char *user; + } *queue; + } out; + } retq; +}; + +enum smb_ioctl_level { + RAW_IOCTL_IOCTL, + RAW_IOCTL_NTIOCTL, + RAW_IOCTL_SMB2, + RAW_IOCTL_SMB2_NO_HANDLE +}; + +/* + union for ioctl() backend +*/ +union smb_ioctl { + /* generic interface */ + struct { + enum smb_ioctl_level level; + struct { + union smb_handle file; + } in; + } generic; + + /* struct for SMBioctl */ + struct { + enum smb_ioctl_level level; + struct { + union smb_handle file; + uint32_t request; + } in; + struct { + DATA_BLOB blob; + } out; + } ioctl; + + + /* struct for NT ioctl call */ + struct { + enum smb_ioctl_level level; + struct { + union smb_handle file; + uint32_t function; + bool fsctl; + uint8_t filter; + uint32_t max_data; + DATA_BLOB blob; + } in; + struct { + DATA_BLOB blob; + } out; + } ntioctl; + + /* SMB2 Ioctl */ + struct smb2_ioctl { + enum smb_ioctl_level level; + struct { + union smb_handle file; + + /* static body buffer 56 (0x38) bytes */ + /* uint16_t buffer_code; 0x39 = 0x38 + 1 */ + uint16_t _pad; + uint32_t function; + /*struct smb2_handle handle;*/ + /* uint32_t out_ofs; */ + /* uint32_t out_size; */ + uint32_t unknown2; + /* uint32_t in_ofs; */ + /* uint32_t in_size; */ + uint32_t max_response_size; + uint64_t flags; + + /* dynamic body */ + DATA_BLOB out; + DATA_BLOB in; + } in; + struct { + union smb_handle file; + + /* static body buffer 48 (0x30) bytes */ + /* uint16_t buffer_code; 0x31 = 0x30 + 1 */ + uint16_t _pad; + uint32_t function; + /* struct smb2_handle handle; */ + /* uint32_t in_ofs; */ + /* uint32_t in_size; */ + /* uint32_t out_ofs; */ + /* uint32_t out_size; */ + uint32_t unknown2; + uint32_t unknown3; + + /* dynamic body */ + DATA_BLOB in; + DATA_BLOB out; + } out; + } smb2; +}; + +enum smb_flush_level { + RAW_FLUSH_FLUSH, + RAW_FLUSH_ALL, + RAW_FLUSH_SMB2 +}; + +union smb_flush { + /* struct for SMBflush */ + struct { + enum smb_flush_level level; + struct { + union smb_handle file; + } in; + } flush, generic; + + /* SMBflush with 0xFFFF wildcard fnum */ + struct { + enum smb_flush_level level; + } flush_all; + + /* SMB2 Flush */ + struct smb2_flush { + enum smb_flush_level level; + struct { + union smb_handle file; + uint16_t reserved1; + uint32_t reserved2; + } in; + struct { + uint16_t reserved; + } out; + } smb2; +}; + +/* struct for SMBcopy */ +struct smb_copy { + struct { + uint16_t tid2; + uint16_t ofun; + uint16_t flags; + const char *path1; + const char *path2; + } in; + struct { + uint16_t count; + } out; +}; + + +/* struct for transact/transact2 call */ +struct smb_trans2 { + struct { + uint16_t max_param; + uint16_t max_data; + uint8_t max_setup; + uint16_t flags; + uint32_t timeout; + uint8_t setup_count; + uint16_t *setup; + const char *trans_name; /* SMBtrans only */ + DATA_BLOB params; + DATA_BLOB data; + } in; + + struct { + uint8_t setup_count; + uint16_t *setup; + DATA_BLOB params; + DATA_BLOB data; + } out; +}; + +/* struct for nttransact2 call */ +struct smb_nttrans { + struct { + uint8_t max_setup; + uint32_t max_param; + uint32_t max_data; + uint8_t setup_count; + uint16_t function; + uint8_t *setup; + DATA_BLOB params; + DATA_BLOB data; + } in; + + struct { + uint8_t setup_count; /* in units of 16 bit words */ + uint8_t *setup; + DATA_BLOB params; + DATA_BLOB data; + } out; +}; + +enum smb_notify_level { + RAW_NOTIFY_NTTRANS, + RAW_NOTIFY_SMB2 +}; + +union smb_notify { + /* struct for nttrans change notify call */ + struct { + enum smb_notify_level level; + + struct { + union smb_handle file; + uint32_t buffer_size; + uint32_t completion_filter; + bool recursive; + } in; + + struct { + uint32_t num_changes; + struct notify_changes { + uint32_t action; + struct smb_wire_string name; + } *changes; + } out; + } nttrans; + + struct smb2_notify { + enum smb_notify_level level; + + struct { + union smb_handle file; + /* static body buffer 32 (0x20) bytes */ + /* uint16_t buffer_code; 0x32 */ + uint16_t recursive; + uint32_t buffer_size; + /*struct smb2_handle file;*/ + uint32_t completion_filter; + uint32_t unknown; + } in; + + struct { + /* static body buffer 8 (0x08) bytes */ + /* uint16_t buffer_code; 0x09 = 0x08 + 1 */ + /* uint16_t blob_ofs; */ + /* uint16_t blob_size; */ + + /* dynamic body */ + /*DATA_BLOB blob;*/ + + /* DATA_BLOB content */ + uint32_t num_changes; + struct notify_changes *changes; + } out; + } smb2; +}; + +enum smb_search_level { + RAW_SEARCH_SEARCH, /* SMBsearch */ + RAW_SEARCH_FFIRST, /* SMBffirst */ + RAW_SEARCH_FUNIQUE, /* SMBfunique */ + RAW_SEARCH_TRANS2, /* SMBtrans2 */ + RAW_SEARCH_SMB2 /* SMB2 Find */ +}; + +enum smb_search_data_level { + RAW_SEARCH_DATA_GENERIC = 0x10000, /* only used in the smbcli_ code */ + RAW_SEARCH_DATA_SEARCH, + RAW_SEARCH_DATA_STANDARD = SMB_FIND_STANDARD, + RAW_SEARCH_DATA_EA_SIZE = SMB_FIND_EA_SIZE, + RAW_SEARCH_DATA_EA_LIST = SMB_FIND_EA_LIST, + RAW_SEARCH_DATA_DIRECTORY_INFO = SMB_FIND_DIRECTORY_INFO, + RAW_SEARCH_DATA_FULL_DIRECTORY_INFO = SMB_FIND_FULL_DIRECTORY_INFO, + RAW_SEARCH_DATA_NAME_INFO = SMB_FIND_NAME_INFO, + RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO = SMB_FIND_BOTH_DIRECTORY_INFO, + RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO = SMB_FIND_ID_FULL_DIRECTORY_INFO, + RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO = SMB_FIND_ID_BOTH_DIRECTORY_INFO, + RAW_SEARCH_DATA_UNIX_INFO = SMB_FIND_UNIX_INFO, + RAW_SEARCH_DATA_UNIX_INFO2 = SMB_FIND_UNIX_INFO2 +}; + +/* union for file search */ +union smb_search_first { + struct { + enum smb_search_level level; + enum smb_search_data_level data_level; + } generic; + + /* search (old) findfirst interface. + Also used for ffirst and funique. */ + struct { + enum smb_search_level level; + enum smb_search_data_level data_level; + + struct { + uint16_t max_count; + uint16_t search_attrib; + const char *pattern; + } in; + struct { + int16_t count; + } out; + } search_first; + + /* trans2 findfirst interface */ + struct { + enum smb_search_level level; + enum smb_search_data_level data_level; + + struct { + uint16_t search_attrib; + uint16_t max_count; + uint16_t flags; + uint32_t storage_type; + const char *pattern; + + /* the ea names are only used for RAW_SEARCH_EA_LIST */ + uint_t num_names; + struct ea_name *ea_names; + } in; + struct { + uint16_t handle; + uint16_t count; + uint16_t end_of_search; + } out; + } t2ffirst; + +/* + SMB2 uses different level numbers for the same old SMB trans2 search levels +*/ +#define SMB2_FIND_DIRECTORY_INFO 0x01 +#define SMB2_FIND_FULL_DIRECTORY_INFO 0x02 +#define SMB2_FIND_BOTH_DIRECTORY_INFO 0x03 +#define SMB2_FIND_NAME_INFO 0x0C +#define SMB2_FIND_ID_BOTH_DIRECTORY_INFO 0x25 +#define SMB2_FIND_ID_FULL_DIRECTORY_INFO 0x26 + +/* flags for SMB2 find */ +#define SMB2_CONTINUE_FLAG_RESTART 0x01 +#define SMB2_CONTINUE_FLAG_SINGLE 0x02 +#define SMB2_CONTINUE_FLAG_INDEX 0x04 +#define SMB2_CONTINUE_FLAG_REOPEN 0x10 + + /* SMB2 Find */ + struct smb2_find { + enum smb_search_level level; + enum smb_search_data_level data_level; + struct { + union smb_handle file; + + /* static body buffer 32 (0x20) bytes */ + /* uint16_t buffer_code; 0x21 = 0x20 + 1 */ + uint8_t level; + uint8_t continue_flags; /* SMB2_CONTINUE_FLAG_* */ + uint32_t file_index; + /* struct smb2_handle handle; */ + /* uint16_t pattern_ofs; */ + /* uint16_t pattern_size; */ + uint32_t max_response_size; + + /* dynamic body */ + const char *pattern; + } in; + struct { + /* static body buffer 8 (0x08) bytes */ + /* uint16_t buffer_code; 0x08 */ + /* uint16_t blob_ofs; */ + /* uint32_t blob_size; */ + + /* dynamic body */ + DATA_BLOB blob; + } out; + } smb2; +}; + +/* union for file search continue */ +union smb_search_next { + struct { + enum smb_search_level level; + enum smb_search_data_level data_level; + } generic; + + /* search (old) findnext interface. Also used + for ffirst when continuing */ + struct { + enum smb_search_level level; + enum smb_search_data_level data_level; + + struct { + uint16_t max_count; + uint16_t search_attrib; + struct smb_search_id { + uint8_t reserved; + char name[11]; + uint8_t handle; + uint32_t server_cookie; + uint32_t client_cookie; + } id; + } in; + struct { + uint16_t count; + } out; + } search_next; + + /* trans2 findnext interface */ + struct { + enum smb_search_level level; + enum smb_search_data_level data_level; + + struct { + uint16_t handle; + uint16_t max_count; + uint32_t resume_key; + uint16_t flags; + const char *last_name; + + /* the ea names are only used for RAW_SEARCH_EA_LIST */ + uint_t num_names; + struct ea_name *ea_names; + } in; + struct { + uint16_t count; + uint16_t end_of_search; + } out; + } t2fnext; + + /* SMB2 Find */ + struct smb2_find smb2; +}; + +/* union for search reply file data */ +union smb_search_data { + /* + * search (old) findfirst + * RAW_SEARCH_DATA_SEARCH + */ + struct { + uint16_t attrib; + time_t write_time; + uint32_t size; + struct smb_search_id id; + const char *name; + } search; + + /* trans2 findfirst RAW_SEARCH_DATA_STANDARD level */ + struct { + uint32_t resume_key; + time_t create_time; + time_t access_time; + time_t write_time; + uint32_t size; + uint32_t alloc_size; + uint16_t attrib; + struct smb_wire_string name; + } standard; + + /* trans2 findfirst RAW_SEARCH_DATA_EA_SIZE level */ + struct { + uint32_t resume_key; + time_t create_time; + time_t access_time; + time_t write_time; + uint32_t size; + uint32_t alloc_size; + uint16_t attrib; + uint32_t ea_size; + struct smb_wire_string name; + } ea_size; + + /* trans2 findfirst RAW_SEARCH_DATA_EA_LIST level */ + struct { + uint32_t resume_key; + time_t create_time; + time_t access_time; + time_t write_time; + uint32_t size; + uint32_t alloc_size; + uint16_t attrib; + struct smb_ea_list eas; + struct smb_wire_string name; + } ea_list; + + /* RAW_SEARCH_DATA_DIRECTORY_INFO interface */ + struct { + uint32_t file_index; + NTTIME create_time; + NTTIME access_time; + NTTIME write_time; + NTTIME change_time; + uint64_t size; + uint64_t alloc_size; + uint32_t attrib; + struct smb_wire_string name; + } directory_info; + + /* RAW_SEARCH_DATA_FULL_DIRECTORY_INFO interface */ + struct { + uint32_t file_index; + NTTIME create_time; + NTTIME access_time; + NTTIME write_time; + NTTIME change_time; + uint64_t size; + uint64_t alloc_size; + uint32_t attrib; + uint32_t ea_size; + struct smb_wire_string name; + } full_directory_info; + + /* RAW_SEARCH_DATA_NAME_INFO interface */ + struct { + uint32_t file_index; + struct smb_wire_string name; + } name_info; + + /* RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO interface */ + struct { + uint32_t file_index; + NTTIME create_time; + NTTIME access_time; + NTTIME write_time; + NTTIME change_time; + uint64_t size; + uint64_t alloc_size; + uint32_t attrib; + uint32_t ea_size; + struct smb_wire_string short_name; + struct smb_wire_string name; + } both_directory_info; + + /* RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO interface */ + struct { + uint32_t file_index; + NTTIME create_time; + NTTIME access_time; + NTTIME write_time; + NTTIME change_time; + uint64_t size; + uint64_t alloc_size; + uint32_t attrib; + uint32_t ea_size; + uint64_t file_id; + struct smb_wire_string name; + } id_full_directory_info; + + /* RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO interface */ + struct { + uint32_t file_index; + NTTIME create_time; + NTTIME access_time; + NTTIME write_time; + NTTIME change_time; + uint64_t size; + uint64_t alloc_size; + uint32_t attrib; + uint32_t ea_size; + uint64_t file_id; + struct smb_wire_string short_name; + struct smb_wire_string name; + } id_both_directory_info; + + /* RAW_SEARCH_DATA_UNIX_INFO interface */ + struct { + uint32_t file_index; + uint64_t size; + uint64_t alloc_size; + NTTIME status_change_time; + NTTIME access_time; + NTTIME change_time; + uint64_t uid; + uint64_t gid; + uint32_t file_type; + uint64_t dev_major; + uint64_t dev_minor; + uint64_t unique_id; + uint64_t permissions; + uint64_t nlink; + const char *name; + } unix_info; + + /* RAW_SEARCH_DATA_UNIX_INFO2 interface */ + struct { + uint32_t file_index; + uint64_t end_of_file; + uint64_t num_bytes; + NTTIME status_change_time; + NTTIME access_time; + NTTIME change_time; + uint64_t uid; + uint64_t gid; + uint32_t file_type; + uint64_t dev_major; + uint64_t dev_minor; + uint64_t unique_id; + uint64_t permissions; + uint64_t nlink; + NTTIME create_time; + uint32_t file_flags; + uint32_t flags_mask; + struct smb_wire_string name; + } unix_info2; +}; + +/* Callback function passed to the raw search interface. */ +typedef bool (*smbcli_search_callback)(void *private_data, const union smb_search_data *file); + +enum smb_search_close_level {RAW_FINDCLOSE_GENERIC, RAW_FINDCLOSE_FCLOSE, RAW_FINDCLOSE_FINDCLOSE}; + +/* union for file search close */ +union smb_search_close { + struct { + enum smb_search_close_level level; + } generic; + + /* SMBfclose (old search) interface */ + struct { + enum smb_search_close_level level; + + struct { + /* max_count and search_attrib are not used, but are present */ + uint16_t max_count; + uint16_t search_attrib; + struct smb_search_id id; + } in; + } fclose; + + /* SMBfindclose interface */ + struct { + enum smb_search_close_level level; + + struct { + uint16_t handle; + } in; + } findclose; +}; + + +/* + struct for SMBecho call +*/ +struct smb_echo { + struct { + uint16_t repeat_count; + uint16_t size; + uint8_t *data; + } in; + struct { + uint16_t count; + uint16_t sequence_number; + uint16_t size; + uint8_t *data; + } out; +}; + +/* + struct for shadow copy volumes + */ +struct smb_shadow_copy { + struct { + union smb_handle file; + uint32_t max_data; + } in; + struct { + uint32_t num_volumes; + uint32_t num_names; + const char **names; + } out; +}; + +#endif /* __LIBCLI_RAW_INTERFACES_H__ */ diff --git a/source4/libcli/raw/ioctl.h b/source4/libcli/raw/ioctl.h new file mode 100644 index 0000000000..a9d3d1b7a6 --- /dev/null +++ b/source4/libcli/raw/ioctl.h @@ -0,0 +1,59 @@ +/* + Unix SMB/CIFS implementation. + ioctl and fsctl definitions + + Copyright (C) Andrew Tridgell 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/>. +*/ + + +/* ioctl codes */ +#define IOCTL_QUERY_JOB_INFO 0x530060 + + +/* filesystem control codes */ +#define FSCTL_METHOD_BUFFERED 0x00000000 +#define FSCTL_METHOD_IN_DIRECT 0x00000001 +#define FSCTL_METHOD_OUT_DIRECT 0x00000002 +#define FSCTL_METHOD_NEITHER 0x00000003 + +#define FSCTL_ACCESS_ANY 0x00000000 +#define FSCTL_ACCESS_READ 0x00004000 +#define FSCTL_ACCESS_WRITE 0x00008000 + +#define FSCTL_FILESYSTEM 0x00090000 +#define FSCTL_REQUEST_OPLOCK_LEVEL_1 (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x0000 | FSCTL_METHOD_BUFFERED) +#define FSCTL_REQUEST_OPLOCK_LEVEL_2 (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x0004 | FSCTL_METHOD_BUFFERED) +#define FSCTL_REQUEST_BATCH_OPLOCK (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x0008 | FSCTL_METHOD_BUFFERED) +#define FSCTL_OPLOCK_BREAK_ACKNOWLEDGE (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x000C | FSCTL_METHOD_BUFFERED) +#define FSCTL_OPBATCH_ACK_CLOSE_PENDING (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x0010 | FSCTL_METHOD_BUFFERED) +#define FSCTL_OPLOCK_BREAK_NOTIFY (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x0014 | FSCTL_METHOD_BUFFERED) +#define FSCTL_FILESYS_GET_STATISTICS (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x0060 | FSCTL_METHOD_BUFFERED) +#define FSCTL_GET_NTFS_VOLUME_DATA (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x0064 | FSCTL_METHOD_BUFFERED) +#define FSCTL_FIND_FILES_BY_SID (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x008C | FSCTL_METHOD_NEITHER) +#define FSCTL_SET_OBJECT_ID (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x0098 | FSCTL_METHOD_BUFFERED) +#define FSCTL_GET_OBJECT_ID (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x009C | FSCTL_METHOD_BUFFERED) +#define FSCTL_DELETE_OBJECT_ID (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x00A0 | FSCTL_METHOD_BUFFERED) +#define FSCTL_SET_REPARSE_POINT (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x00A4 | FSCTL_METHOD_BUFFERED) +#define FSCTL_GET_REPARSE_POINT (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x00A8 | FSCTL_METHOD_BUFFERED) +#define FSCTL_DELETE_REPARSE_POINT (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x00AC | FSCTL_METHOD_BUFFERED) +#define FSCTL_CREATE_OR_GET_OBJECT_ID (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x00C0 | FSCTL_METHOD_BUFFERED) +#define FSCTL_SET_SPARSE (FSCTL_FILESYSTEM | FSCTL_ACCESS_ANY | 0x00C4 | FSCTL_METHOD_BUFFERED) + +#define FSCTL_NAMED_PIPE 0x00110000 +#define FSCTL_NAMED_PIPE_READ_WRITE (FSCTL_NAMED_PIPE | FSCTL_ACCESS_ANY | 0xC014 | FSCTL_METHOD_NEITHER) + +#define FSCTL_NETWORK_FILESYSTEM 0x00140000 +#define FSCTL_GET_SHADOW_COPY_DATA (FSCTL_NETWORK_FILESYSTEM | FSCTL_ACCESS_READ | 0x0064 | FSCTL_METHOD_BUFFERED) diff --git a/source4/libcli/raw/libcliraw.h b/source4/libcli/raw/libcliraw.h new file mode 100644 index 0000000000..a9fcdab9cc --- /dev/null +++ b/source4/libcli/raw/libcliraw.h @@ -0,0 +1,384 @@ +/* + Unix SMB/CIFS implementation. + SMB parameters and setup + + Copyright (C) Andrew Tridgell 2002-2004 + Copyright (C) James Myers 2003 <myersjj@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/>. +*/ + +#ifndef __LIBCLI_RAW_H__ +#define __LIBCLI_RAW_H__ + +#include "libcli/raw/request.h" +#include "librpc/gen_ndr/nbt.h" + +struct smbcli_tree; /* forward declare */ +struct smbcli_request; /* forward declare */ +struct smbcli_session; /* forward declare */ +struct smbcli_transport; /* forward declare */ + +struct resolve_context; +struct cli_credentials; +struct gensec_settings; + +/* default timeout for all smb requests */ +#define SMB_REQUEST_TIMEOUT 60 + +/* context that will be and has been negotiated between the client and server */ +struct smbcli_negotiate { + /* + * negotiated maximum transmit size - this is given to us by the server + */ + uint32_t max_xmit; + + /* maximum number of requests that can be multiplexed */ + uint16_t max_mux; + + /* the negotiatiated protocol */ + enum protocol_types protocol; + + uint8_t sec_mode; /* security mode returned by negprot */ + uint8_t key_len; + DATA_BLOB server_guid; /* server_guid */ + DATA_BLOB secblob; /* cryptkey or negTokenInit blob */ + uint32_t sesskey; + + struct smb_signing_context sign_info; + + /* capabilities that the server reported */ + uint32_t capabilities; + + int server_zone; + time_t server_time; + uint_t readbraw_supported:1; + uint_t writebraw_supported:1; + + char *server_domain; +}; + +/* this is the context for a SMB socket associated with the socket itself */ +struct smbcli_socket { + struct socket_context *sock; + + /* what port we ended up connected to */ + int port; + + /* the hostname we connected to */ + const char *hostname; + + /* the event handle for waiting for socket IO */ + struct { + struct tevent_context *ctx; + struct tevent_fd *fde; + struct tevent_timer *te; + } event; +}; + +/* + this structure allows applications to control the behaviour of the + client library +*/ +struct smbcli_options { + uint_t use_oplocks:1; + uint_t use_level2_oplocks:1; + uint_t use_spnego:1; + uint_t unicode:1; + uint_t ntstatus_support:1; + int max_protocol; + uint32_t max_xmit; + uint16_t max_mux; + int request_timeout; + enum smb_signing_state signing; +}; + +/* this is the context for the client transport layer */ +struct smbcli_transport { + /* socket level info */ + struct smbcli_socket *socket; + + /* the next mid to be allocated - needed for signing and + request matching */ + uint16_t next_mid; + + /* negotiated protocol information */ + struct smbcli_negotiate negotiate; + + /* options to control the behaviour of the client code */ + struct smbcli_options options; + + /* is a readbraw pending? we need to handle that case + specially on receiving packets */ + uint_t readbraw_pending:1; + + /* an idle function - if this is defined then it will be + called once every period microseconds while we are waiting + for a packet */ + struct { + void (*func)(struct smbcli_transport *, void *); + void *private_data; + uint_t period; + } idle; + + /* the error fields from the last message */ + struct { + enum {ETYPE_NONE, ETYPE_SMB, ETYPE_SOCKET, ETYPE_NBT} etype; + union { + NTSTATUS nt_status; + enum {SOCKET_READ_TIMEOUT, + SOCKET_READ_EOF, + SOCKET_READ_ERROR, + SOCKET_WRITE_ERROR, + SOCKET_READ_BAD_SIG} socket_error; + uint_t nbt_error; + } e; + } error; + + struct { + /* a oplock break request handler */ + bool (*handler)(struct smbcli_transport *transport, + uint16_t tid, uint16_t fnum, uint8_t level, void *private_data); + /* private data passed to the oplock handler */ + void *private_data; + } oplock; + + /* a list of async requests that are pending for receive on this connection */ + struct smbcli_request *pending_recv; + + /* remember the called name - some sub-protocols require us to + know the server name */ + struct nbt_name called; + + /* context of the stream -> packet parser */ + struct packet_context *packet; + + /* iconv convenience */ + struct smb_iconv_convenience *iconv_convenience; +}; + +/* this is the context for the user */ + +/* this is the context for the session layer */ +struct smbcli_session { + /* transport layer info */ + struct smbcli_transport *transport; + + /* after a session setup the server provides us with + a vuid identifying the security context */ + uint16_t vuid; + + /* default pid for this session */ + uint32_t pid; + + /* the flags2 for each packet - this allows + the user to control these for torture testing */ + uint16_t flags2; + + DATA_BLOB user_session_key; + + /* the spnego context if we use extented security */ + struct gensec_security *gensec; + + struct smbcli_session_options { + uint_t lanman_auth:1; + uint_t ntlmv2_auth:1; + uint_t plaintext_auth:1; + } options; +}; + +/* + smbcli_tree context: internal state for a tree connection. + */ +struct smbcli_tree { + /* session layer info */ + struct smbcli_session *session; + + uint16_t tid; /* tree id, aka cnum */ + char *device; + char *fs_type; +}; + + +/* + a client request moves between the following 4 states. +*/ +enum smbcli_request_state {SMBCLI_REQUEST_INIT, /* we are creating the request */ + SMBCLI_REQUEST_RECV, /* we are waiting for a matching reply */ + SMBCLI_REQUEST_DONE, /* the request is finished */ + SMBCLI_REQUEST_ERROR}; /* a packet or transport level error has occurred */ + +/* the context for a single SMB request. This is passed to any request-context + * functions (similar to context.h, the server version). + * This will allow requests to be multi-threaded. */ +struct smbcli_request { + /* allow a request to be part of a list of requests */ + struct smbcli_request *next, *prev; + + /* each request is in one of 4 possible states */ + enum smbcli_request_state state; + + /* a request always has a transport context, nearly always has + a session context and usually has a tree context */ + struct smbcli_transport *transport; + struct smbcli_session *session; + struct smbcli_tree *tree; + + /* a receive helper, smbcli_transport_finish_recv will not call + req->async.fn callback handler unless the recv_helper returns + a value > SMBCLI_REQUEST_RECV. */ + struct { + enum smbcli_request_state (*fn)(struct smbcli_request *); + void *private_data; + } recv_helper; + + /* the flags2 from the SMB request, in raw form (host byte + order). Used to parse strings */ + uint16_t flags2; + + /* the NT status for this request. Set by packet receive code + or code detecting error. */ + NTSTATUS status; + + /* the sequence number of this packet - used for signing */ + uint_t seq_num; + + /* list of ntcancel request for this requests */ + struct smbcli_request *ntcancel; + + /* set if this is a one-way request, meaning we are not + expecting a reply from the server. */ + uint_t one_way_request:1; + + /* set this when the request should only increment the signing + counter by one */ + uint_t sign_single_increment:1; + + /* the caller wants to do the signing check */ + bool sign_caller_checks; + + /* give the caller a chance to prevent the talloc_free() in the _recv() function */ + bool do_not_free; + + /* the mid of this packet - used to match replies */ + uint16_t mid; + + struct smb_request_buffer in; + struct smb_request_buffer out; + + /* information on what to do with a reply when it is received + asyncronously. If this is not setup when a reply is received then + the reply is discarded + + The private pointer is private to the caller of the client + library (the application), not private to the library + */ + struct { + void (*fn)(struct smbcli_request *); + void *private_data; + } async; +}; + +/* useful way of catching wct errors with file and line number */ +#define SMBCLI_CHECK_MIN_WCT(req, wcount) if ((req)->in.wct < (wcount)) { \ + DEBUG(1,("Unexpected WCT %d at %s(%d) - expected min %d\n", (req)->in.wct, __FILE__, __LINE__, wcount)); \ + req->status = NT_STATUS_INVALID_PARAMETER; \ + goto failed; \ +} + +#define SMBCLI_CHECK_WCT(req, wcount) if ((req)->in.wct != (wcount)) { \ + DEBUG(1,("Unexpected WCT %d at %s(%d) - expected %d\n", (req)->in.wct, __FILE__, __LINE__, wcount)); \ + req->status = NT_STATUS_INVALID_PARAMETER; \ + goto failed; \ +} + +#include "libcli/raw/interfaces.h" + +NTSTATUS smb_raw_read_recv(struct smbcli_request *req, union smb_read *parms); +struct smbcli_request *smb_raw_read_send(struct smbcli_tree *tree, union smb_read *parms); +NTSTATUS smb_raw_trans_recv(struct smbcli_request *req, + TALLOC_CTX *mem_ctx, + struct smb_trans2 *parms); +size_t smb_raw_max_trans_data(struct smbcli_tree *tree, size_t param_size); +struct smbcli_request *smb_raw_trans_send(struct smbcli_tree *tree, struct smb_trans2 *parms); +NTSTATUS smbcli_request_destroy(struct smbcli_request *req); +struct smbcli_request *smb_raw_write_send(struct smbcli_tree *tree, union smb_write *parms); +struct smbcli_request *smb_raw_close_send(struct smbcli_tree *tree, union smb_close *parms); +NTSTATUS smb_raw_open_recv(struct smbcli_request *req, TALLOC_CTX *mem_ctx, union smb_open *parms); +struct smbcli_request *smb_raw_open_send(struct smbcli_tree *tree, union smb_open *parms); + +bool smbcli_transport_process(struct smbcli_transport *transport); +const char *smbcli_errstr(struct smbcli_tree *tree); +NTSTATUS smb_raw_fsinfo(struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, union smb_fsinfo *fsinfo); +NTSTATUS smb_raw_pathinfo(struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, union smb_fileinfo *parms); +NTSTATUS smb_raw_shadow_data(struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, struct smb_shadow_copy *info); +NTSTATUS smb_raw_fileinfo(struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, union smb_fileinfo *parms); +struct smbcli_tree *smbcli_tree_init(struct smbcli_session *session, TALLOC_CTX *parent_ctx, bool primary); +NTSTATUS smb_raw_tcon(struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, union smb_tcon *parms); +void smbcli_oplock_handler(struct smbcli_transport *transport, + bool (*handler)(struct smbcli_transport *, uint16_t, uint16_t, uint8_t, void *), + void *private_data); +void smbcli_transport_idle_handler(struct smbcli_transport *transport, + void (*idle_func)(struct smbcli_transport *, void *), + uint64_t period, + void *private_data); +NTSTATUS smbcli_request_simple_recv(struct smbcli_request *req); +bool smbcli_oplock_ack(struct smbcli_tree *tree, uint16_t fnum, uint16_t ack_level); +NTSTATUS smb_raw_open(struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, union smb_open *parms); +NTSTATUS smb_raw_close(struct smbcli_tree *tree, union smb_close *parms); +NTSTATUS smb_raw_unlink(struct smbcli_tree *tree, union smb_unlink *parms); +NTSTATUS smb_raw_chkpath(struct smbcli_tree *tree, union smb_chkpath *parms); +NTSTATUS smb_raw_mkdir(struct smbcli_tree *tree, union smb_mkdir *parms); +NTSTATUS smb_raw_rmdir(struct smbcli_tree *tree, struct smb_rmdir *parms); +NTSTATUS smb_raw_rename(struct smbcli_tree *tree, union smb_rename *parms); +NTSTATUS smb_raw_seek(struct smbcli_tree *tree, union smb_seek *parms); +NTSTATUS smb_raw_read(struct smbcli_tree *tree, union smb_read *parms); +NTSTATUS smb_raw_write(struct smbcli_tree *tree, union smb_write *parms); +NTSTATUS smb_raw_lock(struct smbcli_tree *tree, union smb_lock *parms); +NTSTATUS smb_raw_setpathinfo(struct smbcli_tree *tree, union smb_setfileinfo *parms); +NTSTATUS smb_raw_setfileinfo(struct smbcli_tree *tree, union smb_setfileinfo *parms); + +struct smbcli_request *smb_raw_changenotify_send(struct smbcli_tree *tree, union smb_notify *parms); +NTSTATUS smb_raw_changenotify_recv(struct smbcli_request *req, TALLOC_CTX *mem_ctx, union smb_notify *parms); + +NTSTATUS smb_tree_disconnect(struct smbcli_tree *tree); +NTSTATUS smbcli_nt_error(struct smbcli_tree *tree); +NTSTATUS smb_raw_exit(struct smbcli_session *session); +NTSTATUS smb_raw_pathinfo_recv(struct smbcli_request *req, + TALLOC_CTX *mem_ctx, + union smb_fileinfo *parms); +struct smbcli_request *smb_raw_pathinfo_send(struct smbcli_tree *tree, + union smb_fileinfo *parms); +struct smbcli_request *smb_raw_setpathinfo_send(struct smbcli_tree *tree, + union smb_setfileinfo *parms); +struct smbcli_request *smb_raw_echo_send(struct smbcli_transport *transport, + struct smb_echo *p); +NTSTATUS smb_raw_search_first(struct smbcli_tree *tree, + TALLOC_CTX *mem_ctx, + union smb_search_first *io, void *private_data, + smbcli_search_callback callback); +NTSTATUS smb_raw_flush(struct smbcli_tree *tree, union smb_flush *parms); + +NTSTATUS smb_raw_trans(struct smbcli_tree *tree, + TALLOC_CTX *mem_ctx, + struct smb_trans2 *parms); + +struct smbcli_socket *smbcli_sock_connect_byname(const char *host, const char **ports, + TALLOC_CTX *mem_ctx, + struct resolve_context *resolve_ctx, + struct tevent_context *event_ctx, + const char *socket_options); +void smbcli_sock_dead(struct smbcli_socket *sock); + +#endif /* __LIBCLI_RAW__H__ */ diff --git a/source4/libcli/raw/rawacl.c b/source4/libcli/raw/rawacl.c new file mode 100644 index 0000000000..e13ba85361 --- /dev/null +++ b/source4/libcli/raw/rawacl.c @@ -0,0 +1,163 @@ +/* + Unix SMB/CIFS implementation. + ACL get/set operations + + Copyright (C) Andrew Tridgell 2003-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" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "librpc/gen_ndr/ndr_security.h" + +/**************************************************************************** +fetch file ACL (async send) +****************************************************************************/ +struct smbcli_request *smb_raw_query_secdesc_send(struct smbcli_tree *tree, + union smb_fileinfo *io) +{ + struct smb_nttrans nt; + uint8_t params[8]; + + nt.in.max_setup = 0; + nt.in.max_param = 4; + nt.in.max_data = 0xFFFF; + nt.in.setup_count = 0; + nt.in.function = NT_TRANSACT_QUERY_SECURITY_DESC; + nt.in.setup = NULL; + + SSVAL(params, 0, io->query_secdesc.in.file.fnum); + SSVAL(params, 2, 0); /* padding */ + SIVAL(params, 4, io->query_secdesc.in.secinfo_flags); + + nt.in.params.data = params; + nt.in.params.length = 8; + + nt.in.data = data_blob(NULL, 0); + + return smb_raw_nttrans_send(tree, &nt); +} + + +/**************************************************************************** +fetch file ACL (async recv) +****************************************************************************/ +NTSTATUS smb_raw_query_secdesc_recv(struct smbcli_request *req, + TALLOC_CTX *mem_ctx, + union smb_fileinfo *io) +{ + NTSTATUS status; + struct smb_nttrans nt; + struct ndr_pull *ndr; + enum ndr_err_code ndr_err; + + status = smb_raw_nttrans_recv(req, mem_ctx, &nt); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* check that the basics are valid */ + if (nt.out.params.length != 4 || + IVAL(nt.out.params.data, 0) > nt.out.data.length) { + return NT_STATUS_INVALID_PARAMETER; + } + + nt.out.data.length = IVAL(nt.out.params.data, 0); + + ndr = ndr_pull_init_blob(&nt.out.data, mem_ctx, NULL); + if (!ndr) { + return NT_STATUS_INVALID_PARAMETER; + } + + io->query_secdesc.out.sd = talloc(mem_ctx, struct security_descriptor); + if (!io->query_secdesc.out.sd) { + return NT_STATUS_NO_MEMORY; + } + ndr_err = ndr_pull_security_descriptor(ndr, NDR_SCALARS|NDR_BUFFERS, + io->query_secdesc.out.sd); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return ndr_map_error2ntstatus(ndr_err); + } + + return NT_STATUS_OK; +} + + +/**************************************************************************** +fetch file ACL (sync interface) +****************************************************************************/ +NTSTATUS smb_raw_query_secdesc(struct smbcli_tree *tree, + TALLOC_CTX *mem_ctx, + union smb_fileinfo *io) +{ + struct smbcli_request *req = smb_raw_query_secdesc_send(tree, io); + return smb_raw_query_secdesc_recv(req, mem_ctx, io); +} + + + +/**************************************************************************** +set file ACL (async send) +****************************************************************************/ +struct smbcli_request *smb_raw_set_secdesc_send(struct smbcli_tree *tree, + union smb_setfileinfo *io) +{ + struct smb_nttrans nt; + uint8_t params[8]; + struct ndr_push *ndr; + struct smbcli_request *req; + enum ndr_err_code ndr_err; + + nt.in.max_setup = 0; + nt.in.max_param = 0; + nt.in.max_data = 0; + nt.in.setup_count = 0; + nt.in.function = NT_TRANSACT_SET_SECURITY_DESC; + nt.in.setup = NULL; + + SSVAL(params, 0, io->set_secdesc.in.file.fnum); + SSVAL(params, 2, 0); /* padding */ + SIVAL(params, 4, io->set_secdesc.in.secinfo_flags); + + nt.in.params.data = params; + nt.in.params.length = 8; + + ndr = ndr_push_init_ctx(NULL, NULL); + if (!ndr) return NULL; + + ndr_err = ndr_push_security_descriptor(ndr, NDR_SCALARS|NDR_BUFFERS, io->set_secdesc.in.sd); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + talloc_free(ndr); + return NULL; + } + + nt.in.data = ndr_push_blob(ndr); + + req = smb_raw_nttrans_send(tree, &nt); + + talloc_free(ndr); + return req; +} + +/**************************************************************************** +set file ACL (sync interface) +****************************************************************************/ +NTSTATUS smb_raw_set_secdesc(struct smbcli_tree *tree, + union smb_setfileinfo *io) +{ + struct smbcli_request *req = smb_raw_set_secdesc_send(tree, io); + return smbcli_request_simple_recv(req); +} diff --git a/source4/libcli/raw/rawdate.c b/source4/libcli/raw/rawdate.c new file mode 100644 index 0000000000..5264653cc6 --- /dev/null +++ b/source4/libcli/raw/rawdate.c @@ -0,0 +1,82 @@ +/* + Unix SMB/CIFS implementation. + + raw date handling functions + + Copyright (C) Andrew Tridgell 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" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" + +/******************************************************************* +put a dos date into a buffer (time/date format) +This takes GMT time and puts local time for zone_offset in the buffer +********************************************************************/ +void raw_push_dos_date(struct smbcli_transport *transport, + uint8_t *buf, int offset, time_t unixdate) +{ + push_dos_date(buf, offset, unixdate, transport->negotiate.server_zone); +} + +/******************************************************************* +put a dos date into a buffer (date/time format) +This takes GMT time and puts local time in the buffer +********************************************************************/ +void raw_push_dos_date2(struct smbcli_transport *transport, + uint8_t *buf, int offset, time_t unixdate) +{ + push_dos_date2(buf, offset, unixdate, transport->negotiate.server_zone); +} + +/******************************************************************* +put a dos 32 bit "unix like" date into a buffer. This routine takes +GMT and converts it to LOCAL time in zone_offset before putting it +********************************************************************/ +void raw_push_dos_date3(struct smbcli_transport *transport, + uint8_t *buf, int offset, time_t unixdate) +{ + push_dos_date3(buf, offset, unixdate, transport->negotiate.server_zone); +} + +/******************************************************************* +convert a dos date +********************************************************************/ +time_t raw_pull_dos_date(struct smbcli_transport *transport, + const uint8_t *date_ptr) +{ + return pull_dos_date(date_ptr, transport->negotiate.server_zone); +} + +/******************************************************************* +like raw_pull_dos_date() but the words are reversed +********************************************************************/ +time_t raw_pull_dos_date2(struct smbcli_transport *transport, + const uint8_t *date_ptr) +{ + return pull_dos_date2(date_ptr, transport->negotiate.server_zone); +} + +/******************************************************************* + create a unix GMT date from a dos date in 32 bit "unix like" format + these arrive in server zone, with corresponding DST + ******************************************************************/ +time_t raw_pull_dos_date3(struct smbcli_transport *transport, + const uint8_t *date_ptr) +{ + return pull_dos_date3(date_ptr, transport->negotiate.server_zone); +} diff --git a/source4/libcli/raw/raweas.c b/source4/libcli/raw/raweas.c new file mode 100644 index 0000000000..09fd4aa412 --- /dev/null +++ b/source4/libcli/raw/raweas.c @@ -0,0 +1,365 @@ +/* + Unix SMB/CIFS implementation. + parsing of EA (extended attribute) lists + Copyright (C) Andrew Tridgell 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 "smb.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" + +/* + work out how many bytes on the wire a ea list will consume. + This assumes the names are strict ascii, which should be a + reasonable assumption +*/ +size_t ea_list_size(uint_t num_eas, struct ea_struct *eas) +{ + uint_t total = 4; + int i; + for (i=0;i<num_eas;i++) { + total += 4 + strlen(eas[i].name.s)+1 + eas[i].value.length; + } + return total; +} + +/* + work out how many bytes on the wire a ea name list will consume. +*/ +static uint_t ea_name_list_size(uint_t num_names, struct ea_name *eas) +{ + uint_t total = 4; + int i; + for (i=0;i<num_names;i++) { + total += 1 + strlen(eas[i].name.s) + 1; + } + return total; +} + +/* + work out how many bytes on the wire a chained ea list will consume. + This assumes the names are strict ascii, which should be a + reasonable assumption +*/ +size_t ea_list_size_chained(uint_t num_eas, struct ea_struct *eas, unsigned alignment) +{ + uint_t total = 0; + int i; + for (i=0;i<num_eas;i++) { + uint_t len = 8 + strlen(eas[i].name.s)+1 + eas[i].value.length; + len = (len + (alignment-1)) & ~(alignment-1); + total += len; + } + return total; +} + +/* + put a ea_list into a pre-allocated buffer - buffer must be at least + of size ea_list_size() +*/ +void ea_put_list(uint8_t *data, uint_t num_eas, struct ea_struct *eas) +{ + int i; + uint32_t ea_size; + + ea_size = ea_list_size(num_eas, eas); + + SIVAL(data, 0, ea_size); + data += 4; + + for (i=0;i<num_eas;i++) { + uint_t nlen = strlen(eas[i].name.s); + SCVAL(data, 0, eas[i].flags); + SCVAL(data, 1, nlen); + SSVAL(data, 2, eas[i].value.length); + memcpy(data+4, eas[i].name.s, nlen+1); + memcpy(data+4+nlen+1, eas[i].value.data, eas[i].value.length); + data += 4+nlen+1+eas[i].value.length; + } +} + + +/* + put a chained ea_list into a pre-allocated buffer - buffer must be + at least of size ea_list_size() +*/ +void ea_put_list_chained(uint8_t *data, uint_t num_eas, struct ea_struct *eas, + unsigned alignment) +{ + int i; + + for (i=0;i<num_eas;i++) { + uint_t nlen = strlen(eas[i].name.s); + uint32_t len = 8+nlen+1+eas[i].value.length; + uint_t pad = ((len + (alignment-1)) & ~(alignment-1)) - len; + if (i == num_eas-1) { + SIVAL(data, 0, 0); + } else { + SIVAL(data, 0, len+pad); + } + SCVAL(data, 4, eas[i].flags); + SCVAL(data, 5, nlen); + SSVAL(data, 6, eas[i].value.length); + memcpy(data+8, eas[i].name.s, nlen+1); + memcpy(data+8+nlen+1, eas[i].value.data, eas[i].value.length); + memset(data+len, 0, pad); + data += len + pad; + } +} + + +/* + pull a ea_struct from a buffer. Return the number of bytes consumed +*/ +uint_t ea_pull_struct(const DATA_BLOB *blob, + TALLOC_CTX *mem_ctx, + struct ea_struct *ea) +{ + uint8_t nlen; + uint16_t vlen; + + ZERO_STRUCTP(ea); + + if (blob->length < 6) { + return 0; + } + + ea->flags = CVAL(blob->data, 0); + nlen = CVAL(blob->data, 1); + vlen = SVAL(blob->data, 2); + + if (nlen+1+vlen > blob->length-4) { + return 0; + } + + ea->name.s = talloc_strndup(mem_ctx, (const char *)(blob->data+4), nlen); + ea->name.private_length = nlen; + ea->value = data_blob_talloc(mem_ctx, NULL, vlen+1); + if (!ea->value.data) return 0; + if (vlen) { + memcpy(ea->value.data, blob->data+4+nlen+1, vlen); + } + ea->value.data[vlen] = 0; + ea->value.length--; + + return 4 + nlen+1 + vlen; +} + + +/* + pull a ea_list from a buffer +*/ +NTSTATUS ea_pull_list(const DATA_BLOB *blob, + TALLOC_CTX *mem_ctx, + uint_t *num_eas, struct ea_struct **eas) +{ + int n; + uint32_t ea_size, ofs; + + if (blob->length < 4) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + + ea_size = IVAL(blob->data, 0); + if (ea_size > blob->length) { + return NT_STATUS_INVALID_PARAMETER; + } + + ofs = 4; + n = 0; + *num_eas = 0; + *eas = NULL; + + while (ofs < ea_size) { + uint_t len; + DATA_BLOB blob2; + + blob2.data = blob->data + ofs; + blob2.length = ea_size - ofs; + + *eas = talloc_realloc(mem_ctx, *eas, struct ea_struct, n+1); + if (! *eas) return NT_STATUS_NO_MEMORY; + + len = ea_pull_struct(&blob2, mem_ctx, &(*eas)[n]); + if (len == 0) { + return NT_STATUS_INVALID_PARAMETER; + } + + ofs += len; + n++; + } + + *num_eas = n; + + return NT_STATUS_OK; +} + + +/* + pull a chained ea_list from a buffer +*/ +NTSTATUS ea_pull_list_chained(const DATA_BLOB *blob, + TALLOC_CTX *mem_ctx, + uint_t *num_eas, struct ea_struct **eas) +{ + int n; + uint32_t ofs; + + if (blob->length < 4) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + + ofs = 0; + n = 0; + *num_eas = 0; + *eas = NULL; + + while (ofs < blob->length) { + uint_t len; + DATA_BLOB blob2; + uint32_t next_ofs = IVAL(blob->data, ofs); + + blob2.data = blob->data + ofs + 4; + blob2.length = blob->length - (ofs + 4); + + *eas = talloc_realloc(mem_ctx, *eas, struct ea_struct, n+1); + if (! *eas) return NT_STATUS_NO_MEMORY; + + len = ea_pull_struct(&blob2, mem_ctx, &(*eas)[n]); + if (len == 0) { + return NT_STATUS_INVALID_PARAMETER; + } + + ofs += next_ofs; + + if (ofs+4 > blob->length) { + return NT_STATUS_INVALID_PARAMETER; + } + n++; + if (next_ofs == 0) break; + } + + *num_eas = n; + + return NT_STATUS_OK; +} + + +/* + pull a ea_name from a buffer. Return the number of bytes consumed +*/ +static uint_t ea_pull_name(const DATA_BLOB *blob, + TALLOC_CTX *mem_ctx, + struct ea_name *ea) +{ + uint8_t nlen; + + if (blob->length < 2) { + return 0; + } + + nlen = CVAL(blob->data, 0); + + if (nlen+2 > blob->length) { + return 0; + } + + ea->name.s = talloc_strndup(mem_ctx, (const char *)(blob->data+1), nlen); + ea->name.private_length = nlen; + + return nlen+2; +} + + +/* + pull a ea_name list from a buffer +*/ +NTSTATUS ea_pull_name_list(const DATA_BLOB *blob, + TALLOC_CTX *mem_ctx, + uint_t *num_names, struct ea_name **ea_names) +{ + int n; + uint32_t ea_size, ofs; + + if (blob->length < 4) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + + ea_size = IVAL(blob->data, 0); + if (ea_size > blob->length) { + return NT_STATUS_INVALID_PARAMETER; + } + + ofs = 4; + n = 0; + *num_names = 0; + *ea_names = NULL; + + while (ofs < ea_size) { + uint_t len; + DATA_BLOB blob2; + + blob2.data = blob->data + ofs; + blob2.length = ea_size - ofs; + + *ea_names = talloc_realloc(mem_ctx, *ea_names, struct ea_name, n+1); + if (! *ea_names) return NT_STATUS_NO_MEMORY; + + len = ea_pull_name(&blob2, mem_ctx, &(*ea_names)[n]); + if (len == 0) { + return NT_STATUS_INVALID_PARAMETER; + } + + ofs += len; + n++; + } + + *num_names = n; + + return NT_STATUS_OK; +} + + +/* + put a ea_name list into a data blob +*/ +bool ea_push_name_list(TALLOC_CTX *mem_ctx, + DATA_BLOB *data, uint_t num_names, struct ea_name *eas) +{ + int i; + uint32_t ea_size; + uint32_t off; + + ea_size = ea_name_list_size(num_names, eas); + + *data = data_blob_talloc(mem_ctx, NULL, ea_size); + if (data->data == NULL) { + return false; + } + + SIVAL(data->data, 0, ea_size); + off = 4; + + for (i=0;i<num_names;i++) { + uint_t nlen = strlen(eas[i].name.s); + SCVAL(data->data, off, nlen); + memcpy(data->data+off+1, eas[i].name.s, nlen+1); + off += 1+nlen+1; + } + + return true; +} diff --git a/source4/libcli/raw/rawfile.c b/source4/libcli/raw/rawfile.c new file mode 100644 index 0000000000..63de051b33 --- /dev/null +++ b/source4/libcli/raw/rawfile.c @@ -0,0 +1,1017 @@ +/* + Unix SMB/CIFS implementation. + client file operations + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) Jeremy Allison 2001-2002 + Copyright (C) James Myers 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 "smb.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "librpc/gen_ndr/ndr_security.h" + +#define SETUP_REQUEST(cmd, wct, buflen) do { \ + req = smbcli_request_setup(tree, cmd, wct, buflen); \ + if (!req) return NULL; \ +} while (0) + +/** + Return a string representing a CIFS attribute for a file. +**/ +char *attrib_string(TALLOC_CTX *mem_ctx, uint32_t attrib) +{ + int i, len; + const struct { + char c; + uint16_t attr; + } attr_strs[] = { + {'V', FILE_ATTRIBUTE_VOLUME}, + {'D', FILE_ATTRIBUTE_DIRECTORY}, + {'A', FILE_ATTRIBUTE_ARCHIVE}, + {'H', FILE_ATTRIBUTE_HIDDEN}, + {'S', FILE_ATTRIBUTE_SYSTEM}, + {'N', FILE_ATTRIBUTE_NORMAL}, + {'R', FILE_ATTRIBUTE_READONLY}, + {'d', FILE_ATTRIBUTE_DEVICE}, + {'t', FILE_ATTRIBUTE_TEMPORARY}, + {'s', FILE_ATTRIBUTE_SPARSE}, + {'r', FILE_ATTRIBUTE_REPARSE_POINT}, + {'c', FILE_ATTRIBUTE_COMPRESSED}, + {'o', FILE_ATTRIBUTE_OFFLINE}, + {'n', FILE_ATTRIBUTE_NONINDEXED}, + {'e', FILE_ATTRIBUTE_ENCRYPTED} + }; + char *ret; + + ret = talloc_array(mem_ctx, char, ARRAY_SIZE(attr_strs)+1); + if (!ret) { + return NULL; + } + + for (len=i=0; i<ARRAY_SIZE(attr_strs); i++) { + if (attrib & attr_strs[i].attr) { + ret[len++] = attr_strs[i].c; + } + } + + ret[len] = 0; + + talloc_set_name_const(ret, ret); + + return ret; +} + +/**************************************************************************** + Rename a file - async interface +****************************************************************************/ +struct smbcli_request *smb_raw_rename_send(struct smbcli_tree *tree, + union smb_rename *parms) +{ + struct smbcli_request *req = NULL; + struct smb_nttrans nt; + TALLOC_CTX *mem_ctx; + + switch (parms->generic.level) { + case RAW_RENAME_RENAME: + SETUP_REQUEST(SMBmv, 1, 0); + SSVAL(req->out.vwv, VWV(0), parms->rename.in.attrib); + smbcli_req_append_ascii4(req, parms->rename.in.pattern1, STR_TERMINATE); + smbcli_req_append_ascii4(req, parms->rename.in.pattern2, STR_TERMINATE); + break; + + case RAW_RENAME_NTRENAME: + SETUP_REQUEST(SMBntrename, 4, 0); + SSVAL(req->out.vwv, VWV(0), parms->ntrename.in.attrib); + SSVAL(req->out.vwv, VWV(1), parms->ntrename.in.flags); + SIVAL(req->out.vwv, VWV(2), parms->ntrename.in.cluster_size); + smbcli_req_append_ascii4(req, parms->ntrename.in.old_name, STR_TERMINATE); + smbcli_req_append_ascii4(req, parms->ntrename.in.new_name, STR_TERMINATE); + break; + + case RAW_RENAME_NTTRANS: + + mem_ctx = talloc_new(tree); + + nt.in.max_setup = 0; + nt.in.max_param = 0; + nt.in.max_data = 0; + nt.in.setup_count = 0; + nt.in.setup = NULL; + nt.in.function = NT_TRANSACT_RENAME; + nt.in.params = data_blob_talloc(mem_ctx, NULL, 4); + nt.in.data = data_blob(NULL, 0); + + SSVAL(nt.in.params.data, VWV(0), parms->nttrans.in.file.fnum); + SSVAL(nt.in.params.data, VWV(1), parms->nttrans.in.flags); + + smbcli_blob_append_string(tree->session, mem_ctx, + &nt.in.params, parms->nttrans.in.new_name, + STR_TERMINATE); + + req = smb_raw_nttrans_send(tree, &nt); + talloc_free(mem_ctx); + return req; + } + + if (!smbcli_request_send(req)) { + smbcli_request_destroy(req); + return NULL; + } + + return req; +} + +/**************************************************************************** + Rename a file - sync interface +****************************************************************************/ +_PUBLIC_ NTSTATUS smb_raw_rename(struct smbcli_tree *tree, + union smb_rename *parms) +{ + struct smbcli_request *req = smb_raw_rename_send(tree, parms); + return smbcli_request_simple_recv(req); +} + + +/**************************************************************************** + Delete a file - async interface +****************************************************************************/ +struct smbcli_request *smb_raw_unlink_send(struct smbcli_tree *tree, + union smb_unlink *parms) +{ + struct smbcli_request *req; + + SETUP_REQUEST(SMBunlink, 1, 0); + + SSVAL(req->out.vwv, VWV(0), parms->unlink.in.attrib); + smbcli_req_append_ascii4(req, parms->unlink.in.pattern, STR_TERMINATE); + + if (!smbcli_request_send(req)) { + smbcli_request_destroy(req); + return NULL; + } + return req; +} + +/* + delete a file - sync interface +*/ +_PUBLIC_ NTSTATUS smb_raw_unlink(struct smbcli_tree *tree, + union smb_unlink *parms) +{ + struct smbcli_request *req = smb_raw_unlink_send(tree, parms); + return smbcli_request_simple_recv(req); +} + + +/**************************************************************************** + create a directory using TRANSACT2_MKDIR - async interface +****************************************************************************/ +static struct smbcli_request *smb_raw_t2mkdir_send(struct smbcli_tree *tree, + union smb_mkdir *parms) +{ + struct smb_trans2 t2; + uint16_t setup = TRANSACT2_MKDIR; + TALLOC_CTX *mem_ctx; + struct smbcli_request *req; + uint16_t data_total; + + mem_ctx = talloc_init("t2mkdir"); + + data_total = ea_list_size(parms->t2mkdir.in.num_eas, parms->t2mkdir.in.eas); + + t2.in.max_param = 2; + t2.in.max_data = 0; + t2.in.max_setup = 0; + t2.in.flags = 0; + t2.in.timeout = 0; + t2.in.setup_count = 1; + t2.in.setup = &setup; + t2.in.params = data_blob_talloc(mem_ctx, NULL, 4); + t2.in.data = data_blob_talloc(mem_ctx, NULL, data_total); + + SIVAL(t2.in.params.data, VWV(0), 0); /* reserved */ + + smbcli_blob_append_string(tree->session, mem_ctx, + &t2.in.params, parms->t2mkdir.in.path, STR_TERMINATE); + + ea_put_list(t2.in.data.data, parms->t2mkdir.in.num_eas, parms->t2mkdir.in.eas); + + req = smb_raw_trans2_send(tree, &t2); + + talloc_free(mem_ctx); + + return req; +} + +/**************************************************************************** + Create a directory - async interface +****************************************************************************/ +struct smbcli_request *smb_raw_mkdir_send(struct smbcli_tree *tree, + union smb_mkdir *parms) +{ + struct smbcli_request *req; + + if (parms->generic.level == RAW_MKDIR_T2MKDIR) { + return smb_raw_t2mkdir_send(tree, parms); + } + + if (parms->generic.level != RAW_MKDIR_MKDIR) { + return NULL; + } + + SETUP_REQUEST(SMBmkdir, 0, 0); + + smbcli_req_append_ascii4(req, parms->mkdir.in.path, STR_TERMINATE); + + if (!smbcli_request_send(req)) { + return NULL; + } + + return req; +} + +/**************************************************************************** + Create a directory - sync interface +****************************************************************************/ +_PUBLIC_ NTSTATUS smb_raw_mkdir(struct smbcli_tree *tree, + union smb_mkdir *parms) +{ + struct smbcli_request *req = smb_raw_mkdir_send(tree, parms); + return smbcli_request_simple_recv(req); +} + +/**************************************************************************** + Remove a directory - async interface +****************************************************************************/ +struct smbcli_request *smb_raw_rmdir_send(struct smbcli_tree *tree, + struct smb_rmdir *parms) +{ + struct smbcli_request *req; + + SETUP_REQUEST(SMBrmdir, 0, 0); + + smbcli_req_append_ascii4(req, parms->in.path, STR_TERMINATE); + + if (!smbcli_request_send(req)) { + smbcli_request_destroy(req); + return NULL; + } + + return req; +} + +/**************************************************************************** + Remove a directory - sync interface +****************************************************************************/ +_PUBLIC_ NTSTATUS smb_raw_rmdir(struct smbcli_tree *tree, + struct smb_rmdir *parms) +{ + struct smbcli_request *req = smb_raw_rmdir_send(tree, parms); + return smbcli_request_simple_recv(req); +} + + +/* + Open a file using TRANSACT2_OPEN - async recv +*/ +static NTSTATUS smb_raw_nttrans_create_recv(struct smbcli_request *req, + TALLOC_CTX *mem_ctx, + union smb_open *parms) +{ + NTSTATUS status; + struct smb_nttrans nt; + uint8_t *params; + + status = smb_raw_nttrans_recv(req, mem_ctx, &nt); + if (!NT_STATUS_IS_OK(status)) return status; + + if (nt.out.params.length < 69) { + return NT_STATUS_INVALID_PARAMETER; + } + + params = nt.out.params.data; + + parms->ntcreatex.out.oplock_level = CVAL(params, 0); + parms->ntcreatex.out.file.fnum = SVAL(params, 2); + parms->ntcreatex.out.create_action = IVAL(params, 4); + parms->ntcreatex.out.create_time = smbcli_pull_nttime(params, 12); + parms->ntcreatex.out.access_time = smbcli_pull_nttime(params, 20); + parms->ntcreatex.out.write_time = smbcli_pull_nttime(params, 28); + parms->ntcreatex.out.change_time = smbcli_pull_nttime(params, 36); + parms->ntcreatex.out.attrib = IVAL(params, 44); + parms->ntcreatex.out.alloc_size = BVAL(params, 48); + parms->ntcreatex.out.size = BVAL(params, 56); + parms->ntcreatex.out.file_type = SVAL(params, 64); + parms->ntcreatex.out.ipc_state = SVAL(params, 66); + parms->ntcreatex.out.is_directory = CVAL(params, 68); + + return NT_STATUS_OK; +} + + +/* + Open a file using NTTRANS CREATE - async send +*/ +static struct smbcli_request *smb_raw_nttrans_create_send(struct smbcli_tree *tree, + union smb_open *parms) +{ + struct smb_nttrans nt; + uint8_t *params; + TALLOC_CTX *mem_ctx = talloc_new(tree); + uint16_t fname_len; + DATA_BLOB sd_blob, ea_blob; + struct smbcli_request *req; + + nt.in.max_setup = 0; + nt.in.max_param = 101; + nt.in.max_data = 0; + nt.in.setup_count = 0; + nt.in.function = NT_TRANSACT_CREATE; + nt.in.setup = NULL; + + sd_blob = data_blob(NULL, 0); + ea_blob = data_blob(NULL, 0); + + if (parms->ntcreatex.in.sec_desc) { + enum ndr_err_code ndr_err; + ndr_err = ndr_push_struct_blob(&sd_blob, mem_ctx, NULL, + parms->ntcreatex.in.sec_desc, + (ndr_push_flags_fn_t)ndr_push_security_descriptor); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + talloc_free(mem_ctx); + return NULL; + } + } + + if (parms->ntcreatex.in.ea_list) { + uint32_t ea_size = ea_list_size_chained(parms->ntcreatex.in.ea_list->num_eas, + parms->ntcreatex.in.ea_list->eas, 4); + ea_blob = data_blob_talloc(mem_ctx, NULL, ea_size); + if (ea_blob.data == NULL) { + return NULL; + } + ea_put_list_chained(ea_blob.data, + parms->ntcreatex.in.ea_list->num_eas, + parms->ntcreatex.in.ea_list->eas, 4); + } + + nt.in.params = data_blob_talloc(mem_ctx, NULL, 53); + if (nt.in.params.data == NULL) { + talloc_free(mem_ctx); + return NULL; + } + + /* build the parameter section */ + params = nt.in.params.data; + + SIVAL(params, 0, parms->ntcreatex.in.flags); + SIVAL(params, 4, parms->ntcreatex.in.root_fid); + SIVAL(params, 8, parms->ntcreatex.in.access_mask); + SBVAL(params, 12, parms->ntcreatex.in.alloc_size); + SIVAL(params, 20, parms->ntcreatex.in.file_attr); + SIVAL(params, 24, parms->ntcreatex.in.share_access); + SIVAL(params, 28, parms->ntcreatex.in.open_disposition); + SIVAL(params, 32, parms->ntcreatex.in.create_options); + SIVAL(params, 36, sd_blob.length); + SIVAL(params, 40, ea_blob.length); + SIVAL(params, 48, parms->ntcreatex.in.impersonation); + SCVAL(params, 52, parms->ntcreatex.in.security_flags); + + /* the empty string first forces the correct alignment */ + smbcli_blob_append_string(tree->session, mem_ctx, &nt.in.params,"", 0); + fname_len = smbcli_blob_append_string(tree->session, mem_ctx, &nt.in.params, + parms->ntcreatex.in.fname, STR_TERMINATE); + + SIVAL(nt.in.params.data, 44, fname_len); + + /* build the data section */ + nt.in.data = data_blob_talloc(mem_ctx, NULL, sd_blob.length + ea_blob.length); + memcpy(nt.in.data.data, sd_blob.data, sd_blob.length); + memcpy(nt.in.data.data+sd_blob.length, ea_blob.data, ea_blob.length); + + /* send the request on its way */ + req = smb_raw_nttrans_send(tree, &nt); + + talloc_free(mem_ctx); + + return req; +} + + +/**************************************************************************** + Open a file using TRANSACT2_OPEN - async send +****************************************************************************/ +static struct smbcli_request *smb_raw_t2open_send(struct smbcli_tree *tree, + union smb_open *parms) +{ + struct smb_trans2 t2; + uint16_t setup = TRANSACT2_OPEN; + TALLOC_CTX *mem_ctx = talloc_init("smb_raw_t2open"); + struct smbcli_request *req; + uint16_t list_size; + + list_size = ea_list_size(parms->t2open.in.num_eas, parms->t2open.in.eas); + + t2.in.max_param = 30; + t2.in.max_data = 0; + t2.in.max_setup = 0; + t2.in.flags = 0; + t2.in.timeout = 0; + t2.in.setup_count = 1; + t2.in.setup = &setup; + t2.in.params = data_blob_talloc(mem_ctx, NULL, 28); + t2.in.data = data_blob_talloc(mem_ctx, NULL, list_size); + + SSVAL(t2.in.params.data, VWV(0), parms->t2open.in.flags); + SSVAL(t2.in.params.data, VWV(1), parms->t2open.in.open_mode); + SSVAL(t2.in.params.data, VWV(2), parms->t2open.in.search_attrs); + SSVAL(t2.in.params.data, VWV(3), parms->t2open.in.file_attrs); + raw_push_dos_date(tree->session->transport, + t2.in.params.data, VWV(4), parms->t2open.in.write_time); + SSVAL(t2.in.params.data, VWV(6), parms->t2open.in.open_func); + SIVAL(t2.in.params.data, VWV(7), parms->t2open.in.size); + SIVAL(t2.in.params.data, VWV(9), parms->t2open.in.timeout); + SIVAL(t2.in.params.data, VWV(11), 0); + SSVAL(t2.in.params.data, VWV(13), 0); + + smbcli_blob_append_string(tree->session, mem_ctx, + &t2.in.params, parms->t2open.in.fname, + STR_TERMINATE); + + ea_put_list(t2.in.data.data, parms->t2open.in.num_eas, parms->t2open.in.eas); + + req = smb_raw_trans2_send(tree, &t2); + + talloc_free(mem_ctx); + + return req; +} + + +/**************************************************************************** + Open a file using TRANSACT2_OPEN - async recv +****************************************************************************/ +static NTSTATUS smb_raw_t2open_recv(struct smbcli_request *req, TALLOC_CTX *mem_ctx, union smb_open *parms) +{ + struct smbcli_transport *transport = req->transport; + struct smb_trans2 t2; + NTSTATUS status; + + status = smb_raw_trans2_recv(req, mem_ctx, &t2); + if (!NT_STATUS_IS_OK(status)) return status; + + if (t2.out.params.length < 30) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + + parms->t2open.out.file.fnum = SVAL(t2.out.params.data, VWV(0)); + parms->t2open.out.attrib = SVAL(t2.out.params.data, VWV(1)); + parms->t2open.out.write_time = raw_pull_dos_date3(transport, t2.out.params.data + VWV(2)); + parms->t2open.out.size = IVAL(t2.out.params.data, VWV(4)); + parms->t2open.out.access = SVAL(t2.out.params.data, VWV(6)); + parms->t2open.out.ftype = SVAL(t2.out.params.data, VWV(7)); + parms->t2open.out.devstate = SVAL(t2.out.params.data, VWV(8)); + parms->t2open.out.action = SVAL(t2.out.params.data, VWV(9)); + parms->t2open.out.file_id = SVAL(t2.out.params.data, VWV(10)); + + return NT_STATUS_OK; +} + +/**************************************************************************** + Open a file - async send +****************************************************************************/ +_PUBLIC_ struct smbcli_request *smb_raw_open_send(struct smbcli_tree *tree, union smb_open *parms) +{ + int len; + struct smbcli_request *req = NULL; + bool bigoffset = false; + + switch (parms->generic.level) { + case RAW_OPEN_T2OPEN: + return smb_raw_t2open_send(tree, parms); + + case RAW_OPEN_OPEN: + SETUP_REQUEST(SMBopen, 2, 0); + SSVAL(req->out.vwv, VWV(0), parms->openold.in.open_mode); + SSVAL(req->out.vwv, VWV(1), parms->openold.in.search_attrs); + smbcli_req_append_ascii4(req, parms->openold.in.fname, STR_TERMINATE); + break; + + case RAW_OPEN_OPENX: + SETUP_REQUEST(SMBopenX, 15, 0); + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), parms->openx.in.flags); + SSVAL(req->out.vwv, VWV(3), parms->openx.in.open_mode); + SSVAL(req->out.vwv, VWV(4), parms->openx.in.search_attrs); + SSVAL(req->out.vwv, VWV(5), parms->openx.in.file_attrs); + raw_push_dos_date3(tree->session->transport, + req->out.vwv, VWV(6), parms->openx.in.write_time); + SSVAL(req->out.vwv, VWV(8), parms->openx.in.open_func); + SIVAL(req->out.vwv, VWV(9), parms->openx.in.size); + SIVAL(req->out.vwv, VWV(11),parms->openx.in.timeout); + SIVAL(req->out.vwv, VWV(13),0); /* reserved */ + smbcli_req_append_string(req, parms->openx.in.fname, STR_TERMINATE); + break; + + case RAW_OPEN_MKNEW: + SETUP_REQUEST(SMBmknew, 3, 0); + SSVAL(req->out.vwv, VWV(0), parms->mknew.in.attrib); + raw_push_dos_date3(tree->session->transport, + req->out.vwv, VWV(1), parms->mknew.in.write_time); + smbcli_req_append_ascii4(req, parms->mknew.in.fname, STR_TERMINATE); + break; + + case RAW_OPEN_CREATE: + SETUP_REQUEST(SMBcreate, 3, 0); + SSVAL(req->out.vwv, VWV(0), parms->create.in.attrib); + raw_push_dos_date3(tree->session->transport, + req->out.vwv, VWV(1), parms->create.in.write_time); + smbcli_req_append_ascii4(req, parms->create.in.fname, STR_TERMINATE); + break; + + case RAW_OPEN_CTEMP: + SETUP_REQUEST(SMBctemp, 3, 0); + SSVAL(req->out.vwv, VWV(0), parms->ctemp.in.attrib); + raw_push_dos_date3(tree->session->transport, + req->out.vwv, VWV(1), parms->ctemp.in.write_time); + smbcli_req_append_ascii4(req, parms->ctemp.in.directory, STR_TERMINATE); + break; + + case RAW_OPEN_SPLOPEN: + SETUP_REQUEST(SMBsplopen, 2, 0); + SSVAL(req->out.vwv, VWV(0), parms->splopen.in.setup_length); + SSVAL(req->out.vwv, VWV(1), parms->splopen.in.mode); + break; + + case RAW_OPEN_NTCREATEX: + SETUP_REQUEST(SMBntcreateX, 24, 0); + SSVAL(req->out.vwv, VWV(0),SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1),0); + SCVAL(req->out.vwv, VWV(2),0); /* padding */ + SIVAL(req->out.vwv, 7, parms->ntcreatex.in.flags); + SIVAL(req->out.vwv, 11, parms->ntcreatex.in.root_fid); + SIVAL(req->out.vwv, 15, parms->ntcreatex.in.access_mask); + SBVAL(req->out.vwv, 19, parms->ntcreatex.in.alloc_size); + SIVAL(req->out.vwv, 27, parms->ntcreatex.in.file_attr); + SIVAL(req->out.vwv, 31, parms->ntcreatex.in.share_access); + SIVAL(req->out.vwv, 35, parms->ntcreatex.in.open_disposition); + SIVAL(req->out.vwv, 39, parms->ntcreatex.in.create_options); + SIVAL(req->out.vwv, 43, parms->ntcreatex.in.impersonation); + SCVAL(req->out.vwv, 47, parms->ntcreatex.in.security_flags); + + smbcli_req_append_string_len(req, parms->ntcreatex.in.fname, STR_TERMINATE, &len); + SSVAL(req->out.vwv, 5, len); + break; + + case RAW_OPEN_NTTRANS_CREATE: + return smb_raw_nttrans_create_send(tree, parms); + + + case RAW_OPEN_OPENX_READX: + SETUP_REQUEST(SMBopenX, 15, 0); + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), parms->openxreadx.in.flags); + SSVAL(req->out.vwv, VWV(3), parms->openxreadx.in.open_mode); + SSVAL(req->out.vwv, VWV(4), parms->openxreadx.in.search_attrs); + SSVAL(req->out.vwv, VWV(5), parms->openxreadx.in.file_attrs); + raw_push_dos_date3(tree->session->transport, + req->out.vwv, VWV(6), parms->openxreadx.in.write_time); + SSVAL(req->out.vwv, VWV(8), parms->openxreadx.in.open_func); + SIVAL(req->out.vwv, VWV(9), parms->openxreadx.in.size); + SIVAL(req->out.vwv, VWV(11),parms->openxreadx.in.timeout); + SIVAL(req->out.vwv, VWV(13),0); + smbcli_req_append_string(req, parms->openxreadx.in.fname, STR_TERMINATE); + + if (tree->session->transport->negotiate.capabilities & CAP_LARGE_FILES) { + bigoffset = true; + } + + smbcli_chained_request_setup(req, SMBreadX, bigoffset ? 12 : 10, 0); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), 0); + SIVAL(req->out.vwv, VWV(3), parms->openxreadx.in.offset); + SSVAL(req->out.vwv, VWV(5), parms->openxreadx.in.maxcnt & 0xFFFF); + SSVAL(req->out.vwv, VWV(6), parms->openxreadx.in.mincnt); + SIVAL(req->out.vwv, VWV(7), parms->openxreadx.in.maxcnt >> 16); + SSVAL(req->out.vwv, VWV(9), parms->openxreadx.in.remaining); + if (bigoffset) { + SIVAL(req->out.vwv, VWV(10),parms->openxreadx.in.offset>>32); + } + break; + case RAW_OPEN_SMB2: + return NULL; + } + + if (!smbcli_request_send(req)) { + smbcli_request_destroy(req); + return NULL; + } + + return req; +} + +/**************************************************************************** + Open a file - async recv +****************************************************************************/ +_PUBLIC_ NTSTATUS smb_raw_open_recv(struct smbcli_request *req, TALLOC_CTX *mem_ctx, union smb_open *parms) +{ + NTSTATUS status; + + if (!smbcli_request_receive(req) || + smbcli_request_is_error(req)) { + goto failed; + } + + switch (parms->openold.level) { + case RAW_OPEN_T2OPEN: + return smb_raw_t2open_recv(req, mem_ctx, parms); + + case RAW_OPEN_OPEN: + SMBCLI_CHECK_WCT(req, 7); + parms->openold.out.file.fnum = SVAL(req->in.vwv, VWV(0)); + parms->openold.out.attrib = SVAL(req->in.vwv, VWV(1)); + parms->openold.out.write_time = raw_pull_dos_date3(req->transport, + req->in.vwv + VWV(2)); + parms->openold.out.size = IVAL(req->in.vwv, VWV(4)); + parms->openold.out.rmode = SVAL(req->in.vwv, VWV(6)); + break; + + case RAW_OPEN_OPENX: + SMBCLI_CHECK_MIN_WCT(req, 15); + parms->openx.out.file.fnum = SVAL(req->in.vwv, VWV(2)); + parms->openx.out.attrib = SVAL(req->in.vwv, VWV(3)); + parms->openx.out.write_time = raw_pull_dos_date3(req->transport, + req->in.vwv + VWV(4)); + parms->openx.out.size = IVAL(req->in.vwv, VWV(6)); + parms->openx.out.access = SVAL(req->in.vwv, VWV(8)); + parms->openx.out.ftype = SVAL(req->in.vwv, VWV(9)); + parms->openx.out.devstate = SVAL(req->in.vwv, VWV(10)); + parms->openx.out.action = SVAL(req->in.vwv, VWV(11)); + parms->openx.out.unique_fid = IVAL(req->in.vwv, VWV(12)); + if (req->in.wct >= 19) { + parms->openx.out.access_mask = IVAL(req->in.vwv, VWV(15)); + parms->openx.out.unknown = IVAL(req->in.vwv, VWV(17)); + } else { + parms->openx.out.access_mask = 0; + parms->openx.out.unknown = 0; + } + break; + + case RAW_OPEN_MKNEW: + SMBCLI_CHECK_WCT(req, 1); + parms->mknew.out.file.fnum = SVAL(req->in.vwv, VWV(0)); + break; + + case RAW_OPEN_CREATE: + SMBCLI_CHECK_WCT(req, 1); + parms->create.out.file.fnum = SVAL(req->in.vwv, VWV(0)); + break; + + case RAW_OPEN_CTEMP: + SMBCLI_CHECK_WCT(req, 1); + parms->ctemp.out.file.fnum = SVAL(req->in.vwv, VWV(0)); + smbcli_req_pull_string(&req->in.bufinfo, mem_ctx, &parms->ctemp.out.name, req->in.data, -1, STR_TERMINATE | STR_ASCII); + break; + + case RAW_OPEN_SPLOPEN: + SMBCLI_CHECK_WCT(req, 1); + parms->splopen.out.file.fnum = SVAL(req->in.vwv, VWV(0)); + break; + + case RAW_OPEN_NTCREATEX: + SMBCLI_CHECK_MIN_WCT(req, 34); + parms->ntcreatex.out.oplock_level = CVAL(req->in.vwv, 4); + parms->ntcreatex.out.file.fnum = SVAL(req->in.vwv, 5); + parms->ntcreatex.out.create_action = IVAL(req->in.vwv, 7); + parms->ntcreatex.out.create_time = smbcli_pull_nttime(req->in.vwv, 11); + parms->ntcreatex.out.access_time = smbcli_pull_nttime(req->in.vwv, 19); + parms->ntcreatex.out.write_time = smbcli_pull_nttime(req->in.vwv, 27); + parms->ntcreatex.out.change_time = smbcli_pull_nttime(req->in.vwv, 35); + parms->ntcreatex.out.attrib = IVAL(req->in.vwv, 43); + parms->ntcreatex.out.alloc_size = BVAL(req->in.vwv, 47); + parms->ntcreatex.out.size = BVAL(req->in.vwv, 55); + parms->ntcreatex.out.file_type = SVAL(req->in.vwv, 63); + parms->ntcreatex.out.ipc_state = SVAL(req->in.vwv, 65); + parms->ntcreatex.out.is_directory = CVAL(req->in.vwv, 67); + break; + + case RAW_OPEN_NTTRANS_CREATE: + return smb_raw_nttrans_create_recv(req, mem_ctx, parms); + + case RAW_OPEN_OPENX_READX: + SMBCLI_CHECK_MIN_WCT(req, 15); + parms->openxreadx.out.file.fnum = SVAL(req->in.vwv, VWV(2)); + parms->openxreadx.out.attrib = SVAL(req->in.vwv, VWV(3)); + parms->openxreadx.out.write_time = raw_pull_dos_date3(req->transport, + req->in.vwv + VWV(4)); + parms->openxreadx.out.size = IVAL(req->in.vwv, VWV(6)); + parms->openxreadx.out.access = SVAL(req->in.vwv, VWV(8)); + parms->openxreadx.out.ftype = SVAL(req->in.vwv, VWV(9)); + parms->openxreadx.out.devstate = SVAL(req->in.vwv, VWV(10)); + parms->openxreadx.out.action = SVAL(req->in.vwv, VWV(11)); + parms->openxreadx.out.unique_fid = IVAL(req->in.vwv, VWV(12)); + if (req->in.wct >= 19) { + parms->openxreadx.out.access_mask = IVAL(req->in.vwv, VWV(15)); + parms->openxreadx.out.unknown = IVAL(req->in.vwv, VWV(17)); + } else { + parms->openxreadx.out.access_mask = 0; + parms->openxreadx.out.unknown = 0; + } + + status = smbcli_chained_advance(req); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + SMBCLI_CHECK_WCT(req, 12); + parms->openxreadx.out.remaining = SVAL(req->in.vwv, VWV(2)); + parms->openxreadx.out.compaction_mode = SVAL(req->in.vwv, VWV(3)); + parms->openxreadx.out.nread = SVAL(req->in.vwv, VWV(5)); + if (parms->openxreadx.out.nread > + MAX(parms->openxreadx.in.mincnt, parms->openxreadx.in.maxcnt) || + !smbcli_raw_pull_data(&req->in.bufinfo, req->in.hdr + SVAL(req->in.vwv, VWV(6)), + parms->openxreadx.out.nread, + parms->openxreadx.out.data)) { + req->status = NT_STATUS_BUFFER_TOO_SMALL; + } + break; + case RAW_OPEN_SMB2: + req->status = NT_STATUS_INTERNAL_ERROR; + break; + } + +failed: + return smbcli_request_destroy(req); +} + + +/**************************************************************************** + Open a file - sync interface +****************************************************************************/ +_PUBLIC_ NTSTATUS smb_raw_open(struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, union smb_open *parms) +{ + struct smbcli_request *req = smb_raw_open_send(tree, parms); + return smb_raw_open_recv(req, mem_ctx, parms); +} + + +/**************************************************************************** + Close a file - async send +****************************************************************************/ +_PUBLIC_ struct smbcli_request *smb_raw_close_send(struct smbcli_tree *tree, union smb_close *parms) +{ + struct smbcli_request *req = NULL; + + switch (parms->generic.level) { + case RAW_CLOSE_CLOSE: + SETUP_REQUEST(SMBclose, 3, 0); + SSVAL(req->out.vwv, VWV(0), parms->close.in.file.fnum); + raw_push_dos_date3(tree->session->transport, + req->out.vwv, VWV(1), parms->close.in.write_time); + break; + + case RAW_CLOSE_SPLCLOSE: + SETUP_REQUEST(SMBsplclose, 3, 0); + SSVAL(req->out.vwv, VWV(0), parms->splclose.in.file.fnum); + SIVAL(req->out.vwv, VWV(1), 0); /* reserved */ + break; + + case RAW_CLOSE_SMB2: + return NULL; + } + + if (!req) return NULL; + + if (!smbcli_request_send(req)) { + smbcli_request_destroy(req); + return NULL; + } + + return req; +} + + +/**************************************************************************** + Close a file - sync interface +****************************************************************************/ +_PUBLIC_ NTSTATUS smb_raw_close(struct smbcli_tree *tree, union smb_close *parms) +{ + struct smbcli_request *req = smb_raw_close_send(tree, parms); + return smbcli_request_simple_recv(req); +} + + +/**************************************************************************** + Locking calls - async interface +****************************************************************************/ +struct smbcli_request *smb_raw_lock_send(struct smbcli_tree *tree, union smb_lock *parms) +{ + struct smbcli_request *req = NULL; + + switch (parms->generic.level) { + case RAW_LOCK_LOCK: + SETUP_REQUEST(SMBlock, 5, 0); + SSVAL(req->out.vwv, VWV(0), parms->lock.in.file.fnum); + SIVAL(req->out.vwv, VWV(1), parms->lock.in.count); + SIVAL(req->out.vwv, VWV(3), parms->lock.in.offset); + break; + + case RAW_LOCK_UNLOCK: + SETUP_REQUEST(SMBunlock, 5, 0); + SSVAL(req->out.vwv, VWV(0), parms->unlock.in.file.fnum); + SIVAL(req->out.vwv, VWV(1), parms->unlock.in.count); + SIVAL(req->out.vwv, VWV(3), parms->unlock.in.offset); + break; + + case RAW_LOCK_LOCKX: { + struct smb_lock_entry *lockp; + uint_t lck_size = (parms->lockx.in.mode & LOCKING_ANDX_LARGE_FILES)? 20 : 10; + uint_t lock_count = parms->lockx.in.ulock_cnt + parms->lockx.in.lock_cnt; + int i; + + SETUP_REQUEST(SMBlockingX, 8, lck_size * lock_count); + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), parms->lockx.in.file.fnum); + SSVAL(req->out.vwv, VWV(3), parms->lockx.in.mode); + SIVAL(req->out.vwv, VWV(4), parms->lockx.in.timeout); + SSVAL(req->out.vwv, VWV(6), parms->lockx.in.ulock_cnt); + SSVAL(req->out.vwv, VWV(7), parms->lockx.in.lock_cnt); + + /* copy in all the locks */ + lockp = &parms->lockx.in.locks[0]; + for (i = 0; i < lock_count; i++) { + uint8_t *p = req->out.data + lck_size * i; + SSVAL(p, 0, lockp[i].pid); + if (parms->lockx.in.mode & LOCKING_ANDX_LARGE_FILES) { + SSVAL(p, 2, 0); /* reserved */ + SIVAL(p, 4, lockp[i].offset>>32); + SIVAL(p, 8, lockp[i].offset); + SIVAL(p, 12, lockp[i].count>>32); + SIVAL(p, 16, lockp[i].count); + } else { + SIVAL(p, 2, lockp[i].offset); + SIVAL(p, 6, lockp[i].count); + } + } + break; + } + case RAW_LOCK_SMB2: + return NULL; + } + + if (!smbcli_request_send(req)) { + smbcli_request_destroy(req); + return NULL; + } + + return req; +} + +/**************************************************************************** + Locking calls - sync interface +****************************************************************************/ +_PUBLIC_ NTSTATUS smb_raw_lock(struct smbcli_tree *tree, union smb_lock *parms) +{ + struct smbcli_request *req = smb_raw_lock_send(tree, parms); + return smbcli_request_simple_recv(req); +} + + +/**************************************************************************** + Check for existence of a dir - async send +****************************************************************************/ +struct smbcli_request *smb_raw_chkpath_send(struct smbcli_tree *tree, union smb_chkpath *parms) +{ + struct smbcli_request *req; + + SETUP_REQUEST(SMBchkpth, 0, 0); + + smbcli_req_append_ascii4(req, parms->chkpath.in.path, STR_TERMINATE); + + if (!smbcli_request_send(req)) { + smbcli_request_destroy(req); + return NULL; + } + + return req; +} + +/**************************************************************************** + Check for existence of a dir - sync interface +****************************************************************************/ +NTSTATUS smb_raw_chkpath(struct smbcli_tree *tree, union smb_chkpath *parms) +{ + struct smbcli_request *req = smb_raw_chkpath_send(tree, parms); + return smbcli_request_simple_recv(req); +} + +/**************************************************************************** + flush a file - async send + a flush with RAW_FLUSH_ALL will flush all files +****************************************************************************/ +struct smbcli_request *smb_raw_flush_send(struct smbcli_tree *tree, union smb_flush *parms) +{ + struct smbcli_request *req; + uint16_t fnum=0; + + switch (parms->generic.level) { + case RAW_FLUSH_FLUSH: + fnum = parms->flush.in.file.fnum; + break; + case RAW_FLUSH_ALL: + fnum = 0xFFFF; + break; + case RAW_FLUSH_SMB2: + return NULL; + } + + SETUP_REQUEST(SMBflush, 1, 0); + SSVAL(req->out.vwv, VWV(0), fnum); + + if (!smbcli_request_send(req)) { + smbcli_request_destroy(req); + return NULL; + } + + return req; +} + + +/**************************************************************************** + flush a file - sync interface +****************************************************************************/ +_PUBLIC_ NTSTATUS smb_raw_flush(struct smbcli_tree *tree, union smb_flush *parms) +{ + struct smbcli_request *req = smb_raw_flush_send(tree, parms); + return smbcli_request_simple_recv(req); +} + + +/**************************************************************************** + seek a file - async send +****************************************************************************/ +struct smbcli_request *smb_raw_seek_send(struct smbcli_tree *tree, + union smb_seek *parms) +{ + struct smbcli_request *req; + + SETUP_REQUEST(SMBlseek, 4, 0); + + SSVAL(req->out.vwv, VWV(0), parms->lseek.in.file.fnum); + SSVAL(req->out.vwv, VWV(1), parms->lseek.in.mode); + SIVALS(req->out.vwv, VWV(2), parms->lseek.in.offset); + + if (!smbcli_request_send(req)) { + smbcli_request_destroy(req); + return NULL; + } + return req; +} + +/**************************************************************************** + seek a file - async receive +****************************************************************************/ +NTSTATUS smb_raw_seek_recv(struct smbcli_request *req, + union smb_seek *parms) +{ + if (!smbcli_request_receive(req) || + smbcli_request_is_error(req)) { + return smbcli_request_destroy(req); + } + + SMBCLI_CHECK_WCT(req, 2); + parms->lseek.out.offset = IVAL(req->in.vwv, VWV(0)); + +failed: + return smbcli_request_destroy(req); +} + +/* + seek a file - sync interface +*/ +_PUBLIC_ NTSTATUS smb_raw_seek(struct smbcli_tree *tree, + union smb_seek *parms) +{ + struct smbcli_request *req = smb_raw_seek_send(tree, parms); + return smb_raw_seek_recv(req, parms); +} diff --git a/source4/libcli/raw/rawfileinfo.c b/source4/libcli/raw/rawfileinfo.c new file mode 100644 index 0000000000..09ecb40002 --- /dev/null +++ b/source4/libcli/raw/rawfileinfo.c @@ -0,0 +1,776 @@ +/* + Unix SMB/CIFS implementation. + client trans2 operations + Copyright (C) James Myers 2003 + Copyright (C) Andrew Tridgell 2003 + 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" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "librpc/gen_ndr/ndr_security.h" + +/* local macros to make the code more readable */ +#define FINFO_CHECK_MIN_SIZE(size) if (blob->length < (size)) { \ + DEBUG(1,("Unexpected FILEINFO reply size %d for level %u - expected min of %d\n", \ + (int)blob->length, parms->generic.level, (size))); \ + return NT_STATUS_INFO_LENGTH_MISMATCH; \ +} +#define FINFO_CHECK_SIZE(size) if (blob->length != (size)) { \ + DEBUG(1,("Unexpected FILEINFO reply size %d for level %u - expected %d\n", \ + (int)blob->length, parms->generic.level, (size))); \ + return NT_STATUS_INFO_LENGTH_MISMATCH; \ +} + +/* + parse a stream information structure +*/ +NTSTATUS smbcli_parse_stream_info(DATA_BLOB blob, TALLOC_CTX *mem_ctx, + struct stream_information *io) +{ + uint32_t ofs = 0; + io->num_streams = 0; + io->streams = NULL; + + while (blob.length - ofs >= 24) { + uint_t n = io->num_streams; + uint32_t nlen, len; + bool ret; + void *vstr; + io->streams = + talloc_realloc(mem_ctx, io->streams, struct stream_struct, n+1); + if (!io->streams) { + return NT_STATUS_NO_MEMORY; + } + nlen = IVAL(blob.data, ofs + 0x04); + io->streams[n].size = BVAL(blob.data, ofs + 0x08); + io->streams[n].alloc_size = BVAL(blob.data, ofs + 0x10); + if (nlen > blob.length - (ofs + 24)) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + ret = convert_string_talloc(io->streams, + CH_UTF16, CH_UNIX, + blob.data+ofs+24, nlen, &vstr, NULL, false); + if (!ret) { + return NT_STATUS_ILLEGAL_CHARACTER; + } + io->streams[n].stream_name.s = (const char *)vstr; + io->streams[n].stream_name.private_length = nlen; + io->num_streams++; + len = IVAL(blob.data, ofs); + if (len > blob.length - ofs) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + if (len == 0) break; + ofs += len; + } + + return NT_STATUS_OK; +} + +/* + parse the fsinfo 'passthru' level replies +*/ +NTSTATUS smb_raw_fileinfo_passthru_parse(const DATA_BLOB *blob, TALLOC_CTX *mem_ctx, + enum smb_fileinfo_level level, + union smb_fileinfo *parms) +{ + switch (level) { + case RAW_FILEINFO_BASIC_INFORMATION: + /* some servers return 40 bytes and some 36. w2k3 return 40, so thats + what we should do, but we need to accept 36 */ + if (blob->length != 36) { + FINFO_CHECK_SIZE(40); + } + parms->basic_info.out.create_time = smbcli_pull_nttime(blob->data, 0); + parms->basic_info.out.access_time = smbcli_pull_nttime(blob->data, 8); + parms->basic_info.out.write_time = smbcli_pull_nttime(blob->data, 16); + parms->basic_info.out.change_time = smbcli_pull_nttime(blob->data, 24); + parms->basic_info.out.attrib = IVAL(blob->data, 32); + return NT_STATUS_OK; + + case RAW_FILEINFO_STANDARD_INFORMATION: + FINFO_CHECK_SIZE(24); + parms->standard_info.out.alloc_size = BVAL(blob->data, 0); + parms->standard_info.out.size = BVAL(blob->data, 8); + parms->standard_info.out.nlink = IVAL(blob->data, 16); + parms->standard_info.out.delete_pending = CVAL(blob->data, 20); + parms->standard_info.out.directory = CVAL(blob->data, 21); + return NT_STATUS_OK; + + case RAW_FILEINFO_EA_INFORMATION: + FINFO_CHECK_SIZE(4); + parms->ea_info.out.ea_size = IVAL(blob->data, 0); + return NT_STATUS_OK; + + case RAW_FILEINFO_NAME_INFORMATION: + FINFO_CHECK_MIN_SIZE(4); + smbcli_blob_pull_string(NULL, mem_ctx, blob, + &parms->name_info.out.fname, 0, 4, STR_UNICODE); + return NT_STATUS_OK; + + case RAW_FILEINFO_ALL_INFORMATION: + FINFO_CHECK_MIN_SIZE(72); + parms->all_info.out.create_time = smbcli_pull_nttime(blob->data, 0); + parms->all_info.out.access_time = smbcli_pull_nttime(blob->data, 8); + parms->all_info.out.write_time = smbcli_pull_nttime(blob->data, 16); + parms->all_info.out.change_time = smbcli_pull_nttime(blob->data, 24); + parms->all_info.out.attrib = IVAL(blob->data, 32); + parms->all_info.out.alloc_size = BVAL(blob->data, 40); + parms->all_info.out.size = BVAL(blob->data, 48); + parms->all_info.out.nlink = IVAL(blob->data, 56); + parms->all_info.out.delete_pending = CVAL(blob->data, 60); + parms->all_info.out.directory = CVAL(blob->data, 61); +#if 1 + parms->all_info.out.ea_size = IVAL(blob->data, 64); + smbcli_blob_pull_string(NULL, mem_ctx, blob, + &parms->all_info.out.fname, 68, 72, STR_UNICODE); +#else + /* this is what the CIFS spec says - and its totally + wrong, but its useful having it here so we can + quickly adapt to broken servers when running + tests */ + parms->all_info.out.ea_size = IVAL(blob->data, 72); + /* access flags 4 bytes at 76 + current_position 8 bytes at 80 + mode 4 bytes at 88 + alignment 4 bytes at 92 + */ + smbcli_blob_pull_string(NULL, mem_ctx, blob, + &parms->all_info.out.fname, 96, 100, STR_UNICODE); +#endif + return NT_STATUS_OK; + + case RAW_FILEINFO_ALT_NAME_INFORMATION: + FINFO_CHECK_MIN_SIZE(4); + smbcli_blob_pull_string(NULL, mem_ctx, blob, + &parms->alt_name_info.out.fname, 0, 4, STR_UNICODE); + return NT_STATUS_OK; + + case RAW_FILEINFO_STREAM_INFORMATION: + return smbcli_parse_stream_info(*blob, mem_ctx, &parms->stream_info.out); + + case RAW_FILEINFO_INTERNAL_INFORMATION: + FINFO_CHECK_SIZE(8); + parms->internal_information.out.file_id = BVAL(blob->data, 0); + return NT_STATUS_OK; + + case RAW_FILEINFO_ACCESS_INFORMATION: + FINFO_CHECK_SIZE(4); + parms->access_information.out.access_flags = IVAL(blob->data, 0); + return NT_STATUS_OK; + + case RAW_FILEINFO_POSITION_INFORMATION: + FINFO_CHECK_SIZE(8); + parms->position_information.out.position = BVAL(blob->data, 0); + return NT_STATUS_OK; + + case RAW_FILEINFO_MODE_INFORMATION: + FINFO_CHECK_SIZE(4); + parms->mode_information.out.mode = IVAL(blob->data, 0); + return NT_STATUS_OK; + + case RAW_FILEINFO_ALIGNMENT_INFORMATION: + FINFO_CHECK_SIZE(4); + parms->alignment_information.out.alignment_requirement + = IVAL(blob->data, 0); + return NT_STATUS_OK; + + case RAW_FILEINFO_COMPRESSION_INFORMATION: + FINFO_CHECK_SIZE(16); + parms->compression_info.out.compressed_size = BVAL(blob->data, 0); + parms->compression_info.out.format = SVAL(blob->data, 8); + parms->compression_info.out.unit_shift = CVAL(blob->data, 10); + parms->compression_info.out.chunk_shift = CVAL(blob->data, 11); + parms->compression_info.out.cluster_shift = CVAL(blob->data, 12); + /* 3 bytes of padding */ + return NT_STATUS_OK; + + case RAW_FILEINFO_NETWORK_OPEN_INFORMATION: + FINFO_CHECK_SIZE(56); + parms->network_open_information.out.create_time = smbcli_pull_nttime(blob->data, 0); + parms->network_open_information.out.access_time = smbcli_pull_nttime(blob->data, 8); + parms->network_open_information.out.write_time = smbcli_pull_nttime(blob->data, 16); + parms->network_open_information.out.change_time = smbcli_pull_nttime(blob->data, 24); + parms->network_open_information.out.alloc_size = BVAL(blob->data, 32); + parms->network_open_information.out.size = BVAL(blob->data, 40); + parms->network_open_information.out.attrib = IVAL(blob->data, 48); + return NT_STATUS_OK; + + case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION: + FINFO_CHECK_SIZE(8); + parms->attribute_tag_information.out.attrib = IVAL(blob->data, 0); + parms->attribute_tag_information.out.reparse_tag = IVAL(blob->data, 4); + return NT_STATUS_OK; + + case RAW_FILEINFO_SMB2_ALL_EAS: + FINFO_CHECK_MIN_SIZE(4); + return ea_pull_list_chained(blob, mem_ctx, + &parms->all_eas.out.num_eas, + &parms->all_eas.out.eas); + + case RAW_FILEINFO_SMB2_ALL_INFORMATION: + FINFO_CHECK_MIN_SIZE(0x64); + parms->all_info2.out.create_time = smbcli_pull_nttime(blob->data, 0x00); + parms->all_info2.out.access_time = smbcli_pull_nttime(blob->data, 0x08); + parms->all_info2.out.write_time = smbcli_pull_nttime(blob->data, 0x10); + parms->all_info2.out.change_time = smbcli_pull_nttime(blob->data, 0x18); + parms->all_info2.out.attrib = IVAL(blob->data, 0x20); + parms->all_info2.out.unknown1 = IVAL(blob->data, 0x24); + parms->all_info2.out.alloc_size = BVAL(blob->data, 0x28); + parms->all_info2.out.size = BVAL(blob->data, 0x30); + parms->all_info2.out.nlink = IVAL(blob->data, 0x38); + parms->all_info2.out.delete_pending = CVAL(blob->data, 0x3C); + parms->all_info2.out.directory = CVAL(blob->data, 0x3D); + /* 0x3E-0x3F padding */ + parms->all_info2.out.file_id = BVAL(blob->data, 0x40); + parms->all_info2.out.ea_size = IVAL(blob->data, 0x48); + parms->all_info2.out.access_mask = IVAL(blob->data, 0x4C); + parms->all_info2.out.position = BVAL(blob->data, 0x50); + parms->all_info2.out.mode = IVAL(blob->data, 0x58); + parms->all_info2.out.alignment_requirement = IVAL(blob->data, 0x5C); + smbcli_blob_pull_string(NULL, mem_ctx, blob, + &parms->all_info2.out.fname, 0x60, 0x64, STR_UNICODE); + return NT_STATUS_OK; + + case RAW_FILEINFO_SEC_DESC: { + enum ndr_err_code ndr_err; + + parms->query_secdesc.out.sd = talloc(mem_ctx, struct security_descriptor); + NT_STATUS_HAVE_NO_MEMORY(parms->query_secdesc.out.sd); + + ndr_err = ndr_pull_struct_blob(blob, mem_ctx, NULL, + parms->query_secdesc.out.sd, + (ndr_pull_flags_fn_t)ndr_pull_security_descriptor); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return ndr_map_error2ntstatus(ndr_err); + } + + return NT_STATUS_OK; + } + + default: + break; + } + + return NT_STATUS_INVALID_LEVEL; +} + + +/**************************************************************************** + Handle qfileinfo/qpathinfo trans2 backend. +****************************************************************************/ +static NTSTATUS smb_raw_info_backend(struct smbcli_session *session, + TALLOC_CTX *mem_ctx, + union smb_fileinfo *parms, + DATA_BLOB *blob) +{ + switch (parms->generic.level) { + case RAW_FILEINFO_GENERIC: + case RAW_FILEINFO_GETATTR: + case RAW_FILEINFO_GETATTRE: + case RAW_FILEINFO_SEC_DESC: + /* not handled here */ + return NT_STATUS_INVALID_LEVEL; + + case RAW_FILEINFO_STANDARD: + FINFO_CHECK_SIZE(22); + parms->standard.out.create_time = raw_pull_dos_date2(session->transport, + blob->data + 0); + parms->standard.out.access_time = raw_pull_dos_date2(session->transport, + blob->data + 4); + parms->standard.out.write_time = raw_pull_dos_date2(session->transport, + blob->data + 8); + parms->standard.out.size = IVAL(blob->data, 12); + parms->standard.out.alloc_size = IVAL(blob->data, 16); + parms->standard.out.attrib = SVAL(blob->data, 20); + return NT_STATUS_OK; + + case RAW_FILEINFO_EA_SIZE: + FINFO_CHECK_SIZE(26); + parms->ea_size.out.create_time = raw_pull_dos_date2(session->transport, + blob->data + 0); + parms->ea_size.out.access_time = raw_pull_dos_date2(session->transport, + blob->data + 4); + parms->ea_size.out.write_time = raw_pull_dos_date2(session->transport, + blob->data + 8); + parms->ea_size.out.size = IVAL(blob->data, 12); + parms->ea_size.out.alloc_size = IVAL(blob->data, 16); + parms->ea_size.out.attrib = SVAL(blob->data, 20); + parms->ea_size.out.ea_size = IVAL(blob->data, 22); + return NT_STATUS_OK; + + case RAW_FILEINFO_EA_LIST: + FINFO_CHECK_MIN_SIZE(4); + return ea_pull_list(blob, mem_ctx, + &parms->ea_list.out.num_eas, + &parms->ea_list.out.eas); + + case RAW_FILEINFO_ALL_EAS: + FINFO_CHECK_MIN_SIZE(4); + return ea_pull_list(blob, mem_ctx, + &parms->all_eas.out.num_eas, + &parms->all_eas.out.eas); + + case RAW_FILEINFO_IS_NAME_VALID: + /* no data! */ + FINFO_CHECK_SIZE(0); + return NT_STATUS_OK; + + case RAW_FILEINFO_BASIC_INFO: + case RAW_FILEINFO_BASIC_INFORMATION: + return smb_raw_fileinfo_passthru_parse(blob, mem_ctx, + RAW_FILEINFO_BASIC_INFORMATION, parms); + + case RAW_FILEINFO_STANDARD_INFO: + case RAW_FILEINFO_STANDARD_INFORMATION: + return smb_raw_fileinfo_passthru_parse(blob, mem_ctx, + RAW_FILEINFO_STANDARD_INFORMATION, parms); + + case RAW_FILEINFO_EA_INFO: + case RAW_FILEINFO_EA_INFORMATION: + return smb_raw_fileinfo_passthru_parse(blob, mem_ctx, + RAW_FILEINFO_EA_INFORMATION, parms); + + case RAW_FILEINFO_NAME_INFO: + case RAW_FILEINFO_NAME_INFORMATION: + return smb_raw_fileinfo_passthru_parse(blob, mem_ctx, + RAW_FILEINFO_NAME_INFORMATION, parms); + + case RAW_FILEINFO_ALL_INFO: + case RAW_FILEINFO_ALL_INFORMATION: + return smb_raw_fileinfo_passthru_parse(blob, mem_ctx, + RAW_FILEINFO_ALL_INFORMATION, parms); + + case RAW_FILEINFO_ALT_NAME_INFO: + case RAW_FILEINFO_ALT_NAME_INFORMATION: + return smb_raw_fileinfo_passthru_parse(blob, mem_ctx, + RAW_FILEINFO_ALT_NAME_INFORMATION, parms); + + case RAW_FILEINFO_STREAM_INFO: + case RAW_FILEINFO_STREAM_INFORMATION: + return smb_raw_fileinfo_passthru_parse(blob, mem_ctx, + RAW_FILEINFO_STREAM_INFORMATION, parms); + + case RAW_FILEINFO_INTERNAL_INFORMATION: + return smb_raw_fileinfo_passthru_parse(blob, mem_ctx, + RAW_FILEINFO_INTERNAL_INFORMATION, parms); + + case RAW_FILEINFO_ACCESS_INFORMATION: + return smb_raw_fileinfo_passthru_parse(blob, mem_ctx, + RAW_FILEINFO_ACCESS_INFORMATION, parms); + + case RAW_FILEINFO_POSITION_INFORMATION: + return smb_raw_fileinfo_passthru_parse(blob, mem_ctx, + RAW_FILEINFO_POSITION_INFORMATION, parms); + + case RAW_FILEINFO_MODE_INFORMATION: + return smb_raw_fileinfo_passthru_parse(blob, mem_ctx, + RAW_FILEINFO_MODE_INFORMATION, parms); + + case RAW_FILEINFO_ALIGNMENT_INFORMATION: + return smb_raw_fileinfo_passthru_parse(blob, mem_ctx, + RAW_FILEINFO_ALIGNMENT_INFORMATION, parms); + + case RAW_FILEINFO_COMPRESSION_INFO: + case RAW_FILEINFO_COMPRESSION_INFORMATION: + return smb_raw_fileinfo_passthru_parse(blob, mem_ctx, + RAW_FILEINFO_COMPRESSION_INFORMATION, parms); + + case RAW_FILEINFO_UNIX_BASIC: + FINFO_CHECK_SIZE(100); + parms->unix_basic_info.out.end_of_file = BVAL(blob->data, 0); + parms->unix_basic_info.out.num_bytes = BVAL(blob->data, 8); + parms->unix_basic_info.out.status_change_time = smbcli_pull_nttime(blob->data, 16); + parms->unix_basic_info.out.access_time = smbcli_pull_nttime(blob->data, 24); + parms->unix_basic_info.out.change_time = smbcli_pull_nttime(blob->data, 32); + parms->unix_basic_info.out.uid = BVAL(blob->data, 40); + parms->unix_basic_info.out.gid = BVAL(blob->data, 48); + parms->unix_basic_info.out.file_type = IVAL(blob->data, 52); + parms->unix_basic_info.out.dev_major = BVAL(blob->data, 60); + parms->unix_basic_info.out.dev_minor = BVAL(blob->data, 68); + parms->unix_basic_info.out.unique_id = BVAL(blob->data, 76); + parms->unix_basic_info.out.permissions = BVAL(blob->data, 84); + parms->unix_basic_info.out.nlink = BVAL(blob->data, 92); + return NT_STATUS_OK; + + case RAW_FILEINFO_UNIX_INFO2: + FINFO_CHECK_SIZE(116); + parms->unix_info2.out.end_of_file = BVAL(blob->data, 0); + parms->unix_info2.out.num_bytes = BVAL(blob->data, 8); + parms->unix_info2.out.status_change_time = smbcli_pull_nttime(blob->data, 16); + parms->unix_info2.out.access_time = smbcli_pull_nttime(blob->data, 24); + parms->unix_info2.out.change_time = smbcli_pull_nttime(blob->data, 32); + parms->unix_info2.out.uid = BVAL(blob->data, 40); + parms->unix_info2.out.gid = BVAL(blob->data, 48); + parms->unix_info2.out.file_type = IVAL(blob->data, 52); + parms->unix_info2.out.dev_major = BVAL(blob->data, 60); + parms->unix_info2.out.dev_minor = BVAL(blob->data, 68); + parms->unix_info2.out.unique_id = BVAL(blob->data, 76); + parms->unix_info2.out.permissions = BVAL(blob->data, 84); + parms->unix_info2.out.nlink = BVAL(blob->data, 92); + parms->unix_info2.out.create_time = smbcli_pull_nttime(blob->data, 100); + parms->unix_info2.out.file_flags = IVAL(blob->data, 108); + parms->unix_info2.out.flags_mask = IVAL(blob->data, 112); + return NT_STATUS_OK; + + case RAW_FILEINFO_UNIX_LINK: + smbcli_blob_pull_string(session, mem_ctx, blob, + &parms->unix_link_info.out.link_dest, 0, 4, STR_UNICODE); + return NT_STATUS_OK; + + case RAW_FILEINFO_NETWORK_OPEN_INFORMATION: + return smb_raw_fileinfo_passthru_parse(blob, mem_ctx, + RAW_FILEINFO_NETWORK_OPEN_INFORMATION, parms); + + case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION: + return smb_raw_fileinfo_passthru_parse(blob, mem_ctx, + RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION, parms); + + case RAW_FILEINFO_SMB2_ALL_INFORMATION: + return smb_raw_fileinfo_passthru_parse(blob, mem_ctx, + RAW_FILEINFO_SMB2_ALL_INFORMATION, parms); + + case RAW_FILEINFO_SMB2_ALL_EAS: + return smb_raw_fileinfo_passthru_parse(blob, mem_ctx, + RAW_FILEINFO_SMB2_ALL_EAS, parms); + + } + + return NT_STATUS_INVALID_LEVEL; +} + + +/**************************************************************************** + Very raw query file info - returns param/data blobs - (async send) +****************************************************************************/ +static struct smbcli_request *smb_raw_fileinfo_blob_send(struct smbcli_tree *tree, + uint16_t fnum, + uint16_t info_level, + DATA_BLOB data) +{ + struct smb_trans2 tp; + uint16_t setup = TRANSACT2_QFILEINFO; + struct smbcli_request *req; + TALLOC_CTX *mem_ctx = talloc_init("raw_fileinfo"); + + tp.in.max_setup = 0; + tp.in.flags = 0; + tp.in.timeout = 0; + tp.in.setup_count = 1; + tp.in.data = data; + tp.in.max_param = 2; + tp.in.max_data = 0xFFFF; + tp.in.setup = &setup; + + tp.in.params = data_blob_talloc(mem_ctx, NULL, 4); + if (!tp.in.params.data) { + talloc_free(mem_ctx); + return NULL; + } + + SSVAL(tp.in.params.data, 0, fnum); + SSVAL(tp.in.params.data, 2, info_level); + + req = smb_raw_trans2_send(tree, &tp); + + talloc_free(mem_ctx); + + return req; +} + + +/**************************************************************************** + Very raw query file info - returns param/data blobs - (async recv) +****************************************************************************/ +static NTSTATUS smb_raw_fileinfo_blob_recv(struct smbcli_request *req, + TALLOC_CTX *mem_ctx, + DATA_BLOB *blob) +{ + struct smb_trans2 tp; + NTSTATUS status = smb_raw_trans2_recv(req, mem_ctx, &tp); + if (NT_STATUS_IS_OK(status)) { + *blob = tp.out.data; + } + return status; +} + +/**************************************************************************** + Very raw query path info - returns param/data blobs (async send) +****************************************************************************/ +static struct smbcli_request *smb_raw_pathinfo_blob_send(struct smbcli_tree *tree, + const char *fname, + uint16_t info_level, + DATA_BLOB data) +{ + struct smb_trans2 tp; + uint16_t setup = TRANSACT2_QPATHINFO; + struct smbcli_request *req; + TALLOC_CTX *mem_ctx = talloc_init("raw_pathinfo"); + + tp.in.max_setup = 0; + tp.in.flags = 0; + tp.in.timeout = 0; + tp.in.setup_count = 1; + tp.in.data = data; + tp.in.max_param = 2; + tp.in.max_data = 0xFFFF; + tp.in.setup = &setup; + + tp.in.params = data_blob_talloc(mem_ctx, NULL, 6); + if (!tp.in.params.data) { + talloc_free(mem_ctx); + return NULL; + } + + SSVAL(tp.in.params.data, 0, info_level); + SIVAL(tp.in.params.data, 2, 0); + smbcli_blob_append_string(tree->session, mem_ctx, &tp.in.params, + fname, STR_TERMINATE); + + req = smb_raw_trans2_send(tree, &tp); + + talloc_free(mem_ctx); + + return req; +} + +/**************************************************************************** + send a SMBgetatr (async send) +****************************************************************************/ +static struct smbcli_request *smb_raw_getattr_send(struct smbcli_tree *tree, + union smb_fileinfo *parms) +{ + struct smbcli_request *req; + + req = smbcli_request_setup(tree, SMBgetatr, 0, 0); + if (!req) return NULL; + + smbcli_req_append_ascii4(req, parms->getattr.in.file.path, STR_TERMINATE); + + if (!smbcli_request_send(req)) { + smbcli_request_destroy(req); + return NULL; + } + + return req; +} + +/**************************************************************************** + send a SMBgetatr (async recv) +****************************************************************************/ +static NTSTATUS smb_raw_getattr_recv(struct smbcli_request *req, + union smb_fileinfo *parms) +{ + if (!smbcli_request_receive(req) || + smbcli_request_is_error(req)) { + return smbcli_request_destroy(req); + } + + SMBCLI_CHECK_WCT(req, 10); + parms->getattr.out.attrib = SVAL(req->in.vwv, VWV(0)); + parms->getattr.out.write_time = raw_pull_dos_date3(req->transport, + req->in.vwv + VWV(1)); + parms->getattr.out.size = IVAL(req->in.vwv, VWV(3)); + +failed: + return smbcli_request_destroy(req); +} + + +/**************************************************************************** + Handle SMBgetattrE (async send) +****************************************************************************/ +static struct smbcli_request *smb_raw_getattrE_send(struct smbcli_tree *tree, + union smb_fileinfo *parms) +{ + struct smbcli_request *req; + + req = smbcli_request_setup(tree, SMBgetattrE, 1, 0); + if (!req) return NULL; + + SSVAL(req->out.vwv, VWV(0), parms->getattre.in.file.fnum); + if (!smbcli_request_send(req)) { + smbcli_request_destroy(req); + return NULL; + } + + return req; +} + +/**************************************************************************** + Handle SMBgetattrE (async send) +****************************************************************************/ +static NTSTATUS smb_raw_getattrE_recv(struct smbcli_request *req, + union smb_fileinfo *parms) +{ + if (!smbcli_request_receive(req) || + smbcli_request_is_error(req)) { + return smbcli_request_destroy(req); + } + + SMBCLI_CHECK_WCT(req, 11); + parms->getattre.out.create_time = raw_pull_dos_date2(req->transport, + req->in.vwv + VWV(0)); + parms->getattre.out.access_time = raw_pull_dos_date2(req->transport, + req->in.vwv + VWV(2)); + parms->getattre.out.write_time = raw_pull_dos_date2(req->transport, + req->in.vwv + VWV(4)); + parms->getattre.out.size = IVAL(req->in.vwv, VWV(6)); + parms->getattre.out.alloc_size = IVAL(req->in.vwv, VWV(8)); + parms->getattre.out.attrib = SVAL(req->in.vwv, VWV(10)); + +failed: + return smbcli_request_destroy(req); +} + + +/**************************************************************************** + Query file info (async send) +****************************************************************************/ +struct smbcli_request *smb_raw_fileinfo_send(struct smbcli_tree *tree, + union smb_fileinfo *parms) +{ + DATA_BLOB data; + struct smbcli_request *req; + + /* pass off the non-trans2 level to specialised functions */ + if (parms->generic.level == RAW_FILEINFO_GETATTRE) { + return smb_raw_getattrE_send(tree, parms); + } + if (parms->generic.level == RAW_FILEINFO_SEC_DESC) { + return smb_raw_query_secdesc_send(tree, parms); + } + if (parms->generic.level >= RAW_FILEINFO_GENERIC) { + return NULL; + } + + data = data_blob(NULL, 0); + + if (parms->generic.level == RAW_FILEINFO_EA_LIST) { + if (!ea_push_name_list(tree, + &data, + parms->ea_list.in.num_names, + parms->ea_list.in.ea_names)) { + return NULL; + } + } + + req = smb_raw_fileinfo_blob_send(tree, + parms->generic.in.file.fnum, + parms->generic.level, data); + + data_blob_free(&data); + + return req; +} + +/**************************************************************************** + Query file info (async recv) +****************************************************************************/ +NTSTATUS smb_raw_fileinfo_recv(struct smbcli_request *req, + TALLOC_CTX *mem_ctx, + union smb_fileinfo *parms) +{ + DATA_BLOB blob; + NTSTATUS status; + struct smbcli_session *session = req?req->session:NULL; + + if (parms->generic.level == RAW_FILEINFO_GETATTRE) { + return smb_raw_getattrE_recv(req, parms); + } + if (parms->generic.level == RAW_FILEINFO_SEC_DESC) { + return smb_raw_query_secdesc_recv(req, mem_ctx, parms); + } + if (parms->generic.level == RAW_FILEINFO_GETATTR) { + return smb_raw_getattr_recv(req, parms); + } + + status = smb_raw_fileinfo_blob_recv(req, mem_ctx, &blob); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return smb_raw_info_backend(session, mem_ctx, parms, &blob); +} + +/**************************************************************************** + Query file info (sync interface) +****************************************************************************/ +_PUBLIC_ NTSTATUS smb_raw_fileinfo(struct smbcli_tree *tree, + TALLOC_CTX *mem_ctx, + union smb_fileinfo *parms) +{ + struct smbcli_request *req = smb_raw_fileinfo_send(tree, parms); + return smb_raw_fileinfo_recv(req, mem_ctx, parms); +} + +/**************************************************************************** + Query path info (async send) +****************************************************************************/ +_PUBLIC_ struct smbcli_request *smb_raw_pathinfo_send(struct smbcli_tree *tree, + union smb_fileinfo *parms) +{ + DATA_BLOB data; + struct smbcli_request *req; + + if (parms->generic.level == RAW_FILEINFO_GETATTR) { + return smb_raw_getattr_send(tree, parms); + } + if (parms->generic.level >= RAW_FILEINFO_GENERIC) { + return NULL; + } + + data = data_blob(NULL, 0); + + if (parms->generic.level == RAW_FILEINFO_EA_LIST) { + if (!ea_push_name_list(tree, + &data, + parms->ea_list.in.num_names, + parms->ea_list.in.ea_names)) { + return NULL; + } + } + + req = smb_raw_pathinfo_blob_send(tree, parms->generic.in.file.path, + parms->generic.level, data); + data_blob_free(&data); + + return req; +} + +/**************************************************************************** + Query path info (async recv) +****************************************************************************/ +_PUBLIC_ NTSTATUS smb_raw_pathinfo_recv(struct smbcli_request *req, + TALLOC_CTX *mem_ctx, + union smb_fileinfo *parms) +{ + /* recv is idential to fileinfo */ + return smb_raw_fileinfo_recv(req, mem_ctx, parms); +} + +/**************************************************************************** + Query path info (sync interface) +****************************************************************************/ +_PUBLIC_ NTSTATUS smb_raw_pathinfo(struct smbcli_tree *tree, + TALLOC_CTX *mem_ctx, + union smb_fileinfo *parms) +{ + struct smbcli_request *req = smb_raw_pathinfo_send(tree, parms); + return smb_raw_pathinfo_recv(req, mem_ctx, parms); +} diff --git a/source4/libcli/raw/rawfsinfo.c b/source4/libcli/raw/rawfsinfo.c new file mode 100644 index 0000000000..43a0919e38 --- /dev/null +++ b/source4/libcli/raw/rawfsinfo.c @@ -0,0 +1,336 @@ +/* + Unix SMB/CIFS implementation. + + RAW_QFS_* operations + + Copyright (C) Andrew Tridgell 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 "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "librpc/gen_ndr/ndr_misc.h" + +/**************************************************************************** + Query FS Info - SMBdskattr call (async send) +****************************************************************************/ +static struct smbcli_request *smb_raw_dskattr_send(struct smbcli_tree *tree, + union smb_fsinfo *fsinfo) +{ + struct smbcli_request *req; + + req = smbcli_request_setup(tree, SMBdskattr, 0, 0); + + if (!smbcli_request_send(req)) { + smbcli_request_destroy(req); + return NULL; + } + + return req; +} + +/**************************************************************************** + Query FS Info - SMBdskattr call (async recv) +****************************************************************************/ +static NTSTATUS smb_raw_dskattr_recv(struct smbcli_request *req, + union smb_fsinfo *fsinfo) +{ + if (!smbcli_request_receive(req) || + smbcli_request_is_error(req)) { + goto failed; + } + + SMBCLI_CHECK_WCT(req, 5); + fsinfo->dskattr.out.units_total = SVAL(req->in.vwv, VWV(0)); + fsinfo->dskattr.out.blocks_per_unit = SVAL(req->in.vwv, VWV(1)); + fsinfo->dskattr.out.block_size = SVAL(req->in.vwv, VWV(2)); + fsinfo->dskattr.out.units_free = SVAL(req->in.vwv, VWV(3)); + +failed: + return smbcli_request_destroy(req); +} + + +/**************************************************************************** + RAW_QFS_ trans2 interface via blobs (async send) +****************************************************************************/ +static struct smbcli_request *smb_raw_qfsinfo_send(struct smbcli_tree *tree, + TALLOC_CTX *mem_ctx, + uint16_t info_level) +{ + struct smb_trans2 tp; + uint16_t setup = TRANSACT2_QFSINFO; + + tp.in.max_setup = 0; + tp.in.flags = 0; + tp.in.timeout = 0; + tp.in.setup_count = 1; + tp.in.max_param = 0; + tp.in.max_data = 0xFFFF; + tp.in.setup = &setup; + tp.in.data = data_blob(NULL, 0); + tp.in.timeout = 0; + + tp.in.params = data_blob_talloc(mem_ctx, NULL, 2); + if (!tp.in.params.data) { + return NULL; + } + SSVAL(tp.in.params.data, 0, info_level); + + return smb_raw_trans2_send(tree, &tp); +} + +/**************************************************************************** + RAW_QFS_ trans2 interface via blobs (async recv) +****************************************************************************/ +static NTSTATUS smb_raw_qfsinfo_blob_recv(struct smbcli_request *req, + TALLOC_CTX *mem_ctx, + DATA_BLOB *blob) +{ + struct smb_trans2 tp; + NTSTATUS status; + + status = smb_raw_trans2_recv(req, mem_ctx, &tp); + + if (NT_STATUS_IS_OK(status)) { + (*blob) = tp.out.data; + } + + return status; +} + + +/* local macros to make the code more readable */ +#define QFS_CHECK_MIN_SIZE(size) if (blob.length < (size)) { \ + DEBUG(1,("Unexpected QFS reply size %d for level %u - expected min of %d\n", \ + (int)blob.length, fsinfo->generic.level, (size))); \ + status = NT_STATUS_INFO_LENGTH_MISMATCH; \ + goto failed; \ +} +#define QFS_CHECK_SIZE(size) if (blob.length != (size)) { \ + DEBUG(1,("Unexpected QFS reply size %d for level %u - expected %d\n", \ + (int)blob.length, fsinfo->generic.level, (size))); \ + status = NT_STATUS_INFO_LENGTH_MISMATCH; \ + goto failed; \ +} + + +/**************************************************************************** + Query FSInfo raw interface (async send) +****************************************************************************/ +struct smbcli_request *smb_raw_fsinfo_send(struct smbcli_tree *tree, + TALLOC_CTX *mem_ctx, + union smb_fsinfo *fsinfo) +{ + uint16_t info_level; + + /* handle the only non-trans2 call separately */ + if (fsinfo->generic.level == RAW_QFS_DSKATTR) { + return smb_raw_dskattr_send(tree, fsinfo); + } + if (fsinfo->generic.level >= RAW_QFS_GENERIC) { + return NULL; + } + + /* the headers map the trans2 levels direct to info levels */ + info_level = (uint16_t)fsinfo->generic.level; + + return smb_raw_qfsinfo_send(tree, mem_ctx, info_level); +} + +/* + parse the fsinfo 'passthru' level replies +*/ +NTSTATUS smb_raw_fsinfo_passthru_parse(DATA_BLOB blob, TALLOC_CTX *mem_ctx, + enum smb_fsinfo_level level, + union smb_fsinfo *fsinfo) +{ + NTSTATUS status = NT_STATUS_OK; + enum ndr_err_code ndr_err; + int i; + + /* parse the results */ + switch (level) { + case RAW_QFS_VOLUME_INFORMATION: + QFS_CHECK_MIN_SIZE(18); + fsinfo->volume_info.out.create_time = smbcli_pull_nttime(blob.data, 0); + fsinfo->volume_info.out.serial_number = IVAL(blob.data, 8); + smbcli_blob_pull_string(NULL, mem_ctx, &blob, + &fsinfo->volume_info.out.volume_name, + 12, 18, STR_UNICODE); + break; + + case RAW_QFS_SIZE_INFORMATION: + QFS_CHECK_SIZE(24); + fsinfo->size_info.out.total_alloc_units = BVAL(blob.data, 0); + fsinfo->size_info.out.avail_alloc_units = BVAL(blob.data, 8); + fsinfo->size_info.out.sectors_per_unit = IVAL(blob.data, 16); + fsinfo->size_info.out.bytes_per_sector = IVAL(blob.data, 20); + break; + + case RAW_QFS_DEVICE_INFORMATION: + QFS_CHECK_SIZE(8); + fsinfo->device_info.out.device_type = IVAL(blob.data, 0); + fsinfo->device_info.out.characteristics = IVAL(blob.data, 4); + break; + + case RAW_QFS_ATTRIBUTE_INFORMATION: + QFS_CHECK_MIN_SIZE(12); + fsinfo->attribute_info.out.fs_attr = IVAL(blob.data, 0); + fsinfo->attribute_info.out.max_file_component_length = IVAL(blob.data, 4); + smbcli_blob_pull_string(NULL, mem_ctx, &blob, + &fsinfo->attribute_info.out.fs_type, + 8, 12, STR_UNICODE); + break; + + case RAW_QFS_QUOTA_INFORMATION: + QFS_CHECK_SIZE(48); + fsinfo->quota_information.out.unknown[0] = BVAL(blob.data, 0); + fsinfo->quota_information.out.unknown[1] = BVAL(blob.data, 8); + fsinfo->quota_information.out.unknown[2] = BVAL(blob.data, 16); + fsinfo->quota_information.out.quota_soft = BVAL(blob.data, 24); + fsinfo->quota_information.out.quota_hard = BVAL(blob.data, 32); + fsinfo->quota_information.out.quota_flags = BVAL(blob.data, 40); + break; + + case RAW_QFS_FULL_SIZE_INFORMATION: + QFS_CHECK_SIZE(32); + fsinfo->full_size_information.out.total_alloc_units = BVAL(blob.data, 0); + fsinfo->full_size_information.out.call_avail_alloc_units = BVAL(blob.data, 8); + fsinfo->full_size_information.out.actual_avail_alloc_units = BVAL(blob.data, 16); + fsinfo->full_size_information.out.sectors_per_unit = IVAL(blob.data, 24); + fsinfo->full_size_information.out.bytes_per_sector = IVAL(blob.data, 28); + break; + + case RAW_QFS_OBJECTID_INFORMATION: + QFS_CHECK_SIZE(64); + ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, NULL, &fsinfo->objectid_information.out.guid, + (ndr_pull_flags_fn_t)ndr_pull_GUID); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + status = ndr_map_error2ntstatus(ndr_err); + } + for (i=0;i<6;i++) { + fsinfo->objectid_information.out.unknown[i] = BVAL(blob.data, 16 + i*8); + } + break; + + default: + status = NT_STATUS_INVALID_INFO_CLASS; + } + +failed: + return status; +} + + +/**************************************************************************** + Query FSInfo raw interface (async recv) +****************************************************************************/ +NTSTATUS smb_raw_fsinfo_recv(struct smbcli_request *req, + TALLOC_CTX *mem_ctx, + union smb_fsinfo *fsinfo) +{ + DATA_BLOB blob; + NTSTATUS status; + struct smbcli_session *session = req?req->session:NULL; + + if (fsinfo->generic.level == RAW_QFS_DSKATTR) { + return smb_raw_dskattr_recv(req, fsinfo); + } + + status = smb_raw_qfsinfo_blob_recv(req, mem_ctx, &blob); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* parse the results */ + switch (fsinfo->generic.level) { + case RAW_QFS_GENERIC: + case RAW_QFS_DSKATTR: + /* handled above */ + break; + + case RAW_QFS_ALLOCATION: + QFS_CHECK_SIZE(18); + fsinfo->allocation.out.fs_id = IVAL(blob.data, 0); + fsinfo->allocation.out.sectors_per_unit = IVAL(blob.data, 4); + fsinfo->allocation.out.total_alloc_units = IVAL(blob.data, 8); + fsinfo->allocation.out.avail_alloc_units = IVAL(blob.data, 12); + fsinfo->allocation.out.bytes_per_sector = SVAL(blob.data, 16); + break; + + case RAW_QFS_VOLUME: + QFS_CHECK_MIN_SIZE(5); + fsinfo->volume.out.serial_number = IVAL(blob.data, 0); + smbcli_blob_pull_string(session, mem_ctx, &blob, + &fsinfo->volume.out.volume_name, + 4, 5, STR_LEN8BIT | STR_NOALIGN); + break; + + case RAW_QFS_VOLUME_INFO: + case RAW_QFS_VOLUME_INFORMATION: + return smb_raw_fsinfo_passthru_parse(blob, mem_ctx, + RAW_QFS_VOLUME_INFORMATION, fsinfo); + + case RAW_QFS_SIZE_INFO: + case RAW_QFS_SIZE_INFORMATION: + return smb_raw_fsinfo_passthru_parse(blob, mem_ctx, + RAW_QFS_SIZE_INFORMATION, fsinfo); + + case RAW_QFS_DEVICE_INFO: + case RAW_QFS_DEVICE_INFORMATION: + return smb_raw_fsinfo_passthru_parse(blob, mem_ctx, + RAW_QFS_DEVICE_INFORMATION, fsinfo); + + case RAW_QFS_ATTRIBUTE_INFO: + case RAW_QFS_ATTRIBUTE_INFORMATION: + return smb_raw_fsinfo_passthru_parse(blob, mem_ctx, + RAW_QFS_ATTRIBUTE_INFORMATION, fsinfo); + + case RAW_QFS_UNIX_INFO: + QFS_CHECK_SIZE(12); + fsinfo->unix_info.out.major_version = SVAL(blob.data, 0); + fsinfo->unix_info.out.minor_version = SVAL(blob.data, 2); + fsinfo->unix_info.out.capability = SVAL(blob.data, 4); + break; + + case RAW_QFS_QUOTA_INFORMATION: + return smb_raw_fsinfo_passthru_parse(blob, mem_ctx, + RAW_QFS_QUOTA_INFORMATION, fsinfo); + + case RAW_QFS_FULL_SIZE_INFORMATION: + return smb_raw_fsinfo_passthru_parse(blob, mem_ctx, + RAW_QFS_FULL_SIZE_INFORMATION, fsinfo); + + case RAW_QFS_OBJECTID_INFORMATION: + return smb_raw_fsinfo_passthru_parse(blob, mem_ctx, + RAW_QFS_OBJECTID_INFORMATION, fsinfo); + } + +failed: + return status; +} + +/**************************************************************************** + Query FSInfo raw interface (sync interface) +****************************************************************************/ +_PUBLIC_ NTSTATUS smb_raw_fsinfo(struct smbcli_tree *tree, + TALLOC_CTX *mem_ctx, + union smb_fsinfo *fsinfo) +{ + struct smbcli_request *req = smb_raw_fsinfo_send(tree, mem_ctx, fsinfo); + return smb_raw_fsinfo_recv(req, mem_ctx, fsinfo); +} diff --git a/source4/libcli/raw/rawioctl.c b/source4/libcli/raw/rawioctl.c new file mode 100644 index 0000000000..77c7b03f15 --- /dev/null +++ b/source4/libcli/raw/rawioctl.c @@ -0,0 +1,173 @@ +/* + Unix SMB/CIFS implementation. + client file operations + Copyright (C) Andrew Tridgell 2003 + Copyright (C) James J Myers 2003 <myersjj@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/>. +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" + +#define SETUP_REQUEST(cmd, wct, buflen) do { \ + req = smbcli_request_setup(tree, cmd, wct, buflen); \ + if (!req) return NULL; \ +} while (0) + +/* + send a raw smb ioctl - async send +*/ +static struct smbcli_request *smb_raw_smbioctl_send(struct smbcli_tree *tree, + union smb_ioctl *parms) +{ + struct smbcli_request *req; + + SETUP_REQUEST(SMBioctl, 3, 0); + + SSVAL(req->out.vwv, VWV(0), parms->ioctl.in.file.fnum); + SIVAL(req->out.vwv, VWV(1), parms->ioctl.in.request); + + if (!smbcli_request_send(req)) { + smbcli_request_destroy(req); + return NULL; + } + + return req; +} + +/* + send a raw smb ioctl - async recv +*/ +static NTSTATUS smb_raw_smbioctl_recv(struct smbcli_request *req, + TALLOC_CTX *mem_ctx, + union smb_ioctl *parms) +{ + if (!smbcli_request_receive(req) || + smbcli_request_is_error(req)) { + return smbcli_request_destroy(req); + } + + parms->ioctl.out.blob = smbcli_req_pull_blob(&req->in.bufinfo, mem_ctx, req->in.data, -1); + return smbcli_request_destroy(req); +} + + + +/**************************************************************************** +NT ioctl (async send) +****************************************************************************/ +static struct smbcli_request *smb_raw_ntioctl_send(struct smbcli_tree *tree, + union smb_ioctl *parms) +{ + struct smb_nttrans nt; + uint8_t setup[8]; + + nt.in.max_setup = 4; + nt.in.max_param = 0; + nt.in.max_data = parms->ntioctl.in.max_data; + nt.in.setup_count = 4; + nt.in.setup = setup; + SIVAL(setup, 0, parms->ntioctl.in.function); + SSVAL(setup, 4, parms->ntioctl.in.file.fnum); + SCVAL(setup, 6, parms->ntioctl.in.fsctl); + SCVAL(setup, 7, parms->ntioctl.in.filter); + nt.in.function = NT_TRANSACT_IOCTL; + nt.in.params = data_blob(NULL, 0); + nt.in.data = parms->ntioctl.in.blob; + + return smb_raw_nttrans_send(tree, &nt); +} + +/**************************************************************************** +NT ioctl (async recv) +****************************************************************************/ +static NTSTATUS smb_raw_ntioctl_recv(struct smbcli_request *req, + TALLOC_CTX *mem_ctx, + union smb_ioctl *parms) +{ + NTSTATUS status; + struct smb_nttrans nt; + TALLOC_CTX *tmp_mem; + + tmp_mem = talloc_new(mem_ctx); + NT_STATUS_HAVE_NO_MEMORY(tmp_mem); + + status = smb_raw_nttrans_recv(req, tmp_mem, &nt); + if (!NT_STATUS_IS_OK(status)) goto fail; + + parms->ntioctl.out.blob = nt.out.data; + talloc_steal(mem_ctx, parms->ntioctl.out.blob.data); + +fail: + talloc_free(tmp_mem); + return status; +} + + +/* + send a raw ioctl - async send +*/ +struct smbcli_request *smb_raw_ioctl_send(struct smbcli_tree *tree, union smb_ioctl *parms) +{ + struct smbcli_request *req = NULL; + + switch (parms->generic.level) { + case RAW_IOCTL_IOCTL: + req = smb_raw_smbioctl_send(tree, parms); + break; + + case RAW_IOCTL_NTIOCTL: + req = smb_raw_ntioctl_send(tree, parms); + break; + + case RAW_IOCTL_SMB2: + case RAW_IOCTL_SMB2_NO_HANDLE: + return NULL; + } + + return req; +} + +/* + recv a raw ioctl - async recv +*/ +NTSTATUS smb_raw_ioctl_recv(struct smbcli_request *req, + TALLOC_CTX *mem_ctx, union smb_ioctl *parms) +{ + switch (parms->generic.level) { + case RAW_IOCTL_IOCTL: + return smb_raw_smbioctl_recv(req, mem_ctx, parms); + + case RAW_IOCTL_NTIOCTL: + return smb_raw_ntioctl_recv(req, mem_ctx, parms); + + case RAW_IOCTL_SMB2: + case RAW_IOCTL_SMB2_NO_HANDLE: + break; + } + return NT_STATUS_INVALID_LEVEL; +} + +/* + send a raw ioctl - sync interface +*/ +NTSTATUS smb_raw_ioctl(struct smbcli_tree *tree, + TALLOC_CTX *mem_ctx, union smb_ioctl *parms) +{ + struct smbcli_request *req; + req = smb_raw_ioctl_send(tree, parms); + return smb_raw_ioctl_recv(req, mem_ctx, parms); +} diff --git a/source4/libcli/raw/rawlpq.c b/source4/libcli/raw/rawlpq.c new file mode 100644 index 0000000000..eddb3e0843 --- /dev/null +++ b/source4/libcli/raw/rawlpq.c @@ -0,0 +1,49 @@ +/* + Unix SMB/CIFS implementation. + client lpq operations + Copyright (C) Tim Potter 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "smb.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" + +/**************************************************************************** + lpq - async send +****************************************************************************/ +struct smbcli_request *smb_raw_lpq_send(struct smbcli_tree *tree, + union smb_lpq *parms) +{ + return NULL; +} + +/**************************************************************************** + lpq - async receive +****************************************************************************/ +NTSTATUS smb_raw_lpq_recv(struct smbcli_request *req, union smb_lpq *parms) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +/* + lpq - sync interface +*/ +NTSTATUS smb_raw_lpq(struct smbcli_tree *tree, union smb_lpq *parms) +{ + struct smbcli_request *req = smb_raw_lpq_send(tree, parms); + return smb_raw_lpq_recv(req, parms); +} diff --git a/source4/libcli/raw/rawnegotiate.c b/source4/libcli/raw/rawnegotiate.c new file mode 100644 index 0000000000..dedc891ac1 --- /dev/null +++ b/source4/libcli/raw/rawnegotiate.c @@ -0,0 +1,206 @@ +/* + Unix SMB/CIFS implementation. + + SMB client negotiate context management functions + + Copyright (C) Andrew Tridgell 1994-2005 + Copyright (C) James Myers 2003 <myersjj@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/>. +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "system/time.h" + +static const struct { + enum protocol_types 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_LANMAN1,"Windows for Workgroups 3.1a"}, + {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"}, +#if 0 + /* we don't yet handle chaining a SMB transport onto SMB2 */ + {PROTOCOL_SMB2,"SMB 2.002"}, +#endif +}; + +/* + Send a negprot command. +*/ +struct smbcli_request *smb_raw_negotiate_send(struct smbcli_transport *transport, + bool unicode, + int maxprotocol) +{ + struct smbcli_request *req; + int i; + uint16_t flags2 = 0; + + req = smbcli_request_setup_transport(transport, SMBnegprot, 0, 0); + if (!req) { + return NULL; + } + + flags2 |= FLAGS2_32_BIT_ERROR_CODES; + if (unicode) { + flags2 |= FLAGS2_UNICODE_STRINGS; + } + flags2 |= FLAGS2_EXTENDED_ATTRIBUTES; + flags2 |= FLAGS2_LONG_PATH_COMPONENTS; + flags2 |= FLAGS2_IS_LONG_NAME; + + if (transport->options.use_spnego) { + flags2 |= FLAGS2_EXTENDED_SECURITY; + } + + SSVAL(req->out.hdr,HDR_FLG2, flags2); + + /* setup the protocol strings */ + for (i=0; i < ARRAY_SIZE(prots) && prots[i].prot <= maxprotocol; i++) { + smbcli_req_append_bytes(req, (const uint8_t *)"\2", 1); + smbcli_req_append_string(req, prots[i].name, STR_TERMINATE | STR_ASCII); + } + + if (!smbcli_request_send(req)) { + smbcli_request_destroy(req); + return NULL; + } + + return req; +} + +/* + Send a negprot command. +*/ +NTSTATUS smb_raw_negotiate_recv(struct smbcli_request *req) +{ + struct smbcli_transport *transport = req->transport; + int protocol; + + if (!smbcli_request_receive(req) || + smbcli_request_is_error(req)) { + return smbcli_request_destroy(req); + } + + SMBCLI_CHECK_MIN_WCT(req, 1); + + protocol = SVALS(req->in.vwv, VWV(0)); + + if (protocol >= ARRAY_SIZE(prots) || protocol < 0) { + req->status = NT_STATUS_UNSUCCESSFUL; + return smbcli_request_destroy(req); + } + + transport->negotiate.protocol = prots[protocol].prot; + + if (transport->negotiate.protocol >= PROTOCOL_NT1) { + NTTIME ntt; + + /* NT protocol */ + SMBCLI_CHECK_WCT(req, 17); + transport->negotiate.sec_mode = CVAL(req->in.vwv,VWV(1)); + transport->negotiate.max_mux = SVAL(req->in.vwv,VWV(1)+1); + transport->negotiate.max_xmit = IVAL(req->in.vwv,VWV(3)+1); + transport->negotiate.sesskey = IVAL(req->in.vwv,VWV(7)+1); + transport->negotiate.capabilities = IVAL(req->in.vwv,VWV(9)+1); + + /* this time arrives in real GMT */ + ntt = smbcli_pull_nttime(req->in.vwv, VWV(11)+1); + transport->negotiate.server_time = nt_time_to_unix(ntt); + transport->negotiate.server_zone = SVALS(req->in.vwv,VWV(15)+1) * 60; + transport->negotiate.key_len = CVAL(req->in.vwv,VWV(16)+1); + + if (transport->negotiate.capabilities & CAP_EXTENDED_SECURITY) { + if (req->in.data_size < 16) { + goto failed; + } + transport->negotiate.server_guid = smbcli_req_pull_blob(&req->in.bufinfo, transport, req->in.data, 16); + transport->negotiate.secblob = smbcli_req_pull_blob(&req->in.bufinfo, transport, req->in.data + 16, req->in.data_size - 16); + } else { + if (req->in.data_size < (transport->negotiate.key_len)) { + goto failed; + } + transport->negotiate.secblob = smbcli_req_pull_blob(&req->in.bufinfo, transport, req->in.data, transport->negotiate.key_len); + smbcli_req_pull_string(&req->in.bufinfo, transport, &transport->negotiate.server_domain, + req->in.data+transport->negotiate.key_len, + req->in.data_size-transport->negotiate.key_len, STR_UNICODE|STR_NOALIGN); + /* here comes the server name */ + } + + if (transport->negotiate.capabilities & CAP_RAW_MODE) { + transport->negotiate.readbraw_supported = true; + transport->negotiate.writebraw_supported = true; + } + } else if (transport->negotiate.protocol >= PROTOCOL_LANMAN1) { + SMBCLI_CHECK_WCT(req, 13); + transport->negotiate.sec_mode = SVAL(req->in.vwv,VWV(1)); + transport->negotiate.max_xmit = SVAL(req->in.vwv,VWV(2)); + transport->negotiate.sesskey = IVAL(req->in.vwv,VWV(6)); + transport->negotiate.server_zone = SVALS(req->in.vwv,VWV(10)) * 60; + + /* this time is converted to GMT by raw_pull_dos_date */ + transport->negotiate.server_time = raw_pull_dos_date(transport, + req->in.vwv+VWV(8)); + if ((SVAL(req->in.vwv,VWV(5)) & 0x1)) { + transport->negotiate.readbraw_supported = 1; + } + if ((SVAL(req->in.vwv,VWV(5)) & 0x2)) { + transport->negotiate.writebraw_supported = 1; + } + transport->negotiate.secblob = smbcli_req_pull_blob(&req->in.bufinfo, transport, + req->in.data, req->in.data_size); + } else { + /* the old core protocol */ + transport->negotiate.sec_mode = 0; + transport->negotiate.server_time = time(NULL); + transport->negotiate.max_xmit = transport->options.max_xmit; + transport->negotiate.server_zone = get_time_zone(transport->negotiate.server_time); + } + + /* a way to force ascii SMB */ + if (!transport->options.unicode) { + transport->negotiate.capabilities &= ~CAP_UNICODE; + } + + if (!transport->options.ntstatus_support) { + transport->negotiate.capabilities &= ~CAP_STATUS32; + } + + if (!transport->options.use_level2_oplocks) { + transport->negotiate.capabilities &= ~CAP_LEVEL_II_OPLOCKS; + } + +failed: + return smbcli_request_destroy(req); +} + + +/* + Send a negprot command (sync interface) +*/ +NTSTATUS smb_raw_negotiate(struct smbcli_transport *transport, bool unicode, int maxprotocol) +{ + struct smbcli_request *req = smb_raw_negotiate_send(transport, unicode, maxprotocol); + return smb_raw_negotiate_recv(req); +} diff --git a/source4/libcli/raw/rawnotify.c b/source4/libcli/raw/rawnotify.c new file mode 100644 index 0000000000..28ab3eb1a4 --- /dev/null +++ b/source4/libcli/raw/rawnotify.c @@ -0,0 +1,168 @@ +/* + Unix SMB/CIFS implementation. + client change notify operations + Copyright (C) Andrew Tridgell 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 "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "../lib/util/dlinklist.h" + +/**************************************************************************** +change notify (async send) +****************************************************************************/ +_PUBLIC_ struct smbcli_request *smb_raw_changenotify_send(struct smbcli_tree *tree, union smb_notify *parms) +{ + struct smb_nttrans nt; + uint8_t setup[8]; + + if (parms->nttrans.level != RAW_NOTIFY_NTTRANS) { + return NULL; + } + + nt.in.max_setup = 0; + nt.in.max_param = parms->nttrans.in.buffer_size; + nt.in.max_data = 0; + nt.in.setup_count = 4; + nt.in.setup = setup; + SIVAL(setup, 0, parms->nttrans.in.completion_filter); + SSVAL(setup, 4, parms->nttrans.in.file.fnum); + SSVAL(setup, 6, parms->nttrans.in.recursive); + nt.in.function = NT_TRANSACT_NOTIFY_CHANGE; + nt.in.params = data_blob(NULL, 0); + nt.in.data = data_blob(NULL, 0); + + return smb_raw_nttrans_send(tree, &nt); +} + +/**************************************************************************** +change notify (async recv) +****************************************************************************/ +_PUBLIC_ NTSTATUS smb_raw_changenotify_recv(struct smbcli_request *req, + TALLOC_CTX *mem_ctx, union smb_notify *parms) +{ + struct smb_nttrans nt; + NTSTATUS status; + uint32_t ofs, i; + struct smbcli_session *session = req?req->session:NULL; + + if (parms->nttrans.level != RAW_NOTIFY_NTTRANS) { + return NT_STATUS_INVALID_LEVEL; + } + + status = smb_raw_nttrans_recv(req, mem_ctx, &nt); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + parms->nttrans.out.changes = NULL; + parms->nttrans.out.num_changes = 0; + + /* count them */ + for (ofs=0; nt.out.params.length - ofs > 12; ) { + uint32_t next = IVAL(nt.out.params.data, ofs); + parms->nttrans.out.num_changes++; + if (next == 0 || + ofs + next >= nt.out.params.length) break; + ofs += next; + } + + /* allocate array */ + parms->nttrans.out.changes = talloc_array(mem_ctx, struct notify_changes, parms->nttrans.out.num_changes); + if (!parms->nttrans.out.changes) { + return NT_STATUS_NO_MEMORY; + } + + for (i=ofs=0; i<parms->nttrans.out.num_changes; i++) { + parms->nttrans.out.changes[i].action = IVAL(nt.out.params.data, ofs+4); + smbcli_blob_pull_string(session, mem_ctx, &nt.out.params, + &parms->nttrans.out.changes[i].name, + ofs+8, ofs+12, STR_UNICODE); + ofs += IVAL(nt.out.params.data, ofs); + } + + return NT_STATUS_OK; +} + +/**************************************************************************** + handle ntcancel replies from the server, + as the MID of the real reply and the ntcancel reply is the same + we need to do find out to what request the reply belongs +****************************************************************************/ +struct smbcli_request *smbcli_handle_ntcancel_reply(struct smbcli_request *req, + size_t len, const uint8_t *hdr) +{ + struct smbcli_request *ntcancel; + + if (!req) return req; + + if (!req->ntcancel) return req; + + if (len >= MIN_SMB_SIZE + NBT_HDR_SIZE && + (CVAL(hdr, HDR_FLG) & FLAG_REPLY) && + CVAL(hdr,HDR_COM) == SMBntcancel) { + ntcancel = req->ntcancel; + DLIST_REMOVE(req->ntcancel, ntcancel); + + /* + * TODO: untill we understand how the + * smb_signing works for this case we + * return NULL, to just ignore the packet + */ + /*return ntcancel;*/ + return NULL; + } + + return req; +} + +/**************************************************************************** + Send a NT Cancel request - used to hurry along a pending request. Usually + used to cancel a pending change notify request + note that this request does not expect a response! +****************************************************************************/ +NTSTATUS smb_raw_ntcancel(struct smbcli_request *oldreq) +{ + struct smbcli_request *req; + + req = smbcli_request_setup_transport(oldreq->transport, SMBntcancel, 0, 0); + + SSVAL(req->out.hdr, HDR_MID, SVAL(oldreq->out.hdr, HDR_MID)); + SSVAL(req->out.hdr, HDR_PID, SVAL(oldreq->out.hdr, HDR_PID)); + SSVAL(req->out.hdr, HDR_TID, SVAL(oldreq->out.hdr, HDR_TID)); + SSVAL(req->out.hdr, HDR_UID, SVAL(oldreq->out.hdr, HDR_UID)); + + /* this request does not expect a reply, so tell the signing + subsystem not to allocate an id for a reply */ + req->sign_single_increment = 1; + req->one_way_request = 1; + + /* + * smbcli_request_send() free's oneway requests + * but we want to keep it under oldreq->ntcancel + */ + if (!talloc_reference(oldreq, req)) { + talloc_free(req); + return NT_STATUS_NO_MEMORY; + } + + smbcli_request_send(req); + + DLIST_ADD_END(oldreq->ntcancel, req, struct smbcli_request *); + + return NT_STATUS_OK; +} diff --git a/source4/libcli/raw/rawreadwrite.c b/source4/libcli/raw/rawreadwrite.c new file mode 100644 index 0000000000..a8c7996310 --- /dev/null +++ b/source4/libcli/raw/rawreadwrite.c @@ -0,0 +1,349 @@ +/* + Unix SMB/CIFS implementation. + client file read/write routines + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) James Myers 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 "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" + +#define SETUP_REQUEST(cmd, wct, buflen) do { \ + req = smbcli_request_setup(tree, cmd, wct, buflen); \ + if (!req) return NULL; \ +} while (0) + +/**************************************************************************** + low level read operation (async send) +****************************************************************************/ +_PUBLIC_ struct smbcli_request *smb_raw_read_send(struct smbcli_tree *tree, union smb_read *parms) +{ + bool bigoffset = false; + struct smbcli_request *req = NULL; + + switch (parms->generic.level) { + case RAW_READ_READBRAW: + if (tree->session->transport->negotiate.capabilities & CAP_LARGE_FILES) { + bigoffset = true; + } + SETUP_REQUEST(SMBreadbraw, bigoffset? 10:8, 0); + SSVAL(req->out.vwv, VWV(0), parms->readbraw.in.file.fnum); + SIVAL(req->out.vwv, VWV(1), parms->readbraw.in.offset); + SSVAL(req->out.vwv, VWV(3), parms->readbraw.in.maxcnt); + SSVAL(req->out.vwv, VWV(4), parms->readbraw.in.mincnt); + SIVAL(req->out.vwv, VWV(5), parms->readbraw.in.timeout); + SSVAL(req->out.vwv, VWV(7), 0); /* reserved */ + if (bigoffset) { + SIVAL(req->out.vwv, VWV(8),parms->readbraw.in.offset>>32); + } + break; + + case RAW_READ_LOCKREAD: + SETUP_REQUEST(SMBlockread, 5, 0); + SSVAL(req->out.vwv, VWV(0), parms->lockread.in.file.fnum); + SSVAL(req->out.vwv, VWV(1), parms->lockread.in.count); + SIVAL(req->out.vwv, VWV(2), parms->lockread.in.offset); + SSVAL(req->out.vwv, VWV(4), parms->lockread.in.remaining); + break; + + case RAW_READ_READ: + SETUP_REQUEST(SMBread, 5, 0); + SSVAL(req->out.vwv, VWV(0), parms->read.in.file.fnum); + SSVAL(req->out.vwv, VWV(1), parms->read.in.count); + SIVAL(req->out.vwv, VWV(2), parms->read.in.offset); + SSVAL(req->out.vwv, VWV(4), parms->read.in.remaining); + break; + + case RAW_READ_READX: + if (tree->session->transport->negotiate.capabilities & CAP_LARGE_FILES) { + bigoffset = true; + } + SETUP_REQUEST(SMBreadX, bigoffset ? 12 : 10, 0); + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), parms->readx.in.file.fnum); + SIVAL(req->out.vwv, VWV(3), parms->readx.in.offset); + SSVAL(req->out.vwv, VWV(5), parms->readx.in.maxcnt & 0xFFFF); + SSVAL(req->out.vwv, VWV(6), parms->readx.in.mincnt); + SIVAL(req->out.vwv, VWV(7), parms->readx.in.maxcnt >> 16); + SSVAL(req->out.vwv, VWV(9), parms->readx.in.remaining); + /* + * TODO: give an error when the offset is 64 bit + * and the server doesn't support it + */ + if (bigoffset) { + SIVAL(req->out.vwv, VWV(10),parms->readx.in.offset>>32); + } + if (parms->readx.in.read_for_execute) { + uint16_t flags2 = SVAL(req->out.hdr, HDR_FLG2); + flags2 |= FLAGS2_READ_PERMIT_EXECUTE; + SSVAL(req->out.hdr, HDR_FLG2, flags2); + } + break; + + case RAW_READ_SMB2: + return NULL; + } + + if (!smbcli_request_send(req)) { + smbcli_request_destroy(req); + return NULL; + } + + /* the transport layer needs to know that a readbraw is pending + and handle receives a little differently */ + if (parms->generic.level == RAW_READ_READBRAW) { + tree->session->transport->readbraw_pending = 1; + } + + return req; +} + +/**************************************************************************** + low level read operation (async recv) +****************************************************************************/ +_PUBLIC_ NTSTATUS smb_raw_read_recv(struct smbcli_request *req, union smb_read *parms) +{ + if (!smbcli_request_receive(req) || + smbcli_request_is_error(req)) { + goto failed; + } + + switch (parms->generic.level) { + case RAW_READ_READBRAW: + parms->readbraw.out.nread = req->in.size - NBT_HDR_SIZE; + if (parms->readbraw.out.nread > + MAX(parms->readx.in.mincnt, parms->readx.in.maxcnt)) { + req->status = NT_STATUS_BUFFER_TOO_SMALL; + goto failed; + } + memcpy(parms->readbraw.out.data, req->in.buffer + NBT_HDR_SIZE, parms->readbraw.out.nread); + break; + + case RAW_READ_LOCKREAD: + SMBCLI_CHECK_WCT(req, 5); + parms->lockread.out.nread = SVAL(req->in.vwv, VWV(0)); + if (parms->lockread.out.nread > parms->lockread.in.count || + !smbcli_raw_pull_data(&req->in.bufinfo, req->in.data+3, + parms->lockread.out.nread, parms->lockread.out.data)) { + req->status = NT_STATUS_BUFFER_TOO_SMALL; + } + break; + + case RAW_READ_READ: + /* there are 4 reserved words in the reply */ + SMBCLI_CHECK_WCT(req, 5); + parms->read.out.nread = SVAL(req->in.vwv, VWV(0)); + if (parms->read.out.nread > parms->read.in.count || + !smbcli_raw_pull_data(&req->in.bufinfo, req->in.data+3, + parms->read.out.nread, parms->read.out.data)) { + req->status = NT_STATUS_BUFFER_TOO_SMALL; + } + break; + + case RAW_READ_READX: + /* there are 5 reserved words in the reply */ + SMBCLI_CHECK_WCT(req, 12); + parms->readx.out.remaining = SVAL(req->in.vwv, VWV(2)); + parms->readx.out.compaction_mode = SVAL(req->in.vwv, VWV(3)); + parms->readx.out.nread = SVAL(req->in.vwv, VWV(5)); + + /* handle oversize replies for non-chained readx replies with + CAP_LARGE_READX. The snia spec has must to answer for. */ + if ((req->tree->session->transport->negotiate.capabilities & CAP_LARGE_READX) + && CVAL(req->in.vwv, VWV(0)) == SMB_CHAIN_NONE && + req->in.size >= 0x10000) { + parms->readx.out.nread += (SVAL(req->in.vwv, VWV(7)) << 16); + if (req->in.hdr + SVAL(req->in.vwv, VWV(6)) + + parms->readx.out.nread <= + req->in.buffer + req->in.size) { + req->in.data_size += (SVAL(req->in.vwv, VWV(7)) << 16); + + /* update the bufinfo with the new size */ + smb_setup_bufinfo(req); + } + } + + if (parms->readx.out.nread > MAX(parms->readx.in.mincnt, parms->readx.in.maxcnt) || + !smbcli_raw_pull_data(&req->in.bufinfo, req->in.hdr + SVAL(req->in.vwv, VWV(6)), + parms->readx.out.nread, + parms->readx.out.data)) { + req->status = NT_STATUS_BUFFER_TOO_SMALL; + } + break; + + case RAW_READ_SMB2: + req->status = NT_STATUS_INTERNAL_ERROR; + break; + } + +failed: + return smbcli_request_destroy(req); +} + +/**************************************************************************** + low level read operation (sync interface) +****************************************************************************/ +_PUBLIC_ NTSTATUS smb_raw_read(struct smbcli_tree *tree, union smb_read *parms) +{ + struct smbcli_request *req = smb_raw_read_send(tree, parms); + return smb_raw_read_recv(req, parms); +} + + +/**************************************************************************** + raw write interface (async send) +****************************************************************************/ +_PUBLIC_ struct smbcli_request *smb_raw_write_send(struct smbcli_tree *tree, union smb_write *parms) +{ + bool bigoffset = false; + struct smbcli_request *req = NULL; + + switch (parms->generic.level) { + case RAW_WRITE_WRITEUNLOCK: + SETUP_REQUEST(SMBwriteunlock, 5, 3 + parms->writeunlock.in.count); + SSVAL(req->out.vwv, VWV(0), parms->writeunlock.in.file.fnum); + SSVAL(req->out.vwv, VWV(1), parms->writeunlock.in.count); + SIVAL(req->out.vwv, VWV(2), parms->writeunlock.in.offset); + SSVAL(req->out.vwv, VWV(4), parms->writeunlock.in.remaining); + SCVAL(req->out.data, 0, SMB_DATA_BLOCK); + SSVAL(req->out.data, 1, parms->writeunlock.in.count); + if (parms->writeunlock.in.count > 0) { + memcpy(req->out.data+3, parms->writeunlock.in.data, + parms->writeunlock.in.count); + } + break; + + case RAW_WRITE_WRITE: + SETUP_REQUEST(SMBwrite, 5, 3 + parms->write.in.count); + SSVAL(req->out.vwv, VWV(0), parms->write.in.file.fnum); + SSVAL(req->out.vwv, VWV(1), parms->write.in.count); + SIVAL(req->out.vwv, VWV(2), parms->write.in.offset); + SSVAL(req->out.vwv, VWV(4), parms->write.in.remaining); + SCVAL(req->out.data, 0, SMB_DATA_BLOCK); + SSVAL(req->out.data, 1, parms->write.in.count); + if (parms->write.in.count > 0) { + memcpy(req->out.data+3, parms->write.in.data, parms->write.in.count); + } + break; + + case RAW_WRITE_WRITECLOSE: + SETUP_REQUEST(SMBwriteclose, 6, 1 + parms->writeclose.in.count); + SSVAL(req->out.vwv, VWV(0), parms->writeclose.in.file.fnum); + SSVAL(req->out.vwv, VWV(1), parms->writeclose.in.count); + SIVAL(req->out.vwv, VWV(2), parms->writeclose.in.offset); + raw_push_dos_date3(tree->session->transport, + req->out.vwv, VWV(4), parms->writeclose.in.mtime); + SCVAL(req->out.data, 0, 0); + if (parms->writeclose.in.count > 0) { + memcpy(req->out.data+1, parms->writeclose.in.data, + parms->writeclose.in.count); + } + break; + + case RAW_WRITE_WRITEX: + if (tree->session->transport->negotiate.capabilities & CAP_LARGE_FILES) { + bigoffset = true; + } + SETUP_REQUEST(SMBwriteX, bigoffset ? 14 : 12, parms->writex.in.count); + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), parms->writex.in.file.fnum); + SIVAL(req->out.vwv, VWV(3), parms->writex.in.offset); + SIVAL(req->out.vwv, VWV(5), 0); /* reserved */ + SSVAL(req->out.vwv, VWV(7), parms->writex.in.wmode); + SSVAL(req->out.vwv, VWV(8), parms->writex.in.remaining); + SSVAL(req->out.vwv, VWV(9), parms->writex.in.count>>16); + SSVAL(req->out.vwv, VWV(10), parms->writex.in.count); + SSVAL(req->out.vwv, VWV(11), PTR_DIFF(req->out.data, req->out.hdr)); + if (bigoffset) { + SIVAL(req->out.vwv,VWV(12),parms->writex.in.offset>>32); + } + if (parms->writex.in.count > 0) { + memcpy(req->out.data, parms->writex.in.data, parms->writex.in.count); + } + break; + + case RAW_WRITE_SPLWRITE: + SETUP_REQUEST(SMBsplwr, 1, parms->splwrite.in.count); + SSVAL(req->out.vwv, VWV(0), parms->splwrite.in.file.fnum); + if (parms->splwrite.in.count > 0) { + memcpy(req->out.data, parms->splwrite.in.data, parms->splwrite.in.count); + } + break; + + case RAW_WRITE_SMB2: + return NULL; + } + + if (!smbcli_request_send(req)) { + smbcli_request_destroy(req); + return NULL; + } + + return req; +} + + +/**************************************************************************** + raw write interface (async recv) +****************************************************************************/ +NTSTATUS smb_raw_write_recv(struct smbcli_request *req, union smb_write *parms) +{ + if (!smbcli_request_receive(req) || + smbcli_request_is_error(req)) { + goto failed; + } + + switch (parms->generic.level) { + case RAW_WRITE_WRITEUNLOCK: + SMBCLI_CHECK_WCT(req, 1); + parms->writeunlock.out.nwritten = SVAL(req->in.vwv, VWV(0)); + break; + case RAW_WRITE_WRITE: + SMBCLI_CHECK_WCT(req, 1); + parms->write.out.nwritten = SVAL(req->in.vwv, VWV(0)); + break; + case RAW_WRITE_WRITECLOSE: + SMBCLI_CHECK_WCT(req, 1); + parms->writeclose.out.nwritten = SVAL(req->in.vwv, VWV(0)); + break; + case RAW_WRITE_WRITEX: + SMBCLI_CHECK_WCT(req, 6); + parms->writex.out.nwritten = SVAL(req->in.vwv, VWV(2)); + parms->writex.out.nwritten += (CVAL(req->in.vwv, VWV(4)) << 16); + parms->writex.out.remaining = SVAL(req->in.vwv, VWV(3)); + break; + case RAW_WRITE_SPLWRITE: + break; + case RAW_WRITE_SMB2: + req->status = NT_STATUS_INTERNAL_ERROR; + break; + } + +failed: + return smbcli_request_destroy(req); +} + +/**************************************************************************** + raw write interface (sync interface) +****************************************************************************/ +_PUBLIC_ NTSTATUS smb_raw_write(struct smbcli_tree *tree, union smb_write *parms) +{ + struct smbcli_request *req = smb_raw_write_send(tree, parms); + return smb_raw_write_recv(req, parms); +} diff --git a/source4/libcli/raw/rawrequest.c b/source4/libcli/raw/rawrequest.c new file mode 100644 index 0000000000..0f04190a8b --- /dev/null +++ b/source4/libcli/raw/rawrequest.c @@ -0,0 +1,1029 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) James Myers 2003 <myersjj@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/>. +*/ + +/* + this file implements functions for manipulating the 'struct smbcli_request' structure in libsmb +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "../lib/util/dlinklist.h" +#include "lib/events/events.h" +#include "librpc/ndr/libndr.h" +#include "librpc/gen_ndr/ndr_misc.h" + +/* we over allocate the data buffer to prevent too many realloc calls */ +#define REQ_OVER_ALLOCATION 0 + +/* assume that a character will not consume more than 3 bytes per char */ +#define MAX_BYTES_PER_CHAR 3 + +/* setup the bufinfo used for strings and range checking */ +void smb_setup_bufinfo(struct smbcli_request *req) +{ + req->in.bufinfo.mem_ctx = req; + req->in.bufinfo.flags = 0; + if (req->flags2 & FLAGS2_UNICODE_STRINGS) { + req->in.bufinfo.flags = BUFINFO_FLAG_UNICODE; + } + req->in.bufinfo.align_base = req->in.buffer; + req->in.bufinfo.data = req->in.data; + req->in.bufinfo.data_size = req->in.data_size; +} + + +/* destroy a request structure and return final status */ +_PUBLIC_ NTSTATUS smbcli_request_destroy(struct smbcli_request *req) +{ + NTSTATUS status; + + /* this is the error code we give the application for when a + _send() call fails completely */ + if (!req) return NT_STATUS_UNSUCCESSFUL; + + if (req->transport) { + /* remove it from the list of pending requests (a null op if + its not in the list) */ + DLIST_REMOVE(req->transport->pending_recv, req); + } + + if (req->state == SMBCLI_REQUEST_ERROR && + NT_STATUS_IS_OK(req->status)) { + req->status = NT_STATUS_INTERNAL_ERROR; + } + + status = req->status; + + if (!req->do_not_free) { + talloc_free(req); + } + + return status; +} + + +/* + low-level function to setup a request buffer for a non-SMB packet + at the transport level +*/ +struct smbcli_request *smbcli_request_setup_nonsmb(struct smbcli_transport *transport, size_t size) +{ + struct smbcli_request *req; + + req = talloc(transport, struct smbcli_request); + if (!req) { + return NULL; + } + ZERO_STRUCTP(req); + + /* setup the request context */ + req->state = SMBCLI_REQUEST_INIT; + req->transport = transport; + req->session = NULL; + req->tree = NULL; + req->out.size = size; + + /* over allocate by a small amount */ + req->out.allocated = req->out.size + REQ_OVER_ALLOCATION; + + req->out.buffer = talloc_array(req, uint8_t, req->out.allocated); + if (!req->out.buffer) { + return NULL; + } + + SIVAL(req->out.buffer, 0, 0); + + return req; +} + + +/* + setup a SMB packet at transport level +*/ +struct smbcli_request *smbcli_request_setup_transport(struct smbcli_transport *transport, + uint8_t command, uint_t wct, uint_t buflen) +{ + struct smbcli_request *req; + + req = smbcli_request_setup_nonsmb(transport, NBT_HDR_SIZE + MIN_SMB_SIZE + wct*2 + buflen); + + if (!req) return NULL; + + req->out.hdr = req->out.buffer + NBT_HDR_SIZE; + req->out.vwv = req->out.hdr + HDR_VWV; + req->out.wct = wct; + req->out.data = req->out.vwv + VWV(wct) + 2; + req->out.data_size = buflen; + req->out.ptr = req->out.data; + + SCVAL(req->out.hdr, HDR_WCT, wct); + SSVAL(req->out.vwv, VWV(wct), buflen); + + memcpy(req->out.hdr, "\377SMB", 4); + SCVAL(req->out.hdr,HDR_COM,command); + + SCVAL(req->out.hdr,HDR_FLG, FLAG_CASELESS_PATHNAMES); + SSVAL(req->out.hdr,HDR_FLG2, 0); + + if (command != SMBtranss && command != SMBtranss2) { + /* assign a mid */ + req->mid = smbcli_transport_next_mid(transport); + } + + /* copy the pid, uid and mid to the request */ + SSVAL(req->out.hdr, HDR_PID, 0); + SSVAL(req->out.hdr, HDR_UID, 0); + SSVAL(req->out.hdr, HDR_MID, req->mid); + SSVAL(req->out.hdr, HDR_TID,0); + SSVAL(req->out.hdr, HDR_PIDHIGH,0); + SIVAL(req->out.hdr, HDR_RCLS, 0); + memset(req->out.hdr+HDR_SS_FIELD, 0, 10); + + return req; +} + +/* + setup a reply in req->out with the given word count and initial data + buffer size. the caller will then fill in the command words and + data before calling smbcli_request_send() to send the reply on its + way. This interface is used before a session is setup. +*/ +struct smbcli_request *smbcli_request_setup_session(struct smbcli_session *session, + uint8_t command, uint_t wct, size_t buflen) +{ + struct smbcli_request *req; + + req = smbcli_request_setup_transport(session->transport, command, wct, buflen); + + if (!req) return NULL; + + req->session = session; + + SSVAL(req->out.hdr, HDR_FLG2, session->flags2); + SSVAL(req->out.hdr, HDR_PID, session->pid & 0xFFFF); + SSVAL(req->out.hdr, HDR_PIDHIGH, session->pid >> 16); + SSVAL(req->out.hdr, HDR_UID, session->vuid); + + return req; +} + +/* + setup a request for tree based commands +*/ +struct smbcli_request *smbcli_request_setup(struct smbcli_tree *tree, + uint8_t command, + uint_t wct, uint_t buflen) +{ + struct smbcli_request *req; + + req = smbcli_request_setup_session(tree->session, command, wct, buflen); + if (req) { + req->tree = tree; + SSVAL(req->out.hdr,HDR_TID,tree->tid); + } + return req; +} + + +/* + grow the allocation of the data buffer portion of a reply + packet. Note that as this can reallocate the packet buffer this + invalidates any local pointers into the packet. + + To cope with this req->out.ptr is supplied. This will be updated to + point at the same offset into the packet as before this call +*/ +static void smbcli_req_grow_allocation(struct smbcli_request *req, uint_t new_size) +{ + int delta; + uint8_t *buf2; + + delta = new_size - req->out.data_size; + if (delta + req->out.size <= req->out.allocated) { + /* it fits in the preallocation */ + return; + } + + /* we need to realloc */ + req->out.allocated = req->out.size + delta + REQ_OVER_ALLOCATION; + buf2 = talloc_realloc(req, req->out.buffer, uint8_t, req->out.allocated); + if (buf2 == NULL) { + smb_panic("out of memory in req_grow_allocation"); + } + + if (buf2 == req->out.buffer) { + /* the malloc library gave us the same pointer */ + return; + } + + /* update the pointers into the packet */ + req->out.data = buf2 + PTR_DIFF(req->out.data, req->out.buffer); + req->out.ptr = buf2 + PTR_DIFF(req->out.ptr, req->out.buffer); + req->out.vwv = buf2 + PTR_DIFF(req->out.vwv, req->out.buffer); + req->out.hdr = buf2 + PTR_DIFF(req->out.hdr, req->out.buffer); + + req->out.buffer = buf2; +} + + +/* + grow the data buffer portion of a reply packet. Note that as this + can reallocate the packet buffer this invalidates any local pointers + into the packet. + + To cope with this req->out.ptr is supplied. This will be updated to + point at the same offset into the packet as before this call +*/ +static void smbcli_req_grow_data(struct smbcli_request *req, uint_t new_size) +{ + int delta; + + smbcli_req_grow_allocation(req, new_size); + + delta = new_size - req->out.data_size; + + req->out.size += delta; + req->out.data_size += delta; + + /* set the BCC to the new data size */ + SSVAL(req->out.vwv, VWV(req->out.wct), new_size); +} + + +/* + setup a chained reply in req->out with the given word count and + initial data buffer size. +*/ +NTSTATUS smbcli_chained_request_setup(struct smbcli_request *req, + uint8_t command, + uint_t wct, size_t buflen) +{ + uint_t new_size = 1 + (wct*2) + 2 + buflen; + + SSVAL(req->out.vwv, VWV(0), command); + SSVAL(req->out.vwv, VWV(1), req->out.size - NBT_HDR_SIZE); + + smbcli_req_grow_allocation(req, req->out.data_size + new_size); + + req->out.vwv = req->out.buffer + req->out.size + 1; + SCVAL(req->out.vwv, -1, wct); + SSVAL(req->out.vwv, VWV(wct), buflen); + + req->out.size += new_size; + req->out.data_size += new_size; + + return NT_STATUS_OK; +} + +/* + aadvance to the next chained reply in a request +*/ +NTSTATUS smbcli_chained_advance(struct smbcli_request *req) +{ + uint8_t *buffer; + + if (CVAL(req->in.vwv, VWV(0)) == SMB_CHAIN_NONE) { + return NT_STATUS_NOT_FOUND; + } + + buffer = req->in.hdr + SVAL(req->in.vwv, VWV(1)); + + if (buffer + 3 > req->in.buffer + req->in.size) { + return NT_STATUS_BUFFER_TOO_SMALL; + } + + req->in.vwv = buffer + 1; + req->in.wct = CVAL(buffer, 0); + if (buffer + 3 + req->in.wct*2 > req->in.buffer + req->in.size) { + return NT_STATUS_BUFFER_TOO_SMALL; + } + req->in.data = req->in.vwv + 2 + req->in.wct * 2; + req->in.data_size = SVAL(req->in.vwv, VWV(req->in.wct)); + + /* fix the bufinfo */ + smb_setup_bufinfo(req); + + if (buffer + 3 + req->in.wct*2 + req->in.data_size > + req->in.buffer + req->in.size) { + return NT_STATUS_BUFFER_TOO_SMALL; + } + + return NT_STATUS_OK; +} + + +/* + send a message +*/ +bool smbcli_request_send(struct smbcli_request *req) +{ + if (IVAL(req->out.buffer, 0) == 0) { + _smb_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE); + } + + smbcli_request_calculate_sign_mac(req); + + smbcli_transport_send(req); + + return true; +} + + +/* + receive a response to a packet +*/ +bool smbcli_request_receive(struct smbcli_request *req) +{ + /* req can be NULL when a send has failed. This eliminates lots of NULL + checks in each module */ + if (!req) return false; + + /* keep receiving packets until this one is replied to */ + while (req->state <= SMBCLI_REQUEST_RECV) { + if (event_loop_once(req->transport->socket->event.ctx) != 0) { + return false; + } + } + + return req->state == SMBCLI_REQUEST_DONE; +} + + +/* + handle oplock break requests from the server - return true if the request was + an oplock break +*/ +bool smbcli_handle_oplock_break(struct smbcli_transport *transport, uint_t len, const uint8_t *hdr, const uint8_t *vwv) +{ + /* we must be very fussy about what we consider an oplock break to avoid + matching readbraw replies */ + if (len != MIN_SMB_SIZE + VWV(8) + NBT_HDR_SIZE || + (CVAL(hdr, HDR_FLG) & FLAG_REPLY) || + CVAL(hdr,HDR_COM) != SMBlockingX || + SVAL(hdr, HDR_MID) != 0xFFFF || + SVAL(vwv,VWV(6)) != 0 || + SVAL(vwv,VWV(7)) != 0) { + return false; + } + + if (transport->oplock.handler) { + uint16_t tid = SVAL(hdr, HDR_TID); + uint16_t fnum = SVAL(vwv,VWV(2)); + uint8_t level = CVAL(vwv,VWV(3)+1); + transport->oplock.handler(transport, tid, fnum, level, transport->oplock.private_data); + } + + return true; +} + +/* + wait for a reply to be received for a packet that just returns an error + code and nothing more +*/ +_PUBLIC_ NTSTATUS smbcli_request_simple_recv(struct smbcli_request *req) +{ + (void) smbcli_request_receive(req); + return smbcli_request_destroy(req); +} + + +/* Return true if the last packet was in error */ +bool smbcli_request_is_error(struct smbcli_request *req) +{ + return NT_STATUS_IS_ERR(req->status); +} + +/* + append a string into the data portion of the request packet + + return the number of bytes added to the packet +*/ +size_t smbcli_req_append_string(struct smbcli_request *req, const char *str, uint_t flags) +{ + size_t len; + + /* determine string type to use */ + if (!(flags & (STR_ASCII|STR_UNICODE))) { + flags |= (req->transport->negotiate.capabilities & CAP_UNICODE) ? STR_UNICODE : STR_ASCII; + } + + len = (strlen(str)+2) * MAX_BYTES_PER_CHAR; + + smbcli_req_grow_allocation(req, len + req->out.data_size); + + len = push_string(req->out.data + req->out.data_size, str, len, flags); + + smbcli_req_grow_data(req, len + req->out.data_size); + + return len; +} + + +/* + this is like smbcli_req_append_string but it also return the + non-terminated string byte length, which can be less than the number + of bytes consumed in the packet for 2 reasons: + + 1) the string in the packet may be null terminated + 2) the string in the packet may need a 1 byte UCS2 alignment + + this is used in places where the non-terminated string byte length is + placed in the packet as a separate field +*/ +size_t smbcli_req_append_string_len(struct smbcli_request *req, const char *str, uint_t flags, int *len) +{ + int diff = 0; + size_t ret; + + /* determine string type to use */ + if (!(flags & (STR_ASCII|STR_UNICODE))) { + flags |= (req->transport->negotiate.capabilities & CAP_UNICODE) ? STR_UNICODE : STR_ASCII; + } + + /* see if an alignment byte will be used */ + if ((flags & STR_UNICODE) && !(flags & STR_NOALIGN)) { + diff = ucs2_align(NULL, req->out.data + req->out.data_size, flags); + } + + /* do the hard work */ + ret = smbcli_req_append_string(req, str, flags); + + /* see if we need to subtract the termination */ + if (flags & STR_TERMINATE) { + diff += (flags & STR_UNICODE) ? 2 : 1; + } + + if (ret >= diff) { + (*len) = ret - diff; + } else { + (*len) = ret; + } + + return ret; +} + + +/* + push a string into the data portion of the request packet, growing it if necessary + this gets quite tricky - please be very careful to cover all cases when modifying this + + if dest is NULL, then put the string at the end of the data portion of the packet + + if dest_len is -1 then no limit applies +*/ +size_t smbcli_req_append_ascii4(struct smbcli_request *req, const char *str, uint_t flags) +{ + size_t size; + smbcli_req_append_bytes(req, (const uint8_t *)"\4", 1); + size = smbcli_req_append_string(req, str, flags); + return size + 1; +} + + +/* + push a blob into the data portion of the request packet, growing it if necessary + this gets quite tricky - please be very careful to cover all cases when modifying this + + if dest is NULL, then put the blob at the end of the data portion of the packet +*/ +size_t smbcli_req_append_blob(struct smbcli_request *req, const DATA_BLOB *blob) +{ + smbcli_req_grow_allocation(req, req->out.data_size + blob->length); + memcpy(req->out.data + req->out.data_size, blob->data, blob->length); + smbcli_req_grow_data(req, req->out.data_size + blob->length); + return blob->length; +} + +/* + append raw bytes into the data portion of the request packet + return the number of bytes added +*/ +size_t smbcli_req_append_bytes(struct smbcli_request *req, const uint8_t *bytes, size_t byte_len) +{ + smbcli_req_grow_allocation(req, byte_len + req->out.data_size); + memcpy(req->out.data + req->out.data_size, bytes, byte_len); + smbcli_req_grow_data(req, byte_len + req->out.data_size); + return byte_len; +} + +/* + append variable block (type 5 buffer) into the data portion of the request packet + return the number of bytes added +*/ +size_t smbcli_req_append_var_block(struct smbcli_request *req, const uint8_t *bytes, uint16_t byte_len) +{ + smbcli_req_grow_allocation(req, byte_len + 3 + req->out.data_size); + SCVAL(req->out.data + req->out.data_size, 0, 5); + SSVAL(req->out.data + req->out.data_size, 1, byte_len); /* add field length */ + if (byte_len > 0) { + memcpy(req->out.data + req->out.data_size + 3, bytes, byte_len); + } + smbcli_req_grow_data(req, byte_len + 3 + req->out.data_size); + return byte_len + 3; +} + + +/* + pull a UCS2 string from a request packet, returning a talloced unix string + + the string length is limited by the 3 things: + - the data size in the request (end of packet) + - the passed 'byte_len' if it is not -1 + - the end of string (null termination) + + Note that 'byte_len' is the number of bytes in the packet + + on failure zero is returned and *dest is set to NULL, otherwise the number + of bytes consumed in the packet is returned +*/ +static size_t smbcli_req_pull_ucs2(struct request_bufinfo *bufinfo, TALLOC_CTX *mem_ctx, + char **dest, const uint8_t *src, int byte_len, uint_t flags) +{ + int src_len, src_len2, alignment=0; + bool ret; + size_t ret_size; + + if (!(flags & STR_NOALIGN) && ucs2_align(bufinfo->align_base, src, flags)) { + src++; + alignment=1; + if (byte_len != -1) { + byte_len--; + } + } + + src_len = bufinfo->data_size - PTR_DIFF(src, bufinfo->data); + if (src_len < 0) { + *dest = NULL; + return 0; + } + if (byte_len != -1 && src_len > byte_len) { + src_len = byte_len; + } + + src_len2 = utf16_len_n(src, src_len); + + /* ucs2 strings must be at least 2 bytes long */ + if (src_len2 < 2) { + *dest = NULL; + return 0; + } + + ret = convert_string_talloc(mem_ctx, CH_UTF16, CH_UNIX, src, src_len2, (void **)dest, &ret_size, false); + if (!ret) { + *dest = NULL; + return 0; + } + + return src_len2 + alignment; +} + +/* + pull a ascii string from a request packet, returning a talloced string + + the string length is limited by the 3 things: + - the data size in the request (end of packet) + - the passed 'byte_len' if it is not -1 + - the end of string (null termination) + + Note that 'byte_len' is the number of bytes in the packet + + on failure zero is returned and *dest is set to NULL, otherwise the number + of bytes consumed in the packet is returned +*/ +size_t smbcli_req_pull_ascii(struct request_bufinfo *bufinfo, TALLOC_CTX *mem_ctx, + char **dest, const uint8_t *src, int byte_len, uint_t flags) +{ + int src_len, src_len2; + bool ret; + size_t ret_size; + + src_len = bufinfo->data_size - PTR_DIFF(src, bufinfo->data); + if (src_len < 0) { + *dest = NULL; + return 0; + } + if (byte_len != -1 && src_len > byte_len) { + src_len = byte_len; + } + src_len2 = strnlen((const char *)src, src_len); + if (src_len2 < src_len - 1) { + /* include the termination if we didn't reach the end of the packet */ + src_len2++; + } + + ret = convert_string_talloc(mem_ctx, CH_DOS, CH_UNIX, src, src_len2, (void **)dest, &ret_size, false); + + if (!ret) { + *dest = NULL; + return 0; + } + + return ret_size; +} + +/** + pull a string from a request packet, returning a talloced string + + the string length is limited by the 3 things: + - the data size in the request (end of packet) + - the passed 'byte_len' if it is not -1 + - the end of string (null termination) + + Note that 'byte_len' is the number of bytes in the packet + + on failure zero is returned and *dest is set to NULL, otherwise the number + of bytes consumed in the packet is returned +*/ +size_t smbcli_req_pull_string(struct request_bufinfo *bufinfo, TALLOC_CTX *mem_ctx, + char **dest, const uint8_t *src, int byte_len, uint_t flags) +{ + if (!(flags & STR_ASCII) && + (((flags & STR_UNICODE) || (bufinfo->flags & BUFINFO_FLAG_UNICODE)))) { + return smbcli_req_pull_ucs2(bufinfo, mem_ctx, dest, src, byte_len, flags); + } + + return smbcli_req_pull_ascii(bufinfo, mem_ctx, dest, src, byte_len, flags); +} + + +/** + pull a DATA_BLOB from a reply packet, returning a talloced blob + make sure we don't go past end of packet + + if byte_len is -1 then limit the blob only by packet size +*/ +DATA_BLOB smbcli_req_pull_blob(struct request_bufinfo *bufinfo, TALLOC_CTX *mem_ctx, const uint8_t *src, int byte_len) +{ + int src_len; + + src_len = bufinfo->data_size - PTR_DIFF(src, bufinfo->data); + + if (src_len < 0) { + return data_blob(NULL, 0); + } + + if (byte_len != -1 && src_len > byte_len) { + src_len = byte_len; + } + + return data_blob_talloc(mem_ctx, src, src_len); +} + +/* check that a lump of data in a request is within the bounds of the data section of + the packet */ +static bool smbcli_req_data_oob(struct request_bufinfo *bufinfo, const uint8_t *ptr, uint32_t count) +{ + /* be careful with wraparound! */ + if ((uintptr_t)ptr < (uintptr_t)bufinfo->data || + (uintptr_t)ptr >= (uintptr_t)bufinfo->data + bufinfo->data_size || + count > bufinfo->data_size || + (uintptr_t)ptr + count > (uintptr_t)bufinfo->data + bufinfo->data_size) { + return true; + } + return false; +} + +/* + pull a lump of data from a request packet + + return false if any part is outside the data portion of the packet +*/ +bool smbcli_raw_pull_data(struct request_bufinfo *bufinfo, const uint8_t *src, int len, uint8_t *dest) +{ + if (len == 0) return true; + + if (smbcli_req_data_oob(bufinfo, src, len)) { + return false; + } + + memcpy(dest, src, len); + return true; +} + + +/* + put a NTTIME into a packet +*/ +void smbcli_push_nttime(void *base, uint16_t offset, NTTIME t) +{ + SBVAL(base, offset, t); +} + +/* + pull a NTTIME from a packet +*/ +NTTIME smbcli_pull_nttime(void *base, uint16_t offset) +{ + NTTIME ret = BVAL(base, offset); + return ret; +} + +/** + pull a UCS2 string from a blob, returning a talloced unix string + + the string length is limited by the 3 things: + - the data size in the blob + - the passed 'byte_len' if it is not -1 + - the end of string (null termination) + + Note that 'byte_len' is the number of bytes in the packet + + on failure zero is returned and *dest is set to NULL, otherwise the number + of bytes consumed in the blob is returned +*/ +size_t smbcli_blob_pull_ucs2(TALLOC_CTX* mem_ctx, + const DATA_BLOB *blob, const char **dest, + const uint8_t *src, int byte_len, uint_t flags) +{ + int src_len, src_len2, alignment=0; + size_t ret_size; + bool ret; + char *dest2; + + if (src < blob->data || + src >= (blob->data + blob->length)) { + *dest = NULL; + return 0; + } + + src_len = blob->length - PTR_DIFF(src, blob->data); + + if (byte_len != -1 && src_len > byte_len) { + src_len = byte_len; + } + + if (!(flags & STR_NOALIGN) && ucs2_align(blob->data, src, flags)) { + src++; + alignment=1; + src_len--; + } + + if (src_len < 2) { + *dest = NULL; + return 0; + } + + src_len2 = utf16_len_n(src, src_len); + + ret = convert_string_talloc(mem_ctx, CH_UTF16, CH_UNIX, src, src_len2, (void **)&dest2, &ret_size, false); + if (!ret) { + *dest = NULL; + return 0; + } + *dest = dest2; + + return src_len2 + alignment; +} + +/** + pull a ascii string from a blob, returning a talloced string + + the string length is limited by the 3 things: + - the data size in the blob + - the passed 'byte_len' if it is not -1 + - the end of string (null termination) + + Note that 'byte_len' is the number of bytes in the blob + + on failure zero is returned and *dest is set to NULL, otherwise the number + of bytes consumed in the blob is returned +*/ +static size_t smbcli_blob_pull_ascii(TALLOC_CTX *mem_ctx, + const DATA_BLOB *blob, const char **dest, + const uint8_t *src, int byte_len, uint_t flags) +{ + int src_len, src_len2; + size_t ret_size; + bool ret; + char *dest2; + + src_len = blob->length - PTR_DIFF(src, blob->data); + if (src_len < 0) { + *dest = NULL; + return 0; + } + if (byte_len != -1 && src_len > byte_len) { + src_len = byte_len; + } + src_len2 = strnlen((const char *)src, src_len); + + if (src_len2 < src_len - 1) { + /* include the termination if we didn't reach the end of the packet */ + src_len2++; + } + + ret = convert_string_talloc(mem_ctx, CH_DOS, CH_UNIX, src, src_len2, (void **)&dest2, &ret_size, false); + + if (!ret) { + *dest = NULL; + return 0; + } + *dest = dest2; + + return ret_size; +} + +/** + pull a string from a blob, returning a talloced struct smb_wire_string + + the string length is limited by the 3 things: + - the data size in the blob + - length field on the wire + - the end of string (null termination) + + if STR_LEN8BIT is set in the flags then assume the length field is + 8 bits, instead of 32 + + on failure zero is returned and dest->s is set to NULL, otherwise the number + of bytes consumed in the blob is returned +*/ +size_t smbcli_blob_pull_string(struct smbcli_session *session, + TALLOC_CTX *mem_ctx, + const DATA_BLOB *blob, + struct smb_wire_string *dest, + uint16_t len_offset, uint16_t str_offset, + uint_t flags) +{ + int extra; + dest->s = NULL; + + if (!(flags & STR_ASCII)) { + /* this is here to cope with SMB2 calls using the SMB + parsers. SMB2 will pass smbcli_session==NULL, which forces + unicode on (as used by SMB2) */ + if (session == NULL) { + flags |= STR_UNICODE; + } else if (session->transport->negotiate.capabilities & CAP_UNICODE) { + flags |= STR_UNICODE; + } + } + + if (flags & STR_LEN8BIT) { + if (len_offset > blob->length-1) { + return 0; + } + dest->private_length = CVAL(blob->data, len_offset); + } else { + if (len_offset > blob->length-4) { + return 0; + } + dest->private_length = IVAL(blob->data, len_offset); + } + extra = 0; + dest->s = NULL; + if (!(flags & STR_ASCII) && (flags & STR_UNICODE)) { + int align = 0; + if ((str_offset&1) && !(flags & STR_NOALIGN)) { + align = 1; + } + if (flags & STR_LEN_NOTERM) { + extra = 2; + } + return align + extra + smbcli_blob_pull_ucs2(mem_ctx, blob, &dest->s, + blob->data+str_offset+align, + dest->private_length, flags); + } + + if (flags & STR_LEN_NOTERM) { + extra = 1; + } + + return extra + smbcli_blob_pull_ascii(mem_ctx, blob, &dest->s, + blob->data+str_offset, dest->private_length, flags); +} + +/** + pull a string from a blob, returning a talloced char * + + Currently only used by the UNIX search info level. + + the string length is limited by 2 things: + - the data size in the blob + - the end of string (null termination) + + on failure zero is returned and dest->s is set to NULL, otherwise the number + of bytes consumed in the blob is returned +*/ +size_t smbcli_blob_pull_unix_string(struct smbcli_session *session, + TALLOC_CTX *mem_ctx, + DATA_BLOB *blob, + const char **dest, + uint16_t str_offset, + uint_t flags) +{ + int extra = 0; + *dest = NULL; + + if (!(flags & STR_ASCII) && + ((flags & STR_UNICODE) || + (session->transport->negotiate.capabilities & CAP_UNICODE))) { + int align = 0; + if ((str_offset&1) && !(flags & STR_NOALIGN)) { + align = 1; + } + if (flags & STR_LEN_NOTERM) { + extra = 2; + } + return align + extra + smbcli_blob_pull_ucs2(mem_ctx, blob, dest, + blob->data+str_offset+align, + -1, flags); + } + + if (flags & STR_LEN_NOTERM) { + extra = 1; + } + + return extra + smbcli_blob_pull_ascii(mem_ctx, blob, dest, + blob->data+str_offset, -1, flags); +} + + +/* + append a string into a blob +*/ +size_t smbcli_blob_append_string(struct smbcli_session *session, + TALLOC_CTX *mem_ctx, DATA_BLOB *blob, + const char *str, uint_t flags) +{ + size_t max_len; + int len; + + if (!str) return 0; + + /* determine string type to use */ + if (!(flags & (STR_ASCII|STR_UNICODE))) { + flags |= (session->transport->negotiate.capabilities & CAP_UNICODE) ? STR_UNICODE : STR_ASCII; + } + + max_len = (strlen(str)+2) * MAX_BYTES_PER_CHAR; + + blob->data = talloc_realloc(mem_ctx, blob->data, uint8_t, blob->length + max_len); + if (!blob->data) { + return 0; + } + + len = push_string(blob->data + blob->length, str, max_len, flags); + + blob->length += len; + + return len; +} + +/* + pull a GUID structure from the wire. The buffer must be at least 16 + bytes long + */ +enum ndr_err_code smbcli_pull_guid(void *base, uint16_t offset, + struct GUID *guid) +{ + DATA_BLOB blob; + TALLOC_CTX *tmp_ctx = talloc_new(NULL); + enum ndr_err_code ndr_err; + + ZERO_STRUCTP(guid); + + blob.data = offset + (uint8_t *)base; + blob.length = 16; + ndr_err = ndr_pull_struct_blob(&blob, tmp_ctx, NULL, guid, + (ndr_pull_flags_fn_t)ndr_pull_GUID); + talloc_free(tmp_ctx); + return ndr_err; +} + +/* + push a guid onto the wire. The buffer must hold 16 bytes + */ +enum ndr_err_code smbcli_push_guid(void *base, uint16_t offset, + const struct GUID *guid) +{ + TALLOC_CTX *tmp_ctx = talloc_new(NULL); + enum ndr_err_code ndr_err; + DATA_BLOB blob; + ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, NULL, + guid, (ndr_push_flags_fn_t)ndr_push_GUID); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err) || blob.length != 16) { + talloc_free(tmp_ctx); + return ndr_err; + } + memcpy(offset + (uint8_t *)base, blob.data, blob.length); + talloc_free(tmp_ctx); + return ndr_err; +} diff --git a/source4/libcli/raw/rawsearch.c b/source4/libcli/raw/rawsearch.c new file mode 100644 index 0000000000..9c90d45492 --- /dev/null +++ b/source4/libcli/raw/rawsearch.c @@ -0,0 +1,841 @@ +/* + Unix SMB/CIFS implementation. + client directory search routines + Copyright (C) James Myers 2003 <myersjj@samba.org> + 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" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" + +/**************************************************************************** + Old style search backend - process output. +****************************************************************************/ +static void smb_raw_search_backend(struct smbcli_request *req, + TALLOC_CTX *mem_ctx, + uint16_t count, + void *private_data, + smbcli_search_callback callback) + +{ + union smb_search_data search_data; + int i; + uint8_t *p; + + if (req->in.data_size < 3 + count*43) { + req->status = NT_STATUS_INVALID_PARAMETER; + return; + } + + p = req->in.data + 3; + + for (i=0; i < count; i++) { + char *name; + + search_data.search.id.reserved = CVAL(p, 0); + memcpy(search_data.search.id.name, p+1, 11); + search_data.search.id.handle = CVAL(p, 12); + search_data.search.id.server_cookie = IVAL(p, 13); + search_data.search.id.client_cookie = IVAL(p, 17); + search_data.search.attrib = CVAL(p, 21); + search_data.search.write_time = raw_pull_dos_date(req->transport, + p + 22); + search_data.search.size = IVAL(p, 26); + smbcli_req_pull_ascii(&req->in.bufinfo, mem_ctx, &name, p+30, 13, STR_ASCII); + search_data.search.name = name; + if (!callback(private_data, &search_data)) { + break; + } + p += 43; + } +} + +/**************************************************************************** + Old style search first. +****************************************************************************/ +static NTSTATUS smb_raw_search_first_old(struct smbcli_tree *tree, + TALLOC_CTX *mem_ctx, + union smb_search_first *io, void *private_data, + smbcli_search_callback callback) + +{ + struct smbcli_request *req; + uint8_t op = SMBsearch; + + if (io->generic.level == RAW_SEARCH_FFIRST) { + op = SMBffirst; + } else if (io->generic.level == RAW_SEARCH_FUNIQUE) { + op = SMBfunique; + } + + req = smbcli_request_setup(tree, op, 2, 0); + if (!req) { + return NT_STATUS_NO_MEMORY; + } + + SSVAL(req->out.vwv, VWV(0), io->search_first.in.max_count); + SSVAL(req->out.vwv, VWV(1), io->search_first.in.search_attrib); + smbcli_req_append_ascii4(req, io->search_first.in.pattern, STR_TERMINATE); + smbcli_req_append_var_block(req, NULL, 0); + + if (!smbcli_request_send(req) || + !smbcli_request_receive(req)) { + return smbcli_request_destroy(req); + } + + if (NT_STATUS_IS_OK(req->status)) { + io->search_first.out.count = SVAL(req->in.vwv, VWV(0)); + smb_raw_search_backend(req, mem_ctx, io->search_first.out.count, private_data, callback); + } + + return smbcli_request_destroy(req); +} + +/**************************************************************************** + Old style search next. +****************************************************************************/ +static NTSTATUS smb_raw_search_next_old(struct smbcli_tree *tree, + TALLOC_CTX *mem_ctx, + union smb_search_next *io, void *private_data, + smbcli_search_callback callback) + +{ + struct smbcli_request *req; + uint8_t var_block[21]; + uint8_t op = SMBsearch; + + if (io->generic.level == RAW_SEARCH_FFIRST) { + op = SMBffirst; + } + + req = smbcli_request_setup(tree, op, 2, 0); + if (!req) { + return NT_STATUS_NO_MEMORY; + } + + SSVAL(req->out.vwv, VWV(0), io->search_next.in.max_count); + SSVAL(req->out.vwv, VWV(1), io->search_next.in.search_attrib); + smbcli_req_append_ascii4(req, "", STR_TERMINATE); + + SCVAL(var_block, 0, io->search_next.in.id.reserved); + memcpy(&var_block[1], io->search_next.in.id.name, 11); + SCVAL(var_block, 12, io->search_next.in.id.handle); + SIVAL(var_block, 13, io->search_next.in.id.server_cookie); + SIVAL(var_block, 17, io->search_next.in.id.client_cookie); + + smbcli_req_append_var_block(req, var_block, 21); + + if (!smbcli_request_send(req) || + !smbcli_request_receive(req)) { + return smbcli_request_destroy(req); + } + + if (NT_STATUS_IS_OK(req->status)) { + io->search_next.out.count = SVAL(req->in.vwv, VWV(0)); + smb_raw_search_backend(req, mem_ctx, io->search_next.out.count, private_data, callback); + } + + return smbcli_request_destroy(req); +} + + +/**************************************************************************** + Old style search next. +****************************************************************************/ +static NTSTATUS smb_raw_search_close_old(struct smbcli_tree *tree, + union smb_search_close *io) +{ + struct smbcli_request *req; + uint8_t var_block[21]; + + req = smbcli_request_setup(tree, SMBfclose, 2, 0); + if (!req) { + return NT_STATUS_NO_MEMORY; + } + + SSVAL(req->out.vwv, VWV(0), io->fclose.in.max_count); + SSVAL(req->out.vwv, VWV(1), io->fclose.in.search_attrib); + smbcli_req_append_ascii4(req, "", STR_TERMINATE); + + SCVAL(var_block, 0, io->fclose.in.id.reserved); + memcpy(&var_block[1], io->fclose.in.id.name, 11); + SCVAL(var_block, 12, io->fclose.in.id.handle); + SIVAL(var_block, 13, io->fclose.in.id.server_cookie); + SIVAL(var_block, 17, io->fclose.in.id.client_cookie); + + smbcli_req_append_var_block(req, var_block, 21); + + if (!smbcli_request_send(req) || + !smbcli_request_receive(req)) { + return smbcli_request_destroy(req); + } + + return smbcli_request_destroy(req); +} + + + +/**************************************************************************** + Very raw search first - returns param/data blobs. +****************************************************************************/ +static NTSTATUS smb_raw_search_first_blob(struct smbcli_tree *tree, + TALLOC_CTX *mem_ctx, /* used to allocate output blobs */ + union smb_search_first *io, + DATA_BLOB *out_param_blob, + DATA_BLOB *out_data_blob) +{ + struct smb_trans2 tp; + uint16_t setup = TRANSACT2_FINDFIRST; + NTSTATUS status; + + tp.in.max_setup = 0; + tp.in.flags = 0; + tp.in.timeout = 0; + tp.in.setup_count = 1; + tp.in.data = data_blob(NULL, 0); + tp.in.max_param = 10; + tp.in.max_data = 0xFFFF; + tp.in.setup = &setup; + + if (io->t2ffirst.level != RAW_SEARCH_TRANS2) { + return NT_STATUS_INVALID_LEVEL; + } + + if (io->t2ffirst.data_level >= RAW_SEARCH_DATA_GENERIC) { + return NT_STATUS_INVALID_LEVEL; + } + + if (io->t2ffirst.data_level == RAW_SEARCH_DATA_EA_LIST) { + if (!ea_push_name_list(mem_ctx, + &tp.in.data, + io->t2ffirst.in.num_names, + io->t2ffirst.in.ea_names)) { + return NT_STATUS_NO_MEMORY; + } + } + + tp.in.params = data_blob_talloc(mem_ctx, NULL, 12); + if (!tp.in.params.data) { + return NT_STATUS_NO_MEMORY; + } + + SSVAL(tp.in.params.data, 0, io->t2ffirst.in.search_attrib); + SSVAL(tp.in.params.data, 2, io->t2ffirst.in.max_count); + SSVAL(tp.in.params.data, 4, io->t2ffirst.in.flags); + SSVAL(tp.in.params.data, 6, io->t2ffirst.data_level); + SIVAL(tp.in.params.data, 8, io->t2ffirst.in.storage_type); + + smbcli_blob_append_string(tree->session, mem_ctx, &tp.in.params, + io->t2ffirst.in.pattern, STR_TERMINATE); + + status = smb_raw_trans2(tree, mem_ctx, &tp); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + out_param_blob->length = tp.out.params.length; + out_param_blob->data = tp.out.params.data; + out_data_blob->length = tp.out.data.length; + out_data_blob->data = tp.out.data.data; + + return NT_STATUS_OK; +} + + +/**************************************************************************** + Very raw search first - returns param/data blobs. + Used in CIFS-on-CIFS NTVFS. +****************************************************************************/ +static NTSTATUS smb_raw_search_next_blob(struct smbcli_tree *tree, + TALLOC_CTX *mem_ctx, + union smb_search_next *io, + DATA_BLOB *out_param_blob, + DATA_BLOB *out_data_blob) +{ + struct smb_trans2 tp; + uint16_t setup = TRANSACT2_FINDNEXT; + NTSTATUS status; + + tp.in.max_setup = 0; + tp.in.flags = 0; + tp.in.timeout = 0; + tp.in.setup_count = 1; + tp.in.data = data_blob(NULL, 0); + tp.in.max_param = 10; + tp.in.max_data = 0xFFFF; + tp.in.setup = &setup; + + if (io->t2fnext.level != RAW_SEARCH_TRANS2) { + return NT_STATUS_INVALID_LEVEL; + } + + if (io->t2fnext.data_level >= RAW_SEARCH_DATA_GENERIC) { + return NT_STATUS_INVALID_LEVEL; + } + + if (io->t2fnext.data_level == RAW_SEARCH_DATA_EA_LIST) { + if (!ea_push_name_list(mem_ctx, + &tp.in.data, + io->t2fnext.in.num_names, + io->t2fnext.in.ea_names)) { + return NT_STATUS_NO_MEMORY; + } + } + + tp.in.params = data_blob_talloc(mem_ctx, NULL, 12); + if (!tp.in.params.data) { + return NT_STATUS_NO_MEMORY; + } + + SSVAL(tp.in.params.data, 0, io->t2fnext.in.handle); + SSVAL(tp.in.params.data, 2, io->t2fnext.in.max_count); + SSVAL(tp.in.params.data, 4, io->t2fnext.data_level); + SIVAL(tp.in.params.data, 6, io->t2fnext.in.resume_key); + SSVAL(tp.in.params.data, 10, io->t2fnext.in.flags); + + smbcli_blob_append_string(tree->session, mem_ctx, &tp.in.params, + io->t2fnext.in.last_name, + STR_TERMINATE); + + status = smb_raw_trans2(tree, mem_ctx, &tp); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + out_param_blob->length = tp.out.params.length; + out_param_blob->data = tp.out.params.data; + out_data_blob->length = tp.out.data.length; + out_data_blob->data = tp.out.data.data; + + return NT_STATUS_OK; +} + + +/* + parse the wire search formats that are in common between SMB and + SMB2 +*/ +NTSTATUS smb_raw_search_common(TALLOC_CTX *mem_ctx, + enum smb_search_data_level level, + const DATA_BLOB *blob, + union smb_search_data *data, + uint_t *next_ofs, + uint_t str_flags) +{ + uint_t len, blen; + + if (blob->length < 4) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + + *next_ofs = IVAL(blob->data, 0); + if (*next_ofs != 0) { + blen = *next_ofs; + } else { + blen = blob->length; + } + + switch (level) { + case RAW_SEARCH_DATA_DIRECTORY_INFO: + if (blen < 65) return NT_STATUS_INFO_LENGTH_MISMATCH; + data->directory_info.file_index = IVAL(blob->data, 4); + data->directory_info.create_time = smbcli_pull_nttime(blob->data, 8); + data->directory_info.access_time = smbcli_pull_nttime(blob->data, 16); + data->directory_info.write_time = smbcli_pull_nttime(blob->data, 24); + data->directory_info.change_time = smbcli_pull_nttime(blob->data, 32); + data->directory_info.size = BVAL(blob->data, 40); + data->directory_info.alloc_size = BVAL(blob->data, 48); + data->directory_info.attrib = IVAL(blob->data, 56); + len = smbcli_blob_pull_string(NULL, mem_ctx, blob, + &data->directory_info.name, + 60, 64, str_flags); + if (*next_ofs != 0 && *next_ofs < 64+len) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + return NT_STATUS_OK; + + case RAW_SEARCH_DATA_FULL_DIRECTORY_INFO: + if (blen < 69) return NT_STATUS_INFO_LENGTH_MISMATCH; + data->full_directory_info.file_index = IVAL(blob->data, 4); + data->full_directory_info.create_time = smbcli_pull_nttime(blob->data, 8); + data->full_directory_info.access_time = smbcli_pull_nttime(blob->data, 16); + data->full_directory_info.write_time = smbcli_pull_nttime(blob->data, 24); + data->full_directory_info.change_time = smbcli_pull_nttime(blob->data, 32); + data->full_directory_info.size = BVAL(blob->data, 40); + data->full_directory_info.alloc_size = BVAL(blob->data, 48); + data->full_directory_info.attrib = IVAL(blob->data, 56); + data->full_directory_info.ea_size = IVAL(blob->data, 64); + len = smbcli_blob_pull_string(NULL, mem_ctx, blob, + &data->full_directory_info.name, + 60, 68, str_flags); + if (*next_ofs != 0 && *next_ofs < 68+len) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + return NT_STATUS_OK; + + case RAW_SEARCH_DATA_NAME_INFO: + if (blen < 13) return NT_STATUS_INFO_LENGTH_MISMATCH; + data->name_info.file_index = IVAL(blob->data, 4); + len = smbcli_blob_pull_string(NULL, mem_ctx, blob, + &data->name_info.name, + 8, 12, str_flags); + if (*next_ofs != 0 && *next_ofs < 12+len) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + return NT_STATUS_OK; + + + case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO: + if (blen < 95) return NT_STATUS_INFO_LENGTH_MISMATCH; + data->both_directory_info.file_index = IVAL(blob->data, 4); + data->both_directory_info.create_time = smbcli_pull_nttime(blob->data, 8); + data->both_directory_info.access_time = smbcli_pull_nttime(blob->data, 16); + data->both_directory_info.write_time = smbcli_pull_nttime(blob->data, 24); + data->both_directory_info.change_time = smbcli_pull_nttime(blob->data, 32); + data->both_directory_info.size = BVAL(blob->data, 40); + data->both_directory_info.alloc_size = BVAL(blob->data, 48); + data->both_directory_info.attrib = IVAL(blob->data, 56); + data->both_directory_info.ea_size = IVAL(blob->data, 64); + smbcli_blob_pull_string(NULL, mem_ctx, blob, + &data->both_directory_info.short_name, + 68, 70, STR_LEN8BIT | STR_UNICODE); + len = smbcli_blob_pull_string(NULL, mem_ctx, blob, + &data->both_directory_info.name, + 60, 94, str_flags); + if (*next_ofs != 0 && *next_ofs < 94+len) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + return NT_STATUS_OK; + + + case RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO: + if (blen < 81) return NT_STATUS_INFO_LENGTH_MISMATCH; + data->id_full_directory_info.file_index = IVAL(blob->data, 4); + data->id_full_directory_info.create_time = smbcli_pull_nttime(blob->data, 8); + data->id_full_directory_info.access_time = smbcli_pull_nttime(blob->data, 16); + data->id_full_directory_info.write_time = smbcli_pull_nttime(blob->data, 24); + data->id_full_directory_info.change_time = smbcli_pull_nttime(blob->data, 32); + data->id_full_directory_info.size = BVAL(blob->data, 40); + data->id_full_directory_info.alloc_size = BVAL(blob->data, 48); + data->id_full_directory_info.attrib = IVAL(blob->data, 56); + data->id_full_directory_info.ea_size = IVAL(blob->data, 64); + data->id_full_directory_info.file_id = BVAL(blob->data, 72); + len = smbcli_blob_pull_string(NULL, mem_ctx, blob, + &data->id_full_directory_info.name, + 60, 80, str_flags); + if (*next_ofs != 0 && *next_ofs < 80+len) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + return NT_STATUS_OK; + + case RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO: + if (blen < 105) return NT_STATUS_INFO_LENGTH_MISMATCH; + data->id_both_directory_info.file_index = IVAL(blob->data, 4); + data->id_both_directory_info.create_time = smbcli_pull_nttime(blob->data, 8); + data->id_both_directory_info.access_time = smbcli_pull_nttime(blob->data, 16); + data->id_both_directory_info.write_time = smbcli_pull_nttime(blob->data, 24); + data->id_both_directory_info.change_time = smbcli_pull_nttime(blob->data, 32); + data->id_both_directory_info.size = BVAL(blob->data, 40); + data->id_both_directory_info.alloc_size = BVAL(blob->data, 48); + data->id_both_directory_info.attrib = SVAL(blob->data, 56); + data->id_both_directory_info.ea_size = IVAL(blob->data, 64); + smbcli_blob_pull_string(NULL, mem_ctx, blob, + &data->id_both_directory_info.short_name, + 68, 70, STR_LEN8BIT | STR_UNICODE); + data->id_both_directory_info.file_id = BVAL(blob->data, 96); + len = smbcli_blob_pull_string(NULL, mem_ctx, blob, + &data->id_both_directory_info.name, + 60, 104, str_flags); + if (*next_ofs != 0 && *next_ofs < 104+len) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + return NT_STATUS_OK; + + default: + break; + } + + /* invalid level */ + return NT_STATUS_INVALID_INFO_CLASS; +} + + +/* + parse a trans2 search response. + Return the number of bytes consumed + return 0 for success with end of list + return -1 for a parse error +*/ +static int parse_trans2_search(struct smbcli_tree *tree, + TALLOC_CTX *mem_ctx, + enum smb_search_data_level level, + uint16_t flags, + DATA_BLOB *blob, + union smb_search_data *data) +{ + uint_t len, ofs; + uint32_t ea_size; + DATA_BLOB eablob; + NTSTATUS status; + + switch (level) { + case RAW_SEARCH_DATA_GENERIC: + case RAW_SEARCH_DATA_SEARCH: + /* handled elsewhere */ + return -1; + + case RAW_SEARCH_DATA_STANDARD: + if (flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) { + if (blob->length < 4) return -1; + data->standard.resume_key = IVAL(blob->data, 0); + blob->data += 4; + blob->length -= 4; + } + if (blob->length < 24) return -1; + data->standard.create_time = raw_pull_dos_date2(tree->session->transport, + blob->data + 0); + data->standard.access_time = raw_pull_dos_date2(tree->session->transport, + blob->data + 4); + data->standard.write_time = raw_pull_dos_date2(tree->session->transport, + blob->data + 8); + data->standard.size = IVAL(blob->data, 12); + data->standard.alloc_size = IVAL(blob->data, 16); + data->standard.attrib = SVAL(blob->data, 20); + len = smbcli_blob_pull_string(tree->session, mem_ctx, blob, + &data->standard.name, + 22, 23, STR_LEN8BIT | STR_TERMINATE | STR_LEN_NOTERM); + return len + 23; + + case RAW_SEARCH_DATA_EA_SIZE: + if (flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) { + if (blob->length < 4) return -1; + data->ea_size.resume_key = IVAL(blob->data, 0); + blob->data += 4; + blob->length -= 4; + } + if (blob->length < 28) return -1; + data->ea_size.create_time = raw_pull_dos_date2(tree->session->transport, + blob->data + 0); + data->ea_size.access_time = raw_pull_dos_date2(tree->session->transport, + blob->data + 4); + data->ea_size.write_time = raw_pull_dos_date2(tree->session->transport, + blob->data + 8); + data->ea_size.size = IVAL(blob->data, 12); + data->ea_size.alloc_size = IVAL(blob->data, 16); + data->ea_size.attrib = SVAL(blob->data, 20); + data->ea_size.ea_size = IVAL(blob->data, 22); + len = smbcli_blob_pull_string(tree->session, mem_ctx, blob, + &data->ea_size.name, + 26, 27, STR_LEN8BIT | STR_TERMINATE | STR_NOALIGN); + return len + 27 + 1; + + case RAW_SEARCH_DATA_EA_LIST: + if (flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) { + if (blob->length < 4) return -1; + data->ea_list.resume_key = IVAL(blob->data, 0); + blob->data += 4; + blob->length -= 4; + } + if (blob->length < 28) return -1; + data->ea_list.create_time = raw_pull_dos_date2(tree->session->transport, + blob->data + 0); + data->ea_list.access_time = raw_pull_dos_date2(tree->session->transport, + blob->data + 4); + data->ea_list.write_time = raw_pull_dos_date2(tree->session->transport, + blob->data + 8); + data->ea_list.size = IVAL(blob->data, 12); + data->ea_list.alloc_size = IVAL(blob->data, 16); + data->ea_list.attrib = SVAL(blob->data, 20); + ea_size = IVAL(blob->data, 22); + if (ea_size > 0xFFFF) { + return -1; + } + eablob.data = blob->data + 22; + eablob.length = ea_size; + if (eablob.length > blob->length - 24) { + return -1; + } + status = ea_pull_list(&eablob, mem_ctx, + &data->ea_list.eas.num_eas, + &data->ea_list.eas.eas); + if (!NT_STATUS_IS_OK(status)) { + return -1; + } + len = smbcli_blob_pull_string(tree->session, mem_ctx, blob, + &data->ea_list.name, + 22+ea_size, 23+ea_size, + STR_LEN8BIT | STR_NOALIGN); + return len + ea_size + 23 + 1; + + case RAW_SEARCH_DATA_UNIX_INFO: + if (blob->length < 109) return -1; + ofs = IVAL(blob->data, 0); + data->unix_info.file_index = IVAL(blob->data, 4); + data->unix_info.size = BVAL(blob->data, 8); + data->unix_info.alloc_size = BVAL(blob->data, 16); + data->unix_info.status_change_time = smbcli_pull_nttime(blob->data, 24); + data->unix_info.access_time = smbcli_pull_nttime(blob->data, 32); + data->unix_info.change_time = smbcli_pull_nttime(blob->data, 40); + data->unix_info.uid = IVAL(blob->data, 48); + data->unix_info.gid = IVAL(blob->data, 56); + data->unix_info.file_type = IVAL(blob->data, 64); + data->unix_info.dev_major = BVAL(blob->data, 68); + data->unix_info.dev_minor = BVAL(blob->data, 76); + data->unix_info.unique_id = BVAL(blob->data, 84); + data->unix_info.permissions = IVAL(blob->data, 92); + data->unix_info.nlink = IVAL(blob->data, 100); + /* There is no length field for this name but we know it's null terminated. */ + len = smbcli_blob_pull_unix_string(tree->session, mem_ctx, blob, + &data->unix_info.name, 108, 0); + if (ofs != 0 && ofs < 108+len) { + return -1; + } + return ofs; + + case RAW_SEARCH_DATA_UNIX_INFO2: + /* 8 - size of ofs + file_index + * 116 - size of unix_info2 + * 4 - size of name length + * 2 - "." is the shortest name + */ + if (blob->length < (116 + 8 + 4 + 2)) { + return -1; + } + + ofs = IVAL(blob->data, 0); + data->unix_info2.file_index = IVAL(blob->data, 4); + data->unix_info2.end_of_file = BVAL(blob->data, 8); + data->unix_info2.num_bytes = BVAL(blob->data, 16); + data->unix_info2.status_change_time = smbcli_pull_nttime(blob->data, 24); + data->unix_info2.access_time = smbcli_pull_nttime(blob->data, 32); + data->unix_info2.change_time = smbcli_pull_nttime(blob->data, 40); + data->unix_info2.uid = IVAL(blob->data, 48); + data->unix_info2.gid = IVAL(blob->data, 56); + data->unix_info2.file_type = IVAL(blob->data, 64); + data->unix_info2.dev_major = BVAL(blob->data, 68); + data->unix_info2.dev_minor = BVAL(blob->data, 76); + data->unix_info2.unique_id = BVAL(blob->data, 84); + data->unix_info2.permissions = IVAL(blob->data, 92); + data->unix_info2.nlink = IVAL(blob->data, 100); + data->unix_info2.create_time = smbcli_pull_nttime(blob->data, 108); + data->unix_info2.file_flags = IVAL(blob->data, 116); + data->unix_info2.flags_mask = IVAL(blob->data, 120); + + /* There is a 4 byte length field for this name. The length + * does not include the NULL terminator. + */ + len = smbcli_blob_pull_string(tree->session, mem_ctx, blob, + &data->unix_info2.name, + 8 + 116, /* offset to length */ + 8 + 116 + 4, /* offset to string */ + 0); + + if (ofs != 0 && ofs < (8 + 116 + 4 + len)) { + return -1; + } + + return ofs; + + case RAW_SEARCH_DATA_DIRECTORY_INFO: + case RAW_SEARCH_DATA_FULL_DIRECTORY_INFO: + case RAW_SEARCH_DATA_NAME_INFO: + case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO: + case RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO: + case RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO: { + uint_t str_flags = STR_UNICODE; + if (!(tree->session->transport->negotiate.capabilities & CAP_UNICODE)) { + str_flags = STR_ASCII; + } + + status = smb_raw_search_common(mem_ctx, level, blob, data, &ofs, str_flags); + if (!NT_STATUS_IS_OK(status)) { + return -1; + } + return ofs; + } + } + + /* invalid level */ + return -1; +} + +/**************************************************************************** + Trans2 search backend - process output. +****************************************************************************/ +static NTSTATUS smb_raw_t2search_backend(struct smbcli_tree *tree, + TALLOC_CTX *mem_ctx, + enum smb_search_data_level level, + uint16_t flags, + int16_t count, + DATA_BLOB *blob, + void *private_data, + smbcli_search_callback callback) + +{ + int i; + DATA_BLOB blob2; + + blob2.data = blob->data; + blob2.length = blob->length; + + for (i=0; i < count; i++) { + union smb_search_data search_data; + uint_t len; + + len = parse_trans2_search(tree, mem_ctx, level, flags, &blob2, &search_data); + if (len == -1) { + return NT_STATUS_INVALID_PARAMETER; + } + + /* the callback function can tell us that no more will + fit - in that case we stop, but it isn't an error */ + if (!callback(private_data, &search_data)) { + break; + } + + if (len == 0) break; + + blob2.data += len; + blob2.length -= len; + } + + return NT_STATUS_OK; +} + + +/* Implements trans2findfirst2 and old search + */ +_PUBLIC_ NTSTATUS smb_raw_search_first(struct smbcli_tree *tree, + TALLOC_CTX *mem_ctx, + union smb_search_first *io, void *private_data, + smbcli_search_callback callback) +{ + DATA_BLOB p_blob, d_blob; + NTSTATUS status; + + switch (io->generic.level) { + case RAW_SEARCH_SEARCH: + case RAW_SEARCH_FFIRST: + case RAW_SEARCH_FUNIQUE: + return smb_raw_search_first_old(tree, mem_ctx, io, private_data, callback); + + case RAW_SEARCH_TRANS2: + break; + + case RAW_SEARCH_SMB2: + return NT_STATUS_INVALID_LEVEL; + } + + status = smb_raw_search_first_blob(tree, mem_ctx, + io, &p_blob, &d_blob); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (p_blob.length < 10) { + DEBUG(1,("smb_raw_search_first: parms wrong size %d != expected_param_size\n", + (int)p_blob.length)); + return NT_STATUS_INVALID_PARAMETER; + } + + /* process output data */ + io->t2ffirst.out.handle = SVAL(p_blob.data, 0); + io->t2ffirst.out.count = SVAL(p_blob.data, 2); + io->t2ffirst.out.end_of_search = SVAL(p_blob.data, 4); + + status = smb_raw_t2search_backend(tree, mem_ctx, + io->generic.data_level, + io->t2ffirst.in.flags, io->t2ffirst.out.count, + &d_blob, private_data, callback); + + return status; +} + +/* Implements trans2findnext2 and old smbsearch + */ +NTSTATUS smb_raw_search_next(struct smbcli_tree *tree, + TALLOC_CTX *mem_ctx, + union smb_search_next *io, void *private_data, + smbcli_search_callback callback) +{ + DATA_BLOB p_blob, d_blob; + NTSTATUS status; + + switch (io->generic.level) { + case RAW_SEARCH_SEARCH: + case RAW_SEARCH_FFIRST: + return smb_raw_search_next_old(tree, mem_ctx, io, private_data, callback); + + case RAW_SEARCH_FUNIQUE: + return NT_STATUS_INVALID_LEVEL; + + case RAW_SEARCH_TRANS2: + break; + + case RAW_SEARCH_SMB2: + return NT_STATUS_INVALID_LEVEL; + } + + status = smb_raw_search_next_blob(tree, mem_ctx, + io, &p_blob, &d_blob); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (p_blob.length != 8) { + DEBUG(1,("smb_raw_search_next: parms wrong size %d != expected_param_size\n", + (int)p_blob.length)); + return NT_STATUS_INVALID_PARAMETER; + } + + /* process output data */ + io->t2fnext.out.count = SVAL(p_blob.data, 0); + io->t2fnext.out.end_of_search = SVAL(p_blob.data, 2); + + status = smb_raw_t2search_backend(tree, mem_ctx, + io->generic.data_level, + io->t2fnext.in.flags, io->t2fnext.out.count, + &d_blob, private_data, callback); + + return status; +} + +/* + Implements trans2findclose2 + */ +NTSTATUS smb_raw_search_close(struct smbcli_tree *tree, + union smb_search_close *io) +{ + struct smbcli_request *req; + + if (io->generic.level == RAW_FINDCLOSE_FCLOSE) { + return smb_raw_search_close_old(tree, io); + } + + req = smbcli_request_setup(tree, SMBfindclose, 1, 0); + if (!req) { + return NT_STATUS_NO_MEMORY; + } + + SSVAL(req->out.vwv, VWV(0), io->findclose.in.handle); + + if (smbcli_request_send(req)) { + (void) smbcli_request_receive(req); + } + + return smbcli_request_destroy(req); +} diff --git a/source4/libcli/raw/rawsetfileinfo.c b/source4/libcli/raw/rawsetfileinfo.c new file mode 100644 index 0000000000..f7dfb933f1 --- /dev/null +++ b/source4/libcli/raw/rawsetfileinfo.c @@ -0,0 +1,492 @@ +/* + Unix SMB/CIFS implementation. + RAW_SFILEINFO_* calls + Copyright (C) James Myers 2003 + Copyright (C) Andrew Tridgell 2003 + 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" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "librpc/gen_ndr/ndr_security.h" + + +/* + Handle setfileinfo/setpathinfo passthu constructions +*/ +bool smb_raw_setfileinfo_passthru(TALLOC_CTX *mem_ctx, + enum smb_setfileinfo_level level, + union smb_setfileinfo *parms, + DATA_BLOB *blob) +{ + uint_t len; + +#define NEED_BLOB(n) do { \ + *blob = data_blob_talloc(mem_ctx, NULL, n); \ + if (blob->data == NULL && n != 0) return false; \ + } while (0) + + switch (level) { + case RAW_SFILEINFO_BASIC_INFORMATION: + NEED_BLOB(40); + smbcli_push_nttime(blob->data, 0, parms->basic_info.in.create_time); + smbcli_push_nttime(blob->data, 8, parms->basic_info.in.access_time); + smbcli_push_nttime(blob->data, 16, parms->basic_info.in.write_time); + smbcli_push_nttime(blob->data, 24, parms->basic_info.in.change_time); + SIVAL(blob->data, 32, parms->basic_info.in.attrib); + SIVAL(blob->data, 36, 0); /* padding */ + return true; + + case RAW_SFILEINFO_DISPOSITION_INFORMATION: + NEED_BLOB(4); + SIVAL(blob->data, 0, parms->disposition_info.in.delete_on_close); + return true; + + case RAW_SFILEINFO_ALLOCATION_INFORMATION: + NEED_BLOB(8); + SBVAL(blob->data, 0, parms->allocation_info.in.alloc_size); + return true; + + case RAW_SFILEINFO_END_OF_FILE_INFORMATION: + NEED_BLOB(8); + SBVAL(blob->data, 0, parms->end_of_file_info.in.size); + return true; + + case RAW_SFILEINFO_RENAME_INFORMATION: + NEED_BLOB(12); + SIVAL(blob->data, 0, parms->rename_information.in.overwrite); + SIVAL(blob->data, 4, parms->rename_information.in.root_fid); + len = smbcli_blob_append_string(NULL, mem_ctx, blob, + parms->rename_information.in.new_name, + STR_UNICODE|STR_TERMINATE); + SIVAL(blob->data, 8, len - 2); + return true; + + case RAW_SFILEINFO_RENAME_INFORMATION_SMB2: + NEED_BLOB(20); + SIVAL(blob->data, 0, parms->rename_information.in.overwrite); + SBVAL(blob->data, 8, parms->rename_information.in.root_fid); + len = smbcli_blob_append_string(NULL, mem_ctx, blob, + parms->rename_information.in.new_name, + STR_UNICODE|STR_TERMINATE); + SIVAL(blob->data, 16, len - 2); + return true; + + case RAW_SFILEINFO_POSITION_INFORMATION: + NEED_BLOB(8); + SBVAL(blob->data, 0, parms->position_information.in.position); + return true; + + case RAW_SFILEINFO_MODE_INFORMATION: + NEED_BLOB(4); + SIVAL(blob->data, 0, parms->mode_information.in.mode); + return true; + + case RAW_FILEINFO_SEC_DESC: { + enum ndr_err_code ndr_err; + + ndr_err = ndr_push_struct_blob(blob, mem_ctx, NULL, + parms->set_secdesc.in.sd, + (ndr_push_flags_fn_t)ndr_push_security_descriptor); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return false; + } + + return true; + } + + case RAW_SFILEINFO_FULL_EA_INFORMATION: + printf("num_eas=%d\n", parms->full_ea_information.in.eas.num_eas); + NEED_BLOB(ea_list_size_chained( + parms->full_ea_information.in.eas.num_eas, + parms->full_ea_information.in.eas.eas, 4)); + ea_put_list_chained(blob->data, + parms->full_ea_information.in.eas.num_eas, + parms->full_ea_information.in.eas.eas, 4); + return true; + + /* Unhandled levels */ + case RAW_SFILEINFO_PIPE_INFORMATION: + case RAW_SFILEINFO_VALID_DATA_INFORMATION: + case RAW_SFILEINFO_SHORT_NAME_INFORMATION: + case RAW_SFILEINFO_1025: + case RAW_SFILEINFO_1027: + case RAW_SFILEINFO_1029: + case RAW_SFILEINFO_1030: + case RAW_SFILEINFO_1031: + case RAW_SFILEINFO_1032: + case RAW_SFILEINFO_1036: + case RAW_SFILEINFO_1041: + case RAW_SFILEINFO_1042: + case RAW_SFILEINFO_1043: + case RAW_SFILEINFO_1044: + break; + + default: + DEBUG(0,("Unhandled setfileinfo passthru level %d\n", level)); + return false; + } + + return false; +} + +/* + Handle setfileinfo/setpathinfo trans2 backend. +*/ +static bool smb_raw_setinfo_backend(struct smbcli_tree *tree, + TALLOC_CTX *mem_ctx, + union smb_setfileinfo *parms, + DATA_BLOB *blob) +{ + switch (parms->generic.level) { + case RAW_SFILEINFO_GENERIC: + case RAW_SFILEINFO_SETATTR: + case RAW_SFILEINFO_SETATTRE: + case RAW_SFILEINFO_SEC_DESC: + /* not handled here */ + return false; + + case RAW_SFILEINFO_STANDARD: + NEED_BLOB(12); + raw_push_dos_date2(tree->session->transport, + blob->data, 0, parms->standard.in.create_time); + raw_push_dos_date2(tree->session->transport, + blob->data, 4, parms->standard.in.access_time); + raw_push_dos_date2(tree->session->transport, + blob->data, 8, parms->standard.in.write_time); + return true; + + case RAW_SFILEINFO_EA_SET: + NEED_BLOB(ea_list_size(parms->ea_set.in.num_eas, parms->ea_set.in.eas)); + ea_put_list(blob->data, parms->ea_set.in.num_eas, parms->ea_set.in.eas); + return true; + + case RAW_SFILEINFO_BASIC_INFO: + case RAW_SFILEINFO_BASIC_INFORMATION: + return smb_raw_setfileinfo_passthru(mem_ctx, RAW_SFILEINFO_BASIC_INFORMATION, + parms, blob); + + case RAW_SFILEINFO_UNIX_BASIC: + NEED_BLOB(100); + SBVAL(blob->data, 0, parms->unix_basic.in.end_of_file); + SBVAL(blob->data, 8, parms->unix_basic.in.num_bytes); + smbcli_push_nttime(blob->data, 16, parms->unix_basic.in.status_change_time); + smbcli_push_nttime(blob->data, 24, parms->unix_basic.in.access_time); + smbcli_push_nttime(blob->data, 32, parms->unix_basic.in.change_time); + SBVAL(blob->data, 40, parms->unix_basic.in.uid); + SBVAL(blob->data, 48, parms->unix_basic.in.gid); + SIVAL(blob->data, 56, parms->unix_basic.in.file_type); + SBVAL(blob->data, 60, parms->unix_basic.in.dev_major); + SBVAL(blob->data, 68, parms->unix_basic.in.dev_minor); + SBVAL(blob->data, 76, parms->unix_basic.in.unique_id); + SBVAL(blob->data, 84, parms->unix_basic.in.permissions); + SBVAL(blob->data, 92, parms->unix_basic.in.nlink); + return true; + + case RAW_SFILEINFO_UNIX_INFO2: + NEED_BLOB(116); + SBVAL(blob->data, 0, parms->unix_info2.in.end_of_file); + SBVAL(blob->data, 8, parms->unix_info2.in.num_bytes); + smbcli_push_nttime(blob->data, 16, parms->unix_info2.in.status_change_time); + smbcli_push_nttime(blob->data, 24, parms->unix_info2.in.access_time); + smbcli_push_nttime(blob->data, 32, parms->unix_info2.in.change_time); + SBVAL(blob->data, 40,parms->unix_info2.in.uid); + SBVAL(blob->data, 48,parms->unix_info2.in.gid); + SIVAL(blob->data, 56,parms->unix_info2.in.file_type); + SBVAL(blob->data, 60,parms->unix_info2.in.dev_major); + SBVAL(blob->data, 68,parms->unix_info2.in.dev_minor); + SBVAL(blob->data, 76,parms->unix_info2.in.unique_id); + SBVAL(blob->data, 84,parms->unix_info2.in.permissions); + SBVAL(blob->data, 92,parms->unix_info2.in.nlink); + smbcli_push_nttime(blob->data, 100, parms->unix_info2.in.create_time); + SIVAL(blob->data, 108, parms->unix_info2.in.file_flags); + SIVAL(blob->data, 112, parms->unix_info2.in.flags_mask); + return true; + + case RAW_SFILEINFO_DISPOSITION_INFO: + case RAW_SFILEINFO_DISPOSITION_INFORMATION: + return smb_raw_setfileinfo_passthru(mem_ctx, RAW_SFILEINFO_DISPOSITION_INFORMATION, + parms, blob); + + case RAW_SFILEINFO_ALLOCATION_INFO: + case RAW_SFILEINFO_ALLOCATION_INFORMATION: + return smb_raw_setfileinfo_passthru(mem_ctx, RAW_SFILEINFO_ALLOCATION_INFORMATION, + parms, blob); + + case RAW_SFILEINFO_END_OF_FILE_INFO: + case RAW_SFILEINFO_END_OF_FILE_INFORMATION: + return smb_raw_setfileinfo_passthru(mem_ctx, RAW_SFILEINFO_END_OF_FILE_INFORMATION, + parms, blob); + + case RAW_SFILEINFO_RENAME_INFORMATION: + return smb_raw_setfileinfo_passthru(mem_ctx, RAW_SFILEINFO_RENAME_INFORMATION, + parms, blob); + + case RAW_SFILEINFO_POSITION_INFORMATION: + return smb_raw_setfileinfo_passthru(mem_ctx, RAW_SFILEINFO_POSITION_INFORMATION, + parms, blob); + + case RAW_SFILEINFO_MODE_INFORMATION: + return smb_raw_setfileinfo_passthru(mem_ctx, RAW_SFILEINFO_MODE_INFORMATION, + parms, blob); + + /* Unhandled passthru levels */ + case RAW_SFILEINFO_PIPE_INFORMATION: + case RAW_SFILEINFO_VALID_DATA_INFORMATION: + case RAW_SFILEINFO_SHORT_NAME_INFORMATION: + case RAW_SFILEINFO_FULL_EA_INFORMATION: + case RAW_SFILEINFO_1025: + case RAW_SFILEINFO_1027: + case RAW_SFILEINFO_1029: + case RAW_SFILEINFO_1030: + case RAW_SFILEINFO_1031: + case RAW_SFILEINFO_1032: + case RAW_SFILEINFO_1036: + case RAW_SFILEINFO_1041: + case RAW_SFILEINFO_1042: + case RAW_SFILEINFO_1043: + case RAW_SFILEINFO_1044: + return smb_raw_setfileinfo_passthru(mem_ctx, parms->generic.level, + parms, blob); + + /* Unhandled levels */ + + case RAW_SFILEINFO_UNIX_LINK: + case RAW_SFILEINFO_UNIX_HLINK: + case RAW_SFILEINFO_RENAME_INFORMATION_SMB2: + break; + } + + return false; +} + +/**************************************************************************** + Very raw set file info - takes data blob (async send) +****************************************************************************/ +static struct smbcli_request *smb_raw_setfileinfo_blob_send(struct smbcli_tree *tree, + TALLOC_CTX *mem_ctx, + uint16_t fnum, + uint16_t info_level, + DATA_BLOB *blob) +{ + struct smb_trans2 tp; + uint16_t setup = TRANSACT2_SETFILEINFO; + + tp.in.max_setup = 0; + tp.in.flags = 0; + tp.in.timeout = 0; + tp.in.setup_count = 1; + tp.in.max_param = 2; + tp.in.max_data = 0; + tp.in.setup = &setup; + + tp.in.params = data_blob_talloc(mem_ctx, NULL, 6); + if (!tp.in.params.data) { + return NULL; + } + SSVAL(tp.in.params.data, 0, fnum); + SSVAL(tp.in.params.data, 2, info_level); + SSVAL(tp.in.params.data, 4, 0); /* reserved */ + + tp.in.data = *blob; + + return smb_raw_trans2_send(tree, &tp); +} + +/**************************************************************************** + Very raw set path info - takes data blob +****************************************************************************/ +static struct smbcli_request *smb_raw_setpathinfo_blob_send(struct smbcli_tree *tree, + TALLOC_CTX *mem_ctx, + const char *fname, + uint16_t info_level, + DATA_BLOB *blob) +{ + struct smb_trans2 tp; + uint16_t setup = TRANSACT2_SETPATHINFO; + + tp.in.max_setup = 0; + tp.in.flags = 0; + tp.in.timeout = 0; + tp.in.setup_count = 1; + tp.in.max_param = 2; + tp.in.max_data = 0; + tp.in.setup = &setup; + + tp.in.params = data_blob_talloc(mem_ctx, NULL, 6); + if (!tp.in.params.data) { + return NULL; + } + SSVAL(tp.in.params.data, 0, info_level); + SIVAL(tp.in.params.data, 2, 0); + smbcli_blob_append_string(tree->session, mem_ctx, + &tp.in.params, + fname, STR_TERMINATE); + + tp.in.data = *blob; + + return smb_raw_trans2_send(tree, &tp); +} + +/**************************************************************************** + Handle setattr (async send) +****************************************************************************/ +static struct smbcli_request *smb_raw_setattr_send(struct smbcli_tree *tree, + union smb_setfileinfo *parms) +{ + struct smbcli_request *req; + + req = smbcli_request_setup(tree, SMBsetatr, 8, 0); + if (!req) return NULL; + + SSVAL(req->out.vwv, VWV(0), parms->setattr.in.attrib); + raw_push_dos_date3(tree->session->transport, + req->out.vwv, VWV(1), parms->setattr.in.write_time); + memset(req->out.vwv + VWV(3), 0, 10); /* reserved */ + smbcli_req_append_ascii4(req, parms->setattr.in.file.path, STR_TERMINATE); + smbcli_req_append_ascii4(req, "", STR_TERMINATE); + + if (!smbcli_request_send(req)) { + smbcli_request_destroy(req); + return NULL; + } + + return req; +} + +/**************************************************************************** + Handle setattrE. (async send) +****************************************************************************/ +static struct smbcli_request *smb_raw_setattrE_send(struct smbcli_tree *tree, + union smb_setfileinfo *parms) +{ + struct smbcli_request *req; + + req = smbcli_request_setup(tree, SMBsetattrE, 7, 0); + if (!req) return NULL; + + SSVAL(req->out.vwv, VWV(0), parms->setattre.in.file.fnum); + raw_push_dos_date2(tree->session->transport, + req->out.vwv, VWV(1), parms->setattre.in.create_time); + raw_push_dos_date2(tree->session->transport, + req->out.vwv, VWV(3), parms->setattre.in.access_time); + raw_push_dos_date2(tree->session->transport, + req->out.vwv, VWV(5), parms->setattre.in.write_time); + + if (!smbcli_request_send(req)) { + smbcli_request_destroy(req); + return NULL; + } + + return req; +} + +/**************************************************************************** + Set file info (async send) +****************************************************************************/ +struct smbcli_request *smb_raw_setfileinfo_send(struct smbcli_tree *tree, + union smb_setfileinfo *parms) +{ + DATA_BLOB blob; + TALLOC_CTX *mem_ctx; + struct smbcli_request *req; + + if (parms->generic.level == RAW_SFILEINFO_SETATTRE) { + return smb_raw_setattrE_send(tree, parms); + } + if (parms->generic.level == RAW_SFILEINFO_SEC_DESC) { + return smb_raw_set_secdesc_send(tree, parms); + } + if (parms->generic.level >= RAW_SFILEINFO_GENERIC) { + return NULL; + } + + mem_ctx = talloc_init("setpathinfo"); + if (!mem_ctx) return NULL; + + if (!smb_raw_setinfo_backend(tree, mem_ctx, parms, &blob)) { + talloc_free(mem_ctx); + return NULL; + } + + /* send request and process the output */ + req = smb_raw_setfileinfo_blob_send(tree, + mem_ctx, + parms->generic.in.file.fnum, + parms->generic.level, + &blob); + + talloc_free(mem_ctx); + return req; +} + +/**************************************************************************** + Set file info (async send) +****************************************************************************/ +_PUBLIC_ NTSTATUS smb_raw_setfileinfo(struct smbcli_tree *tree, + union smb_setfileinfo *parms) +{ + struct smbcli_request *req = smb_raw_setfileinfo_send(tree, parms); + return smbcli_request_simple_recv(req); +} + + +/**************************************************************************** + Set path info (async send) +****************************************************************************/ +_PUBLIC_ struct smbcli_request *smb_raw_setpathinfo_send(struct smbcli_tree *tree, + union smb_setfileinfo *parms) +{ + DATA_BLOB blob; + TALLOC_CTX *mem_ctx; + struct smbcli_request *req; + + if (parms->generic.level == RAW_SFILEINFO_SETATTR) { + return smb_raw_setattr_send(tree, parms); + } + if (parms->generic.level >= RAW_SFILEINFO_GENERIC) { + return NULL; + } + + mem_ctx = talloc_init("setpathinfo"); + if (!mem_ctx) return NULL; + + if (!smb_raw_setinfo_backend(tree, mem_ctx, parms, &blob)) { + talloc_free(mem_ctx); + return NULL; + } + + /* send request and process the output */ + req = smb_raw_setpathinfo_blob_send(tree, + mem_ctx, + parms->generic.in.file.path, + parms->generic.level, + &blob); + + talloc_free(mem_ctx); + return req; +} + +/**************************************************************************** + Set path info (sync interface) +****************************************************************************/ +_PUBLIC_ NTSTATUS smb_raw_setpathinfo(struct smbcli_tree *tree, + union smb_setfileinfo *parms) +{ + struct smbcli_request *req = smb_raw_setpathinfo_send(tree, parms); + return smbcli_request_simple_recv(req); +} diff --git a/source4/libcli/raw/rawshadow.c b/source4/libcli/raw/rawshadow.c new file mode 100644 index 0000000000..b318c3e025 --- /dev/null +++ b/source4/libcli/raw/rawshadow.c @@ -0,0 +1,82 @@ +/* + Unix SMB/CIFS implementation. + + shadow copy file operations + + Copyright (C) Andrew Tridgell 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" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/raw/ioctl.h" + +/* + get shadow volume data +*/ +_PUBLIC_ NTSTATUS smb_raw_shadow_data(struct smbcli_tree *tree, + TALLOC_CTX *mem_ctx, struct smb_shadow_copy *info) +{ + union smb_ioctl nt; + NTSTATUS status; + DATA_BLOB blob; + uint32_t dlength; + int i; + uint32_t ofs; + + nt.ntioctl.level = RAW_IOCTL_NTIOCTL; + nt.ntioctl.in.function = FSCTL_GET_SHADOW_COPY_DATA; + nt.ntioctl.in.file.fnum = info->in.file.fnum; + nt.ntioctl.in.fsctl = true; + nt.ntioctl.in.filter = 0; + nt.ntioctl.in.max_data = info->in.max_data; + nt.ntioctl.in.blob = data_blob(NULL, 0); + + status = smb_raw_ioctl(tree, mem_ctx, &nt); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + blob = nt.ntioctl.out.blob; + + if (blob.length < 12) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + info->out.num_volumes = IVAL(blob.data, 0); + info->out.num_names = IVAL(blob.data, 4); + dlength = IVAL(blob.data, 8); + if (dlength > blob.length - 12) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + info->out.names = talloc_array(mem_ctx, const char *, info->out.num_names); + NT_STATUS_HAVE_NO_MEMORY(info->out.names); + + ofs = 12; + for (i=0;i<info->out.num_names;i++) { + size_t len; + len = smbcli_blob_pull_ucs2(info->out.names, + &blob, &info->out.names[i], + blob.data+ofs, -1, STR_TERMINATE); + if (len == 0) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + ofs += len; + } + + return status; +} diff --git a/source4/libcli/raw/rawtrans.c b/source4/libcli/raw/rawtrans.c new file mode 100644 index 0000000000..d68df56705 --- /dev/null +++ b/source4/libcli/raw/rawtrans.c @@ -0,0 +1,961 @@ +/* + Unix SMB/CIFS implementation. + raw trans/trans2/nttrans operations + + Copyright (C) James Myers 2003 <myersjj@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/>. +*/ + +#include "includes.h" +#include "../lib/util/dlinklist.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" + +#define TORTURE_TRANS_DATA 0 + +/* + check out of bounds for incoming data +*/ +static bool raw_trans_oob(struct smbcli_request *req, + uint_t offset, uint_t count) +{ + uint8_t *ptr; + + if (count == 0) { + return false; + } + + ptr = req->in.hdr + offset; + + /* be careful with wraparound! */ + if ((uintptr_t)ptr < (uintptr_t)req->in.data || + (uintptr_t)ptr >= (uintptr_t)req->in.data + req->in.data_size || + count > req->in.data_size || + (uintptr_t)ptr + count > (uintptr_t)req->in.data + req->in.data_size) { + return true; + } + return false; +} + +static size_t raw_trans_space_left(struct smbcli_request *req) +{ + if (req->transport->negotiate.max_xmit <= req->out.size) { + return 0; + } + + return req->transport->negotiate.max_xmit - req->out.size; +} + +struct smb_raw_trans2_recv_state { + uint8_t command; + uint32_t params_total; + uint32_t data_total; + uint32_t params_left; + uint32_t data_left; + bool got_first; + uint32_t recvd_data; + uint32_t recvd_param; + struct smb_trans2 io; +}; + +NTSTATUS smb_raw_trans2_recv(struct smbcli_request *req, + TALLOC_CTX *mem_ctx, + struct smb_trans2 *parms) +{ + struct smb_raw_trans2_recv_state *state; + + if (!smbcli_request_receive(req) || + smbcli_request_is_error(req)) { + goto failed; + } + + state = talloc_get_type(req->recv_helper.private_data, + struct smb_raw_trans2_recv_state); + + parms->out = state->io.out; + talloc_steal(mem_ctx, parms->out.setup); + talloc_steal(mem_ctx, parms->out.params.data); + talloc_steal(mem_ctx, parms->out.data.data); + talloc_free(state); + + ZERO_STRUCT(req->recv_helper); + +failed: + return smbcli_request_destroy(req); +} + +static enum smbcli_request_state smb_raw_trans2_ship_rest(struct smbcli_request *req, + struct smb_raw_trans2_recv_state *state); + +/* + * This helper returns SMBCLI_REQUEST_RECV until all data has arrived + */ +static enum smbcli_request_state smb_raw_trans2_recv_helper(struct smbcli_request *req) +{ + struct smb_raw_trans2_recv_state *state = talloc_get_type(req->recv_helper.private_data, + struct smb_raw_trans2_recv_state); + uint16_t param_count, param_ofs, param_disp; + uint16_t data_count, data_ofs, data_disp; + uint16_t total_data, total_param; + uint8_t setup_count; + + /* + * 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 (smbcli_request_is_error(req)) { + goto failed; + } + + if (state->params_left > 0 || state->data_left > 0) { + return smb_raw_trans2_ship_rest(req, state); + } + + SMBCLI_CHECK_MIN_WCT(req, 10); + + total_data = SVAL(req->in.vwv, VWV(1)); + total_param = SVAL(req->in.vwv, VWV(0)); + setup_count = CVAL(req->in.vwv, VWV(9)); + + param_count = SVAL(req->in.vwv, VWV(3)); + param_ofs = SVAL(req->in.vwv, VWV(4)); + param_disp = SVAL(req->in.vwv, VWV(5)); + + data_count = SVAL(req->in.vwv, VWV(6)); + data_ofs = SVAL(req->in.vwv, VWV(7)); + data_disp = SVAL(req->in.vwv, VWV(8)); + + if (!state->got_first) { + if (total_param > 0) { + state->io.out.params = data_blob_talloc(state, NULL, total_param); + if (!state->io.out.params.data) { + goto nomem; + } + } + + if (total_data > 0) { + state->io.out.data = data_blob_talloc(state, NULL, total_data); + if (!state->io.out.data.data) { + goto nomem; + } + } + + if (setup_count > 0) { + uint16_t i; + + SMBCLI_CHECK_WCT(req, 10 + setup_count); + + state->io.out.setup_count = setup_count; + state->io.out.setup = talloc_array(state, uint16_t, setup_count); + if (!state->io.out.setup) { + goto nomem; + } + for (i=0; i < setup_count; i++) { + state->io.out.setup[i] = SVAL(req->in.vwv, VWV(10+i)); + } + } + + state->got_first = true; + } + + if (total_data > state->io.out.data.length || + total_param > state->io.out.params.length) { + /* they must *only* shrink */ + DEBUG(1,("smb_raw_trans2_recv_helper: data/params expanded!\n")); + req->status = NT_STATUS_BUFFER_TOO_SMALL; + goto failed; + } + + state->io.out.data.length = total_data; + state->io.out.params.length = total_param; + + if (data_count + data_disp > total_data || + param_count + param_disp > total_param) { + DEBUG(1,("smb_raw_trans2_recv_helper: Buffer overflow\n")); + req->status = NT_STATUS_BUFFER_TOO_SMALL; + goto failed; + } + + /* check the server isn't being nasty */ + if (raw_trans_oob(req, param_ofs, param_count) || + raw_trans_oob(req, data_ofs, data_count)) { + DEBUG(1,("smb_raw_trans2_recv_helper: out of bounds parameters!\n")); + req->status = NT_STATUS_BUFFER_TOO_SMALL; + goto failed; + } + + if (data_count) { + memcpy(state->io.out.data.data + data_disp, + req->in.hdr + data_ofs, + data_count); + } + + if (param_count) { + memcpy(state->io.out.params.data + param_disp, + req->in.hdr + param_ofs, + param_count); + } + + state->recvd_param += param_count; + state->recvd_data += data_count; + + if (state->recvd_data < total_data || + state->recvd_param < total_param) { + + /* we don't need the in buffer any more */ + talloc_free(req->in.buffer); + ZERO_STRUCT(req->in); + + /* we still wait for more data */ + DEBUG(10,("smb_raw_trans2_recv_helper: more data needed\n")); + return SMBCLI_REQUEST_RECV; + } + + DEBUG(10,("smb_raw_trans2_recv_helper: done\n")); + return SMBCLI_REQUEST_DONE; + +nomem: + req->status = NT_STATUS_NO_MEMORY; +failed: + return SMBCLI_REQUEST_ERROR; +} + +_PUBLIC_ NTSTATUS smb_raw_trans_recv(struct smbcli_request *req, + TALLOC_CTX *mem_ctx, + struct smb_trans2 *parms) +{ + return smb_raw_trans2_recv(req, mem_ctx, parms); +} + + +/* + trans/trans2 raw async interface - only BLOBs used in this interface. +*/ +struct smbcli_request *smb_raw_trans_send_backend(struct smbcli_tree *tree, + struct smb_trans2 *parms, + uint8_t command) +{ + struct smb_raw_trans2_recv_state *state; + struct smbcli_request *req; + int i; + int padding; + size_t space_left; + size_t namelen = 0; + DATA_BLOB params_chunk; + uint16_t ofs; + uint16_t params_ofs = 0; + DATA_BLOB data_chunk; + uint16_t data_ofs = 0; + + if (parms->in.params.length > UINT16_MAX || + parms->in.data.length > UINT16_MAX) { + DEBUG(3,("Attempt to send invalid trans2 request (params %u, data %u)\n", + (unsigned)parms->in.params.length, (unsigned)parms->in.data.length)); + return NULL; + } + + + if (command == SMBtrans) + padding = 1; + else + padding = 3; + + req = smbcli_request_setup(tree, command, + 14 + parms->in.setup_count, + padding); + if (!req) { + return NULL; + } + + state = talloc_zero(req, struct smb_raw_trans2_recv_state); + if (!state) { + smbcli_request_destroy(req); + return NULL; + } + + state->command = command; + + /* make sure we don't leak data via the padding */ + memset(req->out.data, 0, padding); + + /* Watch out, this changes the req->out.* pointers */ + if (command == SMBtrans && parms->in.trans_name) { + namelen = smbcli_req_append_string(req, parms->in.trans_name, + STR_TERMINATE); + } + + ofs = PTR_DIFF(req->out.data,req->out.hdr)+padding+namelen; + + /* see how much bytes of the params block we can ship in the first request */ + space_left = raw_trans_space_left(req); + + params_chunk.length = MIN(parms->in.params.length, space_left); + params_chunk.data = parms->in.params.data; + params_ofs = ofs; + + state->params_left = parms->in.params.length - params_chunk.length; + + if (state->params_left > 0) { + /* we copy the whole params block, if needed we can optimize that latter */ + state->io.in.params = data_blob_talloc(state, NULL, parms->in.params.length); + if (!state->io.in.params.data) { + smbcli_request_destroy(req); + return NULL; + } + memcpy(state->io.in.params.data, + parms->in.params.data, + parms->in.params.length); + } + + /* see how much bytes of the data block we can ship in the first request */ + space_left -= params_chunk.length; + +#if TORTURE_TRANS_DATA + if (space_left > 1) { + space_left /= 2; + } +#endif + + data_chunk.length = MIN(parms->in.data.length, space_left); + data_chunk.data = parms->in.data.data; + data_ofs = params_ofs + params_chunk.length; + + state->data_left = parms->in.data.length - data_chunk.length; + + if (state->data_left > 0) { + /* we copy the whole params block, if needed we can optimize that latter */ + state->io.in.data = data_blob_talloc(state, NULL, parms->in.data.length); + if (!state->io.in.data.data) { + smbcli_request_destroy(req); + return NULL; + } + memcpy(state->io.in.data.data, + parms->in.data.data, + parms->in.data.length); + } + + state->params_total = parms->in.params.length; + state->data_total = parms->in.data.length; + + /* primary request */ + SSVAL(req->out.vwv,VWV(0),parms->in.params.length); + SSVAL(req->out.vwv,VWV(1),parms->in.data.length); + SSVAL(req->out.vwv,VWV(2),parms->in.max_param); + SSVAL(req->out.vwv,VWV(3),parms->in.max_data); + SCVAL(req->out.vwv,VWV(4),parms->in.max_setup); + SCVAL(req->out.vwv,VWV(4)+1,0); /* reserved */ + SSVAL(req->out.vwv,VWV(5),parms->in.flags); + SIVAL(req->out.vwv,VWV(6),parms->in.timeout); + SSVAL(req->out.vwv,VWV(8),0); /* reserved */ + SSVAL(req->out.vwv,VWV(9),params_chunk.length); + SSVAL(req->out.vwv,VWV(10),params_ofs); + SSVAL(req->out.vwv,VWV(11),data_chunk.length); + SSVAL(req->out.vwv,VWV(12),data_ofs); + SCVAL(req->out.vwv,VWV(13),parms->in.setup_count); + SCVAL(req->out.vwv,VWV(13)+1,0); /* reserved */ + for (i=0;i<parms->in.setup_count;i++) { + SSVAL(req->out.vwv,VWV(14)+VWV(i),parms->in.setup[i]); + } + smbcli_req_append_blob(req, ¶ms_chunk); + smbcli_req_append_blob(req, &data_chunk); + + /* add the helper which will check that all multi-part replies are + in before an async client callack will be issued */ + req->recv_helper.fn = smb_raw_trans2_recv_helper; + req->recv_helper.private_data = state; + + if (!smbcli_request_send(req)) { + smbcli_request_destroy(req); + return NULL; + } + + return req; +} + +static enum smbcli_request_state smb_raw_trans2_ship_next(struct smbcli_request *req, + struct smb_raw_trans2_recv_state *state) +{ + struct smbcli_request *req2; + size_t space_left; + DATA_BLOB params_chunk; + uint16_t ofs; + uint16_t params_ofs = 0; + uint16_t params_disp = 0; + DATA_BLOB data_chunk; + uint16_t data_ofs = 0; + uint16_t data_disp = 0; + uint8_t wct; + + if (state->command == SMBtrans2) { + wct = 9; + } else { + wct = 8; + } + + req2 = smbcli_request_setup(req->tree, state->command+1, wct, 0); + if (!req2) { + goto nomem; + } + req2->mid = req->mid; + SSVAL(req2->out.hdr, HDR_MID, req2->mid); + + ofs = PTR_DIFF(req2->out.data,req2->out.hdr); + + /* see how much bytes of the params block we can ship in the first request */ + space_left = raw_trans_space_left(req2); + + params_disp = state->io.in.params.length - state->params_left; + params_chunk.length = MIN(state->params_left, space_left); + params_chunk.data = state->io.in.params.data + params_disp; + params_ofs = ofs; + + state->params_left -= params_chunk.length; + + /* see how much bytes of the data block we can ship in the first request */ + space_left -= params_chunk.length; + +#if TORTURE_TRANS_DATA + if (space_left > 1) { + space_left /= 2; + } +#endif + + data_disp = state->io.in.data.length - state->data_left; + data_chunk.length = MIN(state->data_left, space_left); + data_chunk.data = state->io.in.data.data + data_disp; + data_ofs = params_ofs+params_chunk.length; + + state->data_left -= data_chunk.length; + + SSVAL(req2->out.vwv,VWV(0), state->params_total); + SSVAL(req2->out.vwv,VWV(1), state->data_total); + SSVAL(req2->out.vwv,VWV(2), params_chunk.length); + SSVAL(req2->out.vwv,VWV(3), params_ofs); + SSVAL(req2->out.vwv,VWV(4), params_disp); + SSVAL(req2->out.vwv,VWV(5), data_chunk.length); + SSVAL(req2->out.vwv,VWV(6), data_ofs); + SSVAL(req2->out.vwv,VWV(7), data_disp); + if (wct == 9) { + SSVAL(req2->out.vwv,VWV(8), 0xFFFF); + } + + smbcli_req_append_blob(req2, ¶ms_chunk); + smbcli_req_append_blob(req2, &data_chunk); + + /* + * it's a one way request but we need + * the seq_num, so we destroy req2 by hand + */ + if (!smbcli_request_send(req2)) { + goto failed; + } + + req->seq_num = req2->seq_num; + smbcli_request_destroy(req2); + + return SMBCLI_REQUEST_RECV; + +nomem: + req->status = NT_STATUS_NO_MEMORY; +failed: + if (req2) { + req->status = smbcli_request_destroy(req2); + } + return SMBCLI_REQUEST_ERROR; +} + +static enum smbcli_request_state smb_raw_trans2_ship_rest(struct smbcli_request *req, + struct smb_raw_trans2_recv_state *state) +{ + enum smbcli_request_state ret = SMBCLI_REQUEST_ERROR; + + while (state->params_left > 0 || state->data_left > 0) { + ret = smb_raw_trans2_ship_next(req, state); + if (ret != SMBCLI_REQUEST_RECV) { + break; + } + } + + return ret; +} + + +/* + trans/trans2 raw async interface - only BLOBs used in this interface. + note that this doesn't yet support multi-part requests +*/ +_PUBLIC_ struct smbcli_request *smb_raw_trans_send(struct smbcli_tree *tree, + struct smb_trans2 *parms) +{ + return smb_raw_trans_send_backend(tree, parms, SMBtrans); +} + +struct smbcli_request *smb_raw_trans2_send(struct smbcli_tree *tree, + struct smb_trans2 *parms) +{ + return smb_raw_trans_send_backend(tree, parms, SMBtrans2); +} + +/* + trans2 synchronous blob interface +*/ +NTSTATUS smb_raw_trans2(struct smbcli_tree *tree, + TALLOC_CTX *mem_ctx, + struct smb_trans2 *parms) +{ + struct smbcli_request *req; + req = smb_raw_trans2_send(tree, parms); + if (!req) return NT_STATUS_UNSUCCESSFUL; + return smb_raw_trans2_recv(req, mem_ctx, parms); +} + + +/* + trans synchronous blob interface +*/ +_PUBLIC_ NTSTATUS smb_raw_trans(struct smbcli_tree *tree, + TALLOC_CTX *mem_ctx, + struct smb_trans2 *parms) +{ + struct smbcli_request *req; + req = smb_raw_trans_send(tree, parms); + if (!req) return NT_STATUS_UNSUCCESSFUL; + return smb_raw_trans_recv(req, mem_ctx, parms); +} + +struct smb_raw_nttrans_recv_state { + uint32_t params_total; + uint32_t data_total; + uint32_t params_left; + uint32_t data_left; + bool got_first; + uint32_t recvd_data; + uint32_t recvd_param; + struct smb_nttrans io; +}; + +NTSTATUS smb_raw_nttrans_recv(struct smbcli_request *req, + TALLOC_CTX *mem_ctx, + struct smb_nttrans *parms) +{ + struct smb_raw_nttrans_recv_state *state; + + if (!smbcli_request_receive(req) || + smbcli_request_is_error(req)) { + goto failed; + } + + state = talloc_get_type(req->recv_helper.private_data, + struct smb_raw_nttrans_recv_state); + + parms->out = state->io.out; + talloc_steal(mem_ctx, parms->out.setup); + talloc_steal(mem_ctx, parms->out.params.data); + talloc_steal(mem_ctx, parms->out.data.data); + talloc_free(state); + + ZERO_STRUCT(req->recv_helper); + +failed: + return smbcli_request_destroy(req); +} + +static enum smbcli_request_state smb_raw_nttrans_ship_rest(struct smbcli_request *req, + struct smb_raw_nttrans_recv_state *state); + +/* + * This helper returns SMBCLI_REQUEST_RECV until all data has arrived + */ +static enum smbcli_request_state smb_raw_nttrans_recv_helper(struct smbcli_request *req) +{ + struct smb_raw_nttrans_recv_state *state = talloc_get_type(req->recv_helper.private_data, + struct smb_raw_nttrans_recv_state); + uint32_t param_count, param_ofs, param_disp; + uint32_t data_count, data_ofs, data_disp; + uint32_t total_data, total_param; + uint8_t setup_count; + + /* + * 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 (smbcli_request_is_error(req)) { + goto failed; + } + + /* sanity check */ + if (CVAL(req->in.hdr, HDR_COM) != SMBnttrans) { + DEBUG(0,("smb_raw_nttrans_recv_helper: Expected %s response, got command 0x%02x\n", + "SMBnttrans", + CVAL(req->in.hdr,HDR_COM))); + req->status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto failed; + } + + if (state->params_left > 0 || state->data_left > 0) { + return smb_raw_nttrans_ship_rest(req, state); + } + + /* this is the first packet of the response */ + SMBCLI_CHECK_MIN_WCT(req, 18); + + total_param = IVAL(req->in.vwv, 3); + total_data = IVAL(req->in.vwv, 7); + setup_count = CVAL(req->in.vwv, 35); + + param_count = IVAL(req->in.vwv, 11); + param_ofs = IVAL(req->in.vwv, 15); + param_disp = IVAL(req->in.vwv, 19); + + data_count = IVAL(req->in.vwv, 23); + data_ofs = IVAL(req->in.vwv, 27); + data_disp = IVAL(req->in.vwv, 31); + + if (!state->got_first) { + if (total_param > 0) { + state->io.out.params = data_blob_talloc(state, NULL, total_param); + if (!state->io.out.params.data) { + goto nomem; + } + } + + if (total_data > 0) { + state->io.out.data = data_blob_talloc(state, NULL, total_data); + if (!state->io.out.data.data) { + goto nomem; + } + } + + if (setup_count > 0) { + SMBCLI_CHECK_WCT(req, 18 + setup_count); + + state->io.out.setup_count = setup_count; + state->io.out.setup = talloc_array(state, uint8_t, + setup_count * VWV(1)); + if (!state->io.out.setup) { + goto nomem; + } + memcpy(state->io.out.setup, (uint8_t *)req->out.vwv + VWV(18), + setup_count * VWV(1)); + } + + state->got_first = true; + } + + if (total_data > state->io.out.data.length || + total_param > state->io.out.params.length) { + /* they must *only* shrink */ + DEBUG(1,("smb_raw_nttrans_recv_helper: data/params expanded!\n")); + req->status = NT_STATUS_BUFFER_TOO_SMALL; + goto failed; + } + + state->io.out.data.length = total_data; + state->io.out.params.length = total_param; + + if (data_count + data_disp > total_data || + param_count + param_disp > total_param) { + DEBUG(1,("smb_raw_nttrans_recv_helper: Buffer overflow\n")); + req->status = NT_STATUS_BUFFER_TOO_SMALL; + goto failed; + } + + /* check the server isn't being nasty */ + if (raw_trans_oob(req, param_ofs, param_count) || + raw_trans_oob(req, data_ofs, data_count)) { + DEBUG(1,("smb_raw_nttrans_recv_helper: out of bounds parameters!\n")); + req->status = NT_STATUS_BUFFER_TOO_SMALL; + goto failed; + } + + if (data_count) { + memcpy(state->io.out.data.data + data_disp, + req->in.hdr + data_ofs, + data_count); + } + + if (param_count) { + memcpy(state->io.out.params.data + param_disp, + req->in.hdr + param_ofs, + param_count); + } + + state->recvd_param += param_count; + state->recvd_data += data_count; + + if (state->recvd_data < total_data || + state->recvd_param < total_param) { + + /* we don't need the in buffer any more */ + talloc_free(req->in.buffer); + ZERO_STRUCT(req->in); + + /* we still wait for more data */ + DEBUG(10,("smb_raw_nttrans_recv_helper: more data needed\n")); + return SMBCLI_REQUEST_RECV; + } + + DEBUG(10,("smb_raw_nttrans_recv_helper: done\n")); + return SMBCLI_REQUEST_DONE; + +nomem: + req->status = NT_STATUS_NO_MEMORY; +failed: + return SMBCLI_REQUEST_ERROR; +} + +/**************************************************************************** + nttrans raw - only BLOBs used in this interface. + at the moment we only handle a single primary request +****************************************************************************/ +struct smbcli_request *smb_raw_nttrans_send(struct smbcli_tree *tree, + struct smb_nttrans *parms) +{ + struct smbcli_request *req; + struct smb_raw_nttrans_recv_state *state; + uint32_t ofs; + size_t space_left; + DATA_BLOB params_chunk; + uint32_t params_ofs; + DATA_BLOB data_chunk; + uint32_t data_ofs; + int align = 0; + + /* only align if there are parameters or data */ + if (parms->in.params.length || parms->in.data.length) { + align = 3; + } + + req = smbcli_request_setup(tree, SMBnttrans, + 19 + parms->in.setup_count, align); + if (!req) { + return NULL; + } + + state = talloc_zero(req, struct smb_raw_nttrans_recv_state); + if (!state) { + talloc_free(req); + return NULL; + } + + /* fill in SMB parameters */ + + if (align != 0) { + memset(req->out.data, 0, align); + } + + ofs = PTR_DIFF(req->out.data,req->out.hdr)+align; + + /* see how much bytes of the params block we can ship in the first request */ + space_left = raw_trans_space_left(req); + + params_chunk.length = MIN(parms->in.params.length, space_left); + params_chunk.data = parms->in.params.data; + params_ofs = ofs; + + state->params_left = parms->in.params.length - params_chunk.length; + + if (state->params_left > 0) { + /* we copy the whole params block, if needed we can optimize that latter */ + state->io.in.params = data_blob_talloc(state, NULL, parms->in.params.length); + if (!state->io.in.params.data) { + smbcli_request_destroy(req); + return NULL; + } + memcpy(state->io.in.params.data, + parms->in.params.data, + parms->in.params.length); + } + + /* see how much bytes of the data block we can ship in the first request */ + space_left -= params_chunk.length; + +#if TORTURE_TRANS_DATA + if (space_left > 1) { + space_left /= 2; + } +#endif + + data_chunk.length = MIN(parms->in.data.length, space_left); + data_chunk.data = parms->in.data.data; + data_ofs = params_ofs + params_chunk.length; + + state->data_left = parms->in.data.length - data_chunk.length; + + if (state->data_left > 0) { + /* we copy the whole params block, if needed we can optimize that latter */ + state->io.in.data = data_blob_talloc(state, NULL, parms->in.data.length); + if (!state->io.in.data.data) { + smbcli_request_destroy(req); + return NULL; + } + memcpy(state->io.in.data.data, + parms->in.data.data, + parms->in.data.length); + } + + state->params_total = parms->in.params.length; + state->data_total = parms->in.data.length; + + SCVAL(req->out.vwv, 0, parms->in.max_setup); + SSVAL(req->out.vwv, 1, 0); /* reserved */ + SIVAL(req->out.vwv, 3, parms->in.params.length); + SIVAL(req->out.vwv, 7, parms->in.data.length); + SIVAL(req->out.vwv, 11, parms->in.max_param); + SIVAL(req->out.vwv, 15, parms->in.max_data); + SIVAL(req->out.vwv, 19, params_chunk.length); + SIVAL(req->out.vwv, 23, params_ofs); + SIVAL(req->out.vwv, 27, data_chunk.length); + SIVAL(req->out.vwv, 31, data_ofs); + SCVAL(req->out.vwv, 35, parms->in.setup_count); + SSVAL(req->out.vwv, 36, parms->in.function); + memcpy(req->out.vwv + VWV(19), parms->in.setup, + sizeof(uint16_t) * parms->in.setup_count); + + smbcli_req_append_blob(req, ¶ms_chunk); + smbcli_req_append_blob(req, &data_chunk); + + /* add the helper which will check that all multi-part replies are + in before an async client callack will be issued */ + req->recv_helper.fn = smb_raw_nttrans_recv_helper; + req->recv_helper.private_data = state; + + if (!smbcli_request_send(req)) { + smbcli_request_destroy(req); + return NULL; + } + + return req; +} + +static enum smbcli_request_state smb_raw_nttrans_ship_next(struct smbcli_request *req, + struct smb_raw_nttrans_recv_state *state) +{ + struct smbcli_request *req2; + size_t space_left; + DATA_BLOB params_chunk; + uint32_t ofs; + uint32_t params_ofs = 0; + uint32_t params_disp = 0; + DATA_BLOB data_chunk; + uint32_t data_ofs = 0; + uint32_t data_disp = 0; + + req2 = smbcli_request_setup(req->tree, SMBnttranss, 18, 0); + if (!req2) { + goto nomem; + } + req2->mid = req->mid; + SSVAL(req2->out.hdr, HDR_MID, req2->mid); + + ofs = PTR_DIFF(req2->out.data,req2->out.hdr); + + /* see how much bytes of the params block we can ship in the first request */ + space_left = raw_trans_space_left(req2); + + params_disp = state->io.in.params.length - state->params_left; + params_chunk.length = MIN(state->params_left, space_left); + params_chunk.data = state->io.in.params.data + params_disp; + params_ofs = ofs; + + state->params_left -= params_chunk.length; + + /* see how much bytes of the data block we can ship in the first request */ + space_left -= params_chunk.length; + +#if TORTURE_TRANS_DATA + if (space_left > 1) { + space_left /= 2; + } +#endif + + data_disp = state->io.in.data.length - state->data_left; + data_chunk.length = MIN(state->data_left, space_left); + data_chunk.data = state->io.in.data.data + data_disp; + data_ofs = params_ofs+params_chunk.length; + + state->data_left -= data_chunk.length; + + SSVAL(req2->out.vwv,0, 0); /* reserved */ + SCVAL(req2->out.vwv,2, 0); /* reserved */ + SIVAL(req2->out.vwv,3, state->params_total); + SIVAL(req2->out.vwv,7, state->data_total); + SIVAL(req2->out.vwv,11, params_chunk.length); + SIVAL(req2->out.vwv,15, params_ofs); + SIVAL(req2->out.vwv,19, params_disp); + SIVAL(req2->out.vwv,23, data_chunk.length); + SIVAL(req2->out.vwv,27, data_ofs); + SIVAL(req2->out.vwv,31, data_disp); + SCVAL(req2->out.vwv,35, 0); /* reserved */ + + smbcli_req_append_blob(req2, ¶ms_chunk); + smbcli_req_append_blob(req2, &data_chunk); + + /* + * it's a one way request but we need + * the seq_num, so we destroy req2 by hand + */ + if (!smbcli_request_send(req2)) { + goto failed; + } + + req->seq_num = req2->seq_num; + smbcli_request_destroy(req2); + + return SMBCLI_REQUEST_RECV; + +nomem: + req->status = NT_STATUS_NO_MEMORY; +failed: + if (req2) { + req->status = smbcli_request_destroy(req2); + } + return SMBCLI_REQUEST_ERROR; +} + +static enum smbcli_request_state smb_raw_nttrans_ship_rest(struct smbcli_request *req, + struct smb_raw_nttrans_recv_state *state) +{ + enum smbcli_request_state ret = SMBCLI_REQUEST_ERROR; + + while (state->params_left > 0 || state->data_left > 0) { + ret = smb_raw_nttrans_ship_next(req, state); + if (ret != SMBCLI_REQUEST_RECV) { + break; + } + } + + return ret; +} + + +/**************************************************************************** + receive a SMB nttrans response allocating the necessary memory + ****************************************************************************/ +NTSTATUS smb_raw_nttrans(struct smbcli_tree *tree, + TALLOC_CTX *mem_ctx, + struct smb_nttrans *parms) +{ + struct smbcli_request *req; + + req = smb_raw_nttrans_send(tree, parms); + if (!req) { + return NT_STATUS_UNSUCCESSFUL; + } + + return smb_raw_nttrans_recv(req, mem_ctx, parms); +} diff --git a/source4/libcli/raw/request.h b/source4/libcli/raw/request.h new file mode 100644 index 0000000000..2a572e58ee --- /dev/null +++ b/source4/libcli/raw/request.h @@ -0,0 +1,78 @@ +#ifndef _REQUEST_H +#define _REQUEST_H +/* + Unix SMB/CIFS implementation. + SMB parameters and setup + Copyright (C) Andrew Tridgell 2003 + Copyright (C) James Myers 2003 <myersjj@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/>. +*/ + +#include "libcli/raw/signing.h" + +#define BUFINFO_FLAG_UNICODE 0x0001 +#define BUFINFO_FLAG_SMB2 0x0002 + +/* + buffer limit structure used by both SMB and SMB2 + */ +struct request_bufinfo { + TALLOC_CTX *mem_ctx; + uint32_t flags; + const uint8_t *align_base; + const uint8_t *data; + size_t data_size; +}; + +/* + Shared state structure between client and server, representing the basic packet. +*/ + +struct smb_request_buffer { + /* the raw SMB buffer, including the 4 byte length header */ + uint8_t *buffer; + + /* the size of the raw buffer, including 4 byte header */ + size_t size; + + /* how much has been allocated - on reply the buffer is over-allocated to + prevent too many realloc() calls + */ + size_t allocated; + + /* the start of the SMB header - this is always buffer+4 */ + uint8_t *hdr; + + /* the command words and command word count. vwv points + into the raw buffer */ + uint8_t *vwv; + uint_t wct; + + /* the data buffer and size. data points into the raw buffer */ + uint8_t *data; + size_t data_size; + + /* ptr is used as a moving pointer into the data area + * of the packet. The reason its here and not a local + * variable in each function is that when a realloc of + * a send packet is done we need to move this + * pointer */ + uint8_t *ptr; + + /* this is used to range check and align strings and buffers */ + struct request_bufinfo bufinfo; +}; + +#endif diff --git a/source4/libcli/raw/signing.h b/source4/libcli/raw/signing.h new file mode 100644 index 0000000000..56e977ed7c --- /dev/null +++ b/source4/libcli/raw/signing.h @@ -0,0 +1,43 @@ +#ifndef _SIGNING_H +#define _SIGNING_H +/* + Unix SMB/CIFS implementation. + SMB Signing + + Andrew Bartlett <abartlet@samba.org> 2003-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/>. +*/ + +enum smb_signing_engine_state { + SMB_SIGNING_ENGINE_OFF, + SMB_SIGNING_ENGINE_BSRSPYL, + SMB_SIGNING_ENGINE_ON +}; + +enum smb_signing_state { + SMB_SIGNING_OFF, SMB_SIGNING_SUPPORTED, + SMB_SIGNING_REQUIRED, SMB_SIGNING_AUTO}; + +struct smb_signing_context { + enum smb_signing_engine_state signing_state; + DATA_BLOB mac_key; + uint32_t next_seq_num; + bool allow_smb_signing; + bool doing_signing; + bool mandatory_signing; + bool seen_valid; /* Have I ever seen a validly signed packet? */ +}; + +#endif diff --git a/source4/libcli/raw/smb.h b/source4/libcli/raw/smb.h new file mode 100644 index 0000000000..d4091acf48 --- /dev/null +++ b/source4/libcli/raw/smb.h @@ -0,0 +1,622 @@ +/* + Unix SMB/CIFS implementation. + SMB parameters and setup, plus a whole lot more. + + Copyright (C) Andrew Tridgell 1992-2000 + Copyright (C) John H Terpstra 1996-2002 + Copyright (C) Luke Kenneth Casson Leighton 1996-2000 + Copyright (C) Paul Ashton 1998-2000 + Copyright (C) Simo Sorce 2001-2002 + Copyright (C) Martin Pool 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/>. +*/ + +#ifndef _SMB_H +#define _SMB_H + +/* deny modes */ +#define DENY_DOS 0 +#define DENY_ALL 1 +#define DENY_WRITE 2 +#define DENY_READ 3 +#define DENY_NONE 4 +#define DENY_FCB 7 + +/* open modes */ +#define DOS_OPEN_RDONLY 0 +#define DOS_OPEN_WRONLY 1 +#define DOS_OPEN_RDWR 2 +#define DOS_OPEN_FCB 0xF + + +/**********************************/ +/* SMBopen field definitions */ +#define OPEN_FLAGS_DENY_MASK 0x70 +#define OPEN_FLAGS_DENY_DOS 0x00 +#define OPEN_FLAGS_DENY_ALL 0x10 +#define OPEN_FLAGS_DENY_WRITE 0x20 +#define OPEN_FLAGS_DENY_READ 0x30 +#define OPEN_FLAGS_DENY_NONE 0x40 + +#define OPEN_FLAGS_MODE_MASK 0x0F +#define OPEN_FLAGS_OPEN_READ 0 +#define OPEN_FLAGS_OPEN_WRITE 1 +#define OPEN_FLAGS_OPEN_RDWR 2 +#define OPEN_FLAGS_FCB 0xFF + + +/**********************************/ +/* SMBopenX field definitions */ + +/* OpenX Flags field. */ +#define OPENX_FLAGS_ADDITIONAL_INFO 0x01 +#define OPENX_FLAGS_REQUEST_OPLOCK 0x02 +#define OPENX_FLAGS_REQUEST_BATCH_OPLOCK 0x04 +#define OPENX_FLAGS_EA_LEN 0x08 +#define OPENX_FLAGS_EXTENDED_RETURN 0x10 + +/* desired access (open_mode), split info 4 4-bit nibbles */ +#define OPENX_MODE_ACCESS_MASK 0x000F +#define OPENX_MODE_ACCESS_READ 0x0000 +#define OPENX_MODE_ACCESS_WRITE 0x0001 +#define OPENX_MODE_ACCESS_RDWR 0x0002 +#define OPENX_MODE_ACCESS_EXEC 0x0003 +#define OPENX_MODE_ACCESS_FCB 0x000F + +#define OPENX_MODE_DENY_SHIFT 4 +#define OPENX_MODE_DENY_MASK (0xF << OPENX_MODE_DENY_SHIFT) +#define OPENX_MODE_DENY_DOS (DENY_DOS << OPENX_MODE_DENY_SHIFT) +#define OPENX_MODE_DENY_ALL (DENY_ALL << OPENX_MODE_DENY_SHIFT) +#define OPENX_MODE_DENY_WRITE (DENY_WRITE << OPENX_MODE_DENY_SHIFT) +#define OPENX_MODE_DENY_READ (DENY_READ << OPENX_MODE_DENY_SHIFT) +#define OPENX_MODE_DENY_NONE (DENY_NONE << OPENX_MODE_DENY_SHIFT) +#define OPENX_MODE_DENY_FCB (0xF << OPENX_MODE_DENY_SHIFT) + +#define OPENX_MODE_LOCALITY_MASK 0x0F00 /* what does this do? */ + +#define OPENX_MODE_NO_CACHE 0x1000 +#define OPENX_MODE_WRITE_THRU 0x4000 + +/* open function values */ +#define OPENX_OPEN_FUNC_MASK 0x3 +#define OPENX_OPEN_FUNC_FAIL 0x0 +#define OPENX_OPEN_FUNC_OPEN 0x1 +#define OPENX_OPEN_FUNC_TRUNC 0x2 + +/* The above can be OR'ed with... */ +#define OPENX_OPEN_FUNC_CREATE 0x10 + +/* openx action in reply */ +#define OPENX_ACTION_EXISTED 1 +#define OPENX_ACTION_CREATED 2 +#define OPENX_ACTION_TRUNCATED 3 + + +/**********************************/ +/* SMBntcreateX field definitions */ + +/* ntcreatex flags field. */ +#define NTCREATEX_FLAGS_REQUEST_OPLOCK 0x02 +#define NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK 0x04 +#define NTCREATEX_FLAGS_OPEN_DIRECTORY 0x08 /* TODO: opens parent? we need + a test suite for this */ +#define NTCREATEX_FLAGS_EXTENDED 0x10 + +/* the ntcreatex access_mask field + this is split into 4 pieces + AAAABBBBCCCCCCCCDDDDDDDDDDDDDDDD + A -> GENERIC_RIGHT_* + B -> SEC_RIGHT_* + C -> STD_RIGHT_* + D -> SA_RIGHT_* + + which set of SA_RIGHT_* bits is applicable depends on the type + of object. +*/ + + + +/* ntcreatex share_access field */ +#define NTCREATEX_SHARE_ACCESS_NONE 0 +#define NTCREATEX_SHARE_ACCESS_READ 1 +#define NTCREATEX_SHARE_ACCESS_WRITE 2 +#define NTCREATEX_SHARE_ACCESS_DELETE 4 +#define NTCREATEX_SHARE_ACCESS_MASK 7 + +/* ntcreatex open_disposition field */ +#define NTCREATEX_DISP_SUPERSEDE 0 /* supersede existing file (if it exists) */ +#define NTCREATEX_DISP_OPEN 1 /* if file exists open it, else fail */ +#define NTCREATEX_DISP_CREATE 2 /* if file exists fail, else create it */ +#define NTCREATEX_DISP_OPEN_IF 3 /* if file exists open it, else create it */ +#define NTCREATEX_DISP_OVERWRITE 4 /* if exists overwrite, else fail */ +#define NTCREATEX_DISP_OVERWRITE_IF 5 /* if exists overwrite, else create */ + +/* ntcreatex create_options field */ +#define NTCREATEX_OPTIONS_DIRECTORY 0x0001 +#define NTCREATEX_OPTIONS_WRITE_THROUGH 0x0002 +#define NTCREATEX_OPTIONS_SEQUENTIAL_ONLY 0x0004 +#define NTCREATEX_OPTIONS_NO_INTERMEDIATE_BUFFERING 0x0008 +#define NTCREATEX_OPTIONS_SYNC_ALERT 0x0010 +#define NTCREATEX_OPTIONS_ASYNC_ALERT 0x0020 +#define NTCREATEX_OPTIONS_NON_DIRECTORY_FILE 0x0040 +#define NTCREATEX_OPTIONS_TREE_CONNECTION 0x0080 +#define NTCREATEX_OPTIONS_COMPLETE_IF_OPLOCKED 0x0100 +#define NTCREATEX_OPTIONS_NO_EA_KNOWLEDGE 0x0200 +#define NTCREATEX_OPTIONS_OPEN_FOR_RECOVERY 0x0400 +#define NTCREATEX_OPTIONS_RANDOM_ACCESS 0x0800 +#define NTCREATEX_OPTIONS_DELETE_ON_CLOSE 0x1000 +#define NTCREATEX_OPTIONS_OPEN_BY_FILE_ID 0x2000 +#define NTCREATEX_OPTIONS_BACKUP_INTENT 0x4000 +#define NTCREATEX_OPTIONS_NO_COMPRESSION 0x8000 +/* Must be ignored by the server, per MS-SMB 2.2.8 */ +#define NTCREATEX_OPTIONS_OPFILTER 0x00100000 +#define NTCREATEX_OPTIONS_REPARSE_POINT 0x00200000 +/* Don't pull this file off tape in a HSM system */ +#define NTCREATEX_OPTIONS_NO_RECALL 0x00400000 +/* Must be ignored by the server, per MS-SMB 2.2.8 */ +#define NTCREATEX_OPTIONS_FREE_SPACE_QUERY 0x00800000 + +#define NTCREATEX_OPTIONS_MUST_IGNORE_MASK (NTCREATEX_OPTIONS_TREE_CONNECTION | \ + NTCREATEX_OPTIONS_OPEN_FOR_RECOVERY | \ + NTCREATEX_OPTIONS_FREE_SPACE_QUERY | \ + 0x000F0000) + +#define NTCREATEX_OPTIONS_NOT_SUPPORTED_MASK (NTCREATEX_OPTIONS_OPEN_BY_FILE_ID) + +#define NTCREATEX_OPTIONS_INVALID_PARAM_MASK (NTCREATEX_OPTIONS_OPFILTER | \ + NTCREATEX_OPTIONS_SYNC_ALERT | \ + NTCREATEX_OPTIONS_ASYNC_ALERT | \ + NTCREATEX_OPTIONS_OPFILTER | \ + 0xFF000000) + +/* + * We reuse some ignored flags for private use. + * This values have different meaning for some ntvfs backends. + * + * TODO: use values that are ignore for sure... + */ +#define NTCREATEX_OPTIONS_PRIVATE_DENY_DOS 0x00010000 +#define NTCREATEX_OPTIONS_PRIVATE_DENY_FCB 0x00020000 +#define NTCREATEX_OPTIONS_PRIVATE_MASK (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | \ + NTCREATEX_OPTIONS_PRIVATE_DENY_FCB) + +/* ntcreatex impersonation field */ +#define NTCREATEX_IMPERSONATION_ANONYMOUS 0 +#define NTCREATEX_IMPERSONATION_IDENTIFICATION 1 +#define NTCREATEX_IMPERSONATION_IMPERSONATION 2 +#define NTCREATEX_IMPERSONATION_DELEGATION 3 + +/* ntcreatex security flags bit field */ +#define NTCREATEX_SECURITY_DYNAMIC 1 +#define NTCREATEX_SECURITY_ALL 2 + +/* ntcreatex create_action in reply */ +#define NTCREATEX_ACTION_EXISTED 1 +#define NTCREATEX_ACTION_CREATED 2 +#define NTCREATEX_ACTION_TRUNCATED 3 +/* the value 5 can also be returned when you try to create a directory with + incorrect parameters - what does it mean? maybe created temporary file? */ +#define NTCREATEX_ACTION_UNKNOWN 5 + +#define SMB_MAGIC 0x424D53FF /* 0xFF 'S' 'M' 'B' */ + +/* the basic packet size, assuming no words or bytes. Does not include the NBT header */ +#define MIN_SMB_SIZE 35 + +/* when using NBT encapsulation every packet has a 4 byte header */ +#define NBT_HDR_SIZE 4 + +/* offsets into message header for common items - NOTE: These have + changed from being offsets from the base of the NBT packet to the base of the SMB packet. + this has reduced all these values by 4 +*/ +#define HDR_COM 4 +#define HDR_RCLS 5 +#define HDR_REH 6 +#define HDR_ERR 7 +#define HDR_FLG 9 +#define HDR_FLG2 10 +#define HDR_PIDHIGH 12 +#define HDR_SS_FIELD 14 +#define HDR_TID 24 +#define HDR_PID 26 +#define HDR_UID 28 +#define HDR_MID 30 +#define HDR_WCT 32 +#define HDR_VWV 33 + + +/* types of buffers in core SMB protocol */ +#define SMB_DATA_BLOCK 0x1 +#define SMB_ASCII4 0x4 + + +/* flag defines. CIFS spec 3.1.1 */ +#define FLAG_SUPPORT_LOCKREAD 0x01 +#define FLAG_CLIENT_BUF_AVAIL 0x02 +#define FLAG_RESERVED 0x04 +#define FLAG_CASELESS_PATHNAMES 0x08 +#define FLAG_CANONICAL_PATHNAMES 0x10 +#define FLAG_REQUEST_OPLOCK 0x20 +#define FLAG_REQUEST_BATCH_OPLOCK 0x40 +#define FLAG_REPLY 0x80 + +/* the complete */ +#define SMBmkdir 0x00 /* create directory */ +#define SMBrmdir 0x01 /* delete directory */ +#define SMBopen 0x02 /* open file */ +#define SMBcreate 0x03 /* create file */ +#define SMBclose 0x04 /* close file */ +#define SMBflush 0x05 /* flush file */ +#define SMBunlink 0x06 /* delete file */ +#define SMBmv 0x07 /* rename file */ +#define SMBgetatr 0x08 /* get file attributes */ +#define SMBsetatr 0x09 /* set file attributes */ +#define SMBread 0x0A /* read from file */ +#define SMBwrite 0x0B /* write to file */ +#define SMBlock 0x0C /* lock byte range */ +#define SMBunlock 0x0D /* unlock byte range */ +#define SMBctemp 0x0E /* create temporary file */ +#define SMBmknew 0x0F /* make new file */ +#define SMBchkpth 0x10 /* check directory path */ +#define SMBexit 0x11 /* process exit */ +#define SMBlseek 0x12 /* seek */ +#define SMBtcon 0x70 /* tree connect */ +#define SMBtconX 0x75 /* tree connect and X*/ +#define SMBtdis 0x71 /* tree disconnect */ +#define SMBnegprot 0x72 /* negotiate protocol */ +#define SMBdskattr 0x80 /* get disk attributes */ +#define SMBsearch 0x81 /* search directory */ +#define SMBsplopen 0xC0 /* open print spool file */ +#define SMBsplwr 0xC1 /* write to print spool file */ +#define SMBsplclose 0xC2 /* close print spool file */ +#define SMBsplretq 0xC3 /* return print queue */ +#define SMBsends 0xD0 /* send single block message */ +#define SMBsendb 0xD1 /* send broadcast message */ +#define SMBfwdname 0xD2 /* forward user name */ +#define SMBcancelf 0xD3 /* cancel forward */ +#define SMBgetmac 0xD4 /* get machine name */ +#define SMBsendstrt 0xD5 /* send start of multi-block message */ +#define SMBsendend 0xD6 /* send end of multi-block message */ +#define SMBsendtxt 0xD7 /* send text of multi-block message */ + +/* Core+ protocol */ +#define SMBlockread 0x13 /* Lock a range and read */ +#define SMBwriteunlock 0x14 /* write then range then unlock it */ +#define SMBreadbraw 0x1a /* read a block of data with no smb header */ +#define SMBwritebraw 0x1d /* write a block of data with no smb header */ +#define SMBwritec 0x20 /* secondary write request */ +#define SMBwriteclose 0x2c /* write a file then close it */ + +/* dos extended protocol */ +#define SMBreadBraw 0x1A /* read block raw */ +#define SMBreadBmpx 0x1B /* read block multiplexed */ +#define SMBreadBs 0x1C /* read block (secondary response) */ +#define SMBwriteBraw 0x1D /* write block raw */ +#define SMBwriteBmpx 0x1E /* write block multiplexed */ +#define SMBwriteBs 0x1F /* write block (secondary request) */ +#define SMBwriteC 0x20 /* write complete response */ +#define SMBsetattrE 0x22 /* set file attributes expanded */ +#define SMBgetattrE 0x23 /* get file attributes expanded */ +#define SMBlockingX 0x24 /* lock/unlock byte ranges and X */ +#define SMBtrans 0x25 /* transaction - name, bytes in/out */ +#define SMBtranss 0x26 /* transaction (secondary request/response) */ +#define SMBioctl 0x27 /* IOCTL */ +#define SMBioctls 0x28 /* IOCTL (secondary request/response) */ +#define SMBcopy 0x29 /* copy */ +#define SMBmove 0x2A /* move */ +#define SMBecho 0x2B /* echo */ +#define SMBopenX 0x2D /* open and X */ +#define SMBreadX 0x2E /* read and X */ +#define SMBwriteX 0x2F /* write and X */ +#define SMBsesssetupX 0x73 /* Session Set Up & X (including User Logon) */ +#define SMBffirst 0x82 /* find first */ +#define SMBfunique 0x83 /* find unique */ +#define SMBfclose 0x84 /* find close */ +#define SMBkeepalive 0x85 /* keepalive */ +#define SMBinvalid 0xFE /* invalid command */ + +/* Extended 2.0 protocol */ +#define SMBtrans2 0x32 /* TRANS2 protocol set */ +#define SMBtranss2 0x33 /* TRANS2 protocol set, secondary command */ +#define SMBfindclose 0x34 /* Terminate a TRANSACT2_FINDFIRST */ +#define SMBfindnclose 0x35 /* Terminate a TRANSACT2_FINDNOTIFYFIRST */ +#define SMBulogoffX 0x74 /* user logoff */ + +/* NT SMB extensions. */ +#define SMBnttrans 0xA0 /* NT transact */ +#define SMBnttranss 0xA1 /* NT transact secondary */ +#define SMBntcreateX 0xA2 /* NT create and X */ +#define SMBntcancel 0xA4 /* NT cancel */ +#define SMBntrename 0xA5 /* NT rename */ + +/* used to indicate end of chain */ +#define SMB_CHAIN_NONE 0xFF + +/* These are the trans subcommands */ +#define TRANSACT_SETNAMEDPIPEHANDLESTATE 0x01 +#define TRANSACT_DCERPCCMD 0x26 +#define TRANSACT_WAITNAMEDPIPEHANDLESTATE 0x53 + +/* These are the NT transact sub commands. */ +#define NT_TRANSACT_CREATE 1 +#define NT_TRANSACT_IOCTL 2 +#define NT_TRANSACT_SET_SECURITY_DESC 3 +#define NT_TRANSACT_NOTIFY_CHANGE 4 +#define NT_TRANSACT_RENAME 5 +#define NT_TRANSACT_QUERY_SECURITY_DESC 6 + +/* this is used on a TConX. I'm not sure the name is very helpful though */ +#define SMB_SUPPORT_SEARCH_BITS 0x0001 +#define SMB_SHARE_IN_DFS 0x0002 + +/* Named pipe write mode flags. Used in writeX calls. */ +#define PIPE_RAW_MODE 0x4 +#define PIPE_START_MESSAGE 0x8 + +/* the desired access to use when opening a pipe */ +#define DESIRED_ACCESS_PIPE 0x2019f + + +/* Mapping of generic access rights for files to specific rights. */ +#define FILE_GENERIC_ALL (STANDARD_RIGHTS_REQUIRED_ACCESS| NT_ACCESS_SYNCHRONIZE_ACCESS|FILE_ALL_ACCESS) + +#define FILE_GENERIC_READ (STANDARD_RIGHTS_READ_ACCESS|FILE_READ_DATA|FILE_READ_ATTRIBUTES|\ + FILE_READ_EA|NT_ACCESS_SYNCHRONIZE_ACCESS) + +#define FILE_GENERIC_WRITE (STANDARD_RIGHTS_WRITE_ACCESS|FILE_WRITE_DATA|FILE_WRITE_ATTRIBUTES|\ + FILE_WRITE_EA|FILE_APPEND_DATA|NT_ACCESS_SYNCHRONIZE_ACCESS) + +#define FILE_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE_ACCESS|FILE_READ_ATTRIBUTES|\ + FILE_EXECUTE|NT_ACCESS_SYNCHRONIZE_ACCESS) + + +/* FileAttributes (search attributes) field */ +#define FILE_ATTRIBUTE_READONLY 0x0001 +#define FILE_ATTRIBUTE_HIDDEN 0x0002 +#define FILE_ATTRIBUTE_SYSTEM 0x0004 +#define FILE_ATTRIBUTE_VOLUME 0x0008 +#define FILE_ATTRIBUTE_DIRECTORY 0x0010 +#define FILE_ATTRIBUTE_ARCHIVE 0x0020 +#define FILE_ATTRIBUTE_DEVICE 0x0040 +#define FILE_ATTRIBUTE_NORMAL 0x0080 +#define FILE_ATTRIBUTE_TEMPORARY 0x0100 +#define FILE_ATTRIBUTE_SPARSE 0x0200 +#define FILE_ATTRIBUTE_REPARSE_POINT 0x0400 +#define FILE_ATTRIBUTE_COMPRESSED 0x0800 +#define FILE_ATTRIBUTE_OFFLINE 0x1000 +#define FILE_ATTRIBUTE_NONINDEXED 0x2000 +#define FILE_ATTRIBUTE_ENCRYPTED 0x4000 +#define FILE_ATTRIBUTE_ALL_MASK 0x7FFF + +/* Flags - combined with attributes. */ +#define FILE_FLAG_WRITE_THROUGH 0x80000000L +#define FILE_FLAG_NO_BUFFERING 0x20000000L +#define FILE_FLAG_RANDOM_ACCESS 0x10000000L +#define FILE_FLAG_SEQUENTIAL_SCAN 0x08000000L +#define FILE_FLAG_DELETE_ON_CLOSE 0x04000000L +#define FILE_FLAG_BACKUP_SEMANTICS 0x02000000L /* only if backup/restore privilege? */ +#define FILE_FLAG_POSIX_SEMANTICS 0x01000000L + +/* Responses when opening a file. */ +#define FILE_WAS_SUPERSEDED 0 +#define FILE_WAS_OPENED 1 +#define FILE_WAS_CREATED 2 +#define FILE_WAS_OVERWRITTEN 3 + +/* File type flags */ +#define FILE_TYPE_DISK 0 +#define FILE_TYPE_BYTE_MODE_PIPE 1 +#define FILE_TYPE_MESSAGE_MODE_PIPE 2 +#define FILE_TYPE_PRINTER 3 +#define FILE_TYPE_COMM_DEVICE 4 +#define FILE_TYPE_UNKNOWN 0xFFFF + +/* Flag for NT transact rename call. */ +#define RENAME_REPLACE_IF_EXISTS 1 + +/* flags for SMBntrename call */ +#define RENAME_FLAG_MOVE_CLUSTER_INFORMATION 0x102 /* ???? */ +#define RENAME_FLAG_HARD_LINK 0x103 +#define RENAME_FLAG_RENAME 0x104 +#define RENAME_FLAG_COPY 0x105 + +/* Filesystem Attributes. */ +#define FILE_CASE_SENSITIVE_SEARCH 0x01 +#define FILE_CASE_PRESERVED_NAMES 0x02 +#define FILE_UNICODE_ON_DISK 0x04 +/* According to cifs9f, this is 4, not 8 */ +/* Acconding to testing, this actually sets the security attribute! */ +#define FILE_PERSISTENT_ACLS 0x08 +/* These entries added from cifs9f --tsb */ +#define FILE_FILE_COMPRESSION 0x10 +#define FILE_VOLUME_QUOTAS 0x20 +/* I think this is wrong. JRA #define FILE_DEVICE_IS_MOUNTED 0x20 */ +#define FILE_VOLUME_SPARSE_FILE 0x40 +#define FILE_VOLUME_IS_COMPRESSED 0x8000 + +/* ChangeNotify flags. */ +#define FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001 +#define FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002 +#define FILE_NOTIFY_CHANGE_ATTRIBUTES 0x00000004 +#define FILE_NOTIFY_CHANGE_SIZE 0x00000008 +#define FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010 +#define FILE_NOTIFY_CHANGE_LAST_ACCESS 0x00000020 +#define FILE_NOTIFY_CHANGE_CREATION 0x00000040 +#define FILE_NOTIFY_CHANGE_EA 0x00000080 +#define FILE_NOTIFY_CHANGE_SECURITY 0x00000100 +#define FILE_NOTIFY_CHANGE_STREAM_NAME 0x00000200 +#define FILE_NOTIFY_CHANGE_STREAM_SIZE 0x00000400 +#define FILE_NOTIFY_CHANGE_STREAM_WRITE 0x00000800 + +#define FILE_NOTIFY_CHANGE_NAME \ + (FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_DIR_NAME) + +/* change notify action results */ +#define NOTIFY_ACTION_ADDED 1 +#define NOTIFY_ACTION_REMOVED 2 +#define NOTIFY_ACTION_MODIFIED 3 +#define NOTIFY_ACTION_OLD_NAME 4 +#define NOTIFY_ACTION_NEW_NAME 5 +#define NOTIFY_ACTION_ADDED_STREAM 6 +#define NOTIFY_ACTION_REMOVED_STREAM 7 +#define NOTIFY_ACTION_MODIFIED_STREAM 8 + +/* seek modes for smb_seek */ +#define SEEK_MODE_START 0 +#define SEEK_MODE_CURRENT 1 +#define SEEK_MODE_END 2 + +/* where to find the base of the SMB packet proper */ +/* REWRITE TODO: smb_base needs to be removed */ +#define smb_base(buf) (((char *)(buf))+4) + +/* we don't allow server strings to be longer than 48 characters as + otherwise NT will not honour the announce packets */ +#define MAX_SERVER_STRING_LENGTH 48 + +/* This was set by JHT in liaison with Jeremy Allison early 1997 + * History: + * Version 4.0 - never made public + * Version 4.10 - New to 1.9.16p2, lost in space 1.9.16p3 to 1.9.16p9 + * - Reappeared in 1.9.16p11 with fixed smbd services + * Version 4.20 - To indicate that nmbd and browsing now works better + * Version 4.50 - Set at release of samba-2.2.0 by JHT + * + * Note: In the presence of NT4.X do not set above 4.9 + * Setting this above 4.9 can have undesired side-effects. + * This may change again in Samba-3.0 after further testing. JHT + */ + +#define DEFAULT_MAJOR_VERSION 0x04 +#define DEFAULT_MINOR_VERSION 0x09 + +/* Browser Election Values */ +#define BROWSER_ELECTION_VERSION 0x010f +#define BROWSER_CONSTANT 0xaa55 + +/* Sercurity mode bits. */ +#define NEGOTIATE_SECURITY_USER_LEVEL 0x01 +#define NEGOTIATE_SECURITY_CHALLENGE_RESPONSE 0x02 +#define NEGOTIATE_SECURITY_SIGNATURES_ENABLED 0x04 +#define NEGOTIATE_SECURITY_SIGNATURES_REQUIRED 0x08 + +/* NT Flags2 bits - cifs6.txt section 3.1.2 */ +#define FLAGS2_LONG_PATH_COMPONENTS 0x0001 +#define FLAGS2_EXTENDED_ATTRIBUTES 0x0002 +#define FLAGS2_SMB_SECURITY_SIGNATURES 0x0004 +#define FLAGS2_IS_LONG_NAME 0x0040 +#define FLAGS2_EXTENDED_SECURITY 0x0800 +#define FLAGS2_DFS_PATHNAMES 0x1000 +#define FLAGS2_READ_PERMIT_EXECUTE 0x2000 +#define FLAGS2_32_BIT_ERROR_CODES 0x4000 +#define FLAGS2_UNICODE_STRINGS 0x8000 + + +/* CIFS protocol capabilities */ +#define CAP_RAW_MODE 0x00000001 +#define CAP_MPX_MODE 0x00000002 +#define CAP_UNICODE 0x00000004 +#define CAP_LARGE_FILES 0x00000008 +#define CAP_NT_SMBS 0x00000010 +#define CAP_RPC_REMOTE_APIS 0x00000020 +#define CAP_STATUS32 0x00000040 +#define CAP_LEVEL_II_OPLOCKS 0x00000080 +#define CAP_LOCK_AND_READ 0x00000100 +#define CAP_NT_FIND 0x00000200 +#define CAP_DFS 0x00001000 +#define CAP_W2K_SMBS 0x00002000 +#define CAP_LARGE_READX 0x00004000 +#define CAP_LARGE_WRITEX 0x00008000 +#define CAP_UNIX 0x00800000 /* Capabilities for UNIX extensions. Created by HP. */ +#define CAP_EXTENDED_SECURITY 0x80000000 + +/* + * Global value meaning that the smb_uid field should be + * ingored (in share level security and protocol level == CORE) + */ + +#define UID_FIELD_INVALID 0 + +/* Lock types. */ +#define LOCKING_ANDX_SHARED_LOCK 0x01 +#define LOCKING_ANDX_OPLOCK_RELEASE 0x02 +#define LOCKING_ANDX_CHANGE_LOCKTYPE 0x04 +#define LOCKING_ANDX_CANCEL_LOCK 0x08 +#define LOCKING_ANDX_LARGE_FILES 0x10 + +/* + * Bits we test with. + */ + +#define OPLOCK_NONE 0 +#define OPLOCK_EXCLUSIVE 1 +#define OPLOCK_BATCH 2 +#define OPLOCK_LEVEL_II 4 + +#define CORE_OPLOCK_GRANTED (1<<5) +#define EXTENDED_OPLOCK_GRANTED (1<<15) + +/* + * Return values for oplock types. + */ + +#define NO_OPLOCK_RETURN 0 +#define EXCLUSIVE_OPLOCK_RETURN 1 +#define BATCH_OPLOCK_RETURN 2 +#define LEVEL_II_OPLOCK_RETURN 3 + +/* oplock levels sent in oplock break */ +#define OPLOCK_BREAK_TO_NONE 0 +#define OPLOCK_BREAK_TO_LEVEL_II 1 + + +#define CMD_REPLY 0x8000 + +/* The maximum length of a trust account password. + Used when we randomly create it, 15 char passwords + exceed NT4's max password length */ + +#define DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH 14 + + +/* + filesystem attribute bits +*/ +#define FS_ATTR_CASE_SENSITIVE_SEARCH 0x00000001 +#define FS_ATTR_CASE_PRESERVED_NAMES 0x00000002 +#define FS_ATTR_UNICODE_ON_DISK 0x00000004 +#define FS_ATTR_PERSISTANT_ACLS 0x00000008 +#define FS_ATTR_COMPRESSION 0x00000010 +#define FS_ATTR_QUOTAS 0x00000020 +#define FS_ATTR_SPARSE_FILES 0x00000040 +#define FS_ATTR_REPARSE_POINTS 0x00000080 +#define FS_ATTR_REMOTE_STORAGE 0x00000100 +#define FS_ATTR_LFN_SUPPORT 0x00004000 +#define FS_ATTR_IS_COMPRESSED 0x00008000 +#define FS_ATTR_OBJECT_IDS 0x00010000 +#define FS_ATTR_ENCRYPTION 0x00020000 +#define FS_ATTR_NAMED_STREAMS 0x00040000 + +#define smb_len(buf) (PVAL(buf,3)|(PVAL(buf,2)<<8)|(PVAL(buf,1)<<16)) +#define _smb_setlen(buf,len) do {(buf)[0] = 0; (buf)[1] = ((len)&0x10000)>>16; \ + (buf)[2] = ((len)&0xFF00)>>8; (buf)[3] = (len)&0xFF;} while (0) +#define _smb2_setlen(buf,len) do {(buf)[0] = 0; (buf)[1] = ((len)&0xFF0000)>>16; \ + (buf)[2] = ((len)&0xFF00)>>8; (buf)[3] = (len)&0xFF;} while (0) + +#include "libcli/raw/trans2.h" +#include "libcli/raw/interfaces.h" + +#endif /* _SMB_H */ diff --git a/source4/libcli/raw/smb_signing.c b/source4/libcli/raw/smb_signing.c new file mode 100644 index 0000000000..9f94039078 --- /dev/null +++ b/source4/libcli/raw/smb_signing.c @@ -0,0 +1,407 @@ +/* + Unix SMB/CIFS implementation. + SMB Signing Code + Copyright (C) Jeremy Allison 2002. + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2002-2003 + Copyright (C) James J Myers <myersjj@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" +#include "smb.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "../lib/crypto/crypto.h" + +/*********************************************************** + SMB signing - Common code before we set a new signing implementation +************************************************************/ +bool set_smb_signing_common(struct smb_signing_context *sign_info) +{ + if (sign_info->doing_signing) { + DEBUG(5, ("SMB Signing already in progress, so we don't start it again\n")); + return false; + } + + if (!sign_info->allow_smb_signing) { + DEBUG(5, ("SMB Signing has been locally disabled\n")); + return false; + } + + return true; +} + +/*********************************************************** + SMB signing - Common code before we set a new signing implementation +************************************************************/ +static bool smbcli_set_smb_signing_common(struct smbcli_transport *transport) +{ + if (!set_smb_signing_common(&transport->negotiate.sign_info)) { + return false; + } + + if (!(transport->negotiate.sec_mode & + (NEGOTIATE_SECURITY_SIGNATURES_REQUIRED|NEGOTIATE_SECURITY_SIGNATURES_ENABLED))) { + DEBUG(5, ("SMB Signing is not negotiated by the peer\n")); + return false; + } + + /* These calls are INCOMPATIBLE with SMB signing */ + transport->negotiate.readbraw_supported = false; + transport->negotiate.writebraw_supported = false; + + return true; +} + +void mark_packet_signed(struct smb_request_buffer *out) +{ + uint16_t flags2; + flags2 = SVAL(out->hdr, HDR_FLG2); + flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES; + SSVAL(out->hdr, HDR_FLG2, flags2); +} + +bool signing_good(struct smb_signing_context *sign_info, + unsigned int seq, bool good) +{ + if (good) { + if (!sign_info->doing_signing) { + DEBUG(5, ("Seen valid packet, so turning signing on\n")); + sign_info->doing_signing = true; + } + if (!sign_info->seen_valid) { + DEBUG(5, ("Seen valid packet, so marking signing as 'seen valid'\n")); + sign_info->seen_valid = true; + } + } else { + if (!sign_info->seen_valid) { + /* If we have never seen a good packet, just turn it off */ + DEBUG(5, ("signing_good: signing negotiated but not required and peer\n" + "isn't sending correct signatures. Turning off.\n")); + smbcli_set_signing_off(sign_info); + return true; + } else { + /* bad packet after signing started - fail and disconnect. */ + DEBUG(0, ("signing_good: BAD SIG: seq %u\n", seq)); + return false; + } + } + return true; +} + +void sign_outgoing_message(struct smb_request_buffer *out, DATA_BLOB *mac_key, unsigned int seq_num) +{ + uint8_t calc_md5_mac[16]; + struct MD5Context md5_ctx; + + /* + * Firstly put the sequence number into the first 4 bytes. + * and zero out the next 4 bytes. + */ + SIVAL(out->hdr, HDR_SS_FIELD, seq_num); + SIVAL(out->hdr, HDR_SS_FIELD + 4, 0); + + /* mark the packet as signed - BEFORE we sign it...*/ + mark_packet_signed(out); + + /* Calculate the 16 byte MAC and place first 8 bytes into the field. */ + MD5Init(&md5_ctx); + MD5Update(&md5_ctx, mac_key->data, mac_key->length); + MD5Update(&md5_ctx, + out->buffer + NBT_HDR_SIZE, + out->size - NBT_HDR_SIZE); + MD5Final(calc_md5_mac, &md5_ctx); + + memcpy(&out->hdr[HDR_SS_FIELD], calc_md5_mac, 8); + + DEBUG(5, ("sign_outgoing_message: SENT SIG (seq: %d): sent SMB signature of\n", + seq_num)); + dump_data(5, calc_md5_mac, 8); +/* req->out.hdr[HDR_SS_FIELD+2]=0; + Uncomment this to test if the remote server actually verifies signitures...*/ +} + +bool check_signed_incoming_message(struct smb_request_buffer *in, DATA_BLOB *mac_key, uint_t seq_num) +{ + bool good; + uint8_t calc_md5_mac[16]; + uint8_t *server_sent_mac; + uint8_t sequence_buf[8]; + struct MD5Context md5_ctx; + const size_t offset_end_of_sig = (HDR_SS_FIELD + 8); + int i; + const int sign_range = 0; + + /* room enough for the signature? */ + if (in->size < NBT_HDR_SIZE + HDR_SS_FIELD + 8) { + return false; + } + + if (!mac_key->length) { + /* NO key yet */ + return false; + } + + /* its quite bogus to be guessing sequence numbers, but very useful + when debugging signing implementations */ + for (i = 0-sign_range; i <= 0+sign_range; i++) { + /* + * Firstly put the sequence number into the first 4 bytes. + * and zero out the next 4 bytes. + */ + SIVAL(sequence_buf, 0, seq_num + i); + SIVAL(sequence_buf, 4, 0); + + /* get a copy of the server-sent mac */ + server_sent_mac = &in->hdr[HDR_SS_FIELD]; + + /* Calculate the 16 byte MAC and place first 8 bytes into the field. */ + MD5Init(&md5_ctx); + MD5Update(&md5_ctx, mac_key->data, + mac_key->length); + MD5Update(&md5_ctx, in->hdr, HDR_SS_FIELD); + MD5Update(&md5_ctx, sequence_buf, sizeof(sequence_buf)); + + MD5Update(&md5_ctx, in->hdr + offset_end_of_sig, + in->size - NBT_HDR_SIZE - (offset_end_of_sig)); + MD5Final(calc_md5_mac, &md5_ctx); + + good = (memcmp(server_sent_mac, calc_md5_mac, 8) == 0); + + if (i == 0) { + if (!good) { + DEBUG(5, ("check_signed_incoming_message: BAD SIG (seq: %d): wanted SMB signature of\n", seq_num + i)); + dump_data(5, calc_md5_mac, 8); + + DEBUG(5, ("check_signed_incoming_message: BAD SIG (seq: %d): got SMB signature of\n", seq_num + i)); + dump_data(5, server_sent_mac, 8); + } else { + DEBUG(15, ("check_signed_incoming_message: GOOD SIG (seq: %d): got SMB signature of\n", seq_num + i)); + dump_data(5, server_sent_mac, 8); + } + } + + if (good) break; + } + + if (good && i != 0) { + DEBUG(0,("SIGNING OFFSET %d (should be %d)\n", i, seq_num)); + } + + return good; +} + +static void smbcli_req_allocate_seq_num(struct smbcli_request *req) +{ + req->seq_num = req->transport->negotiate.sign_info.next_seq_num; + + /* some requests (eg. NTcancel) are one way, and the sequence number + should be increased by 1 not 2 */ + if (req->sign_single_increment) { + req->transport->negotiate.sign_info.next_seq_num += 1; + } else { + req->transport->negotiate.sign_info.next_seq_num += 2; + } +} + +/*********************************************************** + SMB signing - Simple implementation - calculate a MAC to send. +************************************************************/ +void smbcli_request_calculate_sign_mac(struct smbcli_request *req) +{ +#if 0 + /* enable this when packet signing is preventing you working out why valgrind + says that data is uninitialised */ + file_save("pkt.dat", req->out.buffer, req->out.size); +#endif + + switch (req->transport->negotiate.sign_info.signing_state) { + case SMB_SIGNING_ENGINE_OFF: + break; + + case SMB_SIGNING_ENGINE_BSRSPYL: + /* mark the packet as signed - BEFORE we sign it...*/ + mark_packet_signed(&req->out); + + /* I wonder what BSRSPYL stands for - but this is what MS + actually sends! */ + memcpy((req->out.hdr + HDR_SS_FIELD), "BSRSPYL ", 8); + break; + + case SMB_SIGNING_ENGINE_ON: + + smbcli_req_allocate_seq_num(req); + sign_outgoing_message(&req->out, + &req->transport->negotiate.sign_info.mac_key, + req->seq_num); + break; + } + return; +} + + +/** + SMB signing - NULL implementation + + @note Used as an initialisation only - it will not correctly + shut down a real signing mechanism +*/ +bool smbcli_set_signing_off(struct smb_signing_context *sign_info) +{ + DEBUG(5, ("Shutdown SMB signing\n")); + sign_info->doing_signing = false; + data_blob_free(&sign_info->mac_key); + sign_info->signing_state = SMB_SIGNING_ENGINE_OFF; + return true; +} + +/** + SMB signing - TEMP implementation - setup the MAC key. + +*/ +bool smbcli_temp_set_signing(struct smbcli_transport *transport) +{ + if (!smbcli_set_smb_signing_common(transport)) { + return false; + } + DEBUG(5, ("BSRSPYL SMB signing enabled\n")); + smbcli_set_signing_off(&transport->negotiate.sign_info); + + transport->negotiate.sign_info.mac_key = data_blob(NULL, 0); + transport->negotiate.sign_info.signing_state = SMB_SIGNING_ENGINE_BSRSPYL; + + return true; +} + +/*********************************************************** + SMB signing - Simple implementation - check a MAC sent by server. +************************************************************/ +/** + * Check a packet supplied by the server. + * @return false if we had an established signing connection + * which had a back checksum, true otherwise + */ +bool smbcli_request_check_sign_mac(struct smbcli_request *req) +{ + bool good; + + if (!req->transport->negotiate.sign_info.doing_signing && + req->sign_caller_checks) { + return true; + } + + req->sign_caller_checks = false; + + switch (req->transport->negotiate.sign_info.signing_state) + { + case SMB_SIGNING_ENGINE_OFF: + return true; + case SMB_SIGNING_ENGINE_BSRSPYL: + return true; + + case SMB_SIGNING_ENGINE_ON: + { + if (req->in.size < (HDR_SS_FIELD + 8)) { + return false; + } else { + good = check_signed_incoming_message(&req->in, + &req->transport->negotiate.sign_info.mac_key, + req->seq_num+1); + + return signing_good(&req->transport->negotiate.sign_info, + req->seq_num+1, good); + } + } + } + return false; +} + + +/*********************************************************** + SMB signing - Simple implementation - setup the MAC key. +************************************************************/ +bool smbcli_simple_set_signing(TALLOC_CTX *mem_ctx, + struct smb_signing_context *sign_info, + const DATA_BLOB *user_session_key, + const DATA_BLOB *response) +{ + if (sign_info->mandatory_signing) { + DEBUG(5, ("Mandatory SMB signing enabled!\n")); + } + + DEBUG(5, ("SMB signing enabled!\n")); + + if (response && response->length) { + sign_info->mac_key = data_blob_talloc(mem_ctx, NULL, response->length + user_session_key->length); + } else { + sign_info->mac_key = data_blob_talloc(mem_ctx, NULL, user_session_key->length); + } + + memcpy(&sign_info->mac_key.data[0], user_session_key->data, user_session_key->length); + + if (response && response->length) { + memcpy(&sign_info->mac_key.data[user_session_key->length],response->data, response->length); + } + + dump_data_pw("Started Signing with key:\n", sign_info->mac_key.data, sign_info->mac_key.length); + + sign_info->signing_state = SMB_SIGNING_ENGINE_ON; + sign_info->next_seq_num = 2; + + return true; +} + + +/*********************************************************** + SMB signing - Simple implementation - setup the MAC key. +************************************************************/ +bool smbcli_transport_simple_set_signing(struct smbcli_transport *transport, + const DATA_BLOB user_session_key, + const DATA_BLOB response) +{ + if (!smbcli_set_smb_signing_common(transport)) { + return false; + } + + return smbcli_simple_set_signing(transport, + &transport->negotiate.sign_info, + &user_session_key, + &response); +} + + +bool smbcli_init_signing(struct smbcli_transport *transport) +{ + transport->negotiate.sign_info.next_seq_num = 0; + transport->negotiate.sign_info.mac_key = data_blob(NULL, 0); + if (!smbcli_set_signing_off(&transport->negotiate.sign_info)) { + return false; + } + + switch (transport->options.signing) { + case SMB_SIGNING_OFF: + transport->negotiate.sign_info.allow_smb_signing = false; + break; + case SMB_SIGNING_SUPPORTED: + case SMB_SIGNING_AUTO: + transport->negotiate.sign_info.allow_smb_signing = true; + break; + case SMB_SIGNING_REQUIRED: + transport->negotiate.sign_info.allow_smb_signing = true; + transport->negotiate.sign_info.mandatory_signing = true; + break; + } + return true; +} diff --git a/source4/libcli/raw/trans2.h b/source4/libcli/raw/trans2.h new file mode 100644 index 0000000000..63632eb5ed --- /dev/null +++ b/source4/libcli/raw/trans2.h @@ -0,0 +1,476 @@ +/* + Unix SMB/CIFS implementation. + SMB transaction2 handling + Copyright (C) Jeremy Allison 1994-2002. + Copyright (C) Andrew Tridgell 1995-2003. + 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/>. +*/ + +#ifndef _TRANS2_H_ +#define _TRANS2_H_ + +/* These are the TRANS2 sub commands */ +#define TRANSACT2_OPEN 0 +#define TRANSACT2_FINDFIRST 1 +#define TRANSACT2_FINDNEXT 2 +#define TRANSACT2_QFSINFO 3 +#define TRANSACT2_SETFSINFO 4 +#define TRANSACT2_QPATHINFO 5 +#define TRANSACT2_SETPATHINFO 6 +#define TRANSACT2_QFILEINFO 7 +#define TRANSACT2_SETFILEINFO 8 +#define TRANSACT2_FSCTL 9 +#define TRANSACT2_IOCTL 0xA +#define TRANSACT2_FINDNOTIFYFIRST 0xB +#define TRANSACT2_FINDNOTIFYNEXT 0xC +#define TRANSACT2_MKDIR 0xD +#define TRANSACT2_SESSION_SETUP 0xE +#define TRANSACT2_GET_DFS_REFERRAL 0x10 +#define TRANSACT2_REPORT_DFS_INCONSISTANCY 0x11 + + +/* trans2 Query FS info levels */ +/* +w2k3 TRANS2ALIASES: +Checking for QFSINFO aliases + Found level 1 (0x001) of size 18 (0x12) + Found level 2 (0x002) of size 12 (0x0c) + Found level 258 (0x102) of size 26 (0x1a) + Found level 259 (0x103) of size 24 (0x18) + Found level 260 (0x104) of size 8 (0x08) + Found level 261 (0x105) of size 20 (0x14) + Found level 1001 (0x3e9) of size 26 (0x1a) + Found level 1003 (0x3eb) of size 24 (0x18) + Found level 1004 (0x3ec) of size 8 (0x08) + Found level 1005 (0x3ed) of size 20 (0x14) + Found level 1006 (0x3ee) of size 48 (0x30) + Found level 1007 (0x3ef) of size 32 (0x20) + Found level 1008 (0x3f0) of size 64 (0x40) +Found 13 levels with success status + Level 261 (0x105) and level 1005 (0x3ed) are possible aliases + Level 260 (0x104) and level 1004 (0x3ec) are possible aliases + Level 259 (0x103) and level 1003 (0x3eb) are possible aliases + Level 258 (0x102) and level 1001 (0x3e9) are possible aliases +Found 4 aliased levels +*/ +#define SMB_QFS_ALLOCATION 1 +#define SMB_QFS_VOLUME 2 +#define SMB_QFS_VOLUME_INFO 0x102 +#define SMB_QFS_SIZE_INFO 0x103 +#define SMB_QFS_DEVICE_INFO 0x104 +#define SMB_QFS_ATTRIBUTE_INFO 0x105 +#define SMB_QFS_UNIX_INFO 0x200 +#define SMB_QFS_POSIX_INFO 0x201 +#define SMB_QFS_POSIX_WHOAMI 0x202 +#define SMB_QFS_VOLUME_INFORMATION 1001 +#define SMB_QFS_SIZE_INFORMATION 1003 +#define SMB_QFS_DEVICE_INFORMATION 1004 +#define SMB_QFS_ATTRIBUTE_INFORMATION 1005 +#define SMB_QFS_QUOTA_INFORMATION 1006 +#define SMB_QFS_FULL_SIZE_INFORMATION 1007 +#define SMB_QFS_OBJECTID_INFORMATION 1008 + + +/* trans2 qfileinfo/qpathinfo */ +/* w2k3 TRANS2ALIASES: +Checking for QPATHINFO aliases +setting up complex file \qpathinfo_aliases.txt + Found level 1 (0x001) of size 22 (0x16) + Found level 2 (0x002) of size 26 (0x1a) + Found level 4 (0x004) of size 41 (0x29) + Found level 6 (0x006) of size 0 (0x00) + Found level 257 (0x101) of size 40 (0x28) + Found level 258 (0x102) of size 24 (0x18) + Found level 259 (0x103) of size 4 (0x04) + Found level 260 (0x104) of size 48 (0x30) + Found level 263 (0x107) of size 126 (0x7e) + Found level 264 (0x108) of size 28 (0x1c) + Found level 265 (0x109) of size 38 (0x26) + Found level 267 (0x10b) of size 16 (0x10) + Found level 1004 (0x3ec) of size 40 (0x28) + Found level 1005 (0x3ed) of size 24 (0x18) + Found level 1006 (0x3ee) of size 8 (0x08) + Found level 1007 (0x3ef) of size 4 (0x04) + Found level 1008 (0x3f0) of size 4 (0x04) + Found level 1009 (0x3f1) of size 48 (0x30) + Found level 1014 (0x3f6) of size 8 (0x08) + Found level 1016 (0x3f8) of size 4 (0x04) + Found level 1017 (0x3f9) of size 4 (0x04) + Found level 1018 (0x3fa) of size 126 (0x7e) + Found level 1021 (0x3fd) of size 28 (0x1c) + Found level 1022 (0x3fe) of size 38 (0x26) + Found level 1028 (0x404) of size 16 (0x10) + Found level 1034 (0x40a) of size 56 (0x38) + Found level 1035 (0x40b) of size 8 (0x08) +Found 27 levels with success status + Level 267 (0x10b) and level 1028 (0x404) are possible aliases + Level 265 (0x109) and level 1022 (0x3fe) are possible aliases + Level 264 (0x108) and level 1021 (0x3fd) are possible aliases + Level 263 (0x107) and level 1018 (0x3fa) are possible aliases + Level 260 (0x104) and level 1009 (0x3f1) are possible aliases + Level 259 (0x103) and level 1007 (0x3ef) are possible aliases + Level 258 (0x102) and level 1005 (0x3ed) are possible aliases + Level 257 (0x101) and level 1004 (0x3ec) are possible aliases +Found 8 aliased levels +*/ +#define SMB_QFILEINFO_STANDARD 1 +#define SMB_QFILEINFO_EA_SIZE 2 +#define SMB_QFILEINFO_EA_LIST 3 +#define SMB_QFILEINFO_ALL_EAS 4 +#define SMB_QFILEINFO_IS_NAME_VALID 6 /* only for QPATHINFO */ +#define SMB_QFILEINFO_BASIC_INFO 0x101 +#define SMB_QFILEINFO_STANDARD_INFO 0x102 +#define SMB_QFILEINFO_EA_INFO 0x103 +#define SMB_QFILEINFO_NAME_INFO 0x104 +#define SMB_QFILEINFO_ALL_INFO 0x107 +#define SMB_QFILEINFO_ALT_NAME_INFO 0x108 +#define SMB_QFILEINFO_STREAM_INFO 0x109 +#define SMB_QFILEINFO_COMPRESSION_INFO 0x10b +#define SMB_QFILEINFO_UNIX_BASIC 0x200 +#define SMB_QFILEINFO_UNIX_LINK 0x201 +#define SMB_QFILEINFO_UNIX_INFO2 0x20b +#define SMB_QFILEINFO_BASIC_INFORMATION 1004 +#define SMB_QFILEINFO_STANDARD_INFORMATION 1005 +#define SMB_QFILEINFO_INTERNAL_INFORMATION 1006 +#define SMB_QFILEINFO_EA_INFORMATION 1007 +#define SMB_QFILEINFO_ACCESS_INFORMATION 1008 +#define SMB_QFILEINFO_NAME_INFORMATION 1009 +#define SMB_QFILEINFO_POSITION_INFORMATION 1014 +#define SMB_QFILEINFO_MODE_INFORMATION 1016 +#define SMB_QFILEINFO_ALIGNMENT_INFORMATION 1017 +#define SMB_QFILEINFO_ALL_INFORMATION 1018 +#define SMB_QFILEINFO_ALT_NAME_INFORMATION 1021 +#define SMB_QFILEINFO_STREAM_INFORMATION 1022 +#define SMB_QFILEINFO_COMPRESSION_INFORMATION 1028 +#define SMB_QFILEINFO_NETWORK_OPEN_INFORMATION 1034 +#define SMB_QFILEINFO_ATTRIBUTE_TAG_INFORMATION 1035 + + + +/* trans2 setfileinfo/setpathinfo levels */ +/* +w2k3 TRANS2ALIASES +Checking for SETFILEINFO aliases +setting up complex file \setfileinfo_aliases.txt + Found level 1 (0x001) of size 2 (0x02) + Found level 2 (0x002) of size 2 (0x02) + Found level 257 (0x101) of size 40 (0x28) + Found level 258 (0x102) of size 2 (0x02) + Found level 259 (0x103) of size 8 (0x08) + Found level 260 (0x104) of size 8 (0x08) + Found level 1004 (0x3ec) of size 40 (0x28) + Found level 1010 (0x3f2) of size 2 (0x02) + Found level 1013 (0x3f5) of size 2 (0x02) + Found level 1014 (0x3f6) of size 8 (0x08) + Found level 1016 (0x3f8) of size 4 (0x04) + Found level 1019 (0x3fb) of size 8 (0x08) + Found level 1020 (0x3fc) of size 8 (0x08) + Found level 1023 (0x3ff) of size 8 (0x08) + Found level 1025 (0x401) of size 16 (0x10) + Found level 1029 (0x405) of size 72 (0x48) + Found level 1032 (0x408) of size 56 (0x38) + Found level 1039 (0x40f) of size 8 (0x08) + Found level 1040 (0x410) of size 8 (0x08) +Found 19 valid levels + +Checking for SETPATHINFO aliases + Found level 1004 (0x3ec) of size 40 (0x28) + Found level 1010 (0x3f2) of size 2 (0x02) + Found level 1013 (0x3f5) of size 2 (0x02) + Found level 1014 (0x3f6) of size 8 (0x08) + Found level 1016 (0x3f8) of size 4 (0x04) + Found level 1019 (0x3fb) of size 8 (0x08) + Found level 1020 (0x3fc) of size 8 (0x08) + Found level 1023 (0x3ff) of size 8 (0x08) + Found level 1025 (0x401) of size 16 (0x10) + Found level 1029 (0x405) of size 72 (0x48) + Found level 1032 (0x408) of size 56 (0x38) + Found level 1039 (0x40f) of size 8 (0x08) + Found level 1040 (0x410) of size 8 (0x08) +Found 13 valid levels +*/ +#define SMB_SFILEINFO_STANDARD 1 +#define SMB_SFILEINFO_EA_SET 2 +#define SMB_SFILEINFO_BASIC_INFO 0x101 +#define SMB_SFILEINFO_DISPOSITION_INFO 0x102 +#define SMB_SFILEINFO_ALLOCATION_INFO 0x103 +#define SMB_SFILEINFO_END_OF_FILE_INFO 0x104 +#define SMB_SFILEINFO_UNIX_BASIC 0x200 +#define SMB_SFILEINFO_UNIX_LINK 0x201 +#define SMB_SPATHINFO_UNIX_HLINK 0x203 +#define SMB_SPATHINFO_POSIX_ACL 0x204 +#define SMB_SPATHINFO_XATTR 0x205 +#define SMB_SFILEINFO_ATTR_FLAGS 0x206 +#define SMB_SFILEINFO_UNIX_INFO2 0x20b +#define SMB_SFILEINFO_BASIC_INFORMATION 1004 +#define SMB_SFILEINFO_RENAME_INFORMATION 1010 +#define SMB_SFILEINFO_LINK_INFORMATION 1011 +#define SMB_SFILEINFO_DISPOSITION_INFORMATION 1013 +#define SMB_SFILEINFO_POSITION_INFORMATION 1014 +#define SMB_SFILEINFO_FULL_EA_INFORMATION 1015 +#define SMB_SFILEINFO_MODE_INFORMATION 1016 +#define SMB_SFILEINFO_ALLOCATION_INFORMATION 1019 +#define SMB_SFILEINFO_END_OF_FILE_INFORMATION 1020 +#define SMB_SFILEINFO_PIPE_INFORMATION 1023 +#define SMB_SFILEINFO_VALID_DATA_INFORMATION 1039 +#define SMB_SFILEINFO_SHORT_NAME_INFORMATION 1040 + +/* filemon shows FilePipeRemoteInformation */ +#define SMB_SFILEINFO_1025 1025 + +/* vista scan responds */ +#define SMB_SFILEINFO_1027 1027 + +/* filemon shows CopyOnWriteInformation */ +#define SMB_SFILEINFO_1029 1029 + +/* filemon shows OleClassIdInformation */ +#define SMB_SFILEINFO_1032 1032 + +/* vista scan responds to these */ +#define SMB_SFILEINFO_1030 1030 +#define SMB_SFILEINFO_1031 1031 +#define SMB_SFILEINFO_1036 1036 +#define SMB_SFILEINFO_1041 1041 +#define SMB_SFILEINFO_1042 1042 +#define SMB_SFILEINFO_1043 1043 +#define SMB_SFILEINFO_1044 1044 + +/* trans2 findfirst levels */ +/* +w2k3 TRANS2ALIASES: +Checking for FINDFIRST aliases + Found level 1 (0x001) of size 68 (0x44) + Found level 2 (0x002) of size 70 (0x46) + Found level 257 (0x101) of size 108 (0x6c) + Found level 258 (0x102) of size 116 (0x74) + Found level 259 (0x103) of size 60 (0x3c) + Found level 260 (0x104) of size 140 (0x8c) + Found level 261 (0x105) of size 124 (0x7c) + Found level 262 (0x106) of size 148 (0x94) +Found 8 levels with success status +Found 0 aliased levels +*/ +#define SMB_FIND_STANDARD 1 +#define SMB_FIND_EA_SIZE 2 +#define SMB_FIND_EA_LIST 3 +#define SMB_FIND_DIRECTORY_INFO 0x101 +#define SMB_FIND_FULL_DIRECTORY_INFO 0x102 +#define SMB_FIND_NAME_INFO 0x103 +#define SMB_FIND_BOTH_DIRECTORY_INFO 0x104 +#define SMB_FIND_ID_FULL_DIRECTORY_INFO 0x105 +#define SMB_FIND_ID_BOTH_DIRECTORY_INFO 0x106 +#define SMB_FIND_UNIX_INFO 0x202 +#define SMB_FIND_UNIX_INFO2 0x20b + +/* flags on trans2 findfirst/findnext that control search */ +#define FLAG_TRANS2_FIND_CLOSE 0x1 +#define FLAG_TRANS2_FIND_CLOSE_IF_END 0x2 +#define FLAG_TRANS2_FIND_REQUIRE_RESUME 0x4 +#define FLAG_TRANS2_FIND_CONTINUE 0x8 +#define FLAG_TRANS2_FIND_BACKUP_INTENT 0x10 + +/* + * DeviceType and Characteristics returned in a + * SMB_QFS_DEVICE_INFO call. + */ +#define QFS_DEVICETYPE_CD_ROM 0x2 +#define QFS_DEVICETYPE_CD_ROM_FILE_SYSTEM 0x3 +#define QFS_DEVICETYPE_DISK 0x7 +#define QFS_DEVICETYPE_DISK_FILE_SYSTEM 0x8 +#define QFS_DEVICETYPE_FILE_SYSTEM 0x9 + +/* Characteristics. */ +#define QFS_TYPE_REMOVABLE_MEDIA 0x1 +#define QFS_TYPE_READ_ONLY_DEVICE 0x2 +#define QFS_TYPE_FLOPPY 0x4 +#define QFS_TYPE_WORM 0x8 +#define QFS_TYPE_REMOTE 0x10 +#define QFS_TYPE_MOUNTED 0x20 +#define QFS_TYPE_VIRTUAL 0x40 + + +/* + * Thursby MAC extensions.... + */ + +/* + * MAC CIFS Extensions have the range 0x300 - 0x2FF reserved. + * Supposedly Microsoft have agreed to this. + */ + +#define MIN_MAC_INFO_LEVEL 0x300 +#define MAX_MAC_INFO_LEVEL 0x3FF +#define SMB_QFS_MAC_FS_INFO 0x301 + + + +/* UNIX CIFS Extensions - created by HP */ +/* + * UNIX CIFS Extensions have the range 0x200 - 0x2FF reserved. + * Supposedly Microsoft have agreed to this. + */ + +#define MIN_UNIX_INFO_LEVEL 0x200 +#define MAX_UNIX_INFO_LEVEL 0x2FF + +#define INFO_LEVEL_IS_UNIX(level) (((level) >= MIN_UNIX_INFO_LEVEL) && ((level) <= MAX_UNIX_INFO_LEVEL)) + +#define SMB_MODE_NO_CHANGE 0xFFFFFFFF /* file mode value which */ + /* means "don't change it" */ +#define SMB_UID_NO_CHANGE 0xFFFFFFFF +#define SMB_GID_NO_CHANGE 0xFFFFFFFF + +#define SMB_SIZE_NO_CHANGE_LO 0xFFFFFFFF +#define SMB_SIZE_NO_CHANGE_HI 0xFFFFFFFF + +#define SMB_TIME_NO_CHANGE_LO 0xFFFFFFFF +#define SMB_TIME_NO_CHANGE_HI 0xFFFFFFFF + +/* +UNIX_BASIC info level: + +Offset Size Name +0 LARGE_INTEGER EndOfFile File size +8 LARGE_INTEGER Blocks Number of bytes used on disk (st_blocks). +16 LARGE_INTEGER CreationTime Creation time +24 LARGE_INTEGER LastAccessTime Last access time +32 LARGE_INTEGER LastModificationTime Last modification time +40 LARGE_INTEGER Uid Numeric user id for the owner +48 LARGE_INTEGER Gid Numeric group id of owner +56 ULONG Type Enumeration specifying the pathname type: + 0 -- File + 1 -- Directory + 2 -- Symbolic link + 3 -- Character device + 4 -- Block device + 5 -- FIFO (named pipe) + 6 -- Unix domain socket + +60 LARGE_INTEGER devmajor Major device number if type is device +68 LARGE_INTEGER devminor Minor device number if type is device +76 LARGE_INTEGER uniqueid This is a server-assigned unique id for the file. The client + will typically map this onto an inode number. The scope of + uniqueness is the share. +84 LARGE_INTEGER permissions Standard UNIX file permissions - see below. +92 LARGE_INTEGER nlinks The number of directory entries that map to this entry + (number of hard links) + +100 - end. +*/ + +/* +SMB_QUERY_FILE_UNIX_INFO2 is SMB_QUERY_FILE_UNIX_BASIC with create +time and file flags appended. The corresponding info level for +findfirst/findnext is SMB_FIND_FILE_UNIX_UNIX2. + +Size Offset Value +--------------------- +0 LARGE_INTEGER EndOfFile File size +8 LARGE_INTEGER Blocks Number of blocks used on disk +16 LARGE_INTEGER ChangeTime Attribute change time +24 LARGE_INTEGER LastAccessTime Last access time +32 LARGE_INTEGER LastModificationTime Last modification time +40 LARGE_INTEGER Uid Numeric user id for the owner +48 LARGE_INTEGER Gid Numeric group id of owner +56 ULONG Type Enumeration specifying the file type +60 LARGE_INTEGER devmajor Major device number if type is device +68 LARGE_INTEGER devminor Minor device number if type is device +76 LARGE_INTEGER uniqueid This is a server-assigned unique id +84 LARGE_INTEGER permissions Standard UNIX permissions +92 LARGE_INTEGER nlinks Number of hard link) +100 LARGE_INTEGER CreationTime Create/birth time +108 ULONG FileFlags File flags enumeration +112 ULONG FileFlagsMask Mask of valid flags +*/ + +/* UNIX filetype mappings. */ + +#define UNIX_TYPE_FILE 0 +#define UNIX_TYPE_DIR 1 +#define UNIX_TYPE_SYMLINK 2 +#define UNIX_TYPE_CHARDEV 3 +#define UNIX_TYPE_BLKDEV 4 +#define UNIX_TYPE_FIFO 5 +#define UNIX_TYPE_SOCKET 6 +#define UNIX_TYPE_UNKNOWN 0xFFFFFFFF + +/* + * Oh this is fun. "Standard UNIX permissions" has no + * meaning in POSIX. We need to define the mapping onto + * and off the wire as this was not done in the original HP + * spec. JRA. + */ + +#define UNIX_X_OTH 0000001 +#define UNIX_W_OTH 0000002 +#define UNIX_R_OTH 0000004 +#define UNIX_X_GRP 0000010 +#define UNIX_W_GRP 0000020 +#define UNIX_R_GRP 0000040 +#define UNIX_X_USR 0000100 +#define UNIX_W_USR 0000200 +#define UNIX_R_USR 0000400 +#define UNIX_STICKY 0001000 +#define UNIX_SET_GID 0002000 +#define UNIX_SET_UID 0004000 + +/* Masks for the above */ +#define UNIX_OTH_MASK 0000007 +#define UNIX_GRP_MASK 0000070 +#define UNIX_USR_MASK 0000700 +#define UNIX_PERM_MASK 0000777 +#define UNIX_EXTRA_MASK 0007000 +#define UNIX_ALL_MASK 0007777 + +/* Flags for the file_flags field in UNIX_INFO2: */ +#define EXT_SECURE_DELETE 0x00000001 +#define EXT_ENABLE_UNDELETE 0x00000002 +#define EXT_SYNCHRONOUS 0x00000004 +#define EXT_IMMUTABLE 0x00000008 +#define EXT_OPEN_APPEND_ONLY 0x00000010 +#define EXT_DO_NOT_BACKUP 0x00000020 +#define EXT_NO_UPDATE_ATIME 0x00000040 +#define EXT_HIDDEN 0x00000080 + +#define SMB_QFILEINFO_UNIX_LINK 0x201 +#define SMB_SFILEINFO_UNIX_LINK 0x201 +#define SMB_SFILEINFO_UNIX_HLINK 0x203 + +/* + Info level for QVOLINFO - returns version of CIFS UNIX extensions, plus + 64-bits worth of capability fun :-). +*/ + +#define SMB_QUERY_CIFS_UNIX_INFO 0x200 + +/* Returns the following. + + UINT16 major version number + UINT16 minor version number + LARGE_INTEGER capability bitfield + +*/ + +#define CIFS_UNIX_MAJOR_VERSION 1 +#define CIFS_UNIX_MINOR_VERSION 0 + +#define CIFS_UNIX_FCNTL_LOCKS_CAP 0x1 +#define CIFS_UNIX_POSIX_ACLS_CAP 0x2 + +/* ... more as we think of them :-). */ + +#endif diff --git a/source4/libcli/resolve/bcast.c b/source4/libcli/resolve/bcast.c new file mode 100644 index 0000000000..da58eb8665 --- /dev/null +++ b/source4/libcli/resolve/bcast.c @@ -0,0 +1,106 @@ +/* + Unix SMB/CIFS implementation. + + broadcast name resolution module + + Copyright (C) Andrew Tridgell 2005 + Copyright (C) Jelmer Vernooij 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" +#include "libcli/resolve/resolve.h" +#include "system/network.h" +#include "lib/socket/netif.h" +#include "param/param.h" + +struct resolve_bcast_data { + struct interface *ifaces; + uint16_t nbt_port; + int nbt_timeout; +}; + +/** + broadcast name resolution method - async send + */ +struct composite_context *resolve_name_bcast_send(TALLOC_CTX *mem_ctx, + struct tevent_context *event_ctx, + void *userdata, uint32_t flags, + uint16_t port, + struct nbt_name *name) +{ + int num_interfaces; + const char **address_list; + struct composite_context *c; + int i, count=0; + struct resolve_bcast_data *data = talloc_get_type(userdata, struct resolve_bcast_data); + + num_interfaces = iface_count(data->ifaces); + + address_list = talloc_array(mem_ctx, const char *, num_interfaces+1); + if (address_list == NULL) return NULL; + + for (i=0;i<num_interfaces;i++) { + const char *bcast = iface_n_bcast(data->ifaces, i); + if (bcast == NULL) continue; + address_list[count] = talloc_strdup(address_list, bcast); + if (address_list[count] == NULL) { + talloc_free(address_list); + return NULL; + } + count++; + } + address_list[count] = NULL; + + c = resolve_name_nbtlist_send(mem_ctx, event_ctx, flags, port, name, + address_list, data->ifaces, data->nbt_port, + data->nbt_timeout, true, false); + talloc_free(address_list); + + return c; +} + +/* + broadcast name resolution method - recv side + */ +NTSTATUS resolve_name_bcast_recv(struct composite_context *c, + TALLOC_CTX *mem_ctx, + struct socket_address ***addrs, + char ***names) +{ + NTSTATUS status = resolve_name_nbtlist_recv(c, mem_ctx, addrs, names); + if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + /* this makes much more sense for a bcast name resolution + timeout */ + status = NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + return status; +} + +bool resolve_context_add_bcast_method(struct resolve_context *ctx, struct interface *ifaces, uint16_t nbt_port, int nbt_timeout) +{ + struct resolve_bcast_data *data = talloc(ctx, struct resolve_bcast_data); + data->ifaces = ifaces; + data->nbt_port = nbt_port; + data->nbt_timeout = nbt_timeout; + return resolve_context_add_method(ctx, resolve_name_bcast_send, resolve_name_bcast_recv, data); +} + +bool resolve_context_add_bcast_method_lp(struct resolve_context *ctx, struct loadparm_context *lp_ctx) +{ + struct interface *ifaces; + load_interfaces(ctx, lp_interfaces(lp_ctx), &ifaces); + return resolve_context_add_bcast_method(ctx, ifaces, lp_nbt_port(lp_ctx), lp_parm_int(lp_ctx, NULL, "nbt", "timeout", 1)); +} diff --git a/source4/libcli/resolve/dns_ex.c b/source4/libcli/resolve/dns_ex.c new file mode 100644 index 0000000000..ce41d0cea8 --- /dev/null +++ b/source4/libcli/resolve/dns_ex.c @@ -0,0 +1,541 @@ +/* + Unix SMB/CIFS implementation. + + async getaddrinfo()/dns_lookup() name resolution module + + Copyright (C) Andrew Tridgell 2005 + Copyright (C) Stefan Metzmacher 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/>. +*/ + +/* + this module uses a fork() per getaddrinfo() or dns_looup() call. + At first that might seem crazy, but it is actually very fast, + and solves many of the tricky problems of keeping a child + hanging around in a librar (like what happens when the parent forks). + We use a talloc destructor to ensure that the child is cleaned up + when we have finished with this name resolution. +*/ + +#include "includes.h" +#include "lib/events/events.h" +#include "system/network.h" +#include "system/filesys.h" +#include "lib/socket/socket.h" +#include "libcli/composite/composite.h" +#include "librpc/gen_ndr/ndr_nbt.h" +#include "libcli/resolve/resolve.h" + +#ifdef class +#undef class +#endif + +#include "heimdal/lib/roken/resolve.h" + +struct dns_ex_state { + bool do_fallback; + uint32_t flags; + uint16_t port; + struct nbt_name name; + struct socket_address **addrs; + char **names; + pid_t child; + int child_fd; + struct tevent_fd *fde; + struct tevent_context *event_ctx; +}; + +/* + kill off a wayward child if needed. This allows us to stop an async + name resolution without leaving a potentially blocking call running + in a child +*/ +static int dns_ex_destructor(struct dns_ex_state *state) +{ + int status; + + kill(state->child, SIGTERM); + close(state->child_fd); + if (waitpid(state->child, &status, WNOHANG) == 0) { + kill(state->child, SIGKILL); + waitpid(state->child, &status, 0); + } + + return 0; +} + +/* + the blocking child +*/ +static void run_child_dns_lookup(struct dns_ex_state *state, int fd) +{ + struct dns_reply *reply; + struct resource_record *rr; + uint32_t count = 0; + uint32_t srv_valid = 0; + struct resource_record **srv_rr; + uint32_t addrs_valid = 0; + struct resource_record **addrs_rr; + char *addrs; + bool first; + uint32_t i; + bool do_srv = (state->flags & RESOLVE_NAME_FLAG_DNS_SRV); + + /* this is the blocking call we are going to lots of trouble + to avoid in the parent */ + reply = dns_lookup(state->name.name, do_srv?"SRV":"A"); + if (!reply) { + goto done; + } + + if (do_srv) { + dns_srv_order(reply); + } + + /* Loop over all returned records and pick the "srv" records */ + for (rr=reply->head; rr; rr=rr->next) { + /* we are only interested in the IN class */ + if (rr->class != C_IN) { + continue; + } + + if (do_srv) { + /* we are only interested in SRV records */ + if (rr->type != T_SRV) { + continue; + } + + /* verify we actually have a SRV record here */ + if (!rr->u.srv) { + continue; + } + + /* Verify we got a port */ + if (rr->u.srv->port == 0) { + continue; + } + } else { + /* we are only interested in A records */ + /* TODO: add AAAA support */ + if (rr->type != T_A) { + continue; + } + + /* verify we actually have a A record here */ + if (!rr->u.a) { + continue; + } + } + count++; + } + + if (count == 0) { + goto done; + } + + srv_rr = talloc_zero_array(state, + struct resource_record *, + count); + if (!srv_rr) { + goto done; + } + + addrs_rr = talloc_zero_array(state, + struct resource_record *, + count); + if (!addrs_rr) { + goto done; + } + + /* Loop over all returned records and pick the records */ + for (rr=reply->head;rr;rr=rr->next) { + /* we are only interested in the IN class */ + if (rr->class != C_IN) { + continue; + } + + if (do_srv) { + /* we are only interested in SRV records */ + if (rr->type != T_SRV) { + continue; + } + + /* verify we actually have a srv record here */ + if (!rr->u.srv) { + continue; + } + + /* Verify we got a port */ + if (rr->u.srv->port == 0) { + continue; + } + + srv_rr[srv_valid] = rr; + srv_valid++; + } else { + /* we are only interested in A records */ + /* TODO: add AAAA support */ + if (rr->type != T_A) { + continue; + } + + /* verify we actually have a A record here */ + if (!rr->u.a) { + continue; + } + + addrs_rr[addrs_valid] = rr; + addrs_valid++; + } + } + + for (i=0; i < srv_valid; i++) { + for (rr=reply->head;rr;rr=rr->next) { + + if (rr->class != C_IN) { + continue; + } + + /* we are only interested in SRV records */ + if (rr->type != T_A) { + continue; + } + + /* verify we actually have a srv record here */ + if (strcmp(&srv_rr[i]->u.srv->target[0], rr->domain) != 0) { + continue; + } + + addrs_rr[i] = rr; + addrs_valid++; + break; + } + } + + if (addrs_valid == 0) { + goto done; + } + + addrs = talloc_strdup(state, ""); + if (!addrs) { + goto done; + } + first = true; + for (i=0; i < count; i++) { + uint16_t port; + if (!addrs_rr[i]) { + continue; + } + + if (srv_rr[i] && + (state->flags & RESOLVE_NAME_FLAG_OVERWRITE_PORT)) { + port = srv_rr[i]->u.srv->port; + } else { + port = state->port; + } + + addrs = talloc_asprintf_append_buffer(addrs, "%s%s:%u/%s", + first?"":",", + inet_ntoa(*addrs_rr[i]->u.a), + port, + addrs_rr[i]->domain); + if (!addrs) { + goto done; + } + first = false; + } + + if (addrs) { + write(fd, addrs, talloc_get_size(addrs)); + } + +done: + close(fd); +} + +/* + the blocking child +*/ +static void run_child_getaddrinfo(struct dns_ex_state *state, int fd) +{ + int ret; + struct addrinfo hints; + struct addrinfo *res; + struct addrinfo *res_list = NULL; + char *addrs; + bool first; + + ZERO_STRUCT(hints); + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = AF_INET;/* TODO: add AF_INET6 support */ + hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; + + ret = getaddrinfo(state->name.name, "0", &hints, &res_list); +#ifdef EAI_NODATA + if (ret == EAI_NODATA && state->do_fallback) { +#else + if (ret == EAI_NONAME && state->do_fallback) { +#endif + /* getaddrinfo() doesn't handle CNAME records */ + run_child_dns_lookup(state, fd); + return; + } + if (ret != 0) { + goto done; + } + + addrs = talloc_strdup(state, ""); + if (!addrs) { + goto done; + } + first = true; + for (res = res_list; res; res = res->ai_next) { + struct sockaddr_in *in; + + if (res->ai_family != AF_INET) { + continue; + } + in = (struct sockaddr_in *)res->ai_addr; + + addrs = talloc_asprintf_append_buffer(addrs, "%s%s:%u/%s", + first?"":",", + inet_ntoa(in->sin_addr), + state->port, + state->name.name); + if (!addrs) { + goto done; + } + first = false; + } + + if (addrs) { + write(fd, addrs, talloc_get_size(addrs)); + } +done: + if (res_list) { + freeaddrinfo(res_list); + } + close(fd); +} + +/* + handle a read event on the pipe +*/ +static void pipe_handler(struct tevent_context *ev, struct tevent_fd *fde, + uint16_t flags, void *private_data) +{ + struct composite_context *c = talloc_get_type(private_data, struct composite_context); + struct dns_ex_state *state = talloc_get_type(c->private_data, + struct dns_ex_state); + char *address; + uint32_t num_addrs, i; + char **addrs; + int ret; + int status; + int value = 0; + + /* if we get any event from the child then we know that we + won't need to kill it off */ + talloc_set_destructor(state, NULL); + + if (ioctl(state->child_fd, FIONREAD, &value) != 0) { + value = 8192; + } + + address = talloc_array(state, char, value+1); + if (address) { + /* yes, we don't care about EAGAIN or other niceities + here. They just can't happen with this parent/child + relationship, and even if they did then giving an error is + the right thing to do */ + ret = read(state->child_fd, address, value); + } else { + ret = -1; + } + close(state->child_fd); + if (waitpid(state->child, &status, WNOHANG) == 0) { + kill(state->child, SIGKILL); + waitpid(state->child, &status, 0); + } + + if (ret <= 0) { + composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + + /* enusre the address looks good */ + address[ret] = 0; + + addrs = str_list_make(state, address, ","); + if (composite_nomem(addrs, c)) return; + + num_addrs = str_list_length((const char * const *)addrs); + + state->addrs = talloc_array(state, struct socket_address *, + num_addrs+1); + if (composite_nomem(state->addrs, c)) return; + + state->names = talloc_array(state, char *, num_addrs+1); + if (composite_nomem(state->names, c)) return; + + for (i=0; i < num_addrs; i++) { + uint32_t port = 0; + char *p = strrchr(addrs[i], ':'); + char *n; + + if (!p) { + composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + + *p = '\0'; + p++; + + n = strrchr(p, '/'); + if (!n) { + composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + + *n = '\0'; + n++; + + if (strcmp(addrs[i], "0.0.0.0") == 0 || + inet_addr(addrs[i]) == INADDR_NONE) { + composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + port = strtoul(p, NULL, 10); + if (port > UINT16_MAX) { + composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return; + } + state->addrs[i] = socket_address_from_strings(state->addrs, + "ipv4", + addrs[i], + port); + if (composite_nomem(state->addrs[i], c)) return; + + state->names[i] = talloc_strdup(state->names, n); + if (composite_nomem(state->names[i], c)) return; + } + state->addrs[i] = NULL; + state->names[i] = NULL; + + composite_done(c); +} + +/* + getaddrinfo() or dns_lookup() name resolution method - async send + */ +struct composite_context *resolve_name_dns_ex_send(TALLOC_CTX *mem_ctx, + struct tevent_context *event_ctx, + void *privdata, + uint32_t flags, + uint16_t port, + struct nbt_name *name, + bool do_fallback) +{ + struct composite_context *c; + struct dns_ex_state *state; + int fd[2] = { -1, -1 }; + int ret; + + c = composite_create(mem_ctx, event_ctx); + if (c == NULL) return NULL; + + if (flags & RESOLVE_NAME_FLAG_FORCE_NBT) { + composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return c; + } + + state = talloc_zero(c, struct dns_ex_state); + if (composite_nomem(state, c)) return c; + c->private_data = state; + + c->status = nbt_name_dup(state, name, &state->name); + if (!composite_is_ok(c)) return c; + + /* setup a pipe to chat to our child */ + ret = pipe(fd); + if (ret == -1) { + composite_error(c, map_nt_error_from_unix(errno)); + return c; + } + + state->do_fallback = do_fallback; + state->flags = flags; + state->port = port; + + state->child_fd = fd[0]; + state->event_ctx = c->event_ctx; + + /* we need to put the child in our event context so + we know when the dns_lookup() has finished */ + state->fde = event_add_fd(c->event_ctx, c, state->child_fd, EVENT_FD_READ, + pipe_handler, c); + if (composite_nomem(state->fde, c)) { + close(fd[0]); + close(fd[1]); + return c; + } + + state->child = fork(); + if (state->child == (pid_t)-1) { + composite_error(c, map_nt_error_from_unix(errno)); + return c; + } + + if (state->child == 0) { + close(fd[0]); + if (state->flags & RESOLVE_NAME_FLAG_FORCE_DNS) { + run_child_dns_lookup(state, fd[1]); + } else { + run_child_getaddrinfo(state, fd[1]); + } + _exit(0); + } + close(fd[1]); + + /* cleanup wayward children */ + talloc_set_destructor(state, dns_ex_destructor); + + return c; +} + +/* + getaddrinfo() or dns_lookup() name resolution method - recv side +*/ +NTSTATUS resolve_name_dns_ex_recv(struct composite_context *c, + TALLOC_CTX *mem_ctx, + struct socket_address ***addrs, + char ***names) +{ + NTSTATUS status; + + status = composite_wait(c); + + if (NT_STATUS_IS_OK(status)) { + struct dns_ex_state *state = talloc_get_type(c->private_data, + struct dns_ex_state); + *addrs = talloc_steal(mem_ctx, state->addrs); + if (names) { + *names = talloc_steal(mem_ctx, state->names); + } + } + + talloc_free(c); + return status; +} diff --git a/source4/libcli/resolve/host.c b/source4/libcli/resolve/host.c new file mode 100644 index 0000000000..755a4e8304 --- /dev/null +++ b/source4/libcli/resolve/host.c @@ -0,0 +1,60 @@ +/* + Unix SMB/CIFS implementation. + + async "host" name resolution module + + Copyright (C) Andrew Tridgell 2005 + Copyright (C) Stefan Metzmacher 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 "lib/events/events.h" +#include "system/network.h" +#include "system/filesys.h" +#include "lib/socket/socket.h" +#include "libcli/composite/composite.h" +#include "librpc/gen_ndr/ndr_nbt.h" +#include "libcli/resolve/resolve.h" + +/* + getaddrinfo() (with fallback to dns_lookup()) name resolution method - async send + */ +struct composite_context *resolve_name_host_send(TALLOC_CTX *mem_ctx, + struct tevent_context *event_ctx, + void *privdata, uint32_t flags, + uint16_t port, + struct nbt_name *name) +{ + return resolve_name_dns_ex_send(mem_ctx, event_ctx, NULL, flags, + port, name, true); +} + +/* + getaddrinfo() (with fallback to dns_lookup()) name resolution method - recv side +*/ +NTSTATUS resolve_name_host_recv(struct composite_context *c, + TALLOC_CTX *mem_ctx, + struct socket_address ***addrs, + char ***names) +{ + return resolve_name_dns_ex_recv(c, mem_ctx, addrs, names); +} + +bool resolve_context_add_host_method(struct resolve_context *ctx) +{ + return resolve_context_add_method(ctx, resolve_name_host_send, resolve_name_host_recv, + NULL); +} diff --git a/source4/libcli/resolve/nbtlist.c b/source4/libcli/resolve/nbtlist.c new file mode 100644 index 0000000000..73085b87cb --- /dev/null +++ b/source4/libcli/resolve/nbtlist.c @@ -0,0 +1,224 @@ +/* + Unix SMB/CIFS implementation. + + nbt list of addresses name resolution module + + Copyright (C) Andrew Tridgell 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/>. +*/ + +/* + TODO: we should lower the timeout, and add retries for each name +*/ + +#include "includes.h" +#include "libcli/composite/composite.h" +#include "system/network.h" +#include "lib/socket/socket.h" +#include "lib/socket/netif.h" +#include "librpc/gen_ndr/ndr_nbt.h" +#include "../libcli/nbt/libnbt.h" +#include "param/param.h" +#include "libcli/resolve/resolve.h" + +struct nbtlist_state { + uint16_t flags; + uint16_t port; + struct nbt_name name; + struct nbt_name_socket *nbtsock; + int num_queries; + struct nbt_name_request **queries; + struct nbt_name_query *io_queries; + struct socket_address **addrs; + char **names; + struct interface *ifaces; +}; + +/* + handle events during nbtlist name resolution +*/ +static void nbtlist_handler(struct nbt_name_request *req) +{ + struct composite_context *c = talloc_get_type(req->async.private_data, + struct composite_context); + struct nbtlist_state *state = talloc_get_type(c->private_data, struct nbtlist_state); + struct nbt_name_query *q; + int i; + + for (i=0;i<state->num_queries;i++) { + if (req == state->queries[i]) break; + } + + if (i == state->num_queries) { + /* not for us?! */ + composite_error(c, NT_STATUS_INTERNAL_ERROR); + return; + } + + q = &state->io_queries[i]; + + c->status = nbt_name_query_recv(req, state, q); + + /* free the network resource directly */ + talloc_free(state->nbtsock); + if (!composite_is_ok(c)) return; + + if (q->out.num_addrs < 1) { + composite_error(c, NT_STATUS_UNEXPECTED_NETWORK_ERROR); + return; + } + + state->addrs = talloc_array(state, struct socket_address *, + q->out.num_addrs + 1); + if (composite_nomem(state->addrs, c)) return; + + state->names = talloc_array(state, char *, q->out.num_addrs + 1); + if (composite_nomem(state->names, c)) return; + + for (i=0;i<q->out.num_addrs;i++) { + state->addrs[i] = socket_address_from_strings(state->addrs, + "ipv4", + q->out.reply_addrs[i], + state->port); + if (composite_nomem(state->addrs[i], c)) return; + + state->names[i] = talloc_strdup(state->names, state->name.name); + if (composite_nomem(state->names[i], c)) return; + } + state->addrs[i] = NULL; + state->names[i] = NULL; + + composite_done(c); +} + +/* + nbtlist name resolution method - async send + */ +struct composite_context *resolve_name_nbtlist_send(TALLOC_CTX *mem_ctx, + struct tevent_context *event_ctx, + uint32_t flags, + uint16_t port, + struct nbt_name *name, + const char **address_list, + struct interface *ifaces, + uint16_t nbt_port, + int nbt_timeout, + bool broadcast, + bool wins_lookup) +{ + struct composite_context *c; + struct nbtlist_state *state; + int i; + + c = composite_create(mem_ctx, event_ctx); + if (c == NULL) return NULL; + + if (flags & RESOLVE_NAME_FLAG_FORCE_DNS) { + composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return c; + } + + if (strlen(name->name) > 15) { + composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return c; + } + + state = talloc(c, struct nbtlist_state); + if (composite_nomem(state, c)) return c; + c->private_data = state; + + state->flags = flags; + state->port = port; + + c->status = nbt_name_dup(state, name, &state->name); + if (!composite_is_ok(c)) return c; + + state->name.name = strupper_talloc(state, state->name.name); + if (composite_nomem(state->name.name, c)) return c; + if (state->name.scope) { + state->name.scope = strupper_talloc(state, state->name.scope); + if (composite_nomem(state->name.scope, c)) return c; + } + + state->ifaces = talloc_reference(state, ifaces); + + /* + * we can't push long names on the wire, + * so bail out here to give a useful error message + */ + if (strlen(state->name.name) > 15) { + composite_error(c, NT_STATUS_OBJECT_NAME_NOT_FOUND); + return c; + } + + state->nbtsock = nbt_name_socket_init(state, event_ctx, + global_iconv_convenience); + if (composite_nomem(state->nbtsock, c)) return c; + + /* count the address_list size */ + for (i=0;address_list[i];i++) /* noop */ ; + + state->num_queries = i; + state->io_queries = talloc_array(state, struct nbt_name_query, state->num_queries); + if (composite_nomem(state->io_queries, c)) return c; + + state->queries = talloc_array(state, struct nbt_name_request *, state->num_queries); + if (composite_nomem(state->queries, c)) return c; + + for (i=0;i<state->num_queries;i++) { + state->io_queries[i].in.name = state->name; + state->io_queries[i].in.dest_addr = talloc_strdup(state->io_queries, address_list[i]); + state->io_queries[i].in.dest_port = nbt_port; + if (composite_nomem(state->io_queries[i].in.dest_addr, c)) return c; + + state->io_queries[i].in.broadcast = broadcast; + state->io_queries[i].in.wins_lookup = wins_lookup; + state->io_queries[i].in.timeout = nbt_timeout; + state->io_queries[i].in.retries = 2; + + state->queries[i] = nbt_name_query_send(state->nbtsock, &state->io_queries[i]); + if (composite_nomem(state->queries[i], c)) return c; + + state->queries[i]->async.fn = nbtlist_handler; + state->queries[i]->async.private_data = c; + } + + return c; +} + +/* + nbt list of addresses name resolution method - recv side + */ +NTSTATUS resolve_name_nbtlist_recv(struct composite_context *c, + TALLOC_CTX *mem_ctx, + struct socket_address ***addrs, + char ***names) +{ + NTSTATUS status; + + status = composite_wait(c); + + if (NT_STATUS_IS_OK(status)) { + struct nbtlist_state *state = talloc_get_type(c->private_data, struct nbtlist_state); + *addrs = talloc_steal(mem_ctx, state->addrs); + if (names) { + *names = talloc_steal(mem_ctx, state->names); + } + } + + talloc_free(c); + return status; +} + diff --git a/source4/libcli/resolve/resolve.c b/source4/libcli/resolve/resolve.c new file mode 100644 index 0000000000..6a3d5daecc --- /dev/null +++ b/source4/libcli/resolve/resolve.c @@ -0,0 +1,283 @@ +/* + Unix SMB/CIFS implementation. + + general name resolution interface + + Copyright (C) Andrew Tridgell 2005 + Copyright (C) Jelmer Vernooij 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" +#include "libcli/composite/composite.h" +#include "libcli/resolve/resolve.h" +#include "librpc/gen_ndr/ndr_nbt.h" +#include "system/network.h" +#include "lib/socket/socket.h" +#include "../lib/util/dlinklist.h" + +struct resolve_state { + struct resolve_context *ctx; + struct resolve_method *method; + uint32_t flags; + uint16_t port; + struct nbt_name name; + struct composite_context *creq; + struct socket_address **addrs; + char **names; +}; + +static struct composite_context *setup_next_method(struct composite_context *c); + + +struct resolve_context { + struct resolve_method { + resolve_name_send_fn send_fn; + resolve_name_recv_fn recv_fn; + void *privdata; + struct resolve_method *prev, *next; + } *methods; +}; + +/** + * Initialize a resolve context + */ +struct resolve_context *resolve_context_init(TALLOC_CTX *mem_ctx) +{ + return talloc_zero(mem_ctx, struct resolve_context); +} + +/** + * Add a resolve method + */ +bool resolve_context_add_method(struct resolve_context *ctx, resolve_name_send_fn send_fn, + resolve_name_recv_fn recv_fn, void *userdata) +{ + struct resolve_method *method = talloc_zero(ctx, struct resolve_method); + + if (method == NULL) + return false; + + method->send_fn = send_fn; + method->recv_fn = recv_fn; + method->privdata = userdata; + DLIST_ADD_END(ctx->methods, method, struct resolve_method *); + return true; +} + +/** + handle completion of one name resolve method +*/ +static void resolve_handler(struct composite_context *creq) +{ + struct composite_context *c = (struct composite_context *)creq->async.private_data; + struct resolve_state *state = talloc_get_type(c->private_data, struct resolve_state); + const struct resolve_method *method = state->method; + + c->status = method->recv_fn(creq, state, &state->addrs, &state->names); + + if (!NT_STATUS_IS_OK(c->status)) { + state->method = state->method->next; + state->creq = setup_next_method(c); + if (state->creq != NULL) { + return; + } + } + + if (!NT_STATUS_IS_OK(c->status)) { + c->state = COMPOSITE_STATE_ERROR; + } else { + c->state = COMPOSITE_STATE_DONE; + } + if (c->async.fn) { + c->async.fn(c); + } +} + + +static struct composite_context *setup_next_method(struct composite_context *c) +{ + struct resolve_state *state = talloc_get_type(c->private_data, struct resolve_state); + struct composite_context *creq = NULL; + + do { + if (state->method) { + creq = state->method->send_fn(c, c->event_ctx, + state->method->privdata, + state->flags, + state->port, + &state->name); + } + if (creq == NULL && state->method) state->method = state->method->next; + + } while (!creq && state->method); + + if (creq) { + creq->async.fn = resolve_handler; + creq->async.private_data = c; + } + + return creq; +} + +/* + general name resolution - async send + */ +struct composite_context *resolve_name_all_send(struct resolve_context *ctx, + uint32_t flags, + uint16_t port, + struct nbt_name *name, + struct tevent_context *event_ctx) +{ + struct composite_context *c; + struct resolve_state *state; + + if (ctx == NULL || event_ctx == NULL) { + return NULL; + } + + c = composite_create(ctx, event_ctx); + if (c == NULL) return NULL; + + if (composite_nomem(c->event_ctx, c)) return c; + + state = talloc(c, struct resolve_state); + if (composite_nomem(state, c)) return c; + c->private_data = state; + + state->flags = flags; + state->port = port; + + c->status = nbt_name_dup(state, name, &state->name); + if (!composite_is_ok(c)) return c; + + state->ctx = talloc_reference(state, ctx); + if (composite_nomem(state->ctx, c)) return c; + + if (is_ipaddress(state->name.name) || + strcasecmp(state->name.name, "localhost") == 0) { + struct in_addr ip = interpret_addr2(state->name.name); + + state->addrs = talloc_array(state, struct socket_address *, 2); + if (composite_nomem(state->addrs, c)) return c; + state->addrs[0] = socket_address_from_strings(state->addrs, "ipv4", + inet_ntoa(ip), 0); + if (composite_nomem(state->addrs[0], c)) return c; + state->addrs[1] = NULL; + state->names = talloc_array(state, char *, 2); + if (composite_nomem(state->names, c)) return c; + state->names[0] = talloc_strdup(state->names, state->name.name); + if (composite_nomem(state->names[0], c)) return c; + state->names[1] = NULL; + composite_done(c); + return c; + } + + state->method = ctx->methods; + if (state->method == NULL) { + composite_error(c, NT_STATUS_BAD_NETWORK_NAME); + return c; + } + state->creq = setup_next_method(c); + if (composite_nomem(state->creq, c)) return c; + + return c; +} + +/* + general name resolution method - recv side + */ +NTSTATUS resolve_name_all_recv(struct composite_context *c, + TALLOC_CTX *mem_ctx, + struct socket_address ***addrs, + char ***names) +{ + NTSTATUS status; + + status = composite_wait(c); + + if (NT_STATUS_IS_OK(status)) { + struct resolve_state *state = talloc_get_type(c->private_data, struct resolve_state); + *addrs = talloc_steal(mem_ctx, state->addrs); + if (names) { + *names = talloc_steal(mem_ctx, state->names); + } + } + + talloc_free(c); + return status; +} + +struct composite_context *resolve_name_send(struct resolve_context *ctx, + struct nbt_name *name, + struct tevent_context *event_ctx) +{ + return resolve_name_all_send(ctx, 0, 0, name, event_ctx); +} + +NTSTATUS resolve_name_recv(struct composite_context *c, + TALLOC_CTX *mem_ctx, + const char **reply_addr) +{ + NTSTATUS status; + struct socket_address **addrs = NULL; + + status = resolve_name_all_recv(c, mem_ctx, &addrs, NULL); + + if (NT_STATUS_IS_OK(status)) { + *reply_addr = talloc_steal(mem_ctx, addrs[0]->addr); + talloc_free(addrs); + } + + return status; +} + +/* + general name resolution - sync call + */ +NTSTATUS resolve_name(struct resolve_context *ctx, + struct nbt_name *name, + TALLOC_CTX *mem_ctx, + const char **reply_addr, + struct tevent_context *ev) +{ + struct composite_context *c = resolve_name_send(ctx, name, ev); + return resolve_name_recv(c, mem_ctx, reply_addr); +} + +/* Initialise a struct nbt_name with a NULL scope */ + +void make_nbt_name(struct nbt_name *nbt, const char *name, int type) +{ + nbt->name = name; + nbt->scope = NULL; + nbt->type = type; +} + +/* Initialise a struct nbt_name with a NBT_NAME_CLIENT (0x00) name */ + +void make_nbt_name_client(struct nbt_name *nbt, const char *name) +{ + make_nbt_name(nbt, name, NBT_NAME_CLIENT); +} + +/* Initialise a struct nbt_name with a NBT_NAME_SERVER (0x20) name */ + +void make_nbt_name_server(struct nbt_name *nbt, const char *name) +{ + make_nbt_name(nbt, name, NBT_NAME_SERVER); +} + + diff --git a/source4/libcli/resolve/resolve.h b/source4/libcli/resolve/resolve.h new file mode 100644 index 0000000000..8ace5740a9 --- /dev/null +++ b/source4/libcli/resolve/resolve.h @@ -0,0 +1,53 @@ +/* + Unix SMB/CIFS implementation. + + general name resolution interface + + Copyright (C) Andrew Tridgell 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/>. +*/ + +#ifndef __LIBCLI_RESOLVE_H__ +#define __LIBCLI_RESOLVE_H__ + +struct socket_address; +struct tevent_context; + +#include "../libcli/nbt/libnbt.h" + +/* force that only NBT name resolution is used */ +#define RESOLVE_NAME_FLAG_FORCE_NBT 0x00000001 +/* force that only DNS name resolution is used */ +#define RESOLVE_NAME_FLAG_FORCE_DNS 0x00000002 +/* tell the dns resolver to do a DNS SRV lookup */ +#define RESOLVE_NAME_FLAG_DNS_SRV 0x00000004 +/* allow the resolver to overwrite the given port, e.g. for DNS SRV */ +#define RESOLVE_NAME_FLAG_OVERWRITE_PORT 0x00000008 + +typedef struct composite_context *(*resolve_name_send_fn)(TALLOC_CTX *mem_ctx, + struct tevent_context *, + void *privdata, + uint32_t flags, + uint16_t port, + struct nbt_name *); +typedef NTSTATUS (*resolve_name_recv_fn)(struct composite_context *creq, + TALLOC_CTX *mem_ctx, + struct socket_address ***addrs, + char ***names); +#include "libcli/resolve/proto.h" +struct interface; +#include "libcli/resolve/lp_proto.h" + +#endif /* __LIBCLI_RESOLVE_H__ */ diff --git a/source4/libcli/resolve/resolve_lp.c b/source4/libcli/resolve/resolve_lp.c new file mode 100644 index 0000000000..b41e2b98d8 --- /dev/null +++ b/source4/libcli/resolve/resolve_lp.c @@ -0,0 +1,46 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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" +#include "libcli/resolve/resolve.h" +#include "param/param.h" + +struct resolve_context *lp_resolve_context(struct loadparm_context *lp_ctx) +{ + const char **methods = lp_name_resolve_order(lp_ctx); + int i; + struct resolve_context *ret = resolve_context_init(lp_ctx); + + if (ret == NULL) + return NULL; + + for (i = 0; methods != NULL && methods[i] != NULL; i++) { + if (!strcmp(methods[i], "wins")) { + resolve_context_add_wins_method_lp(ret, lp_ctx); + } else if (!strcmp(methods[i], "bcast")) { + resolve_context_add_bcast_method_lp(ret, lp_ctx); + } else if (!strcmp(methods[i], "host")) { + resolve_context_add_host_method(ret); + } else { + DEBUG(0, ("Unknown resolve method '%s'\n", methods[i])); + } + } + + return ret; +} diff --git a/source4/libcli/resolve/testsuite.c b/source4/libcli/resolve/testsuite.c new file mode 100644 index 0000000000..fdbb430a9f --- /dev/null +++ b/source4/libcli/resolve/testsuite.c @@ -0,0 +1,90 @@ +/* + Unix SMB/CIFS implementation. + + local test for async resolve code + + Copyright (C) Andrew Tridgell 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" +#include "lib/events/events.h" +#include "libcli/resolve/resolve.h" +#include "torture/torture.h" +#include "system/network.h" + +static bool test_async_resolve(struct torture_context *tctx) +{ + struct nbt_name n; + struct tevent_context *ev; + int timelimit = torture_setting_int(tctx, "timelimit", 2); + const char *host = torture_setting_string(tctx, "host", NULL); + int count = 0; + struct timeval tv = timeval_current(); + TALLOC_CTX *mem_ctx = tctx; + + ev = tctx->ev; + + ZERO_STRUCT(n); + n.name = host; + + torture_comment(tctx, "Testing async resolve of '%s' for %d seconds\n", + host, timelimit); + while (timeval_elapsed(&tv) < timelimit) { + struct socket_address **s; + struct composite_context *c = resolve_name_host_send(mem_ctx, ev, NULL, 0, 0, &n); + torture_assert(tctx, c != NULL, "resolve_name_host_send"); + torture_assert_ntstatus_ok(tctx, resolve_name_host_recv(c, mem_ctx, &s, NULL), + "async resolve failed"); + count++; + } + + torture_comment(tctx, "async rate of %.1f resolves/sec\n", + count/timeval_elapsed(&tv)); + return true; +} + +/* + test resolution using sync method +*/ +static bool test_sync_resolve(struct torture_context *tctx) +{ + int timelimit = torture_setting_int(tctx, "timelimit", 2); + struct timeval tv = timeval_current(); + int count = 0; + const char *host = torture_setting_string(tctx, "host", NULL); + + torture_comment(tctx, "Testing sync resolve of '%s' for %d seconds\n", + host, timelimit); + while (timeval_elapsed(&tv) < timelimit) { + inet_ntoa(interpret_addr2(host)); + count++; + } + + torture_comment(tctx, "sync rate of %.1f resolves/sec\n", + count/timeval_elapsed(&tv)); + return true; +} + + +struct torture_suite *torture_local_resolve(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "RESOLVE"); + + torture_suite_add_simple_test(suite, "async", test_async_resolve); + torture_suite_add_simple_test(suite, "sync", test_sync_resolve); + + return suite; +} diff --git a/source4/libcli/resolve/wins.c b/source4/libcli/resolve/wins.c new file mode 100644 index 0000000000..03eaf8786f --- /dev/null +++ b/source4/libcli/resolve/wins.c @@ -0,0 +1,82 @@ +/* + Unix SMB/CIFS implementation. + + wins name resolution module + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "../libcli/nbt/libnbt.h" +#include "libcli/resolve/resolve.h" +#include "param/param.h" +#include "lib/socket/socket.h" +#include "lib/socket/netif.h" + +struct resolve_wins_data { + const char **address_list; + struct interface *ifaces; + uint16_t nbt_port; + int nbt_timeout; +}; + +/** + wins name resolution method - async send + */ +struct composite_context *resolve_name_wins_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *event_ctx, + void *userdata, + uint32_t flags, + uint16_t port, + struct nbt_name *name) +{ + struct resolve_wins_data *wins_data = talloc_get_type(userdata, struct resolve_wins_data); + if (wins_data->address_list == NULL) return NULL; + return resolve_name_nbtlist_send(mem_ctx, event_ctx, flags, port, name, + wins_data->address_list, wins_data->ifaces, + wins_data->nbt_port, wins_data->nbt_timeout, + false, true); +} + +/* + wins name resolution method - recv side + */ +NTSTATUS resolve_name_wins_recv(struct composite_context *c, + TALLOC_CTX *mem_ctx, + struct socket_address ***addrs, + char ***names) +{ + return resolve_name_nbtlist_recv(c, mem_ctx, addrs, names); +} + +bool resolve_context_add_wins_method(struct resolve_context *ctx, const char **address_list, struct interface *ifaces, uint16_t nbt_port, int nbt_timeout) +{ + struct resolve_wins_data *wins_data = talloc(ctx, struct resolve_wins_data); + wins_data->address_list = (const char **)str_list_copy(wins_data, address_list); + wins_data->ifaces = talloc_reference(wins_data, ifaces); + wins_data->nbt_port = nbt_port; + wins_data->nbt_timeout = nbt_timeout; + return resolve_context_add_method(ctx, resolve_name_wins_send, resolve_name_wins_recv, + wins_data); +} + +bool resolve_context_add_wins_method_lp(struct resolve_context *ctx, struct loadparm_context *lp_ctx) +{ + struct interface *ifaces; + load_interfaces(ctx, lp_interfaces(lp_ctx), &ifaces); + return resolve_context_add_wins_method(ctx, lp_wins_server_list(lp_ctx), ifaces, lp_nbt_port(lp_ctx), lp_parm_int(lp_ctx, NULL, "nbt", "timeout", 1)); +} diff --git a/source4/libcli/security/access_check.c b/source4/libcli/security/access_check.c new file mode 100644 index 0000000000..af6a3d6fb3 --- /dev/null +++ b/source4/libcli/security/access_check.c @@ -0,0 +1,155 @@ +/* + Unix SMB/CIFS implementation. + + security access checking routines + + Copyright (C) Andrew Tridgell 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" +#include "libcli/security/security.h" + + +/* + perform a SEC_FLAG_MAXIMUM_ALLOWED access check +*/ +static uint32_t access_check_max_allowed(const struct security_descriptor *sd, + const struct security_token *token) +{ + uint32_t denied = 0, granted = 0; + unsigned i; + + if (security_token_has_sid(token, sd->owner_sid)) { + granted |= SEC_STD_WRITE_DAC | SEC_STD_READ_CONTROL | SEC_STD_DELETE; + } else if (security_token_has_privilege(token, SEC_PRIV_RESTORE)) { + granted |= SEC_STD_DELETE; + } + + if (sd->dacl == NULL) { + return granted & ~denied; + } + + for (i = 0;i<sd->dacl->num_aces; i++) { + struct security_ace *ace = &sd->dacl->aces[i]; + + if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) { + continue; + } + + if (!security_token_has_sid(token, &ace->trustee)) { + continue; + } + + switch (ace->type) { + case SEC_ACE_TYPE_ACCESS_ALLOWED: + granted |= ace->access_mask; + break; + case SEC_ACE_TYPE_ACCESS_DENIED: + case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT: + denied |= ace->access_mask; + break; + default: /* Other ACE types not handled/supported */ + break; + } + } + + return granted & ~denied; +} + +/* + the main entry point for access checking. +*/ +NTSTATUS sec_access_check(const struct security_descriptor *sd, + const struct security_token *token, + uint32_t access_desired, + uint32_t *access_granted) +{ + int i; + uint32_t bits_remaining; + + *access_granted = access_desired; + bits_remaining = access_desired; + + /* handle the maximum allowed flag */ + if (access_desired & SEC_FLAG_MAXIMUM_ALLOWED) { + access_desired |= access_check_max_allowed(sd, token); + access_desired &= ~SEC_FLAG_MAXIMUM_ALLOWED; + *access_granted = access_desired; + bits_remaining = access_desired & ~SEC_STD_DELETE; + } + + if (access_desired & SEC_FLAG_SYSTEM_SECURITY) { + if (security_token_has_privilege(token, SEC_PRIV_SECURITY)) { + bits_remaining &= ~SEC_FLAG_SYSTEM_SECURITY; + } else { + return NT_STATUS_PRIVILEGE_NOT_HELD; + } + } + + /* a NULL dacl allows access */ + if ((sd->type & SEC_DESC_DACL_PRESENT) && sd->dacl == NULL) { + *access_granted = access_desired; + return NT_STATUS_OK; + } + + /* the owner always gets SEC_STD_WRITE_DAC, SEC_STD_READ_CONTROL and SEC_STD_DELETE */ + if ((bits_remaining & (SEC_STD_WRITE_DAC|SEC_STD_READ_CONTROL|SEC_STD_DELETE)) && + security_token_has_sid(token, sd->owner_sid)) { + bits_remaining &= ~(SEC_STD_WRITE_DAC|SEC_STD_READ_CONTROL|SEC_STD_DELETE); + } + if ((bits_remaining & SEC_STD_DELETE) && + security_token_has_privilege(token, SEC_PRIV_RESTORE)) { + bits_remaining &= ~SEC_STD_DELETE; + } + + if (sd->dacl == NULL) { + goto done; + } + + /* check each ace in turn. */ + for (i=0; bits_remaining && i < sd->dacl->num_aces; i++) { + struct security_ace *ace = &sd->dacl->aces[i]; + + if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) { + continue; + } + + if (!security_token_has_sid(token, &ace->trustee)) { + continue; + } + + switch (ace->type) { + case SEC_ACE_TYPE_ACCESS_ALLOWED: + bits_remaining &= ~ace->access_mask; + break; + case SEC_ACE_TYPE_ACCESS_DENIED: + case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT: + if (bits_remaining & ace->access_mask) { + return NT_STATUS_ACCESS_DENIED; + } + break; + default: /* Other ACE types not handled/supported */ + break; + } + } + +done: + if (bits_remaining != 0) { + return NT_STATUS_ACCESS_DENIED; + } + + return NT_STATUS_OK; +} diff --git a/source4/libcli/security/config.mk b/source4/libcli/security/config.mk new file mode 100644 index 0000000000..d6d9ad5545 --- /dev/null +++ b/source4/libcli/security/config.mk @@ -0,0 +1,10 @@ +[SUBSYSTEM::LIBSECURITY] +PUBLIC_DEPENDENCIES = LIBNDR LIBSECURITY_COMMON + +LIBSECURITY_OBJ_FILES = $(addprefix $(libclisrcdir)/security/, \ + security_token.o security_descriptor.o \ + access_check.o privilege.o sddl.o) \ + ../libcli/security/secace.o \ + ../libcli/security/secacl.o + +$(eval $(call proto_header_template,$(libclisrcdir)/security/proto.h,$(LIBSECURITY_OBJ_FILES:.o=.c))) diff --git a/source4/libcli/security/privilege.c b/source4/libcli/security/privilege.c new file mode 100644 index 0000000000..2cbef13538 --- /dev/null +++ b/source4/libcli/security/privilege.c @@ -0,0 +1,241 @@ +/* + Unix SMB/CIFS implementation. + + manipulate privileges + + Copyright (C) Andrew Tridgell 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" +#include "librpc/gen_ndr/security.h" +#include "libcli/security/security.h" + + +static const struct { + enum sec_privilege privilege; + const char *name; + const char *display_name; +} privilege_names[] = { + {SEC_PRIV_SECURITY, + "SeSecurityPrivilege", + "System security"}, + + {SEC_PRIV_BACKUP, + "SeBackupPrivilege", + "Backup files and directories"}, + + {SEC_PRIV_RESTORE, + "SeRestorePrivilege", + "Restore files and directories"}, + + {SEC_PRIV_SYSTEMTIME, + "SeSystemtimePrivilege", + "Set the system clock"}, + + {SEC_PRIV_SHUTDOWN, + "SeShutdownPrivilege", + "Shutdown the system"}, + + {SEC_PRIV_REMOTE_SHUTDOWN, + "SeRemoteShutdownPrivilege", + "Shutdown the system remotely"}, + + {SEC_PRIV_TAKE_OWNERSHIP, + "SeTakeOwnershipPrivilege", + "Take ownership of files and directories"}, + + {SEC_PRIV_DEBUG, + "SeDebugPrivilege", + "Debug processes"}, + + {SEC_PRIV_SYSTEM_ENVIRONMENT, + "SeSystemEnvironmentPrivilege", + "Modify system environment"}, + + {SEC_PRIV_SYSTEM_PROFILE, + "SeSystemProfilePrivilege", + "Profile the system"}, + + {SEC_PRIV_PROFILE_SINGLE_PROCESS, + "SeProfileSingleProcessPrivilege", + "Profile one process"}, + + {SEC_PRIV_INCREASE_BASE_PRIORITY, + "SeIncreaseBasePriorityPrivilege", + "Increase base priority"}, + + {SEC_PRIV_LOAD_DRIVER, + "SeLoadDriverPrivilege", + "Load drivers"}, + + {SEC_PRIV_CREATE_PAGEFILE, + "SeCreatePagefilePrivilege", + "Create page files"}, + + {SEC_PRIV_INCREASE_QUOTA, + "SeIncreaseQuotaPrivilege", + "Increase quota"}, + + {SEC_PRIV_CHANGE_NOTIFY, + "SeChangeNotifyPrivilege", + "Register for change notify"}, + + {SEC_PRIV_UNDOCK, + "SeUndockPrivilege", + "Undock devices"}, + + {SEC_PRIV_MANAGE_VOLUME, + "SeManageVolumePrivilege", + "Manage system volumes"}, + + {SEC_PRIV_IMPERSONATE, + "SeImpersonatePrivilege", + "Impersonate users"}, + + {SEC_PRIV_CREATE_GLOBAL, + "SeCreateGlobalPrivilege", + "Create global"}, + + {SEC_PRIV_ENABLE_DELEGATION, + "SeEnableDelegationPrivilege", + "Enable Delegation"}, + + {SEC_PRIV_INTERACTIVE_LOGON, + "SeInteractiveLogonRight", + "Interactive logon"}, + + {SEC_PRIV_NETWORK_LOGON, + "SeNetworkLogonRight", + "Network logon"}, + + {SEC_PRIV_REMOTE_INTERACTIVE_LOGON, + "SeRemoteInteractiveLogonRight", + "Remote Interactive logon"} +}; + + +/* + map a privilege id to the wire string constant +*/ +const char *sec_privilege_name(enum sec_privilege privilege) +{ + int i; + for (i=0;i<ARRAY_SIZE(privilege_names);i++) { + if (privilege_names[i].privilege == privilege) { + return privilege_names[i].name; + } + } + return NULL; +} + +/* + map a privilege id to a privilege display name. Return NULL if not found + + TODO: this should use language mappings +*/ +const char *sec_privilege_display_name(enum sec_privilege privilege, uint16_t *language) +{ + int i; + if (privilege < 1 || privilege > 64) { + return NULL; + } + for (i=0;i<ARRAY_SIZE(privilege_names);i++) { + if (privilege_names[i].privilege == privilege) { + return privilege_names[i].display_name; + } + } + return NULL; +} + +/* + map a privilege name to a privilege id. Return -1 if not found +*/ +enum sec_privilege sec_privilege_id(const char *name) +{ + int i; + for (i=0;i<ARRAY_SIZE(privilege_names);i++) { + if (strcasecmp(privilege_names[i].name, name) == 0) { + return privilege_names[i].privilege; + } + } + return -1; +} + + +/* + return a privilege mask given a privilege id +*/ +static uint64_t sec_privilege_mask(enum sec_privilege privilege) +{ + uint64_t mask = 1; + + if (privilege < 1 || privilege > 64) { + return 0; + } + + mask <<= (privilege-1); + return mask; +} + + +/* + return true if a security_token has a particular privilege bit set +*/ +bool security_token_has_privilege(const struct security_token *token, enum sec_privilege privilege) +{ + uint64_t mask; + + if (privilege < 1 || privilege > 64) { + return false; + } + + mask = sec_privilege_mask(privilege); + if (token->privilege_mask & mask) { + return true; + } + return false; +} + +/* + set a bit in the privilege mask +*/ +void security_token_set_privilege(struct security_token *token, enum sec_privilege privilege) +{ + if (privilege < 1 || privilege > 64) { + return; + } + token->privilege_mask |= sec_privilege_mask(privilege); +} + +void security_token_debug_privileges(int dbg_lev, const struct security_token *token) +{ + DEBUGADD(dbg_lev, (" Privileges (0x%16llX):\n", + (unsigned long long) token->privilege_mask)); + + if (token->privilege_mask) { + int i = 0; + uint_t privilege; + + for (privilege = 1; privilege <= 64; privilege++) { + uint64_t mask = sec_privilege_mask(privilege); + + if (token->privilege_mask & mask) { + DEBUGADD(dbg_lev, (" Privilege[%3lu]: %s\n", (unsigned long)i++, + sec_privilege_name(privilege))); + } + } + } +} diff --git a/source4/libcli/security/sddl.c b/source4/libcli/security/sddl.c new file mode 100644 index 0000000000..a8d893f085 --- /dev/null +++ b/source4/libcli/security/sddl.c @@ -0,0 +1,598 @@ +/* + Unix SMB/CIFS implementation. + + security descriptor description language functions + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/security/security.h" +#include "librpc/gen_ndr/ndr_misc.h" +#include "system/locale.h" + +struct flag_map { + const char *name; + uint32_t flag; +}; + +/* + map a series of letter codes into a uint32_t +*/ +static bool sddl_map_flags(const struct flag_map *map, const char *str, + uint32_t *flags, size_t *len) +{ + const char *str0 = str; + if (len) *len = 0; + *flags = 0; + while (str[0] && isupper(str[0])) { + int i; + for (i=0;map[i].name;i++) { + size_t l = strlen(map[i].name); + if (strncmp(map[i].name, str, l) == 0) { + *flags |= map[i].flag; + str += l; + if (len) *len += l; + break; + } + } + if (map[i].name == NULL) { + DEBUG(1, ("Unknown flag - %s in %s\n", str, str0)); + return false; + } + } + return true; +} + +/* + a mapping between the 2 letter SID codes and sid strings +*/ +static const struct { + const char *code; + const char *sid; + uint32_t rid; +} sid_codes[] = { + { "AO", SID_BUILTIN_ACCOUNT_OPERATORS }, + { "BA", SID_BUILTIN_ADMINISTRATORS }, + { "RU", SID_BUILTIN_PREW2K }, + { "PO", SID_BUILTIN_PRINT_OPERATORS }, + { "RS", SID_BUILTIN_RAS_SERVERS }, + + { "AU", SID_NT_AUTHENTICATED_USERS }, + { "SY", SID_NT_SYSTEM }, + { "PS", SID_NT_SELF }, + { "WD", SID_WORLD }, + { "ED", SID_NT_ENTERPRISE_DCS }, + + { "CO", SID_CREATOR_OWNER }, + { "CG", SID_CREATOR_GROUP }, + + { "DA", NULL, DOMAIN_RID_ADMINS }, + { "EA", NULL, DOMAIN_RID_ENTERPRISE_ADMINS }, + { "DD", NULL, DOMAIN_RID_DCS }, + { "DU", NULL, DOMAIN_RID_USERS }, + { "CA", NULL, DOMAIN_RID_CERT_ADMINS }, +}; + +/* + decode a SID + It can either be a special 2 letter code, or in S-* format +*/ +static struct dom_sid *sddl_decode_sid(TALLOC_CTX *mem_ctx, const char **sddlp, + const struct dom_sid *domain_sid) +{ + const char *sddl = (*sddlp); + int i; + + /* see if its in the numeric format */ + if (strncmp(sddl, "S-", 2) == 0) { + struct dom_sid *sid; + char *sid_str; + size_t len = strspn(sddl+2, "-0123456789"); + sid_str = talloc_strndup(mem_ctx, sddl, len+2); + if (!sid_str) { + return NULL; + } + (*sddlp) += len+2; + sid = dom_sid_parse_talloc(mem_ctx, sid_str); + talloc_free(sid_str); + return sid; + } + + /* now check for one of the special codes */ + for (i=0;i<ARRAY_SIZE(sid_codes);i++) { + if (strncmp(sid_codes[i].code, sddl, 2) == 0) break; + } + if (i == ARRAY_SIZE(sid_codes)) { + DEBUG(1,("Unknown sddl sid code '%2.2s'\n", sddl)); + return NULL; + } + + (*sddlp) += 2; + + if (sid_codes[i].sid == NULL) { + return dom_sid_add_rid(mem_ctx, domain_sid, sid_codes[i].rid); + } + + return dom_sid_parse_talloc(mem_ctx, sid_codes[i].sid); +} + +static const struct flag_map ace_types[] = { + { "AU", SEC_ACE_TYPE_SYSTEM_AUDIT }, + { "AL", SEC_ACE_TYPE_SYSTEM_ALARM }, + { "OA", SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT }, + { "OD", SEC_ACE_TYPE_ACCESS_DENIED_OBJECT }, + { "OU", SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT }, + { "OL", SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT }, + { "A", SEC_ACE_TYPE_ACCESS_ALLOWED }, + { "D", SEC_ACE_TYPE_ACCESS_DENIED }, + { NULL, 0 } +}; + +static const struct flag_map ace_flags[] = { + { "OI", SEC_ACE_FLAG_OBJECT_INHERIT }, + { "CI", SEC_ACE_FLAG_CONTAINER_INHERIT }, + { "NP", SEC_ACE_FLAG_NO_PROPAGATE_INHERIT }, + { "IO", SEC_ACE_FLAG_INHERIT_ONLY }, + { "ID", SEC_ACE_FLAG_INHERITED_ACE }, + { "SA", SEC_ACE_FLAG_SUCCESSFUL_ACCESS }, + { "FA", SEC_ACE_FLAG_FAILED_ACCESS }, + { NULL, 0 }, +}; + +static const struct flag_map ace_access_mask[] = { + { "RP", SEC_ADS_READ_PROP }, + { "WP", SEC_ADS_WRITE_PROP }, + { "CR", SEC_ADS_CONTROL_ACCESS }, + { "CC", SEC_ADS_CREATE_CHILD }, + { "DC", SEC_ADS_DELETE_CHILD }, + { "LC", SEC_ADS_LIST }, + { "LO", SEC_ADS_LIST_OBJECT }, + { "RC", SEC_STD_READ_CONTROL }, + { "WO", SEC_STD_WRITE_OWNER }, + { "WD", SEC_STD_WRITE_DAC }, + { "SD", SEC_STD_DELETE }, + { "DT", SEC_ADS_DELETE_TREE }, + { "SW", SEC_ADS_SELF_WRITE }, + { "GA", SEC_GENERIC_ALL }, + { "GR", SEC_GENERIC_READ }, + { "GW", SEC_GENERIC_WRITE }, + { "GX", SEC_GENERIC_EXECUTE }, + { NULL, 0 } +}; + +/* + decode an ACE + return true on success, false on failure + note that this routine modifies the string +*/ +static bool sddl_decode_ace(TALLOC_CTX *mem_ctx, struct security_ace *ace, char *str, + const struct dom_sid *domain_sid) +{ + const char *tok[6]; + const char *s; + int i; + uint32_t v; + struct dom_sid *sid; + + ZERO_STRUCTP(ace); + + /* parse out the 6 tokens */ + tok[0] = str; + for (i=0;i<5;i++) { + char *ptr = strchr(str, ';'); + if (ptr == NULL) return false; + *ptr = 0; + str = ptr+1; + tok[i+1] = str; + } + + /* parse ace type */ + if (!sddl_map_flags(ace_types, tok[0], &v, NULL)) { + return false; + } + ace->type = v; + + /* ace flags */ + if (!sddl_map_flags(ace_flags, tok[1], &v, NULL)) { + return false; + } + ace->flags = v; + + /* access mask */ + if (strncmp(tok[2], "0x", 2) == 0) { + ace->access_mask = strtol(tok[2], NULL, 16); + } else { + if (!sddl_map_flags(ace_access_mask, tok[2], &v, NULL)) { + return false; + } + ace->access_mask = v; + } + + /* object */ + if (tok[3][0] != 0) { + NTSTATUS status = GUID_from_string(tok[3], + &ace->object.object.type.type); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + ace->object.object.flags |= SEC_ACE_OBJECT_TYPE_PRESENT; + } + + /* inherit object */ + if (tok[4][0] != 0) { + NTSTATUS status = GUID_from_string(tok[4], + &ace->object.object.inherited_type.inherited_type); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + ace->object.object.flags |= SEC_ACE_INHERITED_OBJECT_TYPE_PRESENT; + } + + /* trustee */ + s = tok[5]; + sid = sddl_decode_sid(mem_ctx, &s, domain_sid); + if (sid == NULL) { + return false; + } + ace->trustee = *sid; + talloc_free(sid); + + return true; +} + +static const struct flag_map acl_flags[] = { + { "P", SEC_DESC_DACL_PROTECTED }, + { "AR", SEC_DESC_DACL_AUTO_INHERIT_REQ }, + { "AI", SEC_DESC_DACL_AUTO_INHERITED }, + { NULL, 0 } +}; + +/* + decode an ACL +*/ +static struct security_acl *sddl_decode_acl(struct security_descriptor *sd, + const char **sddlp, uint32_t *flags, + const struct dom_sid *domain_sid) +{ + const char *sddl = *sddlp; + struct security_acl *acl; + size_t len; + + *flags = 0; + + acl = talloc_zero(sd, struct security_acl); + if (acl == NULL) return NULL; + acl->revision = SECURITY_ACL_REVISION_NT4; + + if (isupper(sddl[0]) && sddl[1] == ':') { + /* its an empty ACL */ + return acl; + } + + /* work out the ACL flags */ + if (!sddl_map_flags(acl_flags, sddl, flags, &len)) { + talloc_free(acl); + return NULL; + } + sddl += len; + + /* now the ACEs */ + while (*sddl == '(') { + char *astr; + len = strcspn(sddl+1, ")"); + astr = talloc_strndup(acl, sddl+1, len); + if (astr == NULL || sddl[len+1] != ')') { + talloc_free(acl); + return NULL; + } + acl->aces = talloc_realloc(acl, acl->aces, struct security_ace, + acl->num_aces+1); + if (acl->aces == NULL) { + talloc_free(acl); + return NULL; + } + if (!sddl_decode_ace(acl->aces, &acl->aces[acl->num_aces], + astr, domain_sid)) { + talloc_free(acl); + return NULL; + } + switch (acl->aces[acl->num_aces].type) { + case SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT: + case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT: + case SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT: + case SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT: + acl->revision = SECURITY_ACL_REVISION_ADS; + break; + default: + break; + } + talloc_free(astr); + sddl += len+2; + acl->num_aces++; + } + + (*sddlp) = sddl; + return acl; +} + +/* + decode a security descriptor in SDDL format +*/ +struct security_descriptor *sddl_decode(TALLOC_CTX *mem_ctx, const char *sddl, + const struct dom_sid *domain_sid) +{ + struct security_descriptor *sd; + sd = talloc_zero(mem_ctx, struct security_descriptor); + + sd->revision = SECURITY_DESCRIPTOR_REVISION_1; + sd->type = SEC_DESC_SELF_RELATIVE; + + while (*sddl) { + uint32_t flags; + char c = sddl[0]; + if (sddl[1] != ':') goto failed; + + sddl += 2; + switch (c) { + case 'D': + if (sd->dacl != NULL) goto failed; + sd->dacl = sddl_decode_acl(sd, &sddl, &flags, domain_sid); + if (sd->dacl == NULL) goto failed; + sd->type |= flags | SEC_DESC_DACL_PRESENT; + break; + case 'S': + if (sd->sacl != NULL) goto failed; + sd->sacl = sddl_decode_acl(sd, &sddl, &flags, domain_sid); + if (sd->sacl == NULL) goto failed; + /* this relies on the SEC_DESC_SACL_* flags being + 1 bit shifted from the SEC_DESC_DACL_* flags */ + sd->type |= (flags<<1) | SEC_DESC_SACL_PRESENT; + break; + case 'O': + if (sd->owner_sid != NULL) goto failed; + sd->owner_sid = sddl_decode_sid(sd, &sddl, domain_sid); + if (sd->owner_sid == NULL) goto failed; + break; + case 'G': + if (sd->group_sid != NULL) goto failed; + sd->group_sid = sddl_decode_sid(sd, &sddl, domain_sid); + if (sd->group_sid == NULL) goto failed; + break; + } + } + + return sd; + +failed: + DEBUG(2,("Badly formatted SDDL '%s'\n", sddl)); + talloc_free(sd); + return NULL; +} + +/* + turn a set of flags into a string +*/ +static char *sddl_flags_to_string(TALLOC_CTX *mem_ctx, const struct flag_map *map, + uint32_t flags, bool check_all) +{ + int i; + char *s; + + /* try to find an exact match */ + for (i=0;map[i].name;i++) { + if (map[i].flag == flags) { + return talloc_strdup(mem_ctx, map[i].name); + } + } + + s = talloc_strdup(mem_ctx, ""); + + /* now by bits */ + for (i=0;map[i].name;i++) { + if ((flags & map[i].flag) != 0) { + s = talloc_asprintf_append_buffer(s, "%s", map[i].name); + if (s == NULL) goto failed; + flags &= ~map[i].flag; + } + } + + if (check_all && flags != 0) { + goto failed; + } + + return s; + +failed: + talloc_free(s); + return NULL; +} + +/* + encode a sid in SDDL format +*/ +static char *sddl_encode_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid, + const struct dom_sid *domain_sid) +{ + int i; + char *sidstr; + + sidstr = dom_sid_string(mem_ctx, sid); + if (sidstr == NULL) return NULL; + + /* seen if its a well known sid */ + for (i=0;sid_codes[i].sid;i++) { + if (strcmp(sidstr, sid_codes[i].sid) == 0) { + talloc_free(sidstr); + return talloc_strdup(mem_ctx, sid_codes[i].code); + } + } + + /* or a well known rid in our domain */ + if (dom_sid_in_domain(domain_sid, sid)) { + uint32_t rid = sid->sub_auths[sid->num_auths-1]; + for (;i<ARRAY_SIZE(sid_codes);i++) { + if (rid == sid_codes[i].rid) { + talloc_free(sidstr); + return talloc_strdup(mem_ctx, sid_codes[i].code); + } + } + } + + talloc_free(sidstr); + + /* TODO: encode well known sids as two letter codes */ + return dom_sid_string(mem_ctx, sid); +} + + +/* + encode an ACE in SDDL format +*/ +static char *sddl_encode_ace(TALLOC_CTX *mem_ctx, const struct security_ace *ace, + const struct dom_sid *domain_sid) +{ + char *sddl = NULL; + TALLOC_CTX *tmp_ctx; + const char *s_type="", *s_flags="", *s_mask="", + *s_object="", *s_iobject="", *s_trustee=""; + + tmp_ctx = talloc_new(mem_ctx); + if (tmp_ctx == NULL) { + DEBUG(0, ("talloc_new failed\n")); + return NULL; + } + + s_type = sddl_flags_to_string(tmp_ctx, ace_types, ace->type, true); + if (s_type == NULL) goto failed; + + s_flags = sddl_flags_to_string(tmp_ctx, ace_flags, ace->flags, true); + if (s_flags == NULL) goto failed; + + s_mask = sddl_flags_to_string(tmp_ctx, ace_access_mask, ace->access_mask, true); + if (s_mask == NULL) { + s_mask = talloc_asprintf(tmp_ctx, "0x%08x", ace->access_mask); + if (s_mask == NULL) goto failed; + } + + if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT || + ace->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT || + ace->type == SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT || + ace->type == SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT) { + if (ace->object.object.flags & SEC_ACE_OBJECT_TYPE_PRESENT) { + s_object = GUID_string(tmp_ctx, &ace->object.object.type.type); + if (s_object == NULL) goto failed; + } + + if (ace->object.object.flags & SEC_ACE_INHERITED_OBJECT_TYPE_PRESENT) { + s_iobject = GUID_string(tmp_ctx, &ace->object.object.inherited_type.inherited_type); + if (s_iobject == NULL) goto failed; + } + } + + s_trustee = sddl_encode_sid(tmp_ctx, &ace->trustee, domain_sid); + if (s_trustee == NULL) goto failed; + + sddl = talloc_asprintf(mem_ctx, "%s;%s;%s;%s;%s;%s", + s_type, s_flags, s_mask, s_object, s_iobject, s_trustee); + +failed: + talloc_free(tmp_ctx); + return sddl; +} + +/* + encode an ACL in SDDL format +*/ +static char *sddl_encode_acl(TALLOC_CTX *mem_ctx, const struct security_acl *acl, + uint32_t flags, const struct dom_sid *domain_sid) +{ + char *sddl; + int i; + + /* add any ACL flags */ + sddl = sddl_flags_to_string(mem_ctx, acl_flags, flags, false); + if (sddl == NULL) goto failed; + + /* now the ACEs, encoded in braces */ + for (i=0;i<acl->num_aces;i++) { + char *ace = sddl_encode_ace(sddl, &acl->aces[i], domain_sid); + if (ace == NULL) goto failed; + sddl = talloc_asprintf_append_buffer(sddl, "(%s)", ace); + if (sddl == NULL) goto failed; + talloc_free(ace); + } + + return sddl; + +failed: + talloc_free(sddl); + return NULL; +} + + +/* + encode a security descriptor to SDDL format +*/ +char *sddl_encode(TALLOC_CTX *mem_ctx, const struct security_descriptor *sd, + const struct dom_sid *domain_sid) +{ + char *sddl; + TALLOC_CTX *tmp_ctx; + + /* start with a blank string */ + sddl = talloc_strdup(mem_ctx, ""); + if (sddl == NULL) goto failed; + + tmp_ctx = talloc_new(mem_ctx); + + if (sd->owner_sid != NULL) { + char *sid = sddl_encode_sid(tmp_ctx, sd->owner_sid, domain_sid); + if (sid == NULL) goto failed; + sddl = talloc_asprintf_append_buffer(sddl, "O:%s", sid); + if (sddl == NULL) goto failed; + } + + if (sd->group_sid != NULL) { + char *sid = sddl_encode_sid(tmp_ctx, sd->group_sid, domain_sid); + if (sid == NULL) goto failed; + sddl = talloc_asprintf_append_buffer(sddl, "G:%s", sid); + if (sddl == NULL) goto failed; + } + + if ((sd->type & SEC_DESC_DACL_PRESENT) && sd->dacl != NULL) { + char *acl = sddl_encode_acl(tmp_ctx, sd->dacl, sd->type, domain_sid); + if (acl == NULL) goto failed; + sddl = talloc_asprintf_append_buffer(sddl, "D:%s", acl); + if (sddl == NULL) goto failed; + } + + if ((sd->type & SEC_DESC_SACL_PRESENT) && sd->sacl != NULL) { + char *acl = sddl_encode_acl(tmp_ctx, sd->sacl, sd->type>>1, domain_sid); + if (acl == NULL) goto failed; + sddl = talloc_asprintf_append_buffer(sddl, "S:%s", acl); + if (sddl == NULL) goto failed; + } + + talloc_free(tmp_ctx); + return sddl; + +failed: + talloc_free(sddl); + return NULL; +} + + diff --git a/source4/libcli/security/security.h b/source4/libcli/security/security.h new file mode 100644 index 0000000000..2608c9f7ed --- /dev/null +++ b/source4/libcli/security/security.h @@ -0,0 +1,35 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Stefan Metzmacher 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 "librpc/gen_ndr/security.h" + +enum security_user_level { + SECURITY_ANONYMOUS, + SECURITY_USER, + SECURITY_ADMINISTRATOR, + SECURITY_SYSTEM +}; + +struct auth_session_info; + +/* Moved the dom_sid functions to the top level dir with manual proto header */ +#include "libcli/security/dom_sid.h" +#include "libcli/security/secace.h" +#include "libcli/security/secacl.h" +#include "libcli/security/proto.h" diff --git a/source4/libcli/security/security_descriptor.c b/source4/libcli/security/security_descriptor.c new file mode 100644 index 0000000000..8e9c7eb4a9 --- /dev/null +++ b/source4/libcli/security/security_descriptor.c @@ -0,0 +1,533 @@ +/* + Unix SMB/CIFS implementation. + + security descriptror utility functions + + Copyright (C) Andrew Tridgell 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" +#include "libcli/security/security.h" + +/* + return a blank security descriptor (no owners, dacl or sacl) +*/ +struct security_descriptor *security_descriptor_initialise(TALLOC_CTX *mem_ctx) +{ + struct security_descriptor *sd; + + sd = talloc(mem_ctx, struct security_descriptor); + if (!sd) { + return NULL; + } + + sd->revision = SD_REVISION; + /* we mark as self relative, even though it isn't while it remains + a pointer in memory because this simplifies the ndr code later. + All SDs that we store/emit are in fact SELF_RELATIVE + */ + sd->type = SEC_DESC_SELF_RELATIVE; + + sd->owner_sid = NULL; + sd->group_sid = NULL; + sd->sacl = NULL; + sd->dacl = NULL; + + return sd; +} + +static struct security_acl *security_acl_dup(TALLOC_CTX *mem_ctx, + const struct security_acl *oacl) +{ + struct security_acl *nacl; + + nacl = talloc (mem_ctx, struct security_acl); + if (nacl == NULL) { + return NULL; + } + + nacl->aces = (struct security_ace *)talloc_memdup (nacl, oacl->aces, sizeof(struct security_ace) * oacl->num_aces); + if ((nacl->aces == NULL) && (oacl->num_aces > 0)) { + goto failed; + } + + nacl->revision = oacl->revision; + nacl->size = oacl->size; + nacl->num_aces = oacl->num_aces; + + return nacl; + + failed: + talloc_free (nacl); + return NULL; + +} + +/* + talloc and copy a security descriptor + */ +struct security_descriptor *security_descriptor_copy(TALLOC_CTX *mem_ctx, + const struct security_descriptor *osd) +{ + struct security_descriptor *nsd; + + nsd = talloc_zero(mem_ctx, struct security_descriptor); + if (!nsd) { + return NULL; + } + + if (osd->owner_sid) { + nsd->owner_sid = dom_sid_dup(nsd, osd->owner_sid); + if (nsd->owner_sid == NULL) { + goto failed; + } + } + + if (osd->group_sid) { + nsd->group_sid = dom_sid_dup(nsd, osd->group_sid); + if (nsd->group_sid == NULL) { + goto failed; + } + } + + if (osd->sacl) { + nsd->sacl = security_acl_dup(nsd, osd->sacl); + if (nsd->sacl == NULL) { + goto failed; + } + } + + if (osd->dacl) { + nsd->dacl = security_acl_dup(nsd, osd->dacl); + if (nsd->dacl == NULL) { + goto failed; + } + } + + nsd->revision = osd->revision; + nsd->type = osd->type; + + return nsd; + + failed: + talloc_free(nsd); + + return NULL; +} + +/* + add an ACE to an ACL of a security_descriptor +*/ + +static NTSTATUS security_descriptor_acl_add(struct security_descriptor *sd, + bool add_to_sacl, + const struct security_ace *ace) +{ + struct security_acl *acl = NULL; + + if (add_to_sacl) { + acl = sd->sacl; + } else { + acl = sd->dacl; + } + + if (acl == NULL) { + acl = talloc(sd, struct security_acl); + if (acl == NULL) { + return NT_STATUS_NO_MEMORY; + } + acl->revision = SECURITY_ACL_REVISION_NT4; + acl->size = 0; + acl->num_aces = 0; + acl->aces = NULL; + } + + acl->aces = talloc_realloc(acl, acl->aces, + struct security_ace, acl->num_aces+1); + if (acl->aces == NULL) { + return NT_STATUS_NO_MEMORY; + } + + acl->aces[acl->num_aces] = *ace; + + switch (acl->aces[acl->num_aces].type) { + case SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT: + case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT: + case SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT: + case SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT: + acl->revision = SECURITY_ACL_REVISION_ADS; + break; + default: + break; + } + + acl->num_aces++; + + if (add_to_sacl) { + sd->sacl = acl; + sd->type |= SEC_DESC_SACL_PRESENT; + } else { + sd->dacl = acl; + sd->type |= SEC_DESC_DACL_PRESENT; + } + + return NT_STATUS_OK; +} + +/* + add an ACE to the SACL of a security_descriptor +*/ + +NTSTATUS security_descriptor_sacl_add(struct security_descriptor *sd, + const struct security_ace *ace) +{ + return security_descriptor_acl_add(sd, true, ace); +} + +/* + add an ACE to the DACL of a security_descriptor +*/ + +NTSTATUS security_descriptor_dacl_add(struct security_descriptor *sd, + const struct security_ace *ace) +{ + return security_descriptor_acl_add(sd, false, ace); +} + +/* + delete the ACE corresponding to the given trustee in an ACL of a + security_descriptor +*/ + +static NTSTATUS security_descriptor_acl_del(struct security_descriptor *sd, + bool sacl_del, + const struct dom_sid *trustee) +{ + int i; + bool found = false; + struct security_acl *acl = NULL; + + if (sacl_del) { + acl = sd->sacl; + } else { + acl = sd->dacl; + } + + if (acl == NULL) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + /* there can be multiple ace's for one trustee */ + for (i=0;i<acl->num_aces;i++) { + if (dom_sid_equal(trustee, &acl->aces[i].trustee)) { + memmove(&acl->aces[i], &acl->aces[i+1], + sizeof(acl->aces[i]) * (acl->num_aces - (i+1))); + acl->num_aces--; + if (acl->num_aces == 0) { + acl->aces = NULL; + } + found = true; + } + } + + if (!found) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + acl->revision = SECURITY_ACL_REVISION_NT4; + + for (i=0;i<acl->num_aces;i++) { + switch (acl->aces[i].type) { + case SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT: + case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT: + case SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT: + case SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT: + acl->revision = SECURITY_ACL_REVISION_ADS; + return NT_STATUS_OK; + default: + break; /* only for the switch statement */ + } + } + + return NT_STATUS_OK; +} + +/* + delete the ACE corresponding to the given trustee in the DACL of a + security_descriptor +*/ + +NTSTATUS security_descriptor_dacl_del(struct security_descriptor *sd, + const struct dom_sid *trustee) +{ + return security_descriptor_acl_del(sd, false, trustee); +} + +/* + delete the ACE corresponding to the given trustee in the SACL of a + security_descriptor +*/ + +NTSTATUS security_descriptor_sacl_del(struct security_descriptor *sd, + const struct dom_sid *trustee) +{ + return security_descriptor_acl_del(sd, true, trustee); +} + +/* + compare two security ace structures +*/ +bool security_ace_equal(const struct security_ace *ace1, + const struct security_ace *ace2) +{ + if (ace1 == ace2) return true; + if (!ace1 || !ace2) return false; + if (ace1->type != ace2->type) return false; + if (ace1->flags != ace2->flags) return false; + if (ace1->access_mask != ace2->access_mask) return false; + if (!dom_sid_equal(&ace1->trustee, &ace2->trustee)) return false; + + return true; +} + + +/* + compare two security acl structures +*/ +bool security_acl_equal(const struct security_acl *acl1, + const struct security_acl *acl2) +{ + int i; + + if (acl1 == acl2) return true; + if (!acl1 || !acl2) return false; + if (acl1->revision != acl2->revision) return false; + if (acl1->num_aces != acl2->num_aces) return false; + + for (i=0;i<acl1->num_aces;i++) { + if (!security_ace_equal(&acl1->aces[i], &acl2->aces[i])) return false; + } + return true; +} + +/* + compare two security descriptors. +*/ +bool security_descriptor_equal(const struct security_descriptor *sd1, + const struct security_descriptor *sd2) +{ + if (sd1 == sd2) return true; + if (!sd1 || !sd2) return false; + if (sd1->revision != sd2->revision) return false; + if (sd1->type != sd2->type) return false; + + if (!dom_sid_equal(sd1->owner_sid, sd2->owner_sid)) return false; + if (!dom_sid_equal(sd1->group_sid, sd2->group_sid)) return false; + if (!security_acl_equal(sd1->sacl, sd2->sacl)) return false; + if (!security_acl_equal(sd1->dacl, sd2->dacl)) return false; + + return true; +} + +/* + compare two security descriptors, but allow certain (missing) parts + to be masked out of the comparison +*/ +bool security_descriptor_mask_equal(const struct security_descriptor *sd1, + const struct security_descriptor *sd2, + uint32_t mask) +{ + if (sd1 == sd2) return true; + if (!sd1 || !sd2) return false; + if (sd1->revision != sd2->revision) return false; + if ((sd1->type & mask) != (sd2->type & mask)) return false; + + if (!dom_sid_equal(sd1->owner_sid, sd2->owner_sid)) return false; + if (!dom_sid_equal(sd1->group_sid, sd2->group_sid)) return false; + if ((mask & SEC_DESC_DACL_PRESENT) && !security_acl_equal(sd1->dacl, sd2->dacl)) return false; + if ((mask & SEC_DESC_SACL_PRESENT) && !security_acl_equal(sd1->sacl, sd2->sacl)) return false; + + return true; +} + + +static struct security_descriptor *security_descriptor_appendv(struct security_descriptor *sd, + bool add_ace_to_sacl, + va_list ap) +{ + const char *sidstr; + + while ((sidstr = va_arg(ap, const char *))) { + struct dom_sid *sid; + struct security_ace *ace = talloc(sd, struct security_ace); + NTSTATUS status; + + if (ace == NULL) { + talloc_free(sd); + return NULL; + } + ace->type = va_arg(ap, unsigned int); + ace->access_mask = va_arg(ap, unsigned int); + ace->flags = va_arg(ap, unsigned int); + sid = dom_sid_parse_talloc(ace, sidstr); + if (sid == NULL) { + talloc_free(sd); + return NULL; + } + ace->trustee = *sid; + if (add_ace_to_sacl) { + status = security_descriptor_sacl_add(sd, ace); + } else { + status = security_descriptor_dacl_add(sd, ace); + } + /* TODO: check: would talloc_free(ace) here be correct? */ + if (!NT_STATUS_IS_OK(status)) { + talloc_free(sd); + return NULL; + } + } + + return sd; +} + +struct security_descriptor *security_descriptor_append(struct security_descriptor *sd, + ...) +{ + va_list ap; + + va_start(ap, sd); + sd = security_descriptor_appendv(sd, false, ap); + va_end(ap); + + return sd; +} + +static struct security_descriptor *security_descriptor_createv(TALLOC_CTX *mem_ctx, + uint16_t sd_type, + const char *owner_sid, + const char *group_sid, + bool add_ace_to_sacl, + va_list ap) +{ + struct security_descriptor *sd; + + sd = security_descriptor_initialise(mem_ctx); + if (sd == NULL) { + return NULL; + } + + sd->type |= sd_type; + + if (owner_sid) { + sd->owner_sid = dom_sid_parse_talloc(sd, owner_sid); + if (sd->owner_sid == NULL) { + talloc_free(sd); + return NULL; + } + } + if (group_sid) { + sd->group_sid = dom_sid_parse_talloc(sd, group_sid); + if (sd->group_sid == NULL) { + talloc_free(sd); + return NULL; + } + } + + return security_descriptor_appendv(sd, add_ace_to_sacl, ap); +} + +/* + create a security descriptor using string SIDs. This is used by the + torture code to allow the easy creation of complex ACLs + This is a varargs function. The list of DACL ACEs ends with a NULL sid. + + Each ACE contains a set of 4 parameters: + SID, ACCESS_TYPE, MASK, FLAGS + + a typical call would be: + + sd = security_descriptor_dacl_create(mem_ctx, + sd_type_flags, + mysid, + mygroup, + SID_NT_AUTHENTICATED_USERS, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_ALL, + SEC_ACE_FLAG_OBJECT_INHERIT, + NULL); + that would create a sd with one DACL ACE +*/ + +struct security_descriptor *security_descriptor_dacl_create(TALLOC_CTX *mem_ctx, + uint16_t sd_type, + const char *owner_sid, + const char *group_sid, + ...) +{ + struct security_descriptor *sd = NULL; + va_list ap; + va_start(ap, group_sid); + sd = security_descriptor_createv(mem_ctx, sd_type, owner_sid, + group_sid, false, ap); + va_end(ap); + + return sd; +} + +struct security_descriptor *security_descriptor_sacl_create(TALLOC_CTX *mem_ctx, + uint16_t sd_type, + const char *owner_sid, + const char *group_sid, + ...) +{ + struct security_descriptor *sd = NULL; + va_list ap; + va_start(ap, group_sid); + sd = security_descriptor_createv(mem_ctx, sd_type, owner_sid, + group_sid, true, ap); + va_end(ap); + + return sd; +} + +struct security_ace *security_ace_create(TALLOC_CTX *mem_ctx, + const char *sid_str, + enum security_ace_type type, + uint32_t access_mask, + uint8_t flags) + +{ + struct dom_sid *sid; + struct security_ace *ace; + + ace = talloc_zero(mem_ctx, struct security_ace); + if (ace == NULL) { + return NULL; + } + + sid = dom_sid_parse_talloc(ace, sid_str); + if (sid == NULL) { + talloc_free(ace); + return NULL; + } + + ace->trustee = *sid; + ace->type = type; + ace->access_mask = access_mask; + ace->flags = flags; + + return ace; +} diff --git a/source4/libcli/security/security_token.c b/source4/libcli/security/security_token.c new file mode 100644 index 0000000000..e1349e06f8 --- /dev/null +++ b/source4/libcli/security/security_token.c @@ -0,0 +1,169 @@ +/* + Unix SMB/CIFS implementation. + + security descriptror utility functions + + Copyright (C) Andrew Tridgell 2004 + Copyright (C) Stefan Metzmacher 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/security/security.h" +#include "auth/session.h" + +/* + return a blank security token +*/ +struct security_token *security_token_initialise(TALLOC_CTX *mem_ctx) +{ + struct security_token *st; + + st = talloc(mem_ctx, struct security_token); + if (!st) { + return NULL; + } + + st->user_sid = NULL; + st->group_sid = NULL; + st->num_sids = 0; + st->sids = NULL; + st->privilege_mask = 0; + + return st; +} + +/**************************************************************************** + prints a struct security_token to debug output. +****************************************************************************/ +void security_token_debug(int dbg_lev, const struct security_token *token) +{ + TALLOC_CTX *mem_ctx; + int i; + + if (!token) { + DEBUG(dbg_lev, ("Security token: (NULL)\n")); + return; + } + + mem_ctx = talloc_init("security_token_debug()"); + if (!mem_ctx) { + return; + } + + DEBUG(dbg_lev, ("Security token of user %s\n", + dom_sid_string(mem_ctx, token->user_sid) )); + DEBUGADD(dbg_lev, (" SIDs (%lu):\n", + (unsigned long)token->num_sids)); + for (i = 0; i < token->num_sids; i++) { + DEBUGADD(dbg_lev, (" SID[%3lu]: %s\n", (unsigned long)i, + dom_sid_string(mem_ctx, token->sids[i]))); + } + + security_token_debug_privileges(dbg_lev, token); + + talloc_free(mem_ctx); +} + +/* These really should be cheaper... */ + +bool security_token_is_sid(const struct security_token *token, const struct dom_sid *sid) +{ + if (dom_sid_equal(token->user_sid, sid)) { + return true; + } + return false; +} + +bool security_token_is_sid_string(const struct security_token *token, const char *sid_string) +{ + bool ret; + struct dom_sid *sid = dom_sid_parse_talloc(NULL, sid_string); + if (!sid) return false; + + ret = security_token_is_sid(token, sid); + + talloc_free(sid); + return ret; +} + +bool security_token_is_system(const struct security_token *token) +{ + return security_token_is_sid_string(token, SID_NT_SYSTEM); +} + +bool security_token_is_anonymous(const struct security_token *token) +{ + return security_token_is_sid_string(token, SID_NT_ANONYMOUS); +} + +bool security_token_has_sid(const struct security_token *token, const struct dom_sid *sid) +{ + int i; + for (i = 0; i < token->num_sids; i++) { + if (dom_sid_equal(token->sids[i], sid)) { + return true; + } + } + return false; +} + +bool security_token_has_sid_string(const struct security_token *token, const char *sid_string) +{ + bool ret; + struct dom_sid *sid = dom_sid_parse_talloc(NULL, sid_string); + if (!sid) return false; + + ret = security_token_has_sid(token, sid); + + talloc_free(sid); + return ret; +} + +bool security_token_has_builtin_administrators(const struct security_token *token) +{ + return security_token_has_sid_string(token, SID_BUILTIN_ADMINISTRATORS); +} + +bool security_token_has_nt_authenticated_users(const struct security_token *token) +{ + return security_token_has_sid_string(token, SID_NT_AUTHENTICATED_USERS); +} + +enum security_user_level security_session_user_level(struct auth_session_info *session_info) +{ + if (!session_info) { + return SECURITY_ANONYMOUS; + } + + if (security_token_is_system(session_info->security_token)) { + return SECURITY_SYSTEM; + } + + if (security_token_is_anonymous(session_info->security_token)) { + return SECURITY_ANONYMOUS; + } + + if (security_token_has_builtin_administrators(session_info->security_token)) { + return SECURITY_ADMINISTRATOR; + } + + if (security_token_has_nt_authenticated_users(session_info->security_token)) { + return SECURITY_USER; + } + + return SECURITY_ANONYMOUS; +} + diff --git a/source4/libcli/security/tests/bindings.py b/source4/libcli/security/tests/bindings.py new file mode 100644 index 0000000000..24ee01c37f --- /dev/null +++ b/source4/libcli/security/tests/bindings.py @@ -0,0 +1,97 @@ +#!/usr/bin/python + +# Unix SMB/CIFS implementation. +# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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/>. +# + +import unittest +from samba.dcerpc import security + +class SecurityTokenTests(unittest.TestCase): + def setUp(self): + self.token = security.token() + + def test_is_system(self): + self.assertFalse(self.token.is_system()) + + def test_is_anonymous(self): + self.assertFalse(self.token.is_anonymous()) + + def test_has_builtin_administrators(self): + self.assertFalse(self.token.has_builtin_administrators()) + + def test_has_nt_authenticated_users(self): + self.assertFalse(self.token.has_nt_authenticated_users()) + + def test_has_priv(self): + self.assertFalse(self.token.has_privilege(security.SEC_PRIV_SHUTDOWN)) + + def test_set_priv(self): + self.assertFalse(self.token.has_privilege(security.SEC_PRIV_SHUTDOWN)) + self.assertFalse(self.token.set_privilege(security.SEC_PRIV_SHUTDOWN)) + self.assertTrue(self.token.has_privilege(security.SEC_PRIV_SHUTDOWN)) + + +class SecurityDescriptorTests(unittest.TestCase): + def setUp(self): + self.descriptor = security.descriptor() + + def test_from_sddl(self): + desc = security.descriptor.from_sddl("O:AOG:DAD:(A;;RPWPCCDCLCSWRCWDWOGA;;;S-1-0-0)", security.dom_sid("S-2-0-0")) + self.assertEquals(desc.group_sid, security.dom_sid('S-2-0-0-512')) + self.assertEquals(desc.owner_sid, security.dom_sid('S-1-5-32-548')) + self.assertEquals(desc.revision, 1) + self.assertEquals(desc.sacl, None) + self.assertEquals(desc.type, 0x8004) + + def test_as_sddl(self): + text = "O:AOG:DAD:(A;;RPWPCCDCLCSWRCWDWOGA;;;S-1-0-0)" + dom = security.dom_sid("S-2-0-0") + desc1 = security.descriptor.from_sddl(text, dom) + desc2 = security.descriptor.from_sddl(desc1.as_sddl(dom), dom) + self.assertEquals(desc1.group_sid, desc2.group_sid) + self.assertEquals(desc1.owner_sid, desc2.owner_sid) + self.assertEquals(desc1.sacl, desc2.sacl) + self.assertEquals(desc1.type, desc2.type) + + +class DomSidTests(unittest.TestCase): + def test_parse_sid(self): + sid = security.dom_sid("S-1-5-21") + self.assertEquals("S-1-5-21", str(sid)) + + def test_sid_equal(self): + sid1 = security.dom_sid("S-1-5-21") + sid2 = security.dom_sid("S-1-5-21") + self.assertEquals(sid1, sid1) + self.assertEquals(sid1, sid2) + + def test_random(self): + sid = security.random_sid() + self.assertTrue(str(sid).startswith("S-1-5-21-")) + + def test_repr(self): + sid = security.random_sid() + self.assertTrue(repr(sid).startswith("dom_sid('S-1-5-21-")) + + +class PrivilegeTests(unittest.TestCase): + def test_privilege_name(self): + self.assertEquals("SeShutdownPrivilege", security.privilege_name(security.SEC_PRIV_SHUTDOWN)) + + def test_privilege_id(self): + self.assertEquals(security.SEC_PRIV_SHUTDOWN, security.privilege_id("SeShutdownPrivilege")) + diff --git a/source4/libcli/security/tests/sddl.c b/source4/libcli/security/tests/sddl.c new file mode 100644 index 0000000000..9e7705ea92 --- /dev/null +++ b/source4/libcli/security/tests/sddl.c @@ -0,0 +1,105 @@ +/* + Unix SMB/CIFS implementation. + + local testing of SDDL parsing + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/security/security.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/ndr_security.h" + + +/* + test one SDDL example +*/ +static bool test_sddl(struct torture_context *tctx, + const void *test_data) +{ + struct security_descriptor *sd, *sd2; + struct dom_sid *domain; + const char *sddl = (const char *)test_data; + const char *sddl2; + TALLOC_CTX *mem_ctx = tctx; + + + domain = dom_sid_parse_talloc(mem_ctx, "S-1-2-3-4"); + sd = sddl_decode(mem_ctx, sddl, domain); + torture_assert(tctx, sd != NULL, talloc_asprintf(tctx, + "Failed to decode '%s'\n", sddl)); + + sddl2 = sddl_encode(mem_ctx, sd, domain); + torture_assert(tctx, sddl2 != NULL, talloc_asprintf(tctx, + "Failed to re-encode '%s'\n", sddl)); + + sd2 = sddl_decode(mem_ctx, sddl2, domain); + torture_assert(tctx, sd2 != NULL, talloc_asprintf(tctx, + "Failed to decode2 '%s'\n", sddl2)); + + torture_assert(tctx, security_descriptor_equal(sd, sd2), + talloc_asprintf(tctx, "Failed equality test for '%s'\n", sddl)); + +#if 0 + /* flags don't have a canonical order ... */ + if (strcmp(sddl, sddl2) != 0) { + printf("Failed sddl equality test\norig: %s\n new: %s\n", sddl, sddl2); + } +#endif + + if (DEBUGLVL(2)) { + NDR_PRINT_DEBUG(security_descriptor, sd); + } + talloc_free(sd); + talloc_free(domain); + return true; +} + +static const char *examples[] = { + "D:(A;;CC;;;BA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)", + "D:(A;;GA;;;SY)", + "D:(A;;RP;;;WD)(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)(A;;RPLCLORC;;;AU)(A;;RPWPCRLCLOCCRCWDWOSW;;;DA)(A;CI;RPWPCRLCLOCCRCWDWOSDSW;;;BA)(A;;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)(A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;EA)(A;CI;LC;;;RU)(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;;RP;c7407360-20bf-11d0-a768-00aa006e0529;;RU)(OA;CIIO;RPLCLORC;;bf967a9c-0de6-11d0-a285-00aa003049e2;RU)(A;;RPRC;;;RU)(OA;CIIO;RPLCLORC;;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(A;;LCRPLORC;;;ED)(OA;CIIO;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;CIIO;RPLCLORC;;4828CC14-1437-45bc-9B07-AD6F015E5F28;RU)(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;RU)(OA;;RP;b8119fd0-04f6-4762-ab7a-4986c76b3f9a;;AU)(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967aba-0de6-11d0-a285-00aa003049e2;ED)(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a9c-0de6-11d0-a285-00aa003049e2;ED)(OA;CIIO;RP;b7c69e6d-2cc7-11d2-854e-00a0c983f608;bf967a86-0de6-11d0-a285-00aa003049e2;ED)(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;DD)(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)(OA;;CR;e2a36dc9-ae17-47c3-b58b-be34c55ba633;;S-1-5-32-557)(OA;;CR;280f369c-67c7-438e-ae98-1d46f3c6f541;;AU)(OA;;CR;ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501;;AU)(OA;;CR;05c74c5e-4deb-43b4-bd9f-86664c2a7fd5;;AU)S:(AU;SA;WDWOWP;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)(OU;CISA;WP;f30e3bbe-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)(OU;CISA;WP;f30e3bbf-9ff0-11d1-b603-0000f80367c1;bf967aa5-0de6-11d0-a285-00aa003049e2;WD)", + "D:(A;;RPLCLORC;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)", + "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPCRLCLORCSDDT;;;CO)(OA;;WP;4c164200-20c0-11d0-a768-00aa006e0529;;CO)(A;;RPLCLORC;;;AU)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;WD)(A;;CCDC;;;PS)(OA;;CCDC;bf967aa8-0de6-11d0-a285-00aa003049e2;;PO)(OA;;RPWP;bf967a7f-0de6-11d0-a285-00aa003049e2;;CA)(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;PS)(OA;;RPWP;77B5B886-944A-11d1-AEBD-0000F80367C1;;PS)(OA;;SW;72e39547-7b18-11d1-adef-00c04fd8d5cd;;PS)(OA;;SW;72e39547-7b18-11d1-adef-00c04fd8d5cd;;CO)(OA;;SW;f3a64788-5306-11d1-a9c5-0000f80367c1;;CO)(OA;;WP;3e0abfd0-126a-11d0-a060-00aa006c33ed;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;bf967950-0de6-11d0-a285-00aa003049e2;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;WP;bf967953-0de6-11d0-a285-00aa003049e2;bf967a86-0de6-11d0-a285-00aa003049e2;CO)(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;S-1-5-32-560)", + "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)", + "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)(A;;RPLCLORC;;;PS)(OA;;CR;ab721a55-1e2f-11d0-9819-00aa0040529b;;AU)(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;S-1-5-32-560)", + "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;CO)", + "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)S:(AU;SA;CRWP;;;WD)", + "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;AO)(A;;RPLCLORC;;;PS)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;PS)(OA;;CR;ab721a54-1e2f-11d0-9819-00aa0040529b;;PS)(OA;;CR;ab721a56-1e2f-11d0-9819-00aa0040529b;;PS)(OA;;RPWP;77B5B886-944A-11d1-AEBD-0000F80367C1;;PS)(OA;;RPWP;E45795B2-9455-11d1-AEBD-0000F80367C1;;PS)(OA;;RPWP;E45795B3-9455-11d1-AEBD-0000F80367C1;;PS)(OA;;RP;037088f8-0ae1-11d2-b422-00a0c968f939;;RS)(OA;;RP;4c164200-20c0-11d0-a768-00aa006e0529;;RS)(OA;;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;;RS)(A;;RC;;;AU)(OA;;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;;AU)(OA;;RP;77B5B886-944A-11d1-AEBD-0000F80367C1;;AU)(OA;;RP;E45795B3-9455-11d1-AEBD-0000F80367C1;;AU)(OA;;RP;e48d0154-bcf8-11d1-8702-00c04fb96050;;AU)(OA;;CR;ab721a53-1e2f-11d0-9819-00aa0040529b;;WD)(OA;;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;;RS)(OA;;RPWP;bf967a7f-0de6-11d0-a285-00aa003049e2;;CA)(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;S-1-5-32-560)(OA;;WPRP;6db69a1c-9422-11d1-aebd-0000f80367c1;;S-1-5-32-561)", + "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)", + "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)(A;;LCRPLORC;;;ED)", + "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(OA;;CCDC;bf967a86-0de6-11d0-a285-00aa003049e2;;AO)(OA;;CCDC;bf967aba-0de6-11d0-a285-00aa003049e2;;AO)(OA;;CCDC;bf967a9c-0de6-11d0-a285-00aa003049e2;;AO)(OA;;CCDC;bf967aa8-0de6-11d0-a285-00aa003049e2;;PO)(A;;RPLCLORC;;;AU)(A;;LCRPLORC;;;ED)(OA;;CCDC;4828CC14-1437-45bc-9B07-AD6F015E5F28;;AO)", + "D:(A;;RPWPCRCCDCLCLORCWOWDSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)", + "D:(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)", + "D:S:", + "D:(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;DA)(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;;RPLCLORC;;;AU)" +}; + +/* test a set of example SDDL strings */ +struct torture_suite *torture_local_sddl(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "SDDL"); + int i; + + for (i = 0; i < ARRAY_SIZE(examples); i++) { + torture_suite_add_simple_tcase_const(suite, + talloc_asprintf(suite, "%d", i), + test_sddl, examples[i]); + } + + return suite; +} diff --git a/source4/libcli/smb2/break.c b/source4/libcli/smb2/break.c new file mode 100644 index 0000000000..fe0cceb829 --- /dev/null +++ b/source4/libcli/smb2/break.c @@ -0,0 +1,74 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 client oplock break handling + + Copyright (C) Stefan Metzmacher 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 "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" + +/* + send a break request +*/ +struct smb2_request *smb2_break_send(struct smb2_tree *tree, struct smb2_break *io) +{ + struct smb2_request *req; + + req = smb2_request_init_tree(tree, SMB2_OP_BREAK, 0x18, false, 0); + if (req == NULL) return NULL; + + SCVAL(req->out.body, 0x02, io->in.oplock_level); + SCVAL(req->out.body, 0x03, io->in.reserved); + SIVAL(req->out.body, 0x04, io->in.reserved2); + smb2_push_handle(req->out.body+0x08, &io->in.file.handle); + + smb2_transport_send(req); + + return req; +} + + +/* + recv a break reply +*/ +NTSTATUS smb2_break_recv(struct smb2_request *req, struct smb2_break *io) +{ + if (!smb2_request_receive(req) || + !smb2_request_is_ok(req)) { + return smb2_request_destroy(req); + } + + SMB2_CHECK_PACKET_RECV(req, 0x18, false); + + io->out.oplock_level = CVAL(req->in.body, 0x02); + io->out.reserved = CVAL(req->in.body, 0x03); + io->out.reserved2 = IVAL(req->in.body, 0x04); + smb2_pull_handle(req->in.body+0x08, &io->out.file.handle); + + return smb2_request_destroy(req); +} + +/* + sync flush request +*/ +NTSTATUS smb2_break(struct smb2_tree *tree, struct smb2_break *io) +{ + struct smb2_request *req = smb2_break_send(tree, io); + return smb2_break_recv(req, io); +} diff --git a/source4/libcli/smb2/cancel.c b/source4/libcli/smb2/cancel.c new file mode 100644 index 0000000000..65f02187c1 --- /dev/null +++ b/source4/libcli/smb2/cancel.c @@ -0,0 +1,77 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 client notify calls + + Copyright (C) Stefan Metzmacher 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" +#include "libcli/raw/libcliraw.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" + +/* + send a cancel request +*/ +NTSTATUS smb2_cancel(struct smb2_request *r) +{ + NTSTATUS status; + struct smb2_request *c; + uint32_t old_timeout; + uint64_t old_seqnum; + + /* + * if we don't get a pending id yet, we just + * mark the request for pending, so that we directly + * send the cancel after getting the pending id + */ + if (!r->cancel.can_cancel) { + r->cancel.do_cancel++; + return NT_STATUS_OK; + } + + /* we don't want a seqmun for a SMB2 Cancel */ + old_seqnum = r->transport->seqnum; + c = smb2_request_init(r->transport, SMB2_OP_CANCEL, 0x04, false, 0); + r->transport->seqnum = old_seqnum; + NT_STATUS_HAVE_NO_MEMORY(c); + c->seqnum = 0; + + SIVAL(c->out.hdr, SMB2_HDR_FLAGS, 0x00000002); + SSVAL(c->out.hdr, SMB2_HDR_CREDIT, 0x0030); + SIVAL(c->out.hdr, SMB2_HDR_PID, r->cancel.pending_id); + SBVAL(c->out.hdr, SMB2_HDR_MESSAGE_ID, c->seqnum); + if (r->session) { + SBVAL(c->out.hdr, SMB2_HDR_SESSION_ID, r->session->uid); + } + + SSVAL(c->out.body, 0x02, 0); + + old_timeout = c->transport->options.request_timeout; + c->transport->options.request_timeout = 0; + smb2_transport_send(c); + c->transport->options.request_timeout = old_timeout; + + if (c->state == SMB2_REQUEST_ERROR) { + status = c->status; + } else { + status = NT_STATUS_OK; + } + + talloc_free(c); + return status; +} diff --git a/source4/libcli/smb2/close.c b/source4/libcli/smb2/close.c new file mode 100644 index 0000000000..4e6f33095f --- /dev/null +++ b/source4/libcli/smb2/close.c @@ -0,0 +1,80 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 client close handling + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" + +/* + send a close request +*/ +struct smb2_request *smb2_close_send(struct smb2_tree *tree, struct smb2_close *io) +{ + struct smb2_request *req; + + req = smb2_request_init_tree(tree, SMB2_OP_CLOSE, 0x18, false, 0); + if (req == NULL) return NULL; + + SSVAL(req->out.body, 0x02, io->in.flags); + SIVAL(req->out.body, 0x04, 0); /* pad */ + smb2_push_handle(req->out.body+0x08, &io->in.file.handle); + + smb2_transport_send(req); + + return req; +} + + +/* + recv a close reply +*/ +NTSTATUS smb2_close_recv(struct smb2_request *req, struct smb2_close *io) +{ + if (!smb2_request_receive(req) || + !smb2_request_is_ok(req)) { + return smb2_request_destroy(req); + } + + SMB2_CHECK_PACKET_RECV(req, 0x3C, false); + + io->out.flags = SVAL(req->in.body, 0x02); + io->out._pad = IVAL(req->in.body, 0x04); + io->out.create_time = smbcli_pull_nttime(req->in.body, 0x08); + io->out.access_time = smbcli_pull_nttime(req->in.body, 0x10); + io->out.write_time = smbcli_pull_nttime(req->in.body, 0x18); + io->out.change_time = smbcli_pull_nttime(req->in.body, 0x20); + io->out.alloc_size = BVAL(req->in.body, 0x28); + io->out.size = BVAL(req->in.body, 0x30); + io->out.file_attr = IVAL(req->in.body, 0x38); + + return smb2_request_destroy(req); +} + +/* + sync close request +*/ +NTSTATUS smb2_close(struct smb2_tree *tree, struct smb2_close *io) +{ + struct smb2_request *req = smb2_close_send(tree, io); + return smb2_close_recv(req, io); +} diff --git a/source4/libcli/smb2/config.mk b/source4/libcli/smb2/config.mk new file mode 100644 index 0000000000..322bca1416 --- /dev/null +++ b/source4/libcli/smb2/config.mk @@ -0,0 +1,10 @@ +[SUBSYSTEM::LIBCLI_SMB2] +PUBLIC_DEPENDENCIES = LIBCLI_RAW LIBPACKET gensec + +LIBCLI_SMB2_OBJ_FILES = $(addprefix $(libclisrcdir)/smb2/, \ + transport.o request.o negprot.o session.o tcon.o \ + create.o close.o connect.o getinfo.o write.o read.o \ + setinfo.o find.o ioctl.o logoff.o tdis.o flush.o \ + lock.o notify.o cancel.o keepalive.o break.o util.o signing.o) + +$(eval $(call proto_header_template,$(libclisrcdir)/smb2/smb2_proto.h,$(LIBCLI_SMB2_OBJ_FILES:.o=.c))) diff --git a/source4/libcli/smb2/connect.c b/source4/libcli/smb2/connect.c new file mode 100644 index 0000000000..11bec42737 --- /dev/null +++ b/source4/libcli/smb2/connect.c @@ -0,0 +1,311 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 composite connection setup + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "libcli/composite/composite.h" +#include "libcli/resolve/resolve.h" +#include "param/param.h" + +struct smb2_connect_state { + struct cli_credentials *credentials; + struct resolve_context *resolve_ctx; + const char *host; + const char *share; + const char **ports; + const char *socket_options; + struct gensec_settings *gensec_settings; + struct smbcli_options options; + struct smb2_negprot negprot; + struct smb2_tree_connect tcon; + struct smb2_session *session; + struct smb2_tree *tree; +}; + +/* + continue after tcon reply +*/ +static void continue_tcon(struct smb2_request *req) +{ + struct composite_context *c = talloc_get_type(req->async.private_data, + struct composite_context); + struct smb2_connect_state *state = talloc_get_type(c->private_data, + struct smb2_connect_state); + + c->status = smb2_tree_connect_recv(req, &state->tcon); + if (!composite_is_ok(c)) return; + + state->tree->tid = state->tcon.out.tid; + + composite_done(c); +} + +/* + continue after a session setup +*/ +static void continue_session(struct composite_context *creq) +{ + struct composite_context *c = talloc_get_type(creq->async.private_data, + struct composite_context); + struct smb2_connect_state *state = talloc_get_type(c->private_data, + struct smb2_connect_state); + struct smb2_request *req; + + c->status = smb2_session_setup_spnego_recv(creq); + if (!composite_is_ok(c)) return; + + state->tree = smb2_tree_init(state->session, state, true); + if (composite_nomem(state->tree, c)) return; + + state->tcon.in.reserved = 0; + state->tcon.in.path = talloc_asprintf(state, "\\\\%s\\%s", + state->host, state->share); + if (composite_nomem(state->tcon.in.path, c)) return; + + req = smb2_tree_connect_send(state->tree, &state->tcon); + if (composite_nomem(req, c)) return; + + req->async.fn = continue_tcon; + req->async.private_data = c; +} + +/* + continue after negprot reply +*/ +static void continue_negprot(struct smb2_request *req) +{ + struct composite_context *c = talloc_get_type(req->async.private_data, + struct composite_context); + struct smb2_connect_state *state = talloc_get_type(c->private_data, + struct smb2_connect_state); + struct smb2_transport *transport = req->transport; + struct composite_context *creq; + + c->status = smb2_negprot_recv(req, c, &state->negprot); + if (!composite_is_ok(c)) return; + + transport->negotiate.system_time = state->negprot.out.system_time; + transport->negotiate.server_start_time = state->negprot.out.server_start_time; + transport->negotiate.security_mode = state->negprot.out.security_mode; + + switch (transport->options.signing) { + case SMB_SIGNING_OFF: + if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) { + composite_error(c, NT_STATUS_ACCESS_DENIED); + return; + } + transport->signing_required = false; + break; + case SMB_SIGNING_SUPPORTED: + if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) { + transport->signing_required = true; + } else { + transport->signing_required = false; + } + break; + case SMB_SIGNING_AUTO: + if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_ENABLED) { + transport->signing_required = true; + } else { + transport->signing_required = false; + } + break; + case SMB_SIGNING_REQUIRED: + if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_ENABLED) { + transport->signing_required = true; + } else { + composite_error(c, NT_STATUS_ACCESS_DENIED); + return; + } + break; + } + + state->session = smb2_session_init(transport, state->gensec_settings, state, true); + if (composite_nomem(state->session, c)) return; + + creq = smb2_session_setup_spnego_send(state->session, state->credentials); + + composite_continue(c, creq, continue_session, c); +} + +/* + continue after a socket connect completes +*/ +static void continue_socket(struct composite_context *creq) +{ + struct composite_context *c = talloc_get_type(creq->async.private_data, + struct composite_context); + struct smb2_connect_state *state = talloc_get_type(c->private_data, + struct smb2_connect_state); + struct smbcli_socket *sock; + struct smb2_transport *transport; + struct smb2_request *req; + uint16_t dialects[2]; + + c->status = smbcli_sock_connect_recv(creq, state, &sock); + if (!composite_is_ok(c)) return; + + transport = smb2_transport_init(sock, state, &state->options); + if (composite_nomem(transport, c)) return; + + ZERO_STRUCT(state->negprot); + state->negprot.in.dialect_count = 2; + switch (transport->options.signing) { + case SMB_SIGNING_OFF: + state->negprot.in.security_mode = 0; + break; + case SMB_SIGNING_SUPPORTED: + case SMB_SIGNING_AUTO: + state->negprot.in.security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED; + break; + case SMB_SIGNING_REQUIRED: + state->negprot.in.security_mode = + SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED; + break; + } + state->negprot.in.capabilities = 0; + unix_to_nt_time(&state->negprot.in.start_time, time(NULL)); + dialects[0] = SMB2_DIALECT_REVISION; + dialects[1] = 0; + state->negprot.in.dialects = dialects; + + req = smb2_negprot_send(transport, &state->negprot); + if (composite_nomem(req, c)) return; + + req->async.fn = continue_negprot; + req->async.private_data = c; +} + + +/* + continue after a resolve finishes +*/ +static void continue_resolve(struct composite_context *creq) +{ + struct composite_context *c = talloc_get_type(creq->async.private_data, + struct composite_context); + struct smb2_connect_state *state = talloc_get_type(c->private_data, + struct smb2_connect_state); + const char *addr; + const char **ports; + const char *default_ports[] = { "445", NULL }; + + c->status = resolve_name_recv(creq, state, &addr); + if (!composite_is_ok(c)) return; + + if (state->ports == NULL) { + ports = default_ports; + } else { + ports = state->ports; + } + + creq = smbcli_sock_connect_send(state, addr, ports, state->host, state->resolve_ctx, c->event_ctx, state->socket_options); + + composite_continue(c, creq, continue_socket, c); +} + +/* + a composite function that does a full negprot/sesssetup/tcon, returning + a connected smb2_tree + */ +struct composite_context *smb2_connect_send(TALLOC_CTX *mem_ctx, + const char *host, + const char **ports, + const char *share, + struct resolve_context *resolve_ctx, + struct cli_credentials *credentials, + struct tevent_context *ev, + struct smbcli_options *options, + const char *socket_options, + struct gensec_settings *gensec_settings) +{ + struct composite_context *c; + struct smb2_connect_state *state; + struct nbt_name name; + struct composite_context *creq; + + c = composite_create(mem_ctx, ev); + if (c == NULL) return NULL; + + state = talloc(c, struct smb2_connect_state); + if (composite_nomem(state, c)) return c; + c->private_data = state; + + state->credentials = credentials; + state->options = *options; + state->host = talloc_strdup(c, host); + if (composite_nomem(state->host, c)) return c; + state->ports = talloc_reference(state, ports); + state->share = talloc_strdup(c, share); + if (composite_nomem(state->share, c)) return c; + state->resolve_ctx = talloc_reference(state, resolve_ctx); + state->socket_options = talloc_reference(state, socket_options); + state->gensec_settings = talloc_reference(state, gensec_settings); + + ZERO_STRUCT(name); + name.name = host; + + creq = resolve_name_send(resolve_ctx, &name, c->event_ctx); + composite_continue(c, creq, continue_resolve, c); + return c; +} + +/* + receive a connect reply +*/ +NTSTATUS smb2_connect_recv(struct composite_context *c, TALLOC_CTX *mem_ctx, + struct smb2_tree **tree) +{ + NTSTATUS status; + struct smb2_connect_state *state = talloc_get_type(c->private_data, + struct smb2_connect_state); + status = composite_wait(c); + if (NT_STATUS_IS_OK(status)) { + *tree = talloc_steal(mem_ctx, state->tree); + } + talloc_free(c); + return status; +} + +/* + sync version of smb2_connect +*/ +NTSTATUS smb2_connect(TALLOC_CTX *mem_ctx, + const char *host, const char **ports, + const char *share, + struct resolve_context *resolve_ctx, + struct cli_credentials *credentials, + struct smb2_tree **tree, + struct tevent_context *ev, + struct smbcli_options *options, + const char *socket_options, + struct gensec_settings *gensec_settings) +{ + struct composite_context *c = smb2_connect_send(mem_ctx, host, ports, + share, resolve_ctx, + credentials, ev, options, + socket_options, + gensec_settings); + return smb2_connect_recv(c, mem_ctx, tree); +} diff --git a/source4/libcli/smb2/create.c b/source4/libcli/smb2/create.c new file mode 100644 index 0000000000..8a40e56a00 --- /dev/null +++ b/source4/libcli/smb2/create.c @@ -0,0 +1,419 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 client tree handling + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "librpc/gen_ndr/ndr_security.h" + + +/* + parse a set of SMB2 create blobs +*/ +NTSTATUS smb2_create_blob_parse(TALLOC_CTX *mem_ctx, const DATA_BLOB buffer, + struct smb2_create_blobs *blobs) +{ + const uint8_t *data = buffer.data; + uint32_t remaining = buffer.length; + + while (remaining > 0) { + uint32_t next; + uint32_t name_offset, name_length; + uint32_t reserved, data_offset; + uint32_t data_length; + char *tag; + DATA_BLOB b; + NTSTATUS status; + + if (remaining < 16) { + return NT_STATUS_INVALID_PARAMETER; + } + next = IVAL(data, 0); + name_offset = SVAL(data, 4); + name_length = SVAL(data, 6); + reserved = SVAL(data, 8); + data_offset = SVAL(data, 10); + data_length = IVAL(data, 12); + + if ((next & 0x7) != 0 || + next > remaining || + name_offset < 16 || + name_offset > remaining || + name_length != 4 || /* windows enforces this */ + name_offset + name_length > remaining || + data_offset < name_offset + name_length || + data_offset > remaining || + data_offset + (uint64_t)data_length > remaining) { + return NT_STATUS_INVALID_PARAMETER; + } + + tag = talloc_strndup(mem_ctx, (const char *)data + name_offset, name_length); + if (tag == NULL) { + return NT_STATUS_NO_MEMORY; + } + + b = data_blob_const(data+data_offset, data_length); + status = smb2_create_blob_add(mem_ctx, blobs, tag, b); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + talloc_free(tag); + + if (next == 0) break; + + remaining -= next; + data += next; + + if (remaining < 16) { + return NT_STATUS_INVALID_PARAMETER; + } + } + + return NT_STATUS_OK; +} + + +/* + add a blob to a smb2_create attribute blob +*/ +static NTSTATUS smb2_create_blob_push_one(TALLOC_CTX *mem_ctx, DATA_BLOB *buffer, + const struct smb2_create_blob *blob, + bool last) +{ + uint32_t ofs = buffer->length; + size_t tag_length = strlen(blob->tag); + uint8_t pad = smb2_padding_size(blob->data.length+tag_length, 4); + + if (!data_blob_realloc(mem_ctx, buffer, + buffer->length + 0x14 + tag_length + blob->data.length + pad)) + return NT_STATUS_NO_MEMORY; + + if (last) { + SIVAL(buffer->data, ofs+0x00, 0); + } else { + SIVAL(buffer->data, ofs+0x00, 0x14 + tag_length + blob->data.length + pad); + } + SSVAL(buffer->data, ofs+0x04, 0x10); /* offset of tag */ + SIVAL(buffer->data, ofs+0x06, tag_length); /* tag length */ + SSVAL(buffer->data, ofs+0x0A, 0x14 + tag_length); /* offset of data */ + SIVAL(buffer->data, ofs+0x0C, blob->data.length); + memcpy(buffer->data+ofs+0x10, blob->tag, tag_length); + SIVAL(buffer->data, ofs+0x10+tag_length, 0); /* pad? */ + memcpy(buffer->data+ofs+0x14+tag_length, blob->data.data, blob->data.length); + memset(buffer->data+ofs+0x14+tag_length+blob->data.length, 0, pad); + + return NT_STATUS_OK; +} + + +/* + create a buffer of a set of create blobs +*/ +NTSTATUS smb2_create_blob_push(TALLOC_CTX *mem_ctx, DATA_BLOB *buffer, + const struct smb2_create_blobs blobs) +{ + int i; + NTSTATUS status; + + *buffer = data_blob(NULL, 0); + for (i=0; i < blobs.num_blobs; i++) { + bool last = false; + const struct smb2_create_blob *c; + + if ((i + 1) == blobs.num_blobs) { + last = true; + } + + c = &blobs.blobs[i]; + status = smb2_create_blob_push_one(mem_ctx, buffer, c, last); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + return NT_STATUS_OK; +} + + +NTSTATUS smb2_create_blob_add(TALLOC_CTX *mem_ctx, struct smb2_create_blobs *b, + const char *tag, DATA_BLOB data) +{ + struct smb2_create_blob *array; + + array = talloc_realloc(mem_ctx, b->blobs, + struct smb2_create_blob, + b->num_blobs + 1); + NT_STATUS_HAVE_NO_MEMORY(array); + b->blobs = array; + + b->blobs[b->num_blobs].tag = talloc_strdup(b->blobs, tag); + NT_STATUS_HAVE_NO_MEMORY(b->blobs[b->num_blobs].tag); + + if (data.data) { + b->blobs[b->num_blobs].data = data_blob_talloc(b->blobs, + data.data, + data.length); + NT_STATUS_HAVE_NO_MEMORY(b->blobs[b->num_blobs].data.data); + } else { + b->blobs[b->num_blobs].data = data_blob(NULL, 0); + } + + b->num_blobs += 1; + + return NT_STATUS_OK; +} + +/* + send a create request +*/ +struct smb2_request *smb2_create_send(struct smb2_tree *tree, struct smb2_create *io) +{ + struct smb2_request *req; + NTSTATUS status; + DATA_BLOB blob; + struct smb2_create_blobs blobs; + int i; + + ZERO_STRUCT(blobs); + + req = smb2_request_init_tree(tree, SMB2_OP_CREATE, 0x38, true, 0); + if (req == NULL) return NULL; + + SCVAL(req->out.body, 0x02, io->in.security_flags); + SCVAL(req->out.body, 0x03, io->in.oplock_level); + SIVAL(req->out.body, 0x04, io->in.impersonation_level); + SBVAL(req->out.body, 0x08, io->in.create_flags); + SBVAL(req->out.body, 0x10, io->in.reserved); + SIVAL(req->out.body, 0x18, io->in.desired_access); + SIVAL(req->out.body, 0x1C, io->in.file_attributes); + SIVAL(req->out.body, 0x20, io->in.share_access); + SIVAL(req->out.body, 0x24, io->in.create_disposition); + SIVAL(req->out.body, 0x28, io->in.create_options); + + status = smb2_push_o16s16_string(&req->out, 0x2C, io->in.fname); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } + + /* now add all the optional blobs */ + if (io->in.eas.num_eas != 0) { + DATA_BLOB b = data_blob_talloc(req, NULL, + ea_list_size_chained(io->in.eas.num_eas, io->in.eas.eas, 4)); + ea_put_list_chained(b.data, io->in.eas.num_eas, io->in.eas.eas, 4); + status = smb2_create_blob_add(req, &blobs, + SMB2_CREATE_TAG_EXTA, b); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } + data_blob_free(&b); + } + + /* an empty MxAc tag seems to be used to ask the server to + return the maximum access mask allowed on the file */ + if (io->in.query_maximal_access) { + /* TODO: MS-SMB2 2.2.13.2.5 says this can contain a timestamp? What to do + with that if it doesn't match? */ + status = smb2_create_blob_add(req, &blobs, + SMB2_CREATE_TAG_MXAC, data_blob(NULL, 0)); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } + } + + if (io->in.alloc_size != 0) { + uint8_t data[8]; + SBVAL(data, 0, io->in.alloc_size); + status = smb2_create_blob_add(req, &blobs, + SMB2_CREATE_TAG_ALSI, data_blob_const(data, 8)); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } + } + + if (io->in.durable_open) { + status = smb2_create_blob_add(req, &blobs, + SMB2_CREATE_TAG_DHNQ, data_blob_talloc_zero(req, 16)); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } + } + + if (io->in.durable_handle) { + uint8_t data[16]; + smb2_push_handle(data, io->in.durable_handle); + status = smb2_create_blob_add(req, &blobs, + SMB2_CREATE_TAG_DHNC, data_blob_const(data, 16)); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } + } + + if (io->in.timewarp) { + uint8_t data[8]; + SBVAL(data, 0, io->in.timewarp); + status = smb2_create_blob_add(req, &blobs, + SMB2_CREATE_TAG_TWRP, data_blob_const(data, 8)); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } + } + + if (io->in.sec_desc) { + enum ndr_err_code ndr_err; + DATA_BLOB sd_blob; + ndr_err = ndr_push_struct_blob(&sd_blob, req, NULL, + io->in.sec_desc, + (ndr_push_flags_fn_t)ndr_push_security_descriptor); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + talloc_free(req); + return NULL; + } + status = smb2_create_blob_add(req, &blobs, + SMB2_CREATE_TAG_SECD, sd_blob); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } + } + + if (io->in.query_on_disk_id) { + status = smb2_create_blob_add(req, &blobs, + SMB2_CREATE_TAG_QFID, data_blob(NULL, 0)); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } + } + + /* and any custom blobs */ + for (i=0;i<io->in.blobs.num_blobs;i++) { + status = smb2_create_blob_add(req, &blobs, + io->in.blobs.blobs[i].tag, + io->in.blobs.blobs[i].data); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } + } + + + status = smb2_create_blob_push(req, &blob, blobs); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } + + status = smb2_push_o32s32_blob(&req->out, 0x30, blob); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } + + data_blob_free(&blob); + + smb2_transport_send(req); + + return req; +} + + +/* + recv a create reply +*/ +NTSTATUS smb2_create_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, struct smb2_create *io) +{ + NTSTATUS status; + DATA_BLOB blob; + int i; + + if (!smb2_request_receive(req) || + !smb2_request_is_ok(req)) { + return smb2_request_destroy(req); + } + + SMB2_CHECK_PACKET_RECV(req, 0x58, true); + ZERO_STRUCT(io->out); + io->out.oplock_level = CVAL(req->in.body, 0x02); + io->out.reserved = CVAL(req->in.body, 0x03); + io->out.create_action = IVAL(req->in.body, 0x04); + io->out.create_time = smbcli_pull_nttime(req->in.body, 0x08); + io->out.access_time = smbcli_pull_nttime(req->in.body, 0x10); + io->out.write_time = smbcli_pull_nttime(req->in.body, 0x18); + io->out.change_time = smbcli_pull_nttime(req->in.body, 0x20); + io->out.alloc_size = BVAL(req->in.body, 0x28); + io->out.size = BVAL(req->in.body, 0x30); + io->out.file_attr = IVAL(req->in.body, 0x38); + io->out.reserved2 = IVAL(req->in.body, 0x3C); + smb2_pull_handle(req->in.body+0x40, &io->out.file.handle); + status = smb2_pull_o32s32_blob(&req->in, mem_ctx, req->in.body+0x50, &blob); + if (!NT_STATUS_IS_OK(status)) { + smb2_request_destroy(req); + return status; + } + + status = smb2_create_blob_parse(mem_ctx, blob, &io->out.blobs); + if (!NT_STATUS_IS_OK(status)) { + smb2_request_destroy(req); + return status; + } + + /* pull out the parsed blobs */ + for (i=0;i<io->out.blobs.num_blobs;i++) { + if (strcmp(io->out.blobs.blobs[i].tag, SMB2_CREATE_TAG_MXAC) == 0) { + /* TODO: this also contains a status field in + first 4 bytes */ + if (io->out.blobs.blobs[i].data.length != 8) { + smb2_request_destroy(req); + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + io->out.maximal_access = IVAL(io->out.blobs.blobs[i].data.data, 4); + } + if (strcmp(io->out.blobs.blobs[i].tag, SMB2_CREATE_TAG_QFID) == 0) { + if (io->out.blobs.blobs[i].data.length != 32) { + smb2_request_destroy(req); + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + memcpy(io->out.on_disk_id, io->out.blobs.blobs[i].data.data, 32); + } + } + + data_blob_free(&blob); + + return smb2_request_destroy(req); +} + +/* + sync create request +*/ +NTSTATUS smb2_create(struct smb2_tree *tree, TALLOC_CTX *mem_ctx, struct smb2_create *io) +{ + struct smb2_request *req = smb2_create_send(tree, io); + return smb2_create_recv(req, mem_ctx, io); +} diff --git a/source4/libcli/smb2/find.c b/source4/libcli/smb2/find.c new file mode 100644 index 0000000000..8ebfd81bcd --- /dev/null +++ b/source4/libcli/smb2/find.c @@ -0,0 +1,180 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 client find calls + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" + +/* + send a find request +*/ +struct smb2_request *smb2_find_send(struct smb2_tree *tree, struct smb2_find *io) +{ + struct smb2_request *req; + NTSTATUS status; + + req = smb2_request_init_tree(tree, SMB2_OP_FIND, 0x20, true, 0); + if (req == NULL) return NULL; + + SCVAL(req->out.body, 0x02, io->in.level); + SCVAL(req->out.body, 0x03, io->in.continue_flags); + SIVAL(req->out.body, 0x04, io->in.file_index); + smb2_push_handle(req->out.body+0x08, &io->in.file.handle); + + status = smb2_push_o16s16_string(&req->out, 0x18, io->in.pattern); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } + + SIVAL(req->out.body, 0x1C, io->in.max_response_size); + + smb2_transport_send(req); + + return req; +} + + +/* + recv a find reply +*/ +NTSTATUS smb2_find_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, + struct smb2_find *io) +{ + NTSTATUS status; + + if (!smb2_request_receive(req) || + smb2_request_is_error(req)) { + return smb2_request_destroy(req); + } + + SMB2_CHECK_PACKET_RECV(req, 0x08, true); + + status = smb2_pull_o16s32_blob(&req->in, mem_ctx, + req->in.body+0x02, &io->out.blob); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return smb2_request_destroy(req); +} + +/* + sync find request +*/ +NTSTATUS smb2_find(struct smb2_tree *tree, TALLOC_CTX *mem_ctx, + struct smb2_find *io) +{ + struct smb2_request *req = smb2_find_send(tree, io); + return smb2_find_recv(req, mem_ctx, io); +} + + +/* + a varient of smb2_find_recv that parses the resulting blob into + smb_search_data structures +*/ +NTSTATUS smb2_find_level_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, + uint8_t level, uint_t *count, + union smb_search_data **io) +{ + struct smb2_find f; + NTSTATUS status; + DATA_BLOB b; + enum smb_search_data_level smb_level; + uint_t next_ofs=0; + + switch (level) { + case SMB2_FIND_DIRECTORY_INFO: + smb_level = RAW_SEARCH_DATA_DIRECTORY_INFO; + break; + case SMB2_FIND_FULL_DIRECTORY_INFO: + smb_level = RAW_SEARCH_DATA_FULL_DIRECTORY_INFO; + break; + case SMB2_FIND_BOTH_DIRECTORY_INFO: + smb_level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO; + break; + case SMB2_FIND_NAME_INFO: + smb_level = RAW_SEARCH_DATA_NAME_INFO; + break; + case SMB2_FIND_ID_FULL_DIRECTORY_INFO: + smb_level = RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO; + break; + case SMB2_FIND_ID_BOTH_DIRECTORY_INFO: + smb_level = RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO; + break; + default: + return NT_STATUS_INVALID_INFO_CLASS; + } + + status = smb2_find_recv(req, mem_ctx, &f); + NT_STATUS_NOT_OK_RETURN(status); + + b = f.out.blob; + *io = NULL; + *count = 0; + + do { + union smb_search_data *io2; + + io2 = talloc_realloc(mem_ctx, *io, union smb_search_data, (*count)+1); + if (io2 == NULL) { + data_blob_free(&f.out.blob); + talloc_free(*io); + return NT_STATUS_NO_MEMORY; + } + *io = io2; + + status = smb_raw_search_common(*io, smb_level, &b, (*io) + (*count), + &next_ofs, STR_UNICODE); + + if (NT_STATUS_IS_OK(status) && + next_ofs >= b.length) { + data_blob_free(&f.out.blob); + talloc_free(*io); + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + + (*count)++; + + b = data_blob_const(b.data+next_ofs, b.length - next_ofs); + } while (NT_STATUS_IS_OK(status) && next_ofs != 0); + + data_blob_free(&f.out.blob); + + return NT_STATUS_OK; +} + +/* + a varient of smb2_find that parses the resulting blob into + smb_search_data structures +*/ +NTSTATUS smb2_find_level(struct smb2_tree *tree, TALLOC_CTX *mem_ctx, + struct smb2_find *f, + uint_t *count, union smb_search_data **io) +{ + struct smb2_request *req; + + req = smb2_find_send(tree, f); + return smb2_find_level_recv(req, mem_ctx, f->in.level, count, io); +} diff --git a/source4/libcli/smb2/flush.c b/source4/libcli/smb2/flush.c new file mode 100644 index 0000000000..577d1ba1ba --- /dev/null +++ b/source4/libcli/smb2/flush.c @@ -0,0 +1,70 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 client flush handling + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" + +/* + send a flush request +*/ +struct smb2_request *smb2_flush_send(struct smb2_tree *tree, struct smb2_flush *io) +{ + struct smb2_request *req; + + req = smb2_request_init_tree(tree, SMB2_OP_FLUSH, 0x18, false, 0); + if (req == NULL) return NULL; + + SSVAL(req->out.body, 0x02, io->in.reserved1); + SIVAL(req->out.body, 0x04, io->in.reserved2); + smb2_push_handle(req->out.body+0x08, &io->in.file.handle); + + smb2_transport_send(req); + + return req; +} + + +/* + recv a flush reply +*/ +NTSTATUS smb2_flush_recv(struct smb2_request *req, struct smb2_flush *io) +{ + if (!smb2_request_receive(req) || + !smb2_request_is_ok(req)) { + return smb2_request_destroy(req); + } + + SMB2_CHECK_PACKET_RECV(req, 0x04, false); + + io->out.reserved = SVAL(req->in.body, 0x02); + + return smb2_request_destroy(req); +} + +/* + sync flush request +*/ +NTSTATUS smb2_flush(struct smb2_tree *tree, struct smb2_flush *io) +{ + struct smb2_request *req = smb2_flush_send(tree, io); + return smb2_flush_recv(req, io); +} diff --git a/source4/libcli/smb2/getinfo.c b/source4/libcli/smb2/getinfo.c new file mode 100644 index 0000000000..14d911683e --- /dev/null +++ b/source4/libcli/smb2/getinfo.c @@ -0,0 +1,220 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 client getinfo calls + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" + +/* + send a getinfo request +*/ +struct smb2_request *smb2_getinfo_send(struct smb2_tree *tree, struct smb2_getinfo *io) +{ + struct smb2_request *req; + NTSTATUS status; + + req = smb2_request_init_tree(tree, SMB2_OP_GETINFO, 0x28, true, + io->in.blob.length); + if (req == NULL) return NULL; + + SCVAL(req->out.body, 0x02, io->in.info_type); + SCVAL(req->out.body, 0x03, io->in.info_class); + SIVAL(req->out.body, 0x04, io->in.output_buffer_length); + SIVAL(req->out.body, 0x0C, io->in.reserved); + SIVAL(req->out.body, 0x08, io->in.input_buffer_length); + SIVAL(req->out.body, 0x10, io->in.additional_information); + SIVAL(req->out.body, 0x14, io->in.getinfo_flags); + smb2_push_handle(req->out.body+0x18, &io->in.file.handle); + + /* this blob is used for quota queries */ + status = smb2_push_o32s32_blob(&req->out, 0x08, io->in.blob); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } + smb2_transport_send(req); + + return req; +} + + +/* + recv a getinfo reply +*/ +NTSTATUS smb2_getinfo_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, + struct smb2_getinfo *io) +{ + NTSTATUS status; + + if (!smb2_request_receive(req) || + smb2_request_is_error(req)) { + return smb2_request_destroy(req); + } + + SMB2_CHECK_PACKET_RECV(req, 0x08, true); + + status = smb2_pull_o16s16_blob(&req->in, mem_ctx, req->in.body+0x02, &io->out.blob); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return smb2_request_destroy(req); +} + +/* + sync getinfo request +*/ +NTSTATUS smb2_getinfo(struct smb2_tree *tree, TALLOC_CTX *mem_ctx, + struct smb2_getinfo *io) +{ + struct smb2_request *req = smb2_getinfo_send(tree, io); + return smb2_getinfo_recv(req, mem_ctx, io); +} + + +/* + map a generic info level to a SMB2 info level +*/ +uint16_t smb2_getinfo_map_level(uint16_t level, uint8_t info_class) +{ + if (info_class == SMB2_GETINFO_FILE && + level == RAW_FILEINFO_SEC_DESC) { + return SMB2_GETINFO_SECURITY; + } + if ((level & 0xFF) == info_class) { + return level; + } else if (level > 1000) { + return ((level-1000)<<8) | info_class; + } + DEBUG(0,("Unable to map SMB2 info level 0x%04x of class %d\n", + level, info_class)); + return 0; +} + +/* + level specific getinfo call - async send +*/ +struct smb2_request *smb2_getinfo_file_send(struct smb2_tree *tree, union smb_fileinfo *io) +{ + struct smb2_getinfo b; + uint16_t smb2_level = smb2_getinfo_map_level(io->generic.level, SMB2_GETINFO_FILE); + + if (smb2_level == 0) { + return NULL; + } + + ZERO_STRUCT(b); + b.in.info_type = smb2_level & 0xFF; + b.in.info_class = smb2_level >> 8; + b.in.output_buffer_length = 0x10000; + b.in.input_buffer_length = 0; + b.in.file.handle = io->generic.in.file.handle; + + if (io->generic.level == RAW_FILEINFO_SEC_DESC) { + b.in.additional_information = io->query_secdesc.in.secinfo_flags; + } + if (io->generic.level == RAW_FILEINFO_SMB2_ALL_EAS) { + b.in.getinfo_flags = io->all_eas.in.continue_flags; + } + + return smb2_getinfo_send(tree, &b); +} + +/* + recv a getinfo reply and parse the level info +*/ +NTSTATUS smb2_getinfo_file_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, + union smb_fileinfo *io) +{ + struct smb2_getinfo b; + NTSTATUS status; + + status = smb2_getinfo_recv(req, mem_ctx, &b); + NT_STATUS_NOT_OK_RETURN(status); + + status = smb_raw_fileinfo_passthru_parse(&b.out.blob, mem_ctx, io->generic.level, io); + data_blob_free(&b.out.blob); + + return status; +} + +/* + level specific getinfo call +*/ +NTSTATUS smb2_getinfo_file(struct smb2_tree *tree, TALLOC_CTX *mem_ctx, + union smb_fileinfo *io) +{ + struct smb2_request *req = smb2_getinfo_file_send(tree, io); + return smb2_getinfo_file_recv(req, mem_ctx, io); +} + + +/* + level specific getinfo call - async send +*/ +struct smb2_request *smb2_getinfo_fs_send(struct smb2_tree *tree, union smb_fsinfo *io) +{ + struct smb2_getinfo b; + uint16_t smb2_level = smb2_getinfo_map_level(io->generic.level, SMB2_GETINFO_FS); + + if (smb2_level == 0) { + return NULL; + } + + ZERO_STRUCT(b); + b.in.output_buffer_length = 0x10000; + b.in.file.handle = io->generic.handle; + b.in.info_type = smb2_level & 0xFF; + b.in.info_class = smb2_level >> 8; + + return smb2_getinfo_send(tree, &b); +} + +/* + recv a getinfo reply and parse the level info +*/ +NTSTATUS smb2_getinfo_fs_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, + union smb_fsinfo *io) +{ + struct smb2_getinfo b; + NTSTATUS status; + + status = smb2_getinfo_recv(req, mem_ctx, &b); + NT_STATUS_NOT_OK_RETURN(status); + + status = smb_raw_fsinfo_passthru_parse(b.out.blob, mem_ctx, io->generic.level, io); + data_blob_free(&b.out.blob); + + return status; +} + +/* + level specific getinfo call +*/ +NTSTATUS smb2_getinfo_fs(struct smb2_tree *tree, TALLOC_CTX *mem_ctx, + union smb_fsinfo *io) +{ + struct smb2_request *req = smb2_getinfo_fs_send(tree, io); + return smb2_getinfo_fs_recv(req, mem_ctx, io); +} + diff --git a/source4/libcli/smb2/ioctl.c b/source4/libcli/smb2/ioctl.c new file mode 100644 index 0000000000..d81bca517f --- /dev/null +++ b/source4/libcli/smb2/ioctl.c @@ -0,0 +1,109 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 client ioctl call + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" + +/* + send a ioctl request +*/ +struct smb2_request *smb2_ioctl_send(struct smb2_tree *tree, struct smb2_ioctl *io) +{ + NTSTATUS status; + struct smb2_request *req; + + req = smb2_request_init_tree(tree, SMB2_OP_IOCTL, 0x38, true, + io->in.in.length+io->in.out.length); + if (req == NULL) return NULL; + + SSVAL(req->out.body, 0x02, 0); /* pad */ + SIVAL(req->out.body, 0x04, io->in.function); + smb2_push_handle(req->out.body+0x08, &io->in.file.handle); + + status = smb2_push_o32s32_blob(&req->out, 0x18, io->in.out); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } + + SIVAL(req->out.body, 0x20, io->in.unknown2); + + status = smb2_push_o32s32_blob(&req->out, 0x24, io->in.in); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } + + SIVAL(req->out.body, 0x2C, io->in.max_response_size); + SBVAL(req->out.body, 0x30, io->in.flags); + + smb2_transport_send(req); + + return req; +} + + +/* + recv a ioctl reply +*/ +NTSTATUS smb2_ioctl_recv(struct smb2_request *req, + TALLOC_CTX *mem_ctx, struct smb2_ioctl *io) +{ + NTSTATUS status; + + if (!smb2_request_receive(req) || + smb2_request_is_error(req)) { + return smb2_request_destroy(req); + } + + SMB2_CHECK_PACKET_RECV(req, 0x30, true); + + io->out._pad = SVAL(req->in.body, 0x02); + io->out.function = IVAL(req->in.body, 0x04); + smb2_pull_handle(req->in.body+0x08, &io->out.file.handle); + + status = smb2_pull_o32s32_blob(&req->in, mem_ctx, req->in.body+0x18, &io->out.in); + if (!NT_STATUS_IS_OK(status)) { + smb2_request_destroy(req); + return status; + } + + status = smb2_pull_o32s32_blob(&req->in, mem_ctx, req->in.body+0x20, &io->out.out); + if (!NT_STATUS_IS_OK(status)) { + smb2_request_destroy(req); + return status; + } + + io->out.unknown2 = IVAL(req->in.body, 0x28); + io->out.unknown3 = IVAL(req->in.body, 0x2C); + + return smb2_request_destroy(req); +} + +/* + sync ioctl request +*/ +NTSTATUS smb2_ioctl(struct smb2_tree *tree, TALLOC_CTX *mem_ctx, struct smb2_ioctl *io) +{ + struct smb2_request *req = smb2_ioctl_send(tree, io); + return smb2_ioctl_recv(req, mem_ctx, io); +} diff --git a/source4/libcli/smb2/keepalive.c b/source4/libcli/smb2/keepalive.c new file mode 100644 index 0000000000..402b063e81 --- /dev/null +++ b/source4/libcli/smb2/keepalive.c @@ -0,0 +1,65 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 client keepalive handling + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" + +/* + send a keepalive request +*/ +struct smb2_request *smb2_keepalive_send(struct smb2_transport *transport) +{ + struct smb2_request *req; + + req = smb2_request_init(transport, SMB2_OP_KEEPALIVE, 0x04, false, 0); + if (req == NULL) return NULL; + + SSVAL(req->out.body, 0x02, 0); + + smb2_transport_send(req); + + return req; +} + + +/* + recv a keepalive reply +*/ +NTSTATUS smb2_keepalive_recv(struct smb2_request *req) +{ + if (!smb2_request_receive(req) || + !smb2_request_is_ok(req)) { + return smb2_request_destroy(req); + } + + SMB2_CHECK_PACKET_RECV(req, 0x04, false); + return smb2_request_destroy(req); +} + +/* + sync keepalive request +*/ +NTSTATUS smb2_keepalive(struct smb2_transport *transport) +{ + struct smb2_request *req = smb2_keepalive_send(transport); + return smb2_keepalive_recv(req); +} diff --git a/source4/libcli/smb2/lock.c b/source4/libcli/smb2/lock.c new file mode 100644 index 0000000000..62c6e5dba7 --- /dev/null +++ b/source4/libcli/smb2/lock.c @@ -0,0 +1,82 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 client lock handling + + Copyright (C) Stefan Metzmacher 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" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" + +/* + send a lock request +*/ +struct smb2_request *smb2_lock_send(struct smb2_tree *tree, struct smb2_lock *io) +{ + struct smb2_request *req; + int i; + + req = smb2_request_init_tree(tree, SMB2_OP_LOCK, + 24 + io->in.lock_count*24, false, 0); + if (req == NULL) return NULL; + + /* this is quite bizarre - the spec says we must lie about the length! */ + SSVAL(req->out.body, 0, 0x30); + + SSVAL(req->out.body, 0x02, io->in.lock_count); + SIVAL(req->out.body, 0x04, io->in.reserved); + smb2_push_handle(req->out.body+0x08, &io->in.file.handle); + + for (i=0;i<io->in.lock_count;i++) { + SBVAL(req->out.body, 0x18 + i*24, io->in.locks[i].offset); + SBVAL(req->out.body, 0x20 + i*24, io->in.locks[i].length); + SIVAL(req->out.body, 0x28 + i*24, io->in.locks[i].flags); + SIVAL(req->out.body, 0x2C + i*24, io->in.locks[i].reserved); + } + + smb2_transport_send(req); + + return req; +} + + +/* + recv a lock reply +*/ +NTSTATUS smb2_lock_recv(struct smb2_request *req, struct smb2_lock *io) +{ + if (!smb2_request_receive(req) || + smb2_request_is_error(req)) { + return smb2_request_destroy(req); + } + + SMB2_CHECK_PACKET_RECV(req, 0x04, false); + + io->out.reserved = SVAL(req->in.body, 0x02); + + return smb2_request_destroy(req); +} + +/* + sync lock request +*/ +NTSTATUS smb2_lock(struct smb2_tree *tree, struct smb2_lock *io) +{ + struct smb2_request *req = smb2_lock_send(tree, io); + return smb2_lock_recv(req, io); +} diff --git a/source4/libcli/smb2/logoff.c b/source4/libcli/smb2/logoff.c new file mode 100644 index 0000000000..e3f83f27d8 --- /dev/null +++ b/source4/libcli/smb2/logoff.c @@ -0,0 +1,69 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 client logoff handling + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" + +/* + send a logoff request +*/ +struct smb2_request *smb2_logoff_send(struct smb2_session *session) +{ + struct smb2_request *req; + + req = smb2_request_init(session->transport, SMB2_OP_LOGOFF, 0x04, false, 0); + if (req == NULL) return NULL; + + req->session = session; + + SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, session->uid); + + SSVAL(req->out.body, 0x02, 0); + + smb2_transport_send(req); + + return req; +} + + +/* + recv a logoff reply +*/ +NTSTATUS smb2_logoff_recv(struct smb2_request *req) +{ + if (!smb2_request_receive(req) || + !smb2_request_is_ok(req)) { + return smb2_request_destroy(req); + } + + SMB2_CHECK_PACKET_RECV(req, 0x04, false); + return smb2_request_destroy(req); +} + +/* + sync logoff request +*/ +NTSTATUS smb2_logoff(struct smb2_session *session) +{ + struct smb2_request *req = smb2_logoff_send(session); + return smb2_logoff_recv(req); +} diff --git a/source4/libcli/smb2/negprot.c b/source4/libcli/smb2/negprot.c new file mode 100644 index 0000000000..c1f0cf0b24 --- /dev/null +++ b/source4/libcli/smb2/negprot.c @@ -0,0 +1,113 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 client negprot handling + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "librpc/ndr/libndr.h" + +/* + send a negprot request +*/ +struct smb2_request *smb2_negprot_send(struct smb2_transport *transport, + struct smb2_negprot *io) +{ + struct smb2_request *req; + uint16_t size = 0x24 + io->in.dialect_count*2; + enum ndr_err_code ndr_err; + int i; + + req = smb2_request_init(transport, SMB2_OP_NEGPROT, size, false, 0); + if (req == NULL) return NULL; + + + SSVAL(req->out.body, 0x00, 0x24); + SSVAL(req->out.body, 0x02, io->in.dialect_count); + SSVAL(req->out.body, 0x04, io->in.security_mode); + SSVAL(req->out.body, 0x06, io->in.reserved); + SIVAL(req->out.body, 0x08, io->in.capabilities); + ndr_err = smbcli_push_guid(req->out.body, 0x0C, &io->in.client_guid); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + talloc_free(req); + return NULL; + } + smbcli_push_nttime(req->out.body, 0x1C, io->in.start_time); + for (i=0;i<io->in.dialect_count;i++) { + SSVAL(req->out.body, 0x24 + i*2, io->in.dialects[i]); + } + + smb2_transport_send(req); + + return req; +} + +/* + recv a negprot reply +*/ +NTSTATUS smb2_negprot_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, + struct smb2_negprot *io) +{ + NTSTATUS status; + enum ndr_err_code ndr_err; + + if (!smb2_request_receive(req) || + smb2_request_is_error(req)) { + return smb2_request_destroy(req); + } + + SMB2_CHECK_PACKET_RECV(req, 0x40, true); + + io->out.security_mode = SVAL(req->in.body, 0x02); + io->out.dialect_revision = SVAL(req->in.body, 0x04); + io->out.reserved = SVAL(req->in.body, 0x06); + ndr_err = smbcli_pull_guid(req->in.body, 0x08, &io->in.client_guid); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + smb2_request_destroy(req); + return NT_STATUS_INTERNAL_ERROR; + } + io->out.capabilities = IVAL(req->in.body, 0x18); + io->out.max_transact_size = IVAL(req->in.body, 0x1C); + io->out.max_read_size = IVAL(req->in.body, 0x20); + io->out.max_write_size = IVAL(req->in.body, 0x24); + io->out.system_time = smbcli_pull_nttime(req->in.body, 0x28); + io->out.server_start_time = smbcli_pull_nttime(req->in.body, 0x30); + io->out.reserved2 = IVAL(req->in.body, 0x3C); + + status = smb2_pull_o16s16_blob(&req->in, mem_ctx, req->in.body+0x38, &io->out.secblob); + if (!NT_STATUS_IS_OK(status)) { + smb2_request_destroy(req); + return status; + } + + return smb2_request_destroy(req); +} + +/* + sync negprot request +*/ +NTSTATUS smb2_negprot(struct smb2_transport *transport, + TALLOC_CTX *mem_ctx, struct smb2_negprot *io) +{ + struct smb2_request *req = smb2_negprot_send(transport, io); + return smb2_negprot_recv(req, mem_ctx, io); +} diff --git a/source4/libcli/smb2/notify.c b/source4/libcli/smb2/notify.c new file mode 100644 index 0000000000..ef7341cae8 --- /dev/null +++ b/source4/libcli/smb2/notify.c @@ -0,0 +1,114 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 client notify calls + + Copyright (C) Stefan Metzmacher 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" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" + +/* + send a notify request +*/ +struct smb2_request *smb2_notify_send(struct smb2_tree *tree, struct smb2_notify *io) +{ + struct smb2_request *req; + uint32_t old_timeout; + + req = smb2_request_init_tree(tree, SMB2_OP_NOTIFY, 0x20, false, 0); + if (req == NULL) return NULL; + + SSVAL(req->out.hdr, SMB2_HDR_CREDIT, 0x0030); + + SSVAL(req->out.body, 0x02, io->in.recursive); + SIVAL(req->out.body, 0x04, io->in.buffer_size); + smb2_push_handle(req->out.body+0x08, &io->in.file.handle); + SIVAL(req->out.body, 0x18, io->in.completion_filter); + SIVAL(req->out.body, 0x1C, io->in.unknown); + + old_timeout = req->transport->options.request_timeout; + req->transport->options.request_timeout = 0; + smb2_transport_send(req); + req->transport->options.request_timeout = old_timeout; + + return req; +} + + +/* + recv a notify reply +*/ +NTSTATUS smb2_notify_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, + struct smb2_notify *io) +{ + NTSTATUS status; + DATA_BLOB blob; + uint32_t ofs, i; + + if (!smb2_request_receive(req) || + !smb2_request_is_ok(req)) { + return smb2_request_destroy(req); + } + + SMB2_CHECK_PACKET_RECV(req, 0x08, true); + + status = smb2_pull_o16s32_blob(&req->in, mem_ctx, req->in.body+0x02, &blob); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + io->out.changes = NULL; + io->out.num_changes = 0; + + /* count them */ + for (ofs=0; blob.length - ofs > 12; ) { + uint32_t next = IVAL(blob.data, ofs); + io->out.num_changes++; + if (next == 0 || (ofs + next) >= blob.length) break; + ofs += next; + } + + /* allocate array */ + io->out.changes = talloc_array(mem_ctx, struct notify_changes, io->out.num_changes); + if (!io->out.changes) { + return NT_STATUS_NO_MEMORY; + } + + for (i=ofs=0; i<io->out.num_changes; i++) { + io->out.changes[i].action = IVAL(blob.data, ofs+4); + smbcli_blob_pull_string(NULL, mem_ctx, &blob, + &io->out.changes[i].name, + ofs+8, ofs+12, STR_UNICODE); + ofs += IVAL(blob.data, ofs); + } + + return smb2_request_destroy(req); +} + +/* + sync notify request +*/ +NTSTATUS smb2_notify(struct smb2_tree *tree, TALLOC_CTX *mem_ctx, + struct smb2_notify *io) +{ + struct smb2_request *req = smb2_notify_send(tree, io); + return smb2_notify_recv(req, mem_ctx, io); +} diff --git a/source4/libcli/smb2/read.c b/source4/libcli/smb2/read.c new file mode 100644 index 0000000000..9d40e32a4d --- /dev/null +++ b/source4/libcli/smb2/read.c @@ -0,0 +1,87 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 client read call + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" + +/* + send a read request +*/ +struct smb2_request *smb2_read_send(struct smb2_tree *tree, struct smb2_read *io) +{ + struct smb2_request *req; + + req = smb2_request_init_tree(tree, SMB2_OP_READ, 0x30, true, 0); + if (req == NULL) return NULL; + + SCVAL(req->out.body, 0x02, 0); /* pad */ + SCVAL(req->out.body, 0x03, 0); /* reserved */ + SIVAL(req->out.body, 0x04, io->in.length); + SBVAL(req->out.body, 0x08, io->in.offset); + smb2_push_handle(req->out.body+0x10, &io->in.file.handle); + SIVAL(req->out.body, 0x20, io->in.min_count); + SIVAL(req->out.body, 0x24, io->in.channel); + SIVAL(req->out.body, 0x28, io->in.remaining); + SSVAL(req->out.body, 0x2C, io->in.channel_offset); + SSVAL(req->out.body, 0x2E, io->in.channel_length); + + smb2_transport_send(req); + + return req; +} + + +/* + recv a read reply +*/ +NTSTATUS smb2_read_recv(struct smb2_request *req, + TALLOC_CTX *mem_ctx, struct smb2_read *io) +{ + NTSTATUS status; + + if (!smb2_request_receive(req) || + !smb2_request_is_ok(req)) { + return smb2_request_destroy(req); + } + + SMB2_CHECK_PACKET_RECV(req, 0x10, true); + + status = smb2_pull_o16s32_blob(&req->in, mem_ctx, req->in.body+0x02, &io->out.data); + if (!NT_STATUS_IS_OK(status)) { + smb2_request_destroy(req); + return status; + } + + io->out.remaining = IVAL(req->in.body, 0x08); + io->out.reserved = IVAL(req->in.body, 0x0C); + + return smb2_request_destroy(req); +} + +/* + sync read request +*/ +NTSTATUS smb2_read(struct smb2_tree *tree, TALLOC_CTX *mem_ctx, struct smb2_read *io) +{ + struct smb2_request *req = smb2_read_send(tree, io); + return smb2_read_recv(req, mem_ctx, io); +} diff --git a/source4/libcli/smb2/request.c b/source4/libcli/smb2/request.c new file mode 100644 index 0000000000..649a1db8d5 --- /dev/null +++ b/source4/libcli/smb2/request.c @@ -0,0 +1,737 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 client request handling + + Copyright (C) Andrew Tridgell 2005 + Copyright (C) Stefan Metzmacher 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/smb2/smb2.h" +#include "../lib/util/dlinklist.h" +#include "lib/events/events.h" +#include "libcli/smb2/smb2_calls.h" + +/* fill in the bufinfo */ +void smb2_setup_bufinfo(struct smb2_request *req) +{ + req->in.bufinfo.mem_ctx = req; + req->in.bufinfo.flags = BUFINFO_FLAG_UNICODE | BUFINFO_FLAG_SMB2; + req->in.bufinfo.align_base = req->in.buffer; + if (req->in.dynamic) { + req->in.bufinfo.data = req->in.dynamic; + req->in.bufinfo.data_size = req->in.body_size - req->in.body_fixed; + } else { + req->in.bufinfo.data = NULL; + req->in.bufinfo.data_size = 0; + } +} + + +/* destroy a request structure */ +static int smb2_request_destructor(struct smb2_request *req) +{ + if (req->transport) { + /* remove it from the list of pending requests (a null op if + its not in the list) */ + DLIST_REMOVE(req->transport->pending_recv, req); + } + return 0; +} + +/* + initialise a smb2 request +*/ +struct smb2_request *smb2_request_init(struct smb2_transport *transport, uint16_t opcode, + uint16_t body_fixed_size, bool body_dynamic_present, + uint32_t body_dynamic_size) +{ + struct smb2_request *req; + uint64_t seqnum; + + if (body_dynamic_present) { + if (body_dynamic_size == 0) { + body_dynamic_size = 1; + } + } else { + body_dynamic_size = 0; + } + + req = talloc(transport, struct smb2_request); + if (req == NULL) return NULL; + + seqnum = transport->seqnum++; + if (seqnum == UINT64_MAX) { + seqnum = transport->seqnum++; + } + + req->state = SMB2_REQUEST_INIT; + req->transport = transport; + req->session = NULL; + req->tree = NULL; + req->seqnum = seqnum; + req->status = NT_STATUS_OK; + req->async.fn = NULL; + req->next = req->prev = NULL; + + ZERO_STRUCT(req->cancel); + ZERO_STRUCT(req->in); + + req->out.size = SMB2_HDR_BODY+NBT_HDR_SIZE+body_fixed_size; + + req->out.allocated = req->out.size + body_dynamic_size; + req->out.buffer = talloc_array(req, uint8_t, req->out.allocated); + if (req->out.buffer == NULL) { + talloc_free(req); + return NULL; + } + + req->out.hdr = req->out.buffer + NBT_HDR_SIZE; + req->out.body = req->out.hdr + SMB2_HDR_BODY; + req->out.body_fixed= body_fixed_size; + req->out.body_size = body_fixed_size; + req->out.dynamic = (body_dynamic_size ? req->out.body + body_fixed_size : NULL); + + SIVAL(req->out.hdr, 0, SMB2_MAGIC); + SSVAL(req->out.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY); + SSVAL(req->out.hdr, SMB2_HDR_EPOCH, 0); + SIVAL(req->out.hdr, SMB2_HDR_STATUS, 0); + SSVAL(req->out.hdr, SMB2_HDR_OPCODE, opcode); + SSVAL(req->out.hdr, SMB2_HDR_CREDIT, 0); + SIVAL(req->out.hdr, SMB2_HDR_FLAGS, 0); + SIVAL(req->out.hdr, SMB2_HDR_NEXT_COMMAND, 0); + SBVAL(req->out.hdr, SMB2_HDR_MESSAGE_ID, req->seqnum); + SIVAL(req->out.hdr, SMB2_HDR_PID, 0); + SIVAL(req->out.hdr, SMB2_HDR_TID, 0); + SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, 0); + memset(req->out.hdr+SMB2_HDR_SIGNATURE, 0, 16); + + /* set the length of the fixed body part and +1 if there's a dynamic part also */ + SSVAL(req->out.body, 0, body_fixed_size + (body_dynamic_size?1:0)); + + /* + * if we have a dynamic part, make sure the first byte + * which is always be part of the packet is initialized + */ + if (body_dynamic_size) { + req->out.size += 1; + SCVAL(req->out.dynamic, 0, 0); + } + + talloc_set_destructor(req, smb2_request_destructor); + + return req; +} + +/* + initialise a smb2 request for tree operations +*/ +struct smb2_request *smb2_request_init_tree(struct smb2_tree *tree, uint16_t opcode, + uint16_t body_fixed_size, bool body_dynamic_present, + uint32_t body_dynamic_size) +{ + struct smb2_request *req = smb2_request_init(tree->session->transport, opcode, + body_fixed_size, body_dynamic_present, + body_dynamic_size); + if (req == NULL) return NULL; + + SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, tree->session->uid); + SIVAL(req->out.hdr, SMB2_HDR_TID, tree->tid); + req->session = tree->session; + req->tree = tree; + + return req; +} + +/* destroy a request structure and return final status */ +NTSTATUS smb2_request_destroy(struct smb2_request *req) +{ + NTSTATUS status; + + /* this is the error code we give the application for when a + _send() call fails completely */ + if (!req) return NT_STATUS_UNSUCCESSFUL; + + if (req->state == SMB2_REQUEST_ERROR && + NT_STATUS_IS_OK(req->status)) { + status = NT_STATUS_INTERNAL_ERROR; + } else { + status = req->status; + } + + talloc_free(req); + return status; +} + +/* + receive a response to a packet +*/ +bool smb2_request_receive(struct smb2_request *req) +{ + /* req can be NULL when a send has failed. This eliminates lots of NULL + checks in each module */ + if (!req) return false; + + /* keep receiving packets until this one is replied to */ + while (req->state <= SMB2_REQUEST_RECV) { + if (event_loop_once(req->transport->socket->event.ctx) != 0) { + return false; + } + } + + return req->state == SMB2_REQUEST_DONE; +} + +/* Return true if the last packet was in error */ +bool smb2_request_is_error(struct smb2_request *req) +{ + return NT_STATUS_IS_ERR(req->status); +} + +/* Return true if the last packet was OK */ +bool smb2_request_is_ok(struct smb2_request *req) +{ + return NT_STATUS_IS_OK(req->status); +} + +/* + check if a range in the reply body is out of bounds +*/ +bool smb2_oob(struct smb2_request_buffer *buf, const uint8_t *ptr, size_t size) +{ + if (size == 0) { + /* zero bytes is never out of range */ + return false; + } + /* be careful with wraparound! */ + if ((uintptr_t)ptr < (uintptr_t)buf->body || + (uintptr_t)ptr >= (uintptr_t)buf->body + buf->body_size || + size > buf->body_size || + (uintptr_t)ptr + size > (uintptr_t)buf->body + buf->body_size) { + return true; + } + return false; +} + +size_t smb2_padding_size(uint32_t offset, size_t n) +{ + if ((offset & (n-1)) == 0) return 0; + return n - (offset & (n-1)); +} + +static size_t smb2_padding_fix(struct smb2_request_buffer *buf) +{ + if (buf->dynamic == (buf->body + buf->body_fixed)) { + return 1; + } + return 0; +} + +/* + grow a SMB2 buffer by the specified amount +*/ +static NTSTATUS smb2_grow_buffer(struct smb2_request_buffer *buf, size_t increase) +{ + size_t dynamic_ofs; + uint8_t *buffer_ptr; + uint32_t newsize = buf->size + increase; + + /* a packet size should be limited a bit */ + if (newsize >= 0x00FFFFFF) return NT_STATUS_MARSHALL_OVERFLOW; + + if (newsize <= buf->allocated) return NT_STATUS_OK; + + dynamic_ofs = buf->dynamic - buf->buffer; + + buffer_ptr = talloc_realloc(buf, buf->buffer, uint8_t, newsize); + NT_STATUS_HAVE_NO_MEMORY(buffer_ptr); + + buf->buffer = buffer_ptr; + buf->hdr = buf->buffer + NBT_HDR_SIZE; + buf->body = buf->hdr + SMB2_HDR_BODY; + buf->dynamic = buf->buffer + dynamic_ofs; + buf->allocated = newsize; + + return NT_STATUS_OK; +} + +/* + pull a uint16_t ofs/ uint16_t length/blob triple from a data blob + the ptr points to the start of the offset/length pair +*/ +NTSTATUS smb2_pull_o16s16_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob) +{ + uint16_t ofs, size; + if (smb2_oob(buf, ptr, 4)) { + return NT_STATUS_INVALID_PARAMETER; + } + ofs = SVAL(ptr, 0); + size = SVAL(ptr, 2); + if (ofs == 0) { + *blob = data_blob(NULL, 0); + return NT_STATUS_OK; + } + if (smb2_oob(buf, buf->hdr + ofs, size)) { + return NT_STATUS_INVALID_PARAMETER; + } + *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size); + NT_STATUS_HAVE_NO_MEMORY(blob->data); + return NT_STATUS_OK; +} + +/* + push a uint16_t ofs/ uint16_t length/blob triple into a data blob + the ofs points to the start of the offset/length pair, and is relative + to the body start +*/ +NTSTATUS smb2_push_o16s16_blob(struct smb2_request_buffer *buf, + uint16_t ofs, DATA_BLOB blob) +{ + NTSTATUS status; + size_t offset; + size_t padding_length; + size_t padding_fix; + uint8_t *ptr = buf->body+ofs; + + if (buf->dynamic == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + /* we have only 16 bit for the size */ + if (blob.length > 0xFFFF) { + return NT_STATUS_INVALID_PARAMETER; + } + + /* check if there're enough room for ofs and size */ + if (smb2_oob(buf, ptr, 4)) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (blob.data == NULL) { + if (blob.length != 0) { + return NT_STATUS_INTERNAL_ERROR; + } + SSVAL(ptr, 0, 0); + SSVAL(ptr, 2, 0); + return NT_STATUS_OK; + } + + offset = buf->dynamic - buf->hdr; + padding_length = smb2_padding_size(offset, 2); + offset += padding_length; + padding_fix = smb2_padding_fix(buf); + + SSVAL(ptr, 0, offset); + SSVAL(ptr, 2, blob.length); + + status = smb2_grow_buffer(buf, blob.length + padding_length - padding_fix); + NT_STATUS_NOT_OK_RETURN(status); + + memset(buf->dynamic, 0, padding_length); + buf->dynamic += padding_length; + + memcpy(buf->dynamic, blob.data, blob.length); + buf->dynamic += blob.length; + + buf->size += blob.length + padding_length - padding_fix; + buf->body_size += blob.length + padding_length; + + return NT_STATUS_OK; +} + + +/* + push a uint16_t ofs/ uint32_t length/blob triple into a data blob + the ofs points to the start of the offset/length pair, and is relative + to the body start +*/ +NTSTATUS smb2_push_o16s32_blob(struct smb2_request_buffer *buf, + uint16_t ofs, DATA_BLOB blob) +{ + NTSTATUS status; + size_t offset; + size_t padding_length; + size_t padding_fix; + uint8_t *ptr = buf->body+ofs; + + if (buf->dynamic == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + /* check if there're enough room for ofs and size */ + if (smb2_oob(buf, ptr, 6)) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (blob.data == NULL) { + if (blob.length != 0) { + return NT_STATUS_INTERNAL_ERROR; + } + SSVAL(ptr, 0, 0); + SIVAL(ptr, 2, 0); + return NT_STATUS_OK; + } + + offset = buf->dynamic - buf->hdr; + padding_length = smb2_padding_size(offset, 2); + offset += padding_length; + padding_fix = smb2_padding_fix(buf); + + SSVAL(ptr, 0, offset); + SIVAL(ptr, 2, blob.length); + + status = smb2_grow_buffer(buf, blob.length + padding_length - padding_fix); + NT_STATUS_NOT_OK_RETURN(status); + + memset(buf->dynamic, 0, padding_length); + buf->dynamic += padding_length; + + memcpy(buf->dynamic, blob.data, blob.length); + buf->dynamic += blob.length; + + buf->size += blob.length + padding_length - padding_fix; + buf->body_size += blob.length + padding_length; + + return NT_STATUS_OK; +} + + +/* + push a uint32_t ofs/ uint32_t length/blob triple into a data blob + the ofs points to the start of the offset/length pair, and is relative + to the body start +*/ +NTSTATUS smb2_push_o32s32_blob(struct smb2_request_buffer *buf, + uint32_t ofs, DATA_BLOB blob) +{ + NTSTATUS status; + size_t offset; + size_t padding_length; + size_t padding_fix; + uint8_t *ptr = buf->body+ofs; + + if (buf->dynamic == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + /* check if there're enough room for ofs and size */ + if (smb2_oob(buf, ptr, 8)) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (blob.data == NULL) { + if (blob.length != 0) { + return NT_STATUS_INTERNAL_ERROR; + } + SIVAL(ptr, 0, 0); + SIVAL(ptr, 4, 0); + return NT_STATUS_OK; + } + + offset = buf->dynamic - buf->hdr; + padding_length = smb2_padding_size(offset, 8); + offset += padding_length; + padding_fix = smb2_padding_fix(buf); + + SIVAL(ptr, 0, offset); + SIVAL(ptr, 4, blob.length); + + status = smb2_grow_buffer(buf, blob.length + padding_length - padding_fix); + NT_STATUS_NOT_OK_RETURN(status); + + memset(buf->dynamic, 0, padding_length); + buf->dynamic += padding_length; + + memcpy(buf->dynamic, blob.data, blob.length); + buf->dynamic += blob.length; + + buf->size += blob.length + padding_length - padding_fix; + buf->body_size += blob.length + padding_length; + + return NT_STATUS_OK; +} + + +/* + push a uint32_t length/ uint32_t ofs/blob triple into a data blob + the ofs points to the start of the length/offset pair, and is relative + to the body start +*/ +NTSTATUS smb2_push_s32o32_blob(struct smb2_request_buffer *buf, + uint32_t ofs, DATA_BLOB blob) +{ + NTSTATUS status; + size_t offset; + size_t padding_length; + size_t padding_fix; + uint8_t *ptr = buf->body+ofs; + + if (buf->dynamic == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + /* check if there're enough room for ofs and size */ + if (smb2_oob(buf, ptr, 8)) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (blob.data == NULL) { + if (blob.length != 0) { + return NT_STATUS_INTERNAL_ERROR; + } + SIVAL(ptr, 0, 0); + SIVAL(ptr, 4, 0); + return NT_STATUS_OK; + } + + offset = buf->dynamic - buf->hdr; + padding_length = smb2_padding_size(offset, 8); + offset += padding_length; + padding_fix = smb2_padding_fix(buf); + + SIVAL(ptr, 0, blob.length); + SIVAL(ptr, 4, offset); + + status = smb2_grow_buffer(buf, blob.length + padding_length - padding_fix); + NT_STATUS_NOT_OK_RETURN(status); + + memset(buf->dynamic, 0, padding_length); + buf->dynamic += padding_length; + + memcpy(buf->dynamic, blob.data, blob.length); + buf->dynamic += blob.length; + + buf->size += blob.length + padding_length - padding_fix; + buf->body_size += blob.length + padding_length; + + return NT_STATUS_OK; +} + +/* + pull a uint16_t ofs/ uint32_t length/blob triple from a data blob + the ptr points to the start of the offset/length pair +*/ +NTSTATUS smb2_pull_o16s32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob) +{ + uint16_t ofs; + uint32_t size; + + if (smb2_oob(buf, ptr, 6)) { + return NT_STATUS_INVALID_PARAMETER; + } + ofs = SVAL(ptr, 0); + size = IVAL(ptr, 2); + if (ofs == 0) { + *blob = data_blob(NULL, 0); + return NT_STATUS_OK; + } + if (smb2_oob(buf, buf->hdr + ofs, size)) { + return NT_STATUS_INVALID_PARAMETER; + } + *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size); + NT_STATUS_HAVE_NO_MEMORY(blob->data); + return NT_STATUS_OK; +} + +/* + pull a uint32_t ofs/ uint32_t length/blob triple from a data blob + the ptr points to the start of the offset/length pair +*/ +NTSTATUS smb2_pull_o32s32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob) +{ + uint32_t ofs, size; + if (smb2_oob(buf, ptr, 8)) { + return NT_STATUS_INVALID_PARAMETER; + } + ofs = IVAL(ptr, 0); + size = IVAL(ptr, 4); + if (ofs == 0) { + *blob = data_blob(NULL, 0); + return NT_STATUS_OK; + } + if (smb2_oob(buf, buf->hdr + ofs, size)) { + return NT_STATUS_INVALID_PARAMETER; + } + *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size); + NT_STATUS_HAVE_NO_MEMORY(blob->data); + return NT_STATUS_OK; +} + +/* + pull a uint16_t ofs/ uint32_t length/blob triple from a data blob + the ptr points to the start of the offset/length pair + + In this varient the uint16_t is padded by an extra 2 bytes, making + the size aligned on 4 byte boundary +*/ +NTSTATUS smb2_pull_o16As32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob) +{ + uint32_t ofs, size; + if (smb2_oob(buf, ptr, 8)) { + return NT_STATUS_INVALID_PARAMETER; + } + ofs = SVAL(ptr, 0); + size = IVAL(ptr, 4); + if (ofs == 0) { + *blob = data_blob(NULL, 0); + return NT_STATUS_OK; + } + if (smb2_oob(buf, buf->hdr + ofs, size)) { + return NT_STATUS_INVALID_PARAMETER; + } + *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size); + NT_STATUS_HAVE_NO_MEMORY(blob->data); + return NT_STATUS_OK; +} + +/* + pull a uint32_t length/ uint32_t ofs/blob triple from a data blob + the ptr points to the start of the offset/length pair +*/ +NTSTATUS smb2_pull_s32o32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob) +{ + uint32_t ofs, size; + if (smb2_oob(buf, ptr, 8)) { + return NT_STATUS_INVALID_PARAMETER; + } + size = IVAL(ptr, 0); + ofs = IVAL(ptr, 4); + if (ofs == 0) { + *blob = data_blob(NULL, 0); + return NT_STATUS_OK; + } + if (smb2_oob(buf, buf->hdr + ofs, size)) { + return NT_STATUS_INVALID_PARAMETER; + } + *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size); + NT_STATUS_HAVE_NO_MEMORY(blob->data); + return NT_STATUS_OK; +} + +/* + pull a uint32_t length/ uint16_t ofs/blob triple from a data blob + the ptr points to the start of the offset/length pair +*/ +NTSTATUS smb2_pull_s32o16_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob) +{ + uint32_t ofs, size; + if (smb2_oob(buf, ptr, 8)) { + return NT_STATUS_INVALID_PARAMETER; + } + size = IVAL(ptr, 0); + ofs = SVAL(ptr, 4); + if (ofs == 0) { + *blob = data_blob(NULL, 0); + return NT_STATUS_OK; + } + if (smb2_oob(buf, buf->hdr + ofs, size)) { + return NT_STATUS_INVALID_PARAMETER; + } + *blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size); + NT_STATUS_HAVE_NO_MEMORY(blob->data); + return NT_STATUS_OK; +} + +/* + pull a string in a uint16_t ofs/ uint16_t length/blob format + UTF-16 without termination +*/ +NTSTATUS smb2_pull_o16s16_string(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, + uint8_t *ptr, const char **str) +{ + DATA_BLOB blob; + NTSTATUS status; + void *vstr; + bool ret; + + status = smb2_pull_o16s16_blob(buf, mem_ctx, ptr, &blob); + NT_STATUS_NOT_OK_RETURN(status); + + if (blob.data == NULL) { + *str = NULL; + return NT_STATUS_OK; + } + + if (blob.length == 0) { + char *s; + s = talloc_strdup(mem_ctx, ""); + NT_STATUS_HAVE_NO_MEMORY(s); + *str = s; + return NT_STATUS_OK; + } + + ret = convert_string_talloc(mem_ctx, CH_UTF16, CH_UNIX, + blob.data, blob.length, &vstr, NULL, false); + data_blob_free(&blob); + (*str) = (char *)vstr; + if (!ret) { + return NT_STATUS_ILLEGAL_CHARACTER; + } + return NT_STATUS_OK; +} + +/* + push a string in a uint16_t ofs/ uint16_t length/blob format + UTF-16 without termination +*/ +NTSTATUS smb2_push_o16s16_string(struct smb2_request_buffer *buf, + uint16_t ofs, const char *str) +{ + DATA_BLOB blob; + NTSTATUS status; + bool ret; + + if (str == NULL) { + return smb2_push_o16s16_blob(buf, ofs, data_blob(NULL, 0)); + } + + if (*str == 0) { + blob.data = discard_const_p(uint8_t, str); + blob.length = 0; + return smb2_push_o16s16_blob(buf, ofs, blob); + } + + ret = convert_string_talloc(buf->buffer, CH_UNIX, CH_UTF16, + str, strlen(str), (void **)&blob.data, &blob.length, + false); + if (!ret) { + return NT_STATUS_ILLEGAL_CHARACTER; + } + + status = smb2_push_o16s16_blob(buf, ofs, blob); + data_blob_free(&blob); + return status; +} + +/* + push a file handle into a buffer +*/ +void smb2_push_handle(uint8_t *data, struct smb2_handle *h) +{ + SBVAL(data, 0, h->data[0]); + SBVAL(data, 8, h->data[1]); +} + +/* + pull a file handle from a buffer +*/ +void smb2_pull_handle(uint8_t *ptr, struct smb2_handle *h) +{ + h->data[0] = BVAL(ptr, 0); + h->data[1] = BVAL(ptr, 8); +} diff --git a/source4/libcli/smb2/session.c b/source4/libcli/smb2/session.c new file mode 100644 index 0000000000..127bb9bcae --- /dev/null +++ b/source4/libcli/smb2/session.c @@ -0,0 +1,274 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 client session handling + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "libcli/composite/composite.h" +#include "auth/gensec/gensec.h" + +/** + initialise a smb2_session structure + */ +struct smb2_session *smb2_session_init(struct smb2_transport *transport, + struct gensec_settings *settings, + TALLOC_CTX *parent_ctx, bool primary) +{ + struct smb2_session *session; + NTSTATUS status; + + session = talloc_zero(parent_ctx, struct smb2_session); + if (!session) { + return NULL; + } + if (primary) { + session->transport = talloc_steal(session, transport); + } else { + session->transport = talloc_reference(session, transport); + } + + /* prepare a gensec context for later use */ + status = gensec_client_start(session, &session->gensec, + session->transport->socket->event.ctx, + settings); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(session); + return NULL; + } + + gensec_want_feature(session->gensec, GENSEC_FEATURE_SESSION_KEY); + + return session; +} + +/** + send a session setup request +*/ +struct smb2_request *smb2_session_setup_send(struct smb2_session *session, + struct smb2_session_setup *io) +{ + struct smb2_request *req; + NTSTATUS status; + + req = smb2_request_init(session->transport, SMB2_OP_SESSSETUP, + 0x18, true, io->in.secblob.length); + if (req == NULL) return NULL; + + SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, session->uid); + SCVAL(req->out.body, 0x02, io->in.vc_number); + SCVAL(req->out.body, 0x03, io->in.security_mode); + SIVAL(req->out.body, 0x04, io->in.capabilities); + SIVAL(req->out.body, 0x08, io->in.channel); + SBVAL(req->out.body, 0x10, io->in.previous_sessionid); + + req->session = session; + + status = smb2_push_o16s16_blob(&req->out, 0x0C, io->in.secblob); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } + + smb2_transport_send(req); + + return req; +} + + +/** + recv a session setup reply +*/ +NTSTATUS smb2_session_setup_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, + struct smb2_session_setup *io) +{ + NTSTATUS status; + + if (!smb2_request_receive(req) || + (smb2_request_is_error(req) && + !NT_STATUS_EQUAL(req->status, NT_STATUS_MORE_PROCESSING_REQUIRED))) { + return smb2_request_destroy(req); + } + + SMB2_CHECK_PACKET_RECV(req, 0x08, true); + + io->out.session_flags = SVAL(req->in.body, 0x02); + io->out.uid = BVAL(req->in.hdr, SMB2_HDR_SESSION_ID); + + status = smb2_pull_o16s16_blob(&req->in, mem_ctx, req->in.body+0x04, &io->out.secblob); + if (!NT_STATUS_IS_OK(status)) { + smb2_request_destroy(req); + return status; + } + + return smb2_request_destroy(req); +} + +/* + sync session setup request +*/ +NTSTATUS smb2_session_setup(struct smb2_session *session, + TALLOC_CTX *mem_ctx, struct smb2_session_setup *io) +{ + struct smb2_request *req = smb2_session_setup_send(session, io); + return smb2_session_setup_recv(req, mem_ctx, io); +} + + +struct smb2_session_state { + struct smb2_session_setup io; + struct smb2_request *req; + NTSTATUS gensec_status; +}; + +/* + handle continuations of the spnego session setup +*/ +static void session_request_handler(struct smb2_request *req) +{ + struct composite_context *c = talloc_get_type(req->async.private_data, + struct composite_context); + struct smb2_session_state *state = talloc_get_type(c->private_data, + struct smb2_session_state); + struct smb2_session *session = req->session; + + c->status = smb2_session_setup_recv(req, c, &state->io); + if (NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED) || + (NT_STATUS_IS_OK(c->status) && + NT_STATUS_EQUAL(state->gensec_status, NT_STATUS_MORE_PROCESSING_REQUIRED))) { + NTSTATUS session_key_err; + DATA_BLOB session_key; + c->status = gensec_update(session->gensec, c, + state->io.out.secblob, + &state->io.in.secblob); + state->gensec_status = c->status; + + session_key_err = gensec_session_key(session->gensec, &session_key); + if (NT_STATUS_IS_OK(session_key_err)) { + session->session_key = session_key; + } + } + + session->uid = state->io.out.uid; + + if (NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + state->req = smb2_session_setup_send(session, &state->io); + if (state->req == NULL) { + composite_error(c, NT_STATUS_NO_MEMORY); + return; + } + + state->req->async.fn = session_request_handler; + state->req->async.private_data = c; + return; + } + + if (!NT_STATUS_IS_OK(c->status)) { + composite_error(c, c->status); + return; + } + + if (session->transport->signing_required) { + if (session->session_key.length == 0) { + DEBUG(0,("Wrong session key length %u for SMB2 signing\n", + (unsigned)session->session_key.length)); + composite_error(c, NT_STATUS_ACCESS_DENIED); + return; + } + session->signing_active = true; + } + + composite_done(c); +} + +/* + a composite function that does a full SPNEGO session setup + */ +struct composite_context *smb2_session_setup_spnego_send(struct smb2_session *session, + struct cli_credentials *credentials) +{ + struct composite_context *c; + struct smb2_session_state *state; + + c = composite_create(session, session->transport->socket->event.ctx); + if (c == NULL) return NULL; + + state = talloc(c, struct smb2_session_state); + if (composite_nomem(state, c)) return c; + c->private_data = state; + + ZERO_STRUCT(state->io); + state->io.in.vc_number = 0; + if (session->transport->signing_required) { + state->io.in.security_mode = + SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED; + } + state->io.in.capabilities = 0; + state->io.in.channel = 0; + state->io.in.previous_sessionid = 0; + + c->status = gensec_set_credentials(session->gensec, credentials); + if (!composite_is_ok(c)) return c; + + c->status = gensec_set_target_hostname(session->gensec, + session->transport->socket->hostname); + if (!composite_is_ok(c)) return c; + + c->status = gensec_set_target_service(session->gensec, "cifs"); + if (!composite_is_ok(c)) return c; + + c->status = gensec_start_mech_by_oid(session->gensec, GENSEC_OID_SPNEGO); + if (!composite_is_ok(c)) return c; + + c->status = gensec_update(session->gensec, c, + session->transport->negotiate.secblob, + &state->io.in.secblob); + if (!NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + composite_error(c, c->status); + return c; + } + state->gensec_status = c->status; + + state->req = smb2_session_setup_send(session, &state->io); + composite_continue_smb2(c, state->req, session_request_handler, c); + return c; +} + +/* + receive a composite session setup reply +*/ +NTSTATUS smb2_session_setup_spnego_recv(struct composite_context *c) +{ + NTSTATUS status; + status = composite_wait(c); + talloc_free(c); + return status; +} + +/* + sync version of smb2_session_setup_spnego +*/ +NTSTATUS smb2_session_setup_spnego(struct smb2_session *session, + struct cli_credentials *credentials) +{ + struct composite_context *c = smb2_session_setup_spnego_send(session, credentials); + return smb2_session_setup_spnego_recv(c); +} diff --git a/source4/libcli/smb2/setinfo.c b/source4/libcli/smb2/setinfo.c new file mode 100644 index 0000000000..69c0f45b63 --- /dev/null +++ b/source4/libcli/smb2/setinfo.c @@ -0,0 +1,122 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 client setinfo calls + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" + +/* + send a setinfo request +*/ +struct smb2_request *smb2_setinfo_send(struct smb2_tree *tree, struct smb2_setinfo *io) +{ + NTSTATUS status; + struct smb2_request *req; + + req = smb2_request_init_tree(tree, SMB2_OP_SETINFO, 0x20, true, io->in.blob.length); + if (req == NULL) return NULL; + + SSVAL(req->out.body, 0x02, io->in.level); + + status = smb2_push_s32o32_blob(&req->out, 0x04, io->in.blob); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } + + SIVAL(req->out.body, 0x0C, io->in.flags); + smb2_push_handle(req->out.body+0x10, &io->in.file.handle); + + smb2_transport_send(req); + + return req; +} + + +/* + recv a setinfo reply +*/ +NTSTATUS smb2_setinfo_recv(struct smb2_request *req) +{ + if (!smb2_request_receive(req) || + !smb2_request_is_ok(req)) { + return smb2_request_destroy(req); + } + + SMB2_CHECK_PACKET_RECV(req, 0x02, false); + + return smb2_request_destroy(req); +} + +/* + sync setinfo request +*/ +NTSTATUS smb2_setinfo(struct smb2_tree *tree, struct smb2_setinfo *io) +{ + struct smb2_request *req = smb2_setinfo_send(tree, io); + return smb2_setinfo_recv(req); +} + +/* + level specific file setinfo call - async send +*/ +struct smb2_request *smb2_setinfo_file_send(struct smb2_tree *tree, union smb_setfileinfo *io) +{ + struct smb2_setinfo b; + uint16_t smb2_level = smb2_getinfo_map_level(io->generic.level, SMB2_GETINFO_FILE); + struct smb2_request *req; + + if (smb2_level == 0) { + return NULL; + } + + ZERO_STRUCT(b); + b.in.level = smb2_level; + b.in.file.handle = io->generic.in.file.handle; + + /* change levels so the parsers know it is SMB2 */ + if (io->generic.level == RAW_SFILEINFO_RENAME_INFORMATION) { + io->generic.level = RAW_SFILEINFO_RENAME_INFORMATION_SMB2; + } + + if (!smb_raw_setfileinfo_passthru(tree, io->generic.level, io, &b.in.blob)) { + return NULL; + } + + if (io->generic.level == RAW_SFILEINFO_SEC_DESC) { + b.in.flags = io->set_secdesc.in.secinfo_flags; + } + + req = smb2_setinfo_send(tree, &b); + data_blob_free(&b.in.blob); + return req; +} + +/* + level specific file setinfo call - sync +*/ +NTSTATUS smb2_setinfo_file(struct smb2_tree *tree, union smb_setfileinfo *io) +{ + struct smb2_request *req = smb2_setinfo_file_send(tree, io); + return smb2_setinfo_recv(req); +} diff --git a/source4/libcli/smb2/signing.c b/source4/libcli/smb2/signing.c new file mode 100644 index 0000000000..101fb00c12 --- /dev/null +++ b/source4/libcli/smb2/signing.c @@ -0,0 +1,117 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 Signing Code + + Copyright (C) Andrew Tridgell <tridge@samba.org> 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 "libcli/raw/libcliraw.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "../lib/crypto/crypto.h" + +/* + sign an outgoing message + */ +NTSTATUS smb2_sign_message(struct smb2_request_buffer *buf, DATA_BLOB session_key) +{ + struct HMACSHA256Context m; + uint8_t res[32]; + uint64_t session_id; + + if (buf->size < NBT_HDR_SIZE + SMB2_HDR_SIGNATURE + 16) { + /* can't sign non-SMB2 messages */ + return NT_STATUS_OK; + } + + session_id = BVAL(buf->hdr, SMB2_HDR_SESSION_ID); + if (session_id == 0) { + /* we don't sign messages with a zero session_id. See + MS-SMB2 3.2.4.1.1 */ + return NT_STATUS_OK; + } + + if (session_key.length == 0) { + DEBUG(2,("Wrong session key length %u for SMB2 signing\n", + (unsigned)session_key.length)); + return NT_STATUS_ACCESS_DENIED; + } + + memset(buf->hdr + SMB2_HDR_SIGNATURE, 0, 16); + + SIVAL(buf->hdr, SMB2_HDR_FLAGS, IVAL(buf->hdr, SMB2_HDR_FLAGS) | SMB2_HDR_FLAG_SIGNED); + + ZERO_STRUCT(m); + hmac_sha256_init(session_key.data, MIN(session_key.length, 16), &m); + hmac_sha256_update(buf->buffer+NBT_HDR_SIZE, buf->size-NBT_HDR_SIZE, &m); + hmac_sha256_final(res, &m); + DEBUG(5,("signed SMB2 message of size %u\n", (unsigned)buf->size - NBT_HDR_SIZE)); + + memcpy(buf->hdr + SMB2_HDR_SIGNATURE, res, 16); + + return NT_STATUS_OK; +} + +/* + check an incoming signature + */ +NTSTATUS smb2_check_signature(struct smb2_request_buffer *buf, DATA_BLOB session_key) +{ + uint64_t session_id; + struct HMACSHA256Context m; + uint8_t res[SHA256_DIGEST_LENGTH]; + uint8_t sig[16]; + + if (buf->size < NBT_HDR_SIZE + SMB2_HDR_SIGNATURE + 16) { + /* can't check non-SMB2 messages */ + return NT_STATUS_OK; + } + + session_id = BVAL(buf->hdr, SMB2_HDR_SESSION_ID); + if (session_id == 0) { + /* don't sign messages with a zero session_id. See + MS-SMB2 3.2.4.1.1 */ + return NT_STATUS_OK; + } + + if (session_key.length == 0) { + /* we don't have the session key yet */ + return NT_STATUS_OK; + } + + memcpy(sig, buf->hdr+SMB2_HDR_SIGNATURE, 16); + + memset(buf->hdr + SMB2_HDR_SIGNATURE, 0, 16); + + ZERO_STRUCT(m); + hmac_sha256_init(session_key.data, MIN(session_key.length, 16), &m); + hmac_sha256_update(buf->hdr, buf->size-NBT_HDR_SIZE, &m); + hmac_sha256_final(res, &m); + + memcpy(buf->hdr+SMB2_HDR_SIGNATURE, sig, 16); + + if (memcmp(res, sig, 16) != 0) { + DEBUG(0,("Bad SMB2 signature for message of size %u\n", + (unsigned)buf->size-NBT_HDR_SIZE)); + dump_data(0, sig, 16); + dump_data(0, res, 16); + return NT_STATUS_ACCESS_DENIED; + } + + return NT_STATUS_OK; +} diff --git a/source4/libcli/smb2/smb2.h b/source4/libcli/smb2/smb2.h new file mode 100644 index 0000000000..d1d5b842c3 --- /dev/null +++ b/source4/libcli/smb2/smb2.h @@ -0,0 +1,303 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 client library header + + Copyright (C) Andrew Tridgell 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/>. +*/ + +#ifndef __LIBCLI_SMB2_SMB2_H__ +#define __LIBCLI_SMB2_SMB2_H__ + +#include "libcli/raw/request.h" +#include "libcli/raw/libcliraw.h" + +struct smb2_handle; + +/* + information returned from the negotiate process +*/ +struct smb2_negotiate { + DATA_BLOB secblob; + NTTIME system_time; + NTTIME server_start_time; + uint16_t security_mode; +}; + +/* this is the context for the smb2 transport layer */ +struct smb2_transport { + /* socket level info */ + struct smbcli_socket *socket; + + struct smb2_negotiate negotiate; + + /* next seqnum to allocate */ + uint64_t seqnum; + + /* a list of requests that are pending for receive on this + connection */ + struct smb2_request *pending_recv; + + /* context of the stream -> packet parser */ + struct packet_context *packet; + + /* an idle function - if this is defined then it will be + called once every period microseconds while we are waiting + for a packet */ + struct { + void (*func)(struct smb2_transport *, void *); + void *private_data; + uint_t period; + } idle; + + struct { + /* a oplock break request handler */ + bool (*handler)(struct smb2_transport *transport, + const struct smb2_handle *handle, + uint8_t level, void *private_data); + /* private data passed to the oplock handler */ + void *private_data; + } oplock; + + struct smbcli_options options; + + bool signing_required; +}; + + +/* + SMB2 tree context +*/ +struct smb2_tree { + struct smb2_session *session; + uint32_t tid; +}; + +/* + SMB2 session context +*/ +struct smb2_session { + struct smb2_transport *transport; + struct gensec_security *gensec; + uint64_t uid; + DATA_BLOB session_key; + bool signing_active; +}; + + +struct smb2_request_buffer { + /* the raw SMB2 buffer, including the 4 byte length header */ + uint8_t *buffer; + + /* the size of the raw buffer, including 4 byte header */ + size_t size; + + /* how much has been allocated - on reply the buffer is over-allocated to + prevent too many realloc() calls + */ + size_t allocated; + + /* the start of the SMB2 header - this is always buffer+4 */ + uint8_t *hdr; + + /* the packet body */ + uint8_t *body; + size_t body_fixed; + size_t body_size; + + /* this point to the next dynamic byte that can be used + * this will be moved when some dynamic data is pushed + */ + uint8_t *dynamic; + + /* this is used to range check and align strings and buffers */ + struct request_bufinfo bufinfo; +}; + + +/* + a client request moves between the following 4 states. +*/ +enum smb2_request_state {SMB2_REQUEST_INIT, /* we are creating the request */ + SMB2_REQUEST_RECV, /* we are waiting for a matching reply */ + SMB2_REQUEST_DONE, /* the request is finished */ + SMB2_REQUEST_ERROR}; /* a packet or transport level error has occurred */ + +/* the context for a single SMB2 request */ +struct smb2_request { + /* allow a request to be part of a list of requests */ + struct smb2_request *next, *prev; + + /* each request is in one of 3 possible states */ + enum smb2_request_state state; + + struct smb2_transport *transport; + struct smb2_session *session; + struct smb2_tree *tree; + + uint64_t seqnum; + + struct { + bool do_cancel; + bool can_cancel; + uint32_t pending_id; + } cancel; + + /* the NT status for this request. Set by packet receive code + or code detecting error. */ + NTSTATUS status; + + struct smb2_request_buffer in; + struct smb2_request_buffer out; + + /* information on what to do with a reply when it is received + asyncronously. If this is not setup when a reply is received then + the reply is discarded + + The private pointer is private to the caller of the client + library (the application), not private to the library + */ + struct { + void (*fn)(struct smb2_request *); + void *private_data; + } async; +}; + + +#define SMB2_MIN_SIZE 0x42 +#define SMB2_MIN_SIZE_NO_BODY 0x40 + +/* offsets into header elements for a sync SMB2 request */ +#define SMB2_HDR_PROTOCOL_ID 0x00 +#define SMB2_HDR_LENGTH 0x04 +#define SMB2_HDR_EPOCH 0x06 +#define SMB2_HDR_STATUS 0x08 +#define SMB2_HDR_OPCODE 0x0c +#define SMB2_HDR_CREDIT 0x0e +#define SMB2_HDR_FLAGS 0x10 +#define SMB2_HDR_NEXT_COMMAND 0x14 +#define SMB2_HDR_MESSAGE_ID 0x18 +#define SMB2_HDR_PID 0x20 +#define SMB2_HDR_TID 0x24 +#define SMB2_HDR_SESSION_ID 0x28 +#define SMB2_HDR_SIGNATURE 0x30 /* 16 bytes */ +#define SMB2_HDR_BODY 0x40 + +/* header flags */ +#define SMB2_HDR_FLAG_REDIRECT 0x01 +#define SMB2_HDR_FLAG_ASYNC 0x02 +#define SMB2_HDR_FLAG_CHAINED 0x04 +#define SMB2_HDR_FLAG_SIGNED 0x08 +#define SMB2_HDR_FLAG_DFS 0x10000000 + +/* SMB2 opcodes */ +#define SMB2_OP_NEGPROT 0x00 +#define SMB2_OP_SESSSETUP 0x01 +#define SMB2_OP_LOGOFF 0x02 +#define SMB2_OP_TCON 0x03 +#define SMB2_OP_TDIS 0x04 +#define SMB2_OP_CREATE 0x05 +#define SMB2_OP_CLOSE 0x06 +#define SMB2_OP_FLUSH 0x07 +#define SMB2_OP_READ 0x08 +#define SMB2_OP_WRITE 0x09 +#define SMB2_OP_LOCK 0x0a +#define SMB2_OP_IOCTL 0x0b +#define SMB2_OP_CANCEL 0x0c +#define SMB2_OP_KEEPALIVE 0x0d +#define SMB2_OP_FIND 0x0e +#define SMB2_OP_NOTIFY 0x0f +#define SMB2_OP_GETINFO 0x10 +#define SMB2_OP_SETINFO 0x11 +#define SMB2_OP_BREAK 0x12 + +#define SMB2_MAGIC 0x424D53FE /* 0xFE 'S' 'M' 'B' */ + +/* the dialect we support */ +#define SMB2_DIALECT_REVISION 0x202 + +/* SMB2 negotiate security_mode */ +#define SMB2_NEGOTIATE_SIGNING_ENABLED 0x01 +#define SMB2_NEGOTIATE_SIGNING_REQUIRED 0x02 + +/* SMB2 capabilities - only 1 so far. I'm sure more will be added */ +#define SMB2_CAP_DFS 0x0 +/* so we can spot new caps as added */ +#define SMB2_CAP_ALL SMB2_CAP_DFS + +/* SMB2 share flags */ +#define SMB2_SHAREFLAG_MANUAL_CACHING 0x0000 +#define SMB2_SHAREFLAG_AUTO_CACHING 0x0010 +#define SMB2_SHAREFLAG_VDO_CACHING 0x0020 +#define SMB2_SHAREFLAG_NO_CACHING 0x0030 +#define SMB2_SHAREFLAG_DFS 0x0001 +#define SMB2_SHAREFLAG_DFS_ROOT 0x0002 +#define SMB2_SHAREFLAG_RESTRICT_EXCLUSIVE_OPENS 0x0100 +#define SMB2_SHAREFLAG_FORCE_SHARED_DELETE 0x0200 +#define SMB2_SHAREFLAG_ALLOW_NAMESPACE_CACHING 0x0400 +#define SMB2_SHAREFLAG_ACCESS_BASED_DIRECTORY_ENUM 0x0800 +#define SMB2_SHAREFLAG_ALL 0x0F33 + +/* SMB2 create security flags */ +#define SMB2_SECURITY_DYNAMIC_TRACKING 0x01 +#define SMB2_SECURITY_EFFECTIVE_ONLY 0x02 + +/* SMB2 requested oplock levels */ +#define SMB2_OPLOCK_LEVEL_NONE 0x00 +#define SMB2_OPLOCK_LEVEL_II 0x01 +#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08 +#define SMB2_OPLOCK_LEVEL_BATCH 0x09 + +/* SMB2 impersonation levels */ +#define SMB2_IMPERSONATION_ANONYMOUS 0x00 +#define SMB2_IMPERSONATION_IDENTIFICATION 0x01 +#define SMB2_IMPERSONATION_IMPERSONATION 0x02 +#define SMB2_IMPERSONATION_DELEGATE 0x03 + +/* SMB2 create tags */ +#define SMB2_CREATE_TAG_EXTA "ExtA" +#define SMB2_CREATE_TAG_MXAC "MxAc" +#define SMB2_CREATE_TAG_SECD "SecD" +#define SMB2_CREATE_TAG_DHNQ "DHnQ" +#define SMB2_CREATE_TAG_DHNC "DHnC" +#define SMB2_CREATE_TAG_ALSI "AlSi" +#define SMB2_CREATE_TAG_TWRP "TWrp" +#define SMB2_CREATE_TAG_QFID "QFid" + +/* SMB2 Create ignore some more create_options */ +#define SMB2_CREATE_OPTIONS_NOT_SUPPORTED_MASK (NTCREATEX_OPTIONS_TREE_CONNECTION | \ + NTCREATEX_OPTIONS_OPFILTER) + +/* + check that a body has the expected size +*/ +#define SMB2_CHECK_PACKET_RECV(req, size, dynamic) do { \ + size_t is_size = req->in.body_size; \ + uint16_t field_size = SVAL(req->in.body, 0); \ + uint16_t want_size = ((dynamic)?(size)+1:(size)); \ + if (is_size < (size)) { \ + DEBUG(0,("%s: buffer too small 0x%x. Expected 0x%x\n", \ + __location__, (unsigned)is_size, (unsigned)want_size)); \ + return NT_STATUS_BUFFER_TOO_SMALL; \ + }\ + if (field_size != want_size) { \ + DEBUG(0,("%s: unexpected fixed body size 0x%x. Expected 0x%x\n", \ + __location__, (unsigned)field_size, (unsigned)want_size)); \ + return NT_STATUS_INVALID_PARAMETER; \ + } \ +} while (0) + +#endif diff --git a/source4/libcli/smb2/smb2_calls.h b/source4/libcli/smb2/smb2_calls.h new file mode 100644 index 0000000000..b89770fbe6 --- /dev/null +++ b/source4/libcli/smb2/smb2_calls.h @@ -0,0 +1,111 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 client calls + + Copyright (C) Andrew Tridgell 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 "libcli/raw/interfaces.h" + +struct smb2_negprot { + struct { + uint16_t dialect_count; /* size of dialects array */ + uint16_t security_mode; /* 0==signing disabled + 1==signing enabled */ + uint16_t reserved; + uint32_t capabilities; + struct GUID client_guid; + NTTIME start_time; + uint16_t *dialects; + } in; + struct { + /* static body buffer 64 (0x40) bytes */ + /* uint16_t buffer_code; 0x41 = 0x40 + 1 */ + uint16_t security_mode; /* SMB2_NEGOTIATE_SIGNING_* */ + uint16_t dialect_revision; + uint16_t reserved; + struct GUID server_guid; + uint32_t capabilities; + uint32_t max_transact_size; + uint32_t max_read_size; + uint32_t max_write_size; + NTTIME system_time; + NTTIME server_start_time; + /* uint16_t secblob_ofs */ + /* uint16_t secblob_size */ + uint32_t reserved2; + DATA_BLOB secblob; + } out; +}; + +/* getinfo classes */ +#define SMB2_GETINFO_FILE 0x01 +#define SMB2_GETINFO_FS 0x02 +#define SMB2_GETINFO_SECURITY 0x03 +#define SMB2_GETINFO_QUOTA 0x04 + +#define SMB2_GETINFO_ADD_OWNER_SECURITY 0x01 +#define SMB2_GETINFO_ADD_GROUP_SECURITY 0x02 +#define SMB2_GETINFO_ADD_DACL_SECURITY 0x04 +#define SMB2_GETINFO_ADD_SACL_SECURITY 0x08 +#define SMB2_GETINFO_ADD_LABEL_SECURITY 0x10 + +/* NOTE! the getinfo fs and file levels exactly match up with the + 'passthru' SMB levels, which are levels >= 1000. The SMB2 client + lib uses the names from the libcli/raw/ library */ + +struct smb2_getinfo { + struct { + /* static body buffer 40 (0x28) bytes */ + /* uint16_t buffer_code; 0x29 = 0x28 + 1 */ + uint8_t info_type; + uint8_t info_class; + uint32_t output_buffer_length; + /* uint32_t input_buffer_offset; */ + uint32_t reserved; + uint32_t input_buffer_length; + uint32_t additional_information; /* SMB2_GETINFO_ADD_* */ + uint32_t getinfo_flags; /* level specific */ + union smb_handle file; + DATA_BLOB blob; + } in; + + struct { + /* static body buffer 8 (0x08) bytes */ + /* uint16_t buffer_code; 0x09 = 0x08 + 1 */ + /* uint16_t blob_ofs; */ + /* uint16_t blob_size; */ + + /* dynamic body */ + DATA_BLOB blob; + } out; +}; + +struct smb2_setinfo { + struct { + uint16_t level; + uint32_t flags; + union smb_handle file; + DATA_BLOB blob; + } in; +}; + +struct cli_credentials; +struct tevent_context; +struct resolve_context; +struct gensec_settings; +#include "libcli/smb2/smb2_proto.h" diff --git a/source4/libcli/smb2/tcon.c b/source4/libcli/smb2/tcon.c new file mode 100644 index 0000000000..ec7152b264 --- /dev/null +++ b/source4/libcli/smb2/tcon.c @@ -0,0 +1,112 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 client tree handling + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" + +/* + initialise a smb2_session structure + */ +struct smb2_tree *smb2_tree_init(struct smb2_session *session, + TALLOC_CTX *parent_ctx, bool primary) +{ + struct smb2_tree *tree; + + tree = talloc_zero(parent_ctx, struct smb2_tree); + if (!session) { + return NULL; + } + if (primary) { + tree->session = talloc_steal(tree, session); + } else { + tree->session = talloc_reference(tree, session); + } + return tree; +} + +/* + send a tree connect +*/ +struct smb2_request *smb2_tree_connect_send(struct smb2_tree *tree, + struct smb2_tree_connect *io) +{ + struct smb2_request *req; + NTSTATUS status; + + req = smb2_request_init(tree->session->transport, SMB2_OP_TCON, + 0x08, true, 0); + if (req == NULL) return NULL; + + SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID, tree->session->uid); + req->session = tree->session; + + SSVAL(req->out.body, 0x02, io->in.reserved); + status = smb2_push_o16s16_string(&req->out, 0x04, io->in.path); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } + + smb2_transport_send(req); + + return req; +} + + +/* + recv a tree connect reply +*/ +NTSTATUS smb2_tree_connect_recv(struct smb2_request *req, struct smb2_tree_connect *io) +{ + if (!smb2_request_receive(req) || + smb2_request_is_error(req)) { + return smb2_request_destroy(req); + } + + SMB2_CHECK_PACKET_RECV(req, 0x10, false); + + io->out.tid = IVAL(req->in.hdr, SMB2_HDR_TID); + + io->out.share_type = CVAL(req->in.body, 0x02); + io->out.reserved = CVAL(req->in.body, 0x03); + io->out.flags = IVAL(req->in.body, 0x04); + io->out.capabilities= IVAL(req->in.body, 0x08); + io->out.access_mask = IVAL(req->in.body, 0x0C); + + if (io->out.capabilities & ~SMB2_CAP_ALL) { + DEBUG(0,("Unknown capabilities mask 0x%x\n", io->out.capabilities)); + } + if (io->out.flags & ~SMB2_SHAREFLAG_ALL) { + DEBUG(0,("Unknown tcon shareflag 0x%x\n", io->out.flags)); + } + + return smb2_request_destroy(req); +} + +/* + sync tree connect request +*/ +NTSTATUS smb2_tree_connect(struct smb2_tree *tree, struct smb2_tree_connect *io) +{ + struct smb2_request *req = smb2_tree_connect_send(tree, io); + return smb2_tree_connect_recv(req, io); +} diff --git a/source4/libcli/smb2/tdis.c b/source4/libcli/smb2/tdis.c new file mode 100644 index 0000000000..5adad9dc6e --- /dev/null +++ b/source4/libcli/smb2/tdis.c @@ -0,0 +1,65 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 client tdis handling + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" + +/* + send a tdis request +*/ +struct smb2_request *smb2_tdis_send(struct smb2_tree *tree) +{ + struct smb2_request *req; + + req = smb2_request_init_tree(tree, SMB2_OP_TDIS, 0x04, false, 0); + if (req == NULL) return NULL; + + SSVAL(req->out.body, 0x02, 0); + + smb2_transport_send(req); + + return req; +} + + +/* + recv a tdis reply +*/ +NTSTATUS smb2_tdis_recv(struct smb2_request *req) +{ + if (!smb2_request_receive(req) || + !smb2_request_is_ok(req)) { + return smb2_request_destroy(req); + } + + SMB2_CHECK_PACKET_RECV(req, 0x04, false); + return smb2_request_destroy(req); +} + +/* + sync tdis request +*/ +NTSTATUS smb2_tdis(struct smb2_tree *tree) +{ + struct smb2_request *req = smb2_tdis_send(tree); + return smb2_tdis_recv(req); +} diff --git a/source4/libcli/smb2/transport.c b/source4/libcli/smb2/transport.c new file mode 100644 index 0000000000..e112544c62 --- /dev/null +++ b/source4/libcli/smb2/transport.c @@ -0,0 +1,417 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 client transport context management functions + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "lib/socket/socket.h" +#include "lib/events/events.h" +#include "lib/stream/packet.h" +#include "../lib/util/dlinklist.h" + + +/* + an event has happened on the socket +*/ +static void smb2_transport_event_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, void *private_data) +{ + struct smb2_transport *transport = talloc_get_type(private_data, + struct smb2_transport); + if (flags & EVENT_FD_READ) { + packet_recv(transport->packet); + return; + } + if (flags & EVENT_FD_WRITE) { + packet_queue_run(transport->packet); + } +} + +/* + destroy a transport + */ +static int transport_destructor(struct smb2_transport *transport) +{ + smb2_transport_dead(transport, NT_STATUS_LOCAL_DISCONNECT); + return 0; +} + + +/* + handle receive errors +*/ +static void smb2_transport_error(void *private_data, NTSTATUS status) +{ + struct smb2_transport *transport = talloc_get_type(private_data, + struct smb2_transport); + smb2_transport_dead(transport, status); +} + +static NTSTATUS smb2_transport_finish_recv(void *private_data, DATA_BLOB blob); + +/* + create a transport structure based on an established socket +*/ +struct smb2_transport *smb2_transport_init(struct smbcli_socket *sock, + TALLOC_CTX *parent_ctx, + struct smbcli_options *options) +{ + struct smb2_transport *transport; + + transport = talloc_zero(parent_ctx, struct smb2_transport); + if (!transport) return NULL; + + transport->socket = talloc_steal(transport, sock); + transport->options = *options; + + /* setup the stream -> packet parser */ + transport->packet = packet_init(transport); + if (transport->packet == NULL) { + talloc_free(transport); + return NULL; + } + packet_set_private(transport->packet, transport); + packet_set_socket(transport->packet, transport->socket->sock); + packet_set_callback(transport->packet, smb2_transport_finish_recv); + packet_set_full_request(transport->packet, packet_full_request_nbt); + packet_set_error_handler(transport->packet, smb2_transport_error); + packet_set_event_context(transport->packet, transport->socket->event.ctx); + packet_set_nofree(transport->packet); + + /* take over event handling from the socket layer - it only + handles events up until we are connected */ + talloc_free(transport->socket->event.fde); + transport->socket->event.fde = event_add_fd(transport->socket->event.ctx, + transport->socket, + socket_get_fd(transport->socket->sock), + EVENT_FD_READ, + smb2_transport_event_handler, + transport); + + packet_set_fde(transport->packet, transport->socket->event.fde); + packet_set_serialise(transport->packet); + + talloc_set_destructor(transport, transport_destructor); + + return transport; +} + +/* + mark the transport as dead +*/ +void smb2_transport_dead(struct smb2_transport *transport, NTSTATUS status) +{ + smbcli_sock_dead(transport->socket); + + if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) { + status = NT_STATUS_UNEXPECTED_NETWORK_ERROR; + } + + /* kill all pending receives */ + while (transport->pending_recv) { + struct smb2_request *req = transport->pending_recv; + req->state = SMB2_REQUEST_ERROR; + req->status = status; + DLIST_REMOVE(transport->pending_recv, req); + if (req->async.fn) { + req->async.fn(req); + } + } +} + +static NTSTATUS smb2_handle_oplock_break(struct smb2_transport *transport, + const DATA_BLOB *blob) +{ + uint8_t *hdr; + uint16_t opcode; + + hdr = blob->data+NBT_HDR_SIZE; + + if (blob->length < (SMB2_MIN_SIZE+0x18)) { + DEBUG(1,("Discarding smb2 oplock reply of size %u\n", + (unsigned)blob->length)); + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + opcode = SVAL(hdr, SMB2_HDR_OPCODE); + + if (opcode != SMB2_OP_BREAK) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + if (transport->oplock.handler) { + uint8_t *body = hdr+SMB2_HDR_BODY; + struct smb2_handle h; + uint8_t level; + + level = CVAL(body, 0x02); + smb2_pull_handle(body+0x08, &h); + + transport->oplock.handler(transport, &h, level, + transport->oplock.private_data); + } else { + DEBUG(5,("Got SMB2 oplock break with no handler\n")); + } + + return NT_STATUS_OK; +} + +/* + we have a full request in our receive buffer - match it to a pending request + and process + */ +static NTSTATUS smb2_transport_finish_recv(void *private_data, DATA_BLOB blob) +{ + struct smb2_transport *transport = talloc_get_type(private_data, + struct smb2_transport); + uint8_t *buffer, *hdr; + int len; + struct smb2_request *req = NULL; + uint64_t seqnum; + uint32_t flags; + uint16_t buffer_code; + uint32_t dynamic_size; + uint32_t i; + NTSTATUS status; + + buffer = blob.data; + len = blob.length; + + hdr = buffer+NBT_HDR_SIZE; + + if (len < SMB2_MIN_SIZE) { + DEBUG(1,("Discarding smb2 reply of size %d\n", len)); + goto error; + } + + flags = IVAL(hdr, SMB2_HDR_FLAGS); + seqnum = BVAL(hdr, SMB2_HDR_MESSAGE_ID); + + /* see MS-SMB2 3.2.5.19 */ + if (seqnum == UINT64_MAX) { + return smb2_handle_oplock_break(transport, &blob); + } + + /* match the incoming request against the list of pending requests */ + for (req=transport->pending_recv; req; req=req->next) { + if (req->seqnum == seqnum) break; + } + + if (!req) { + DEBUG(1,("Discarding unmatched reply with seqnum 0x%llx op %d\n", + (long long)seqnum, SVAL(hdr, SMB2_HDR_OPCODE))); + goto error; + } + + /* fill in the 'in' portion of the matching request */ + req->in.buffer = buffer; + talloc_steal(req, buffer); + req->in.size = len; + req->in.allocated = req->in.size; + + req->in.hdr = hdr; + req->in.body = hdr+SMB2_HDR_BODY; + req->in.body_size = req->in.size - (SMB2_HDR_BODY+NBT_HDR_SIZE); + req->status = NT_STATUS(IVAL(hdr, SMB2_HDR_STATUS)); + + if ((flags & SMB2_HDR_FLAG_ASYNC) && + NT_STATUS_EQUAL(req->status, STATUS_PENDING)) { + req->cancel.can_cancel = true; + req->cancel.pending_id = IVAL(hdr, SMB2_HDR_PID); + for (i=0; i< req->cancel.do_cancel; i++) { + smb2_cancel(req); + } + talloc_free(buffer); + return NT_STATUS_OK; + } + + if (req->session && req->session->signing_active) { + status = smb2_check_signature(&req->in, + req->session->session_key); + if (!NT_STATUS_IS_OK(status)) { + /* the spec says to ignore packets with a bad signature */ + talloc_free(buffer); + return status; + } + } + + buffer_code = SVAL(req->in.body, 0); + req->in.body_fixed = (buffer_code & ~1); + req->in.dynamic = NULL; + dynamic_size = req->in.body_size - req->in.body_fixed; + if (dynamic_size != 0 && (buffer_code & 1)) { + req->in.dynamic = req->in.body + req->in.body_fixed; + if (smb2_oob(&req->in, req->in.dynamic, dynamic_size)) { + DEBUG(1,("SMB2 request invalid dynamic size 0x%x\n", + dynamic_size)); + goto error; + } + } + + smb2_setup_bufinfo(req); + + DEBUG(2, ("SMB2 RECV seqnum=0x%llx\n", (long long)req->seqnum)); + dump_data(5, req->in.body, req->in.body_size); + + /* if this request has an async handler then call that to + notify that the reply has been received. This might destroy + the request so it must happen last */ + DLIST_REMOVE(transport->pending_recv, req); + req->state = SMB2_REQUEST_DONE; + if (req->async.fn) { + req->async.fn(req); + } + return NT_STATUS_OK; + +error: + dump_data(5, buffer, len); + if (req) { + DLIST_REMOVE(transport->pending_recv, req); + req->state = SMB2_REQUEST_ERROR; + if (req->async.fn) { + req->async.fn(req); + } + } else { + talloc_free(buffer); + } + return NT_STATUS_UNSUCCESSFUL; +} + +/* + handle timeouts of individual smb requests +*/ +static void smb2_timeout_handler(struct tevent_context *ev, struct tevent_timer *te, + struct timeval t, void *private_data) +{ + struct smb2_request *req = talloc_get_type(private_data, struct smb2_request); + + if (req->state == SMB2_REQUEST_RECV) { + DLIST_REMOVE(req->transport->pending_recv, req); + } + req->status = NT_STATUS_IO_TIMEOUT; + req->state = SMB2_REQUEST_ERROR; + if (req->async.fn) { + req->async.fn(req); + } +} + + +/* + destroy a request +*/ +static int smb2_request_destructor(struct smb2_request *req) +{ + if (req->state == SMB2_REQUEST_RECV) { + DLIST_REMOVE(req->transport->pending_recv, req); + } + return 0; +} + + +/* + put a request into the send queue +*/ +void smb2_transport_send(struct smb2_request *req) +{ + DATA_BLOB blob; + NTSTATUS status; + + _smb2_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE); + + DEBUG(2, ("SMB2 send seqnum=0x%llx\n", (long long)req->seqnum)); + dump_data(5, req->out.body, req->out.body_size); + + /* check if the transport is dead */ + if (req->transport->socket->sock == NULL) { + req->state = SMB2_REQUEST_ERROR; + req->status = NT_STATUS_NET_WRITE_FAULT; + return; + } + + /* possibly sign the message */ + if (req->session && req->session->signing_active) { + status = smb2_sign_message(&req->out, req->session->session_key); + if (!NT_STATUS_IS_OK(status)) { + req->state = SMB2_REQUEST_ERROR; + req->status = status; + return; + } + } + + blob = data_blob_const(req->out.buffer, req->out.size); + status = packet_send(req->transport->packet, blob); + if (!NT_STATUS_IS_OK(status)) { + req->state = SMB2_REQUEST_ERROR; + req->status = status; + return; + } + + req->state = SMB2_REQUEST_RECV; + DLIST_ADD(req->transport->pending_recv, req); + + /* add a timeout */ + if (req->transport->options.request_timeout) { + event_add_timed(req->transport->socket->event.ctx, req, + timeval_current_ofs(req->transport->options.request_timeout, 0), + smb2_timeout_handler, req); + } + + talloc_set_destructor(req, smb2_request_destructor); +} + +static void idle_handler(struct tevent_context *ev, + struct tevent_timer *te, struct timeval t, void *private_data) +{ + struct smb2_transport *transport = talloc_get_type(private_data, + struct smb2_transport); + struct timeval next = timeval_add(&t, 0, transport->idle.period); + transport->socket->event.te = event_add_timed(transport->socket->event.ctx, + transport, + next, + idle_handler, transport); + transport->idle.func(transport, transport->idle.private_data); +} + +/* + setup the idle handler for a transport + the period is in microseconds +*/ +void smb2_transport_idle_handler(struct smb2_transport *transport, + void (*idle_func)(struct smb2_transport *, void *), + uint64_t period, + void *private_data) +{ + transport->idle.func = idle_func; + transport->idle.private_data = private_data; + transport->idle.period = period; + + if (transport->socket->event.te != NULL) { + talloc_free(transport->socket->event.te); + } + + transport->socket->event.te = event_add_timed(transport->socket->event.ctx, + transport, + timeval_current_ofs(0, period), + idle_handler, transport); +} diff --git a/source4/libcli/smb2/util.c b/source4/libcli/smb2/util.c new file mode 100644 index 0000000000..a360d8fbdf --- /dev/null +++ b/source4/libcli/smb2/util.c @@ -0,0 +1,216 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 client utility functions + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "libcli/smb_composite/smb_composite.h" + +/* + simple close wrapper with SMB2 +*/ +NTSTATUS smb2_util_close(struct smb2_tree *tree, struct smb2_handle h) +{ + struct smb2_close c; + + ZERO_STRUCT(c); + c.in.file.handle = h; + + return smb2_close(tree, &c); +} + +/* + unlink a file with SMB2 +*/ +NTSTATUS smb2_util_unlink(struct smb2_tree *tree, const char *fname) +{ + union smb_unlink io; + + ZERO_STRUCT(io); + io.unlink.in.pattern = fname; + + return smb2_composite_unlink(tree, &io); +} + + +/* + rmdir with SMB2 +*/ +NTSTATUS smb2_util_rmdir(struct smb2_tree *tree, const char *dname) +{ + struct smb_rmdir io; + + ZERO_STRUCT(io); + io.in.path = dname; + + return smb2_composite_rmdir(tree, &io); +} + + +/* + mkdir with SMB2 +*/ +NTSTATUS smb2_util_mkdir(struct smb2_tree *tree, const char *dname) +{ + union smb_mkdir io; + + ZERO_STRUCT(io); + io.mkdir.level = RAW_MKDIR_MKDIR; + io.mkdir.in.path = dname; + + return smb2_composite_mkdir(tree, &io); +} + + +/* + set file attribute with SMB2 +*/ +NTSTATUS smb2_util_setatr(struct smb2_tree *tree, const char *name, uint32_t attrib) +{ + union smb_setfileinfo io; + + ZERO_STRUCT(io); + io.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION; + io.basic_info.in.file.path = name; + io.basic_info.in.attrib = attrib; + + return smb2_composite_setpathinfo(tree, &io); +} + + + + +/* + recursively descend a tree deleting all files + returns the number of files deleted, or -1 on error +*/ +int smb2_deltree(struct smb2_tree *tree, const char *dname) +{ + NTSTATUS status; + uint32_t total_deleted = 0; + uint_t count, i; + union smb_search_data *list; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct smb2_find f; + struct smb2_create create_parm; + + /* it might be a file */ + status = smb2_util_unlink(tree, dname); + if (NT_STATUS_IS_OK(status)) { + talloc_free(tmp_ctx); + return 1; + } + if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) || + NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_NOT_FOUND) || + NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_FILE)) { + talloc_free(tmp_ctx); + return 0; + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_CANNOT_DELETE)) { + /* it could be read-only */ + status = smb2_util_setatr(tree, dname, FILE_ATTRIBUTE_NORMAL); + status = smb2_util_unlink(tree, dname); + } + if (NT_STATUS_IS_OK(status)) { + talloc_free(tmp_ctx); + return 1; + } + + ZERO_STRUCT(create_parm); + create_parm.in.desired_access = SEC_FILE_READ_DATA; + create_parm.in.share_access = + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + create_parm.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + create_parm.in.create_disposition = NTCREATEX_DISP_OPEN; + create_parm.in.fname = dname; + + status = smb2_create(tree, tmp_ctx, &create_parm); + if (NT_STATUS_IS_ERR(status)) { + DEBUG(2,("Failed to open %s - %s\n", dname, nt_errstr(status))); + talloc_free(tmp_ctx); + return -1; + } + + + ZERO_STRUCT(f); + f.in.file.handle = create_parm.out.file.handle; + f.in.max_response_size = 0x10000; + f.in.level = SMB2_FIND_NAME_INFO; + f.in.pattern = "*"; + + status = smb2_find_level(tree, tmp_ctx, &f, &count, &list); + if (NT_STATUS_IS_ERR(status)) { + DEBUG(2,("Failed to list %s - %s\n", + dname, nt_errstr(status))); + smb2_util_close(tree, create_parm.out.file.handle); + talloc_free(tmp_ctx); + return -1; + } + + for (i=0;i<count;i++) { + char *name; + if (strcmp(".", list[i].name_info.name.s) == 0 || + strcmp("..", list[i].name_info.name.s) == 0) { + continue; + } + name = talloc_asprintf(tmp_ctx, "%s\\%s", dname, list[i].name_info.name.s); + status = smb2_util_unlink(tree, name); + if (NT_STATUS_EQUAL(status, NT_STATUS_CANNOT_DELETE)) { + /* it could be read-only */ + status = smb2_util_setatr(tree, name, FILE_ATTRIBUTE_NORMAL); + status = smb2_util_unlink(tree, name); + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) { + int ret; + ret = smb2_deltree(tree, name); + if (ret > 0) total_deleted += ret; + } + talloc_free(name); + if (NT_STATUS_IS_OK(status)) { + total_deleted++; + } + } + + smb2_util_close(tree, create_parm.out.file.handle); + + status = smb2_util_rmdir(tree, dname); + if (NT_STATUS_EQUAL(status, NT_STATUS_CANNOT_DELETE)) { + /* it could be read-only */ + status = smb2_util_setatr(tree, dname, FILE_ATTRIBUTE_NORMAL); + status = smb2_util_rmdir(tree, dname); + } + + if (NT_STATUS_IS_ERR(status)) { + DEBUG(2,("Failed to delete %s - %s\n", + dname, nt_errstr(status))); + talloc_free(tmp_ctx); + return -1; + } + + talloc_free(tmp_ctx); + + return total_deleted; +} diff --git a/source4/libcli/smb2/write.c b/source4/libcli/smb2/write.c new file mode 100644 index 0000000000..bc283370d7 --- /dev/null +++ b/source4/libcli/smb2/write.c @@ -0,0 +1,81 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 client write call + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" + +/* + send a write request +*/ +struct smb2_request *smb2_write_send(struct smb2_tree *tree, struct smb2_write *io) +{ + NTSTATUS status; + struct smb2_request *req; + + req = smb2_request_init_tree(tree, SMB2_OP_WRITE, 0x30, true, io->in.data.length); + if (req == NULL) return NULL; + + status = smb2_push_o16s32_blob(&req->out, 0x02, io->in.data); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } + + SBVAL(req->out.body, 0x08, io->in.offset); + smb2_push_handle(req->out.body+0x10, &io->in.file.handle); + + SBVAL(req->out.body, 0x20, io->in.unknown1); + SBVAL(req->out.body, 0x28, io->in.unknown2); + + smb2_transport_send(req); + + return req; +} + + +/* + recv a write reply +*/ +NTSTATUS smb2_write_recv(struct smb2_request *req, struct smb2_write *io) +{ + if (!smb2_request_receive(req) || + smb2_request_is_error(req)) { + return smb2_request_destroy(req); + } + + SMB2_CHECK_PACKET_RECV(req, 0x10, true); + + io->out._pad = SVAL(req->in.body, 0x02); + io->out.nwritten = IVAL(req->in.body, 0x04); + io->out.unknown1 = BVAL(req->in.body, 0x08); + + return smb2_request_destroy(req); +} + +/* + sync write request +*/ +NTSTATUS smb2_write(struct smb2_tree *tree, struct smb2_write *io) +{ + struct smb2_request *req = smb2_write_send(tree, io); + return smb2_write_recv(req, io); +} diff --git a/source4/libcli/smb_composite/appendacl.c b/source4/libcli/smb_composite/appendacl.c new file mode 100644 index 0000000000..69ed62a106 --- /dev/null +++ b/source4/libcli/smb_composite/appendacl.c @@ -0,0 +1,313 @@ +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/composite/composite.h" +#include "libcli/security/security.h" +#include "libcli/smb_composite/smb_composite.h" + +/* the stages of this call */ +enum appendacl_stage {APPENDACL_OPENPATH, APPENDACL_GET, + APPENDACL_SET, APPENDACL_GETAGAIN, APPENDACL_CLOSEPATH}; + +static void appendacl_handler(struct smbcli_request *req); + +struct appendacl_state { + enum appendacl_stage stage; + struct smb_composite_appendacl *io; + + union smb_open *io_open; + union smb_setfileinfo *io_setfileinfo; + union smb_fileinfo *io_fileinfo; + + struct smbcli_request *req; +}; + + +static NTSTATUS appendacl_open(struct composite_context *c, + struct smb_composite_appendacl *io) +{ + struct appendacl_state *state = talloc_get_type(c->private_data, struct appendacl_state); + struct smbcli_tree *tree = state->req->tree; + NTSTATUS status; + + status = smb_raw_open_recv(state->req, c, state->io_open); + NT_STATUS_NOT_OK_RETURN(status); + + /* setup structures for getting fileinfo */ + state->io_fileinfo = talloc(c, union smb_fileinfo); + NT_STATUS_HAVE_NO_MEMORY(state->io_fileinfo); + + state->io_fileinfo->query_secdesc.level = RAW_FILEINFO_SEC_DESC; + state->io_fileinfo->query_secdesc.in.file.fnum = state->io_open->ntcreatex.out.file.fnum; + state->io_fileinfo->query_secdesc.in.secinfo_flags = SECINFO_DACL; + + state->req = smb_raw_fileinfo_send(tree, state->io_fileinfo); + NT_STATUS_HAVE_NO_MEMORY(state->req); + + /* set the handler */ + state->req->async.fn = appendacl_handler; + state->req->async.private_data = c; + state->stage = APPENDACL_GET; + + talloc_free (state->io_open); + + return NT_STATUS_OK; +} + +static NTSTATUS appendacl_get(struct composite_context *c, + struct smb_composite_appendacl *io) +{ + struct appendacl_state *state = talloc_get_type(c->private_data, struct appendacl_state); + struct smbcli_tree *tree = state->req->tree; + int i; + NTSTATUS status; + + status = smb_raw_fileinfo_recv(state->req, state->io_fileinfo, state->io_fileinfo); + NT_STATUS_NOT_OK_RETURN(status); + + /* setup structures for setting fileinfo */ + state->io_setfileinfo = talloc(c, union smb_setfileinfo); + NT_STATUS_HAVE_NO_MEMORY(state->io_setfileinfo); + + state->io_setfileinfo->set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + state->io_setfileinfo->set_secdesc.in.file.fnum = state->io_fileinfo->query_secdesc.in.file.fnum; + + state->io_setfileinfo->set_secdesc.in.secinfo_flags = SECINFO_DACL; + state->io_setfileinfo->set_secdesc.in.sd = state->io_fileinfo->query_secdesc.out.sd; + talloc_steal(state->io_setfileinfo, state->io_setfileinfo->set_secdesc.in.sd); + + /* append all aces from io->in.sd->dacl to new security descriptor */ + if (io->in.sd->dacl != NULL) { + for (i = 0; i < io->in.sd->dacl->num_aces; i++) { + security_descriptor_dacl_add(state->io_setfileinfo->set_secdesc.in.sd, + &(io->in.sd->dacl->aces[i])); + } + } + + status = smb_raw_setfileinfo(tree, state->io_setfileinfo); + NT_STATUS_NOT_OK_RETURN(status); + + state->req = smb_raw_setfileinfo_send(tree, state->io_setfileinfo); + NT_STATUS_HAVE_NO_MEMORY(state->req); + + /* call handler when done setting new security descriptor on file */ + state->req->async.fn = appendacl_handler; + state->req->async.private_data = c; + state->stage = APPENDACL_SET; + + talloc_free (state->io_fileinfo); + + return NT_STATUS_OK; +} + +static NTSTATUS appendacl_set(struct composite_context *c, + struct smb_composite_appendacl *io) +{ + struct appendacl_state *state = talloc_get_type(c->private_data, struct appendacl_state); + struct smbcli_tree *tree = state->req->tree; + NTSTATUS status; + + status = smbcli_request_simple_recv(state->req); + NT_STATUS_NOT_OK_RETURN(status); + + /* setup structures for getting fileinfo */ + state->io_fileinfo = talloc(c, union smb_fileinfo); + NT_STATUS_HAVE_NO_MEMORY(state->io_fileinfo); + + + state->io_fileinfo->query_secdesc.level = RAW_FILEINFO_SEC_DESC; + state->io_fileinfo->query_secdesc.in.file.fnum = state->io_setfileinfo->set_secdesc.in.file.fnum; + state->io_fileinfo->query_secdesc.in.secinfo_flags = SECINFO_DACL; + + state->req = smb_raw_fileinfo_send(tree, state->io_fileinfo); + NT_STATUS_HAVE_NO_MEMORY(state->req); + + /* set the handler */ + state->req->async.fn = appendacl_handler; + state->req->async.private_data = c; + state->stage = APPENDACL_GETAGAIN; + + talloc_free (state->io_setfileinfo); + + return NT_STATUS_OK; +} + + +static NTSTATUS appendacl_getagain(struct composite_context *c, + struct smb_composite_appendacl *io) +{ + struct appendacl_state *state = talloc_get_type(c->private_data, struct appendacl_state); + struct smbcli_tree *tree = state->req->tree; + union smb_close *io_close; + NTSTATUS status; + + status = smb_raw_fileinfo_recv(state->req, c, state->io_fileinfo); + NT_STATUS_NOT_OK_RETURN(status); + + io->out.sd = state->io_fileinfo->query_secdesc.out.sd; + + /* setup structures for close */ + io_close = talloc(c, union smb_close); + NT_STATUS_HAVE_NO_MEMORY(io_close); + + io_close->close.level = RAW_CLOSE_CLOSE; + io_close->close.in.file.fnum = state->io_fileinfo->query_secdesc.in.file.fnum; + io_close->close.in.write_time = 0; + + state->req = smb_raw_close_send(tree, io_close); + NT_STATUS_HAVE_NO_MEMORY(state->req); + + /* call the handler */ + state->req->async.fn = appendacl_handler; + state->req->async.private_data = c; + state->stage = APPENDACL_CLOSEPATH; + + talloc_free (state->io_fileinfo); + + return NT_STATUS_OK; +} + + + +static NTSTATUS appendacl_close(struct composite_context *c, + struct smb_composite_appendacl *io) +{ + struct appendacl_state *state = talloc_get_type(c->private_data, struct appendacl_state); + NTSTATUS status; + + status = smbcli_request_simple_recv(state->req); + NT_STATUS_NOT_OK_RETURN(status); + + c->state = COMPOSITE_STATE_DONE; + + return NT_STATUS_OK; +} + +/* + handler for completion of a sub-request in appendacl +*/ +static void appendacl_handler(struct smbcli_request *req) +{ + struct composite_context *c = (struct composite_context *)req->async.private_data; + struct appendacl_state *state = talloc_get_type(c->private_data, struct appendacl_state); + + /* when this handler is called, the stage indicates what + call has just finished */ + switch (state->stage) { + case APPENDACL_OPENPATH: + c->status = appendacl_open(c, state->io); + break; + + case APPENDACL_GET: + c->status = appendacl_get(c, state->io); + break; + + case APPENDACL_SET: + c->status = appendacl_set(c, state->io); + break; + + case APPENDACL_GETAGAIN: + c->status = appendacl_getagain(c, state->io); + break; + + case APPENDACL_CLOSEPATH: + c->status = appendacl_close(c, state->io); + break; + } + + /* We should get here if c->state >= SMBCLI_REQUEST_DONE */ + if (!NT_STATUS_IS_OK(c->status)) { + c->state = COMPOSITE_STATE_ERROR; + } + + if (c->state >= COMPOSITE_STATE_DONE && + c->async.fn) { + c->async.fn(c); + } +} + + +/* + composite appendacl call - does an open followed by a number setfileinfo, + after that new acls are read with fileinfo, followed by a close +*/ +struct composite_context *smb_composite_appendacl_send(struct smbcli_tree *tree, + struct smb_composite_appendacl *io) +{ + struct composite_context *c; + struct appendacl_state *state; + + c = talloc_zero(tree, struct composite_context); + if (c == NULL) goto failed; + + state = talloc(c, struct appendacl_state); + if (state == NULL) goto failed; + + state->io = io; + + c->private_data = state; + c->state = COMPOSITE_STATE_IN_PROGRESS; + c->event_ctx = tree->session->transport->socket->event.ctx; + + /* setup structures for opening file */ + state->io_open = talloc_zero(c, union smb_open); + if (state->io_open == NULL) goto failed; + + state->io_open->ntcreatex.level = RAW_OPEN_NTCREATEX; + state->io_open->ntcreatex.in.root_fid = 0; + state->io_open->ntcreatex.in.flags = 0; + state->io_open->ntcreatex.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + state->io_open->ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + state->io_open->ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + state->io_open->ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + state->io_open->ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + state->io_open->ntcreatex.in.security_flags = 0; + state->io_open->ntcreatex.in.fname = io->in.fname; + + /* send the open on its way */ + state->req = smb_raw_open_send(tree, state->io_open); + if (state->req == NULL) goto failed; + + /* setup the callback handler */ + state->req->async.fn = appendacl_handler; + state->req->async.private_data = c; + state->stage = APPENDACL_OPENPATH; + + return c; + +failed: + talloc_free(c); + return NULL; +} + + +/* + composite appendacl call - recv side +*/ +NTSTATUS smb_composite_appendacl_recv(struct composite_context *c, TALLOC_CTX *mem_ctx) +{ + NTSTATUS status; + + status = composite_wait(c); + + if (NT_STATUS_IS_OK(status)) { + struct appendacl_state *state = talloc_get_type(c->private_data, struct appendacl_state); + state->io->out.sd = security_descriptor_copy (mem_ctx, state->io->out.sd); + } + + talloc_free(c); + return status; +} + + +/* + composite appendacl call - sync interface +*/ +NTSTATUS smb_composite_appendacl(struct smbcli_tree *tree, + TALLOC_CTX *mem_ctx, + struct smb_composite_appendacl *io) +{ + struct composite_context *c = smb_composite_appendacl_send(tree, io); + return smb_composite_appendacl_recv(c, mem_ctx); +} + diff --git a/source4/libcli/smb_composite/connect.c b/source4/libcli/smb_composite/connect.c new file mode 100644 index 0000000000..3db777ddc8 --- /dev/null +++ b/source4/libcli/smb_composite/connect.c @@ -0,0 +1,520 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Andrew Tridgell 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/>. +*/ +/* + a composite API for making a full SMB connection +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/composite/composite.h" +#include "libcli/smb_composite/smb_composite.h" +#include "lib/events/events.h" +#include "libcli/resolve/resolve.h" +#include "auth/credentials/credentials.h" +#include "librpc/gen_ndr/ndr_nbt.h" +#include "param/param.h" + +/* the stages of this call */ +enum connect_stage {CONNECT_RESOLVE, + CONNECT_SOCKET, + CONNECT_SESSION_REQUEST, + CONNECT_NEGPROT, + CONNECT_SESSION_SETUP, + CONNECT_SESSION_SETUP_ANON, + CONNECT_TCON, + CONNECT_DONE +}; + +struct connect_state { + enum connect_stage stage; + struct smbcli_socket *sock; + struct smbcli_transport *transport; + struct smbcli_session *session; + struct smb_composite_connect *io; + union smb_tcon *io_tcon; + struct smb_composite_sesssetup *io_setup; + struct smbcli_request *req; + struct composite_context *creq; +}; + + +static void request_handler(struct smbcli_request *); +static void composite_handler(struct composite_context *); + +/* + a tree connect request has completed +*/ +static NTSTATUS connect_tcon(struct composite_context *c, + struct smb_composite_connect *io) +{ + struct connect_state *state = talloc_get_type(c->private_data, struct connect_state); + NTSTATUS status; + + status = smb_raw_tcon_recv(state->req, c, state->io_tcon); + NT_STATUS_NOT_OK_RETURN(status); + + io->out.tree->tid = state->io_tcon->tconx.out.tid; + if (state->io_tcon->tconx.out.dev_type) { + io->out.tree->device = talloc_strdup(io->out.tree, + state->io_tcon->tconx.out.dev_type); + } + if (state->io_tcon->tconx.out.fs_type) { + io->out.tree->fs_type = talloc_strdup(io->out.tree, + state->io_tcon->tconx.out.fs_type); + } + + state->stage = CONNECT_DONE; + + return NT_STATUS_OK; +} + + +/* + a session setup request with anonymous fallback has completed +*/ +static NTSTATUS connect_session_setup_anon(struct composite_context *c, + struct smb_composite_connect *io) +{ + struct connect_state *state = talloc_get_type(c->private_data, struct connect_state); + NTSTATUS status; + + status = smb_composite_sesssetup_recv(state->creq); + NT_STATUS_NOT_OK_RETURN(status); + + io->out.anonymous_fallback_done = true; + + state->session->vuid = state->io_setup->out.vuid; + + /* setup for a tconx */ + state->io_tcon = talloc(c, union smb_tcon); + NT_STATUS_HAVE_NO_MEMORY(state->io_tcon); + + /* connect to a share using a tree connect */ + state->io_tcon->generic.level = RAW_TCON_TCONX; + state->io_tcon->tconx.in.flags = 0; + state->io_tcon->tconx.in.password = data_blob(NULL, 0); + + state->io_tcon->tconx.in.path = talloc_asprintf(state->io_tcon, + "\\\\%s\\%s", + io->in.called_name, + io->in.service); + NT_STATUS_HAVE_NO_MEMORY(state->io_tcon->tconx.in.path); + if (!io->in.service_type) { + state->io_tcon->tconx.in.device = "?????"; + } else { + state->io_tcon->tconx.in.device = io->in.service_type; + } + + state->req = smb_raw_tcon_send(io->out.tree, state->io_tcon); + NT_STATUS_HAVE_NO_MEMORY(state->req); + if (state->req->state == SMBCLI_REQUEST_ERROR) { + return state->req->status; + } + + state->req->async.fn = request_handler; + state->req->async.private_data = c; + state->stage = CONNECT_TCON; + + return NT_STATUS_OK; +} + +/* + a session setup request has completed +*/ +static NTSTATUS connect_session_setup(struct composite_context *c, + struct smb_composite_connect *io) +{ + struct connect_state *state = talloc_get_type(c->private_data, struct connect_state); + NTSTATUS status; + + status = smb_composite_sesssetup_recv(state->creq); + + if (!NT_STATUS_IS_OK(status) && + !cli_credentials_is_anonymous(state->io->in.credentials) && + io->in.fallback_to_anonymous) { + + state->io_setup->in.credentials = cli_credentials_init(state); + NT_STATUS_HAVE_NO_MEMORY(state->io_setup->in.credentials); + cli_credentials_set_workstation(state->io_setup->in.credentials, + cli_credentials_get_workstation(state->io->in.credentials), + CRED_SPECIFIED); + cli_credentials_set_anonymous(state->io_setup->in.credentials); + + /* If the preceding attempt was with extended security, we + * have been given a uid in the NTLMSSP_CHALLENGE reply. This + * would lead to an invalid uid in the anonymous fallback */ + state->session->vuid = 0; + data_blob_free(&state->session->user_session_key); + talloc_free(state->session->gensec); + state->session->gensec = NULL; + + state->creq = smb_composite_sesssetup_send(state->session, + state->io_setup); + NT_STATUS_HAVE_NO_MEMORY(state->creq); + if (state->creq->state == COMPOSITE_STATE_ERROR) { + return state->creq->status; + } + state->creq->async.fn = composite_handler; + state->creq->async.private_data = c; + state->stage = CONNECT_SESSION_SETUP_ANON; + + return NT_STATUS_OK; + } + + NT_STATUS_NOT_OK_RETURN(status); + + state->session->vuid = state->io_setup->out.vuid; + + /* If we don't have a remote share name then this indicates that + * we don't want to do a tree connect */ + if (!io->in.service) { + state->stage = CONNECT_DONE; + return NT_STATUS_OK; + } + + state->io_tcon = talloc(c, union smb_tcon); + NT_STATUS_HAVE_NO_MEMORY(state->io_tcon); + + /* connect to a share using a tree connect */ + state->io_tcon->generic.level = RAW_TCON_TCONX; + state->io_tcon->tconx.in.flags = 0; + state->io_tcon->tconx.in.password = data_blob(NULL, 0); + + state->io_tcon->tconx.in.path = talloc_asprintf(state->io_tcon, + "\\\\%s\\%s", + io->in.called_name, + io->in.service); + NT_STATUS_HAVE_NO_MEMORY(state->io_tcon->tconx.in.path); + if (!io->in.service_type) { + state->io_tcon->tconx.in.device = "?????"; + } else { + state->io_tcon->tconx.in.device = io->in.service_type; + } + + state->req = smb_raw_tcon_send(io->out.tree, state->io_tcon); + NT_STATUS_HAVE_NO_MEMORY(state->req); + if (state->req->state == SMBCLI_REQUEST_ERROR) { + return state->req->status; + } + + state->req->async.fn = request_handler; + state->req->async.private_data = c; + state->stage = CONNECT_TCON; + + return NT_STATUS_OK; +} + +/* + a negprot request has completed +*/ +static NTSTATUS connect_negprot(struct composite_context *c, + struct smb_composite_connect *io) +{ + struct connect_state *state = talloc_get_type(c->private_data, struct connect_state); + NTSTATUS status; + + status = smb_raw_negotiate_recv(state->req); + NT_STATUS_NOT_OK_RETURN(status); + + /* next step is a session setup */ + state->session = smbcli_session_init(state->transport, state, true, io->in.session_options); + NT_STATUS_HAVE_NO_MEMORY(state->session); + + /* setup for a tconx (or at least have the structure ready to + * return, if we won't go that far) */ + io->out.tree = smbcli_tree_init(state->session, state, true); + NT_STATUS_HAVE_NO_MEMORY(io->out.tree); + + /* If we don't have any credentials then this indicates that + * we don't want to do a session setup */ + if (!io->in.credentials) { + state->stage = CONNECT_DONE; + return NT_STATUS_OK; + } + + state->io_setup = talloc(c, struct smb_composite_sesssetup); + NT_STATUS_HAVE_NO_MEMORY(state->io_setup); + + /* prepare a session setup to establish a security context */ + state->io_setup->in.sesskey = state->transport->negotiate.sesskey; + state->io_setup->in.capabilities = state->transport->negotiate.capabilities; + state->io_setup->in.credentials = io->in.credentials; + state->io_setup->in.workgroup = io->in.workgroup; + state->io_setup->in.gensec_settings = io->in.gensec_settings; + + state->creq = smb_composite_sesssetup_send(state->session, state->io_setup); + NT_STATUS_HAVE_NO_MEMORY(state->creq); + if (state->creq->state == COMPOSITE_STATE_ERROR) { + return state->creq->status; + } + + state->creq->async.fn = composite_handler; + state->creq->async.private_data = c; + + state->stage = CONNECT_SESSION_SETUP; + + return NT_STATUS_OK; +} + +/* + setup a negprot send +*/ +static NTSTATUS connect_send_negprot(struct composite_context *c, + struct smb_composite_connect *io) +{ + struct connect_state *state = talloc_get_type(c->private_data, struct connect_state); + + state->req = smb_raw_negotiate_send(state->transport, io->in.options.unicode, io->in.options.max_protocol); + NT_STATUS_HAVE_NO_MEMORY(state->req); + + state->req->async.fn = request_handler; + state->req->async.private_data = c; + state->stage = CONNECT_NEGPROT; + + return NT_STATUS_OK; +} + + +/* + a session request operation has completed +*/ +static NTSTATUS connect_session_request(struct composite_context *c, + struct smb_composite_connect *io) +{ + struct connect_state *state = talloc_get_type(c->private_data, struct connect_state); + NTSTATUS status; + + status = smbcli_transport_connect_recv(state->req); + NT_STATUS_NOT_OK_RETURN(status); + + /* next step is a negprot */ + return connect_send_negprot(c, io); +} + +/* + a socket connection operation has completed +*/ +static NTSTATUS connect_socket(struct composite_context *c, + struct smb_composite_connect *io) +{ + struct connect_state *state = talloc_get_type(c->private_data, struct connect_state); + NTSTATUS status; + struct nbt_name calling, called; + + status = smbcli_sock_connect_recv(state->creq, state, &state->sock); + NT_STATUS_NOT_OK_RETURN(status); + + /* the socket is up - we can initialise the smbcli transport layer */ + state->transport = smbcli_transport_init(state->sock, state, true, + &io->in.options, io->in.iconv_convenience); + NT_STATUS_HAVE_NO_MEMORY(state->transport); + + if (is_ipaddress(state->sock->hostname) && + (state->io->in.called_name != NULL)) { + /* If connecting to an IP address, we might want the real name + * of the host for later kerberos. The called name is a better + * approximation */ + state->sock->hostname = + talloc_strdup(state->sock, io->in.called_name); + NT_STATUS_HAVE_NO_MEMORY(state->sock->hostname); + } + + make_nbt_name_client(&calling, cli_credentials_get_workstation(io->in.credentials)); + + nbt_choose_called_name(state, &called, io->in.called_name, NBT_NAME_SERVER); + + /* we have a connected socket - next step is a session + request, if needed. Port 445 doesn't need it, so it goes + straight to the negprot */ + if (state->sock->port == 445) { + status = nbt_name_dup(state->transport, &called, + &state->transport->called); + NT_STATUS_NOT_OK_RETURN(status); + return connect_send_negprot(c, io); + } + + state->req = smbcli_transport_connect_send(state->transport, &calling, &called); + NT_STATUS_HAVE_NO_MEMORY(state->req); + + state->req->async.fn = request_handler; + state->req->async.private_data = c; + state->stage = CONNECT_SESSION_REQUEST; + + return NT_STATUS_OK; +} + + +/* + called when name resolution is finished +*/ +static NTSTATUS connect_resolve(struct composite_context *c, + struct smb_composite_connect *io) +{ + struct connect_state *state = talloc_get_type(c->private_data, struct connect_state); + NTSTATUS status; + const char *address; + + status = resolve_name_recv(state->creq, state, &address); + NT_STATUS_NOT_OK_RETURN(status); + + state->creq = smbcli_sock_connect_send(state, address, + io->in.dest_ports, + io->in.dest_host, + NULL, c->event_ctx, + io->in.socket_options); + NT_STATUS_HAVE_NO_MEMORY(state->creq); + + state->stage = CONNECT_SOCKET; + state->creq->async.private_data = c; + state->creq->async.fn = composite_handler; + + return NT_STATUS_OK; +} + + +/* + handle and dispatch state transitions +*/ +static void state_handler(struct composite_context *c) +{ + struct connect_state *state = talloc_get_type(c->private_data, struct connect_state); + + switch (state->stage) { + case CONNECT_RESOLVE: + c->status = connect_resolve(c, state->io); + break; + case CONNECT_SOCKET: + c->status = connect_socket(c, state->io); + break; + case CONNECT_SESSION_REQUEST: + c->status = connect_session_request(c, state->io); + break; + case CONNECT_NEGPROT: + c->status = connect_negprot(c, state->io); + break; + case CONNECT_SESSION_SETUP: + c->status = connect_session_setup(c, state->io); + break; + case CONNECT_SESSION_SETUP_ANON: + c->status = connect_session_setup_anon(c, state->io); + break; + case CONNECT_TCON: + c->status = connect_tcon(c, state->io); + break; + } + + if (state->stage == CONNECT_DONE) { + /* all done! */ + composite_done(c); + } else { + composite_is_ok(c); + } +} + + +/* + handler for completion of a smbcli_request sub-request +*/ +static void request_handler(struct smbcli_request *req) +{ + struct composite_context *c = talloc_get_type(req->async.private_data, + struct composite_context); + state_handler(c); +} + +/* + handler for completion of a smbcli_composite sub-request +*/ +static void composite_handler(struct composite_context *creq) +{ + struct composite_context *c = talloc_get_type(creq->async.private_data, + struct composite_context); + state_handler(c); +} + +/* + a function to establish a smbcli_tree from scratch +*/ +struct composite_context *smb_composite_connect_send(struct smb_composite_connect *io, + TALLOC_CTX *mem_ctx, + struct resolve_context *resolve_ctx, + struct tevent_context *event_ctx) +{ + struct composite_context *c; + struct connect_state *state; + struct nbt_name name; + + c = talloc_zero(mem_ctx, struct composite_context); + if (c == NULL) goto failed; + + c->event_ctx = talloc_reference(c, event_ctx); + if (c->event_ctx == NULL) goto failed; + + state = talloc_zero(c, struct connect_state); + if (state == NULL) goto failed; + + if (io->in.gensec_settings == NULL) goto failed; + state->io = io; + + c->state = COMPOSITE_STATE_IN_PROGRESS; + c->private_data = state; + + state->stage = CONNECT_RESOLVE; + make_nbt_name_server(&name, io->in.dest_host); + state->creq = resolve_name_send(resolve_ctx, &name, c->event_ctx); + + if (state->creq == NULL) goto failed; + state->creq->async.private_data = c; + state->creq->async.fn = composite_handler; + + return c; +failed: + talloc_free(c); + return NULL; +} + +/* + recv half of async composite connect code +*/ +NTSTATUS smb_composite_connect_recv(struct composite_context *c, TALLOC_CTX *mem_ctx) +{ + NTSTATUS status; + + status = composite_wait(c); + + if (NT_STATUS_IS_OK(status)) { + struct connect_state *state = talloc_get_type(c->private_data, struct connect_state); + talloc_steal(mem_ctx, state->io->out.tree); + } + + talloc_free(c); + return status; +} + +/* + sync version of smb_composite_connect +*/ +NTSTATUS smb_composite_connect(struct smb_composite_connect *io, TALLOC_CTX *mem_ctx, + struct resolve_context *resolve_ctx, + struct tevent_context *ev) +{ + struct composite_context *c = smb_composite_connect_send(io, mem_ctx, resolve_ctx, ev); + return smb_composite_connect_recv(c, mem_ctx); +} diff --git a/source4/libcli/smb_composite/fetchfile.c b/source4/libcli/smb_composite/fetchfile.c new file mode 100644 index 0000000000..210a2940b9 --- /dev/null +++ b/source4/libcli/smb_composite/fetchfile.c @@ -0,0 +1,192 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Volker Lendecke 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/>. +*/ +/* + a composite API for loading a whole file into memory +*/ + +#include "includes.h" +#include "libcli/composite/composite.h" +#include "libcli/smb_composite/smb_composite.h" +#include "libcli/resolve/resolve.h" + +enum fetchfile_stage {FETCHFILE_CONNECT, + FETCHFILE_READ}; + +struct fetchfile_state { + enum fetchfile_stage stage; + struct smb_composite_fetchfile *io; + struct composite_context *creq; + struct smb_composite_connect *connect; + struct smb_composite_loadfile *loadfile; +}; + +static void fetchfile_composite_handler(struct composite_context *req); + +static NTSTATUS fetchfile_connect(struct composite_context *c, + struct smb_composite_fetchfile *io) +{ + NTSTATUS status; + struct fetchfile_state *state; + state = talloc_get_type(c->private_data, struct fetchfile_state); + + status = smb_composite_connect_recv(state->creq, c); + NT_STATUS_NOT_OK_RETURN(status); + + state->loadfile = talloc(state, struct smb_composite_loadfile); + NT_STATUS_HAVE_NO_MEMORY(state->loadfile); + + state->loadfile->in.fname = io->in.filename; + + state->creq = smb_composite_loadfile_send(state->connect->out.tree, + state->loadfile); + NT_STATUS_HAVE_NO_MEMORY(state->creq); + + state->creq->async.private_data = c; + state->creq->async.fn = fetchfile_composite_handler; + + state->stage = FETCHFILE_READ; + + return NT_STATUS_OK; +} + +static NTSTATUS fetchfile_read(struct composite_context *c, + struct smb_composite_fetchfile *io) +{ + NTSTATUS status; + struct fetchfile_state *state; + state = talloc_get_type(c->private_data, struct fetchfile_state); + + status = smb_composite_loadfile_recv(state->creq, NULL); + NT_STATUS_NOT_OK_RETURN(status); + + io->out.data = state->loadfile->out.data; + io->out.size = state->loadfile->out.size; + + c->state = COMPOSITE_STATE_DONE; + if (c->async.fn) + c->async.fn(c); + + return NT_STATUS_OK; +} + +static void fetchfile_state_handler(struct composite_context *c) +{ + struct fetchfile_state *state; + NTSTATUS status = NT_STATUS_UNSUCCESSFUL; + + state = talloc_get_type(c->private_data, struct fetchfile_state); + + /* when this handler is called, the stage indicates what + call has just finished */ + switch (state->stage) { + case FETCHFILE_CONNECT: + status = fetchfile_connect(c, state->io); + break; + case FETCHFILE_READ: + status = fetchfile_read(c, state->io); + break; + } + + if (!NT_STATUS_IS_OK(status)) { + c->status = status; + c->state = COMPOSITE_STATE_ERROR; + if (c->async.fn) { + c->async.fn(c); + } + } +} + +static void fetchfile_composite_handler(struct composite_context *creq) +{ + struct composite_context *c = talloc_get_type(creq->async.private_data, + struct composite_context); + fetchfile_state_handler(c); +} + +struct composite_context *smb_composite_fetchfile_send(struct smb_composite_fetchfile *io, + struct tevent_context *event_ctx) +{ + struct composite_context *c; + struct fetchfile_state *state; + + c = talloc_zero(NULL, struct composite_context); + if (c == NULL) goto failed; + + state = talloc(c, struct fetchfile_state); + if (state == NULL) goto failed; + + state->connect = talloc(state, struct smb_composite_connect); + if (state->connect == NULL) goto failed; + + state->io = io; + + state->connect->in.dest_host = io->in.dest_host; + state->connect->in.dest_ports = io->in.ports; + state->connect->in.socket_options = io->in.socket_options; + state->connect->in.called_name = io->in.called_name; + state->connect->in.service = io->in.service; + state->connect->in.service_type = io->in.service_type; + state->connect->in.credentials = io->in.credentials; + state->connect->in.fallback_to_anonymous = false; + state->connect->in.workgroup = io->in.workgroup; + state->connect->in.gensec_settings = io->in.gensec_settings; + state->connect->in.iconv_convenience = io->in.iconv_convenience; + + state->connect->in.options = io->in.options; + state->connect->in.session_options = io->in.session_options; + + state->creq = smb_composite_connect_send(state->connect, state, + io->in.resolve_ctx, event_ctx); + if (state->creq == NULL) goto failed; + + state->creq->async.private_data = c; + state->creq->async.fn = fetchfile_composite_handler; + + c->state = COMPOSITE_STATE_IN_PROGRESS; + state->stage = FETCHFILE_CONNECT; + c->private_data = state; + + return c; + failed: + talloc_free(c); + return NULL; +} + +NTSTATUS smb_composite_fetchfile_recv(struct composite_context *c, + TALLOC_CTX *mem_ctx) +{ + NTSTATUS status; + + status = composite_wait(c); + + if (NT_STATUS_IS_OK(status)) { + struct fetchfile_state *state = talloc_get_type(c->private_data, struct fetchfile_state); + talloc_steal(mem_ctx, state->io->out.data); + } + + talloc_free(c); + return status; +} + +NTSTATUS smb_composite_fetchfile(struct smb_composite_fetchfile *io, + TALLOC_CTX *mem_ctx) +{ + struct composite_context *c = smb_composite_fetchfile_send(io, NULL); + return smb_composite_fetchfile_recv(c, mem_ctx); +} diff --git a/source4/libcli/smb_composite/fsinfo.c b/source4/libcli/smb_composite/fsinfo.c new file mode 100644 index 0000000000..3bc93b62ab --- /dev/null +++ b/source4/libcli/smb_composite/fsinfo.c @@ -0,0 +1,210 @@ +/* + a composite API for quering file system information +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/composite/composite.h" +#include "libcli/smb_composite/smb_composite.h" +#include "libcli/resolve/resolve.h" + +/* the stages of this call */ +enum fsinfo_stage {FSINFO_CONNECT, FSINFO_QUERY}; + + +static void fsinfo_raw_handler(struct smbcli_request *req); +static void fsinfo_composite_handler(struct composite_context *c); +static void fsinfo_state_handler(struct composite_context *c); + +struct fsinfo_state { + enum fsinfo_stage stage; + struct composite_context *creq; + struct smb_composite_fsinfo *io; + struct smb_composite_connect *connect; + union smb_fsinfo *fsinfo; + struct smbcli_tree *tree; + struct smbcli_request *req; +}; + +static NTSTATUS fsinfo_connect(struct composite_context *c, + struct smb_composite_fsinfo *io) +{ + NTSTATUS status; + struct fsinfo_state *state; + state = talloc_get_type(c->private_data, struct fsinfo_state); + + status = smb_composite_connect_recv(state->creq, c); + NT_STATUS_NOT_OK_RETURN(status); + + state->fsinfo = talloc(state, union smb_fsinfo); + NT_STATUS_HAVE_NO_MEMORY(state->fsinfo); + + state->fsinfo->generic.level = io->in.level; + + state->req = smb_raw_fsinfo_send(state->connect->out.tree, + state, + state->fsinfo); + NT_STATUS_HAVE_NO_MEMORY(state->req); + + state->req->async.private_data = c; + state->req->async.fn = fsinfo_raw_handler; + + state->stage = FSINFO_QUERY; + + return NT_STATUS_OK; +} + +static NTSTATUS fsinfo_query(struct composite_context *c, + struct smb_composite_fsinfo *io) +{ + NTSTATUS status; + struct fsinfo_state *state; + state = talloc_get_type(c->private_data, struct fsinfo_state); + + status = smb_raw_fsinfo_recv(state->req, state, state->fsinfo); + NT_STATUS_NOT_OK_RETURN(status); + + state->io->out.fsinfo = state->fsinfo; + + c->state = COMPOSITE_STATE_DONE; + + if (c->async.fn) + c->async.fn(c); + + return NT_STATUS_OK; + +} + +/* + handler for completion of a sub-request in fsinfo +*/ +static void fsinfo_state_handler(struct composite_context *creq) +{ + struct fsinfo_state *state = talloc_get_type(creq->private_data, struct fsinfo_state); + + /* when this handler is called, the stage indicates what + call has just finished */ + switch (state->stage) { + case FSINFO_CONNECT: + creq->status = fsinfo_connect(creq, state->io); + break; + + case FSINFO_QUERY: + creq->status = fsinfo_query(creq, state->io); + break; + } + + if (!NT_STATUS_IS_OK(creq->status)) { + creq->state = COMPOSITE_STATE_ERROR; + } + + if (creq->state >= COMPOSITE_STATE_DONE && creq->async.fn) { + creq->async.fn(creq); + } +} + +/* + As raw and composite handlers take different requests, we need to handlers + to adapt both for the same state machine in fsinfo_state_handler() +*/ +static void fsinfo_raw_handler(struct smbcli_request *req) +{ + struct composite_context *c = talloc_get_type(req->async.private_data, + struct composite_context); + fsinfo_state_handler(c); +} + +static void fsinfo_composite_handler(struct composite_context *creq) +{ + struct composite_context *c = talloc_get_type(creq->async.private_data, + struct composite_context); + fsinfo_state_handler(c); +} + +/* + composite fsinfo call - connects to a tree and queries a file system information +*/ +struct composite_context *smb_composite_fsinfo_send(struct smbcli_tree *tree, + struct smb_composite_fsinfo *io, + struct resolve_context *resolve_ctx) +{ + struct composite_context *c; + struct fsinfo_state *state; + + c = talloc_zero(tree, struct composite_context); + if (c == NULL) goto failed; + + state = talloc(c, struct fsinfo_state); + if (state == NULL) goto failed; + + state->io = io; + + state->connect = talloc(state, struct smb_composite_connect); + + if (state->connect == NULL) goto failed; + + state->connect->in.dest_host = io->in.dest_host; + state->connect->in.dest_ports = io->in.dest_ports; + state->connect->in.socket_options = io->in.socket_options; + state->connect->in.called_name = io->in.called_name; + state->connect->in.service = io->in.service; + state->connect->in.service_type = io->in.service_type; + state->connect->in.credentials = io->in.credentials; + state->connect->in.fallback_to_anonymous = false; + state->connect->in.workgroup = io->in.workgroup; + state->connect->in.iconv_convenience = io->in.iconv_convenience; + state->connect->in.gensec_settings = io->in.gensec_settings; + + state->connect->in.options = tree->session->transport->options; + state->connect->in.session_options = tree->session->options; + + c->state = COMPOSITE_STATE_IN_PROGRESS; + state->stage = FSINFO_CONNECT; + c->private_data = state; + + state->creq = smb_composite_connect_send(state->connect, state, + resolve_ctx, c->event_ctx); + + if (state->creq == NULL) goto failed; + + state->creq->async.private_data = c; + state->creq->async.fn = fsinfo_composite_handler; + + return c; +failed: + talloc_free(c); + return NULL; +} + +/* + composite fsinfo call - recv side +*/ +NTSTATUS smb_composite_fsinfo_recv(struct composite_context *c, TALLOC_CTX *mem_ctx) +{ + NTSTATUS status; + + status = composite_wait(c); + + if (NT_STATUS_IS_OK(status)) { + struct fsinfo_state *state = talloc_get_type(c->private_data, struct fsinfo_state); + talloc_steal(mem_ctx, state->io->out.fsinfo); + } + + talloc_free(c); + return status; +} + + +/* + composite fsinfo call - sync interface +*/ +NTSTATUS smb_composite_fsinfo(struct smbcli_tree *tree, + TALLOC_CTX *mem_ctx, + struct smb_composite_fsinfo *io, + struct resolve_context *resolve_ctx) +{ + struct composite_context *c = smb_composite_fsinfo_send(tree, io, resolve_ctx); + return smb_composite_fsinfo_recv(c, mem_ctx); +} + diff --git a/source4/libcli/smb_composite/loadfile.c b/source4/libcli/smb_composite/loadfile.c new file mode 100644 index 0000000000..994c29c77d --- /dev/null +++ b/source4/libcli/smb_composite/loadfile.c @@ -0,0 +1,293 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Andrew Tridgell 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/>. +*/ +/* + a composite API for loading a whole file into memory +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/composite/composite.h" +#include "libcli/smb_composite/smb_composite.h" + +/* the stages of this call */ +enum loadfile_stage {LOADFILE_OPEN, LOADFILE_READ, LOADFILE_CLOSE}; + + +static void loadfile_handler(struct smbcli_request *req); + +struct loadfile_state { + enum loadfile_stage stage; + struct smb_composite_loadfile *io; + struct smbcli_request *req; + union smb_open *io_open; + union smb_read *io_read; +}; + +/* + setup for the close +*/ +static NTSTATUS setup_close(struct composite_context *c, + struct smbcli_tree *tree, uint16_t fnum) +{ + struct loadfile_state *state = talloc_get_type(c->private_data, struct loadfile_state); + union smb_close *io_close; + + /* nothing to read, setup the close */ + io_close = talloc(c, union smb_close); + NT_STATUS_HAVE_NO_MEMORY(io_close); + + io_close->close.level = RAW_CLOSE_CLOSE; + io_close->close.in.file.fnum = fnum; + io_close->close.in.write_time = 0; + + state->req = smb_raw_close_send(tree, io_close); + NT_STATUS_HAVE_NO_MEMORY(state->req); + + /* call the handler again when the close is done */ + state->req->async.fn = loadfile_handler; + state->req->async.private_data = c; + state->stage = LOADFILE_CLOSE; + + return NT_STATUS_OK; +} + +/* + called when the open is done - pull the results and setup for the + first readx, or close if the file is zero size +*/ +static NTSTATUS loadfile_open(struct composite_context *c, + struct smb_composite_loadfile *io) +{ + struct loadfile_state *state = talloc_get_type(c->private_data, struct loadfile_state); + struct smbcli_tree *tree = state->req->tree; + NTSTATUS status; + + status = smb_raw_open_recv(state->req, c, state->io_open); + NT_STATUS_NOT_OK_RETURN(status); + + /* don't allow stupidly large loads */ + if (state->io_open->ntcreatex.out.size > 100*1000*1000) { + return NT_STATUS_INSUFFICIENT_RESOURCES; + } + + /* allocate space for the file data */ + io->out.size = state->io_open->ntcreatex.out.size; + io->out.data = talloc_array(c, uint8_t, io->out.size); + NT_STATUS_HAVE_NO_MEMORY(io->out.data); + + if (io->out.size == 0) { + return setup_close(c, tree, state->io_open->ntcreatex.out.file.fnum); + } + + /* setup for the read */ + state->io_read = talloc(c, union smb_read); + NT_STATUS_HAVE_NO_MEMORY(state->io_read); + + state->io_read->readx.level = RAW_READ_READX; + state->io_read->readx.in.file.fnum = state->io_open->ntcreatex.out.file.fnum; + state->io_read->readx.in.offset = 0; + state->io_read->readx.in.mincnt = MIN(32768, io->out.size); + state->io_read->readx.in.maxcnt = state->io_read->readx.in.mincnt; + state->io_read->readx.in.remaining = 0; + state->io_read->readx.in.read_for_execute = false; + state->io_read->readx.out.data = io->out.data; + + state->req = smb_raw_read_send(tree, state->io_read); + NT_STATUS_HAVE_NO_MEMORY(state->req); + + /* call the handler again when the first read is done */ + state->req->async.fn = loadfile_handler; + state->req->async.private_data = c; + state->stage = LOADFILE_READ; + + talloc_free(state->io_open); + + return NT_STATUS_OK; +} + + +/* + called when a read is done - pull the results and setup for the + next read, or close if the file is all done +*/ +static NTSTATUS loadfile_read(struct composite_context *c, + struct smb_composite_loadfile *io) +{ + struct loadfile_state *state = talloc_get_type(c->private_data, struct loadfile_state); + struct smbcli_tree *tree = state->req->tree; + NTSTATUS status; + + status = smb_raw_read_recv(state->req, state->io_read); + NT_STATUS_NOT_OK_RETURN(status); + + /* we might be done */ + if (state->io_read->readx.in.offset + + state->io_read->readx.out.nread == io->out.size) { + return setup_close(c, tree, state->io_read->readx.in.file.fnum); + } + + /* setup for the next read */ + state->io_read->readx.in.offset += state->io_read->readx.out.nread; + state->io_read->readx.in.mincnt = MIN(32768, io->out.size - state->io_read->readx.in.offset); + state->io_read->readx.out.data = io->out.data + state->io_read->readx.in.offset; + + state->req = smb_raw_read_send(tree, state->io_read); + NT_STATUS_HAVE_NO_MEMORY(state->req); + + /* call the handler again when the read is done */ + state->req->async.fn = loadfile_handler; + state->req->async.private_data = c; + + return NT_STATUS_OK; +} + +/* + called when the close is done, check the status and cleanup +*/ +static NTSTATUS loadfile_close(struct composite_context *c, + struct smb_composite_loadfile *io) +{ + struct loadfile_state *state = talloc_get_type(c->private_data, struct loadfile_state); + NTSTATUS status; + + status = smbcli_request_simple_recv(state->req); + NT_STATUS_NOT_OK_RETURN(status); + + c->state = COMPOSITE_STATE_DONE; + + return NT_STATUS_OK; +} + + +/* + handler for completion of a sub-request in loadfile +*/ +static void loadfile_handler(struct smbcli_request *req) +{ + struct composite_context *c = (struct composite_context *)req->async.private_data; + struct loadfile_state *state = talloc_get_type(c->private_data, struct loadfile_state); + + /* when this handler is called, the stage indicates what + call has just finished */ + switch (state->stage) { + case LOADFILE_OPEN: + c->status = loadfile_open(c, state->io); + break; + + case LOADFILE_READ: + c->status = loadfile_read(c, state->io); + break; + + case LOADFILE_CLOSE: + c->status = loadfile_close(c, state->io); + break; + } + + if (!NT_STATUS_IS_OK(c->status)) { + c->state = COMPOSITE_STATE_ERROR; + } + + if (c->state >= COMPOSITE_STATE_DONE && + c->async.fn) { + c->async.fn(c); + } +} + +/* + composite loadfile call - does an openx followed by a number of readx calls, + followed by a close +*/ +struct composite_context *smb_composite_loadfile_send(struct smbcli_tree *tree, + struct smb_composite_loadfile *io) +{ + struct composite_context *c; + struct loadfile_state *state; + + c = talloc_zero(tree, struct composite_context); + if (c == NULL) goto failed; + + state = talloc(c, struct loadfile_state); + if (state == NULL) goto failed; + + state->io = io; + + c->private_data = state; + c->state = COMPOSITE_STATE_IN_PROGRESS; + c->event_ctx = tree->session->transport->socket->event.ctx; + + /* setup for the open */ + state->io_open = talloc_zero(c, union smb_open); + if (state->io_open == NULL) goto failed; + + state->io_open->ntcreatex.level = RAW_OPEN_NTCREATEX; + state->io_open->ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED; + state->io_open->ntcreatex.in.access_mask = SEC_FILE_READ_DATA; + state->io_open->ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + state->io_open->ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + state->io_open->ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + state->io_open->ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + state->io_open->ntcreatex.in.fname = io->in.fname; + + /* send the open on its way */ + state->req = smb_raw_open_send(tree, state->io_open); + if (state->req == NULL) goto failed; + + /* setup the callback handler */ + state->req->async.fn = loadfile_handler; + state->req->async.private_data = c; + state->stage = LOADFILE_OPEN; + + return c; + +failed: + talloc_free(c); + return NULL; +} + + +/* + composite loadfile call - recv side +*/ +NTSTATUS smb_composite_loadfile_recv(struct composite_context *c, TALLOC_CTX *mem_ctx) +{ + NTSTATUS status; + + status = composite_wait(c); + + if (NT_STATUS_IS_OK(status)) { + struct loadfile_state *state = talloc_get_type(c->private_data, struct loadfile_state); + talloc_steal(mem_ctx, state->io->out.data); + } + + talloc_free(c); + return status; +} + + +/* + composite loadfile call - sync interface +*/ +NTSTATUS smb_composite_loadfile(struct smbcli_tree *tree, + TALLOC_CTX *mem_ctx, + struct smb_composite_loadfile *io) +{ + struct composite_context *c = smb_composite_loadfile_send(tree, io); + return smb_composite_loadfile_recv(c, mem_ctx); +} + diff --git a/source4/libcli/smb_composite/savefile.c b/source4/libcli/smb_composite/savefile.c new file mode 100644 index 0000000000..25a35c01a9 --- /dev/null +++ b/source4/libcli/smb_composite/savefile.c @@ -0,0 +1,288 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Andrew Tridgell 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/>. +*/ +/* + a composite API for saving a whole file from memory +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/composite/composite.h" +#include "libcli/smb_composite/smb_composite.h" + +/* the stages of this call */ +enum savefile_stage {SAVEFILE_OPEN, SAVEFILE_WRITE, SAVEFILE_CLOSE}; + +static void savefile_handler(struct smbcli_request *req); + +struct savefile_state { + enum savefile_stage stage; + off_t total_written; + struct smb_composite_savefile *io; + union smb_open *io_open; + union smb_write *io_write; + struct smbcli_request *req; +}; + + +/* + setup for the close +*/ +static NTSTATUS setup_close(struct composite_context *c, + struct smbcli_tree *tree, uint16_t fnum) +{ + struct savefile_state *state = talloc_get_type(c->private_data, struct savefile_state); + union smb_close *io_close; + + /* nothing to write, setup the close */ + io_close = talloc(c, union smb_close); + NT_STATUS_HAVE_NO_MEMORY(io_close); + + io_close->close.level = RAW_CLOSE_CLOSE; + io_close->close.in.file.fnum = fnum; + io_close->close.in.write_time = 0; + + state->req = smb_raw_close_send(tree, io_close); + NT_STATUS_HAVE_NO_MEMORY(state->req); + + /* call the handler again when the close is done */ + state->stage = SAVEFILE_CLOSE; + state->req->async.fn = savefile_handler; + state->req->async.private_data = c; + + return NT_STATUS_OK; +} + +/* + called when the open is done - pull the results and setup for the + first writex, or close if the file is zero size +*/ +static NTSTATUS savefile_open(struct composite_context *c, + struct smb_composite_savefile *io) +{ + struct savefile_state *state = talloc_get_type(c->private_data, struct savefile_state); + union smb_write *io_write; + struct smbcli_tree *tree = state->req->tree; + NTSTATUS status; + uint32_t max_xmit = tree->session->transport->negotiate.max_xmit; + + status = smb_raw_open_recv(state->req, c, state->io_open); + NT_STATUS_NOT_OK_RETURN(status); + + if (io->in.size == 0) { + return setup_close(c, tree, state->io_open->ntcreatex.out.file.fnum); + } + + /* setup for the first write */ + io_write = talloc(c, union smb_write); + NT_STATUS_HAVE_NO_MEMORY(io_write); + + io_write->writex.level = RAW_WRITE_WRITEX; + io_write->writex.in.file.fnum = state->io_open->ntcreatex.out.file.fnum; + io_write->writex.in.offset = 0; + io_write->writex.in.wmode = 0; + io_write->writex.in.remaining = 0; + io_write->writex.in.count = MIN(max_xmit - 100, io->in.size); + io_write->writex.in.data = io->in.data; + state->io_write = io_write; + + state->req = smb_raw_write_send(tree, io_write); + NT_STATUS_HAVE_NO_MEMORY(state->req); + + /* call the handler again when the first write is done */ + state->stage = SAVEFILE_WRITE; + state->req->async.fn = savefile_handler; + state->req->async.private_data = c; + talloc_free(state->io_open); + + return NT_STATUS_OK; +} + + +/* + called when a write is done - pull the results and setup for the + next write, or close if the file is all done +*/ +static NTSTATUS savefile_write(struct composite_context *c, + struct smb_composite_savefile *io) +{ + struct savefile_state *state = talloc_get_type(c->private_data, struct savefile_state); + struct smbcli_tree *tree = state->req->tree; + NTSTATUS status; + uint32_t max_xmit = tree->session->transport->negotiate.max_xmit; + + status = smb_raw_write_recv(state->req, state->io_write); + NT_STATUS_NOT_OK_RETURN(status); + + state->total_written += state->io_write->writex.out.nwritten; + + /* we might be done */ + if (state->io_write->writex.out.nwritten != state->io_write->writex.in.count || + state->total_written == io->in.size) { + return setup_close(c, tree, state->io_write->writex.in.file.fnum); + } + + /* setup for the next write */ + state->io_write->writex.in.offset = state->total_written; + state->io_write->writex.in.count = MIN(max_xmit - 100, + io->in.size - state->total_written); + state->io_write->writex.in.data = io->in.data + state->total_written; + + state->req = smb_raw_write_send(tree, state->io_write); + NT_STATUS_HAVE_NO_MEMORY(state->req); + + /* call the handler again when the write is done */ + state->req->async.fn = savefile_handler; + state->req->async.private_data = c; + + return NT_STATUS_OK; +} + +/* + called when the close is done, check the status and cleanup +*/ +static NTSTATUS savefile_close(struct composite_context *c, + struct smb_composite_savefile *io) +{ + struct savefile_state *state = talloc_get_type(c->private_data, struct savefile_state); + NTSTATUS status; + + status = smbcli_request_simple_recv(state->req); + NT_STATUS_NOT_OK_RETURN(status); + + if (state->total_written != io->in.size) { + return NT_STATUS_DISK_FULL; + } + + c->state = COMPOSITE_STATE_DONE; + + return NT_STATUS_OK; +} + + +/* + handler for completion of a sub-request in savefile +*/ +static void savefile_handler(struct smbcli_request *req) +{ + struct composite_context *c = (struct composite_context *)req->async.private_data; + struct savefile_state *state = talloc_get_type(c->private_data, struct savefile_state); + + /* when this handler is called, the stage indicates what + call has just finished */ + switch (state->stage) { + case SAVEFILE_OPEN: + c->status = savefile_open(c, state->io); + break; + + case SAVEFILE_WRITE: + c->status = savefile_write(c, state->io); + break; + + case SAVEFILE_CLOSE: + c->status = savefile_close(c, state->io); + break; + } + + if (!NT_STATUS_IS_OK(c->status)) { + c->state = COMPOSITE_STATE_ERROR; + } + + if (c->state >= COMPOSITE_STATE_DONE && + c->async.fn) { + c->async.fn(c); + } +} + +/* + composite savefile call - does an openx followed by a number of writex calls, + followed by a close +*/ +struct composite_context *smb_composite_savefile_send(struct smbcli_tree *tree, + struct smb_composite_savefile *io) +{ + struct composite_context *c; + struct savefile_state *state; + union smb_open *io_open; + + c = talloc_zero(tree, struct composite_context); + if (c == NULL) goto failed; + + c->state = COMPOSITE_STATE_IN_PROGRESS; + c->event_ctx = tree->session->transport->socket->event.ctx; + + state = talloc(c, struct savefile_state); + if (state == NULL) goto failed; + + state->stage = SAVEFILE_OPEN; + state->total_written = 0; + state->io = io; + + /* setup for the open */ + io_open = talloc_zero(c, union smb_open); + if (io_open == NULL) goto failed; + + io_open->ntcreatex.level = RAW_OPEN_NTCREATEX; + io_open->ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED; + io_open->ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA; + io_open->ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io_open->ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + io_open->ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io_open->ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io_open->ntcreatex.in.fname = io->in.fname; + state->io_open = io_open; + + /* send the open on its way */ + state->req = smb_raw_open_send(tree, io_open); + if (state->req == NULL) goto failed; + + /* setup the callback handler */ + state->req->async.fn = savefile_handler; + state->req->async.private_data = c; + c->private_data = state; + + return c; + +failed: + talloc_free(c); + return NULL; +} + + +/* + composite savefile call - recv side +*/ +NTSTATUS smb_composite_savefile_recv(struct composite_context *c) +{ + NTSTATUS status; + status = composite_wait(c); + talloc_free(c); + return status; +} + + +/* + composite savefile call - sync interface +*/ +NTSTATUS smb_composite_savefile(struct smbcli_tree *tree, + struct smb_composite_savefile *io) +{ + struct composite_context *c = smb_composite_savefile_send(tree, io); + return smb_composite_savefile_recv(c); +} diff --git a/source4/libcli/smb_composite/sesssetup.c b/source4/libcli/smb_composite/sesssetup.c new file mode 100644 index 0000000000..83d15e98eb --- /dev/null +++ b/source4/libcli/smb_composite/sesssetup.c @@ -0,0 +1,570 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Andrew Tridgell 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/>. +*/ +/* + a composite API for making handling a generic async session setup +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/composite/composite.h" +#include "libcli/smb_composite/smb_composite.h" +#include "libcli/smb_composite/proto.h" +#include "libcli/auth/libcli_auth.h" +#include "auth/auth.h" +#include "auth/gensec/gensec.h" +#include "auth/credentials/credentials.h" +#include "version.h" +#include "param/param.h" + +struct sesssetup_state { + union smb_sesssetup setup; + NTSTATUS remote_status; + NTSTATUS gensec_status; + struct smb_composite_sesssetup *io; + struct smbcli_request *req; +}; + +static int sesssetup_state_destructor(struct sesssetup_state *state) +{ + if (state->req) { + talloc_free(state->req); + state->req = NULL; + } + + return 0; +} + +static NTSTATUS session_setup_old(struct composite_context *c, + struct smbcli_session *session, + struct smb_composite_sesssetup *io, + struct smbcli_request **req); +static NTSTATUS session_setup_nt1(struct composite_context *c, + struct smbcli_session *session, + struct smb_composite_sesssetup *io, + struct smbcli_request **req); +static NTSTATUS session_setup_spnego(struct composite_context *c, + struct smbcli_session *session, + struct smb_composite_sesssetup *io, + struct smbcli_request **req); + +/* + store the user session key for a transport +*/ +static void set_user_session_key(struct smbcli_session *session, + const DATA_BLOB *session_key) +{ + session->user_session_key = data_blob_talloc(session, + session_key->data, + session_key->length); +} + +/* + handler for completion of a smbcli_request sub-request +*/ +static void request_handler(struct smbcli_request *req) +{ + struct composite_context *c = (struct composite_context *)req->async.private_data; + struct sesssetup_state *state = talloc_get_type(c->private_data, struct sesssetup_state); + struct smbcli_session *session = req->session; + DATA_BLOB session_key = data_blob(NULL, 0); + DATA_BLOB null_data_blob = data_blob(NULL, 0); + NTSTATUS session_key_err, nt_status; + struct smbcli_request *check_req = NULL; + + if (req->sign_caller_checks) { + req->do_not_free = true; + check_req = req; + } + + state->remote_status = smb_raw_sesssetup_recv(req, state, &state->setup); + c->status = state->remote_status; + state->req = NULL; + + /* + * we only need to check the signature if the + * NT_STATUS_OK is returned + */ + if (!NT_STATUS_IS_OK(state->remote_status)) { + talloc_free(check_req); + check_req = NULL; + } + + switch (state->setup.old.level) { + case RAW_SESSSETUP_OLD: + state->io->out.vuid = state->setup.old.out.vuid; + /* This doesn't work, as this only happens on old + * protocols, where this comparison won't match. */ + if (NT_STATUS_EQUAL(c->status, NT_STATUS_LOGON_FAILURE)) { + /* we neet to reset the vuid for a new try */ + session->vuid = 0; + if (cli_credentials_wrong_password(state->io->in.credentials)) { + nt_status = session_setup_old(c, session, + state->io, + &state->req); + if (NT_STATUS_IS_OK(nt_status)) { + talloc_free(check_req); + c->status = nt_status; + composite_continue_smb(c, state->req, request_handler, c); + return; + } + } + } + break; + + case RAW_SESSSETUP_NT1: + state->io->out.vuid = state->setup.nt1.out.vuid; + if (NT_STATUS_EQUAL(c->status, NT_STATUS_LOGON_FAILURE)) { + /* we neet to reset the vuid for a new try */ + session->vuid = 0; + if (cli_credentials_wrong_password(state->io->in.credentials)) { + nt_status = session_setup_nt1(c, session, + state->io, + &state->req); + if (NT_STATUS_IS_OK(nt_status)) { + talloc_free(check_req); + c->status = nt_status; + composite_continue_smb(c, state->req, request_handler, c); + return; + } + } + } + break; + + case RAW_SESSSETUP_SPNEGO: + state->io->out.vuid = state->setup.spnego.out.vuid; + if (NT_STATUS_EQUAL(c->status, NT_STATUS_LOGON_FAILURE)) { + /* we need to reset the vuid for a new try */ + session->vuid = 0; + if (cli_credentials_wrong_password(state->io->in.credentials)) { + nt_status = session_setup_spnego(c, session, + state->io, + &state->req); + if (NT_STATUS_IS_OK(nt_status)) { + talloc_free(check_req); + c->status = nt_status; + composite_continue_smb(c, state->req, request_handler, c); + return; + } + } + } + if (!NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED) && + !NT_STATUS_IS_OK(c->status)) { + break; + } + if (NT_STATUS_EQUAL(state->gensec_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + + /* The status value here, from the earlier pass at GENSEC is + * vital to the security of the system. Even if the other end + * accepts, if GENSEC claims 'MORE_PROCESSING_REQUIRED' then + * you must keep feeding it blobs, or else the remote + * host/attacker might avoid mutal authentication + * requirements */ + + state->gensec_status = gensec_update(session->gensec, state, + state->setup.spnego.out.secblob, + &state->setup.spnego.in.secblob); + c->status = state->gensec_status; + if (!NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED) && + !NT_STATUS_IS_OK(c->status)) { + break; + } + } else { + state->setup.spnego.in.secblob = data_blob(NULL, 0); + } + + if (NT_STATUS_IS_OK(state->remote_status)) { + if (state->setup.spnego.in.secblob.length) { + c->status = NT_STATUS_INTERNAL_ERROR; + break; + } + session_key_err = gensec_session_key(session->gensec, &session_key); + if (NT_STATUS_IS_OK(session_key_err)) { + set_user_session_key(session, &session_key); + smbcli_transport_simple_set_signing(session->transport, session_key, null_data_blob); + } + } + + if (state->setup.spnego.in.secblob.length) { + /* + * set the session->vuid value only for calling + * smb_raw_sesssetup_send() + */ + uint16_t vuid = session->vuid; + session->vuid = state->io->out.vuid; + state->req = smb_raw_sesssetup_send(session, &state->setup); + session->vuid = vuid; + if (state->req) { + state->req->sign_caller_checks = true; + } + composite_continue_smb(c, state->req, request_handler, c); + return; + } + break; + + case RAW_SESSSETUP_SMB2: + c->status = NT_STATUS_INTERNAL_ERROR; + break; + } + + if (check_req) { + check_req->sign_caller_checks = false; + if (!smbcli_request_check_sign_mac(check_req)) { + c->status = NT_STATUS_ACCESS_DENIED; + } + talloc_free(check_req); + check_req = NULL; + } + + /* enforce the local signing required flag */ + if (NT_STATUS_IS_OK(c->status) && !cli_credentials_is_anonymous(state->io->in.credentials)) { + if (!session->transport->negotiate.sign_info.doing_signing + && session->transport->negotiate.sign_info.mandatory_signing) { + DEBUG(0, ("SMB signing required, but server does not support it\n")); + c->status = NT_STATUS_ACCESS_DENIED; + } + } + + if (!NT_STATUS_IS_OK(c->status)) { + composite_error(c, c->status); + return; + } + + composite_done(c); +} + + +/* + send a nt1 style session setup +*/ +static NTSTATUS session_setup_nt1(struct composite_context *c, + struct smbcli_session *session, + struct smb_composite_sesssetup *io, + struct smbcli_request **req) +{ + NTSTATUS nt_status = NT_STATUS_INTERNAL_ERROR; + struct sesssetup_state *state = talloc_get_type(c->private_data, struct sesssetup_state); + DATA_BLOB names_blob = NTLMv2_generate_names_blob(state, session->transport->socket->hostname, cli_credentials_get_domain(io->in.credentials)); + DATA_BLOB session_key = data_blob(NULL, 0); + int flags = CLI_CRED_NTLM_AUTH; + + smbcli_temp_set_signing(session->transport); + + if (session->options.lanman_auth) { + flags |= CLI_CRED_LANMAN_AUTH; + } + + if (session->options.ntlmv2_auth) { + flags |= CLI_CRED_NTLMv2_AUTH; + } + + state->setup.nt1.level = RAW_SESSSETUP_NT1; + state->setup.nt1.in.bufsize = session->transport->options.max_xmit; + state->setup.nt1.in.mpx_max = session->transport->options.max_mux; + state->setup.nt1.in.vc_num = 1; + state->setup.nt1.in.sesskey = io->in.sesskey; + state->setup.nt1.in.capabilities = io->in.capabilities; + state->setup.nt1.in.os = "Unix"; + state->setup.nt1.in.lanman = talloc_asprintf(state, "Samba %s", SAMBA_VERSION_STRING); + + cli_credentials_get_ntlm_username_domain(io->in.credentials, state, + &state->setup.nt1.in.user, + &state->setup.nt1.in.domain); + + + if (session->transport->negotiate.sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) { + nt_status = cli_credentials_get_ntlm_response(io->in.credentials, state, + &flags, + session->transport->negotiate.secblob, + names_blob, + &state->setup.nt1.in.password1, + &state->setup.nt1.in.password2, + NULL, &session_key); + NT_STATUS_NOT_OK_RETURN(nt_status); + } else if (session->options.plaintext_auth) { + const char *password = cli_credentials_get_password(io->in.credentials); + state->setup.nt1.in.password1 = data_blob_talloc(state, password, strlen(password)); + state->setup.nt1.in.password2 = data_blob(NULL, 0); + } else { + /* could match windows client and return 'cannot logon from this workstation', but it just confuses everybody */ + return NT_STATUS_INVALID_PARAMETER; + } + + *req = smb_raw_sesssetup_send(session, &state->setup); + if (!*req) { + return NT_STATUS_NO_MEMORY; + } + + if (NT_STATUS_IS_OK(nt_status)) { + smbcli_transport_simple_set_signing(session->transport, session_key, + state->setup.nt1.in.password2); + set_user_session_key(session, &session_key); + + data_blob_free(&session_key); + } + + return (*req)->status; +} + + +/* + old style session setup (pre NT1 protocol level) +*/ +static NTSTATUS session_setup_old(struct composite_context *c, + struct smbcli_session *session, + struct smb_composite_sesssetup *io, + struct smbcli_request **req) +{ + NTSTATUS nt_status; + struct sesssetup_state *state = talloc_get_type(c->private_data, struct sesssetup_state); + const char *password = cli_credentials_get_password(io->in.credentials); + DATA_BLOB names_blob = NTLMv2_generate_names_blob(state, session->transport->socket->hostname, cli_credentials_get_domain(io->in.credentials)); + DATA_BLOB session_key; + int flags = 0; + if (session->options.lanman_auth) { + flags |= CLI_CRED_LANMAN_AUTH; + } + + if (session->options.ntlmv2_auth) { + flags |= CLI_CRED_NTLMv2_AUTH; + } + + state->setup.old.level = RAW_SESSSETUP_OLD; + state->setup.old.in.bufsize = session->transport->options.max_xmit; + state->setup.old.in.mpx_max = session->transport->options.max_mux; + state->setup.old.in.vc_num = 1; + state->setup.old.in.sesskey = io->in.sesskey; + state->setup.old.in.os = "Unix"; + state->setup.old.in.lanman = talloc_asprintf(state, "Samba %s", SAMBA_VERSION_STRING); + cli_credentials_get_ntlm_username_domain(io->in.credentials, state, + &state->setup.old.in.user, + &state->setup.old.in.domain); + + if (session->transport->negotiate.sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) { + nt_status = cli_credentials_get_ntlm_response(io->in.credentials, state, + &flags, + session->transport->negotiate.secblob, + names_blob, + &state->setup.old.in.password, + NULL, + NULL, &session_key); + NT_STATUS_NOT_OK_RETURN(nt_status); + set_user_session_key(session, &session_key); + + data_blob_free(&session_key); + } else if (session->options.plaintext_auth) { + state->setup.old.in.password = data_blob_talloc(state, password, strlen(password)); + } else { + /* could match windows client and return 'cannot logon from this workstation', but it just confuses everybody */ + return NT_STATUS_INVALID_PARAMETER; + } + + *req = smb_raw_sesssetup_send(session, &state->setup); + if (!*req) { + return NT_STATUS_NO_MEMORY; + } + return (*req)->status; +} + + +/* + Modern, all singing, all dancing extended security (and possibly SPNEGO) request +*/ +static NTSTATUS session_setup_spnego(struct composite_context *c, + struct smbcli_session *session, + struct smb_composite_sesssetup *io, + struct smbcli_request **req) +{ + struct sesssetup_state *state = talloc_get_type(c->private_data, struct sesssetup_state); + NTSTATUS status; + const char *chosen_oid = NULL; + + state->setup.spnego.level = RAW_SESSSETUP_SPNEGO; + state->setup.spnego.in.bufsize = session->transport->options.max_xmit; + state->setup.spnego.in.mpx_max = session->transport->options.max_mux; + state->setup.spnego.in.vc_num = 1; + state->setup.spnego.in.sesskey = io->in.sesskey; + state->setup.spnego.in.capabilities = io->in.capabilities; + state->setup.spnego.in.os = "Unix"; + state->setup.spnego.in.lanman = talloc_asprintf(state, "Samba %s", SAMBA_VERSION_STRING); + state->setup.spnego.in.workgroup = io->in.workgroup; + + smbcli_temp_set_signing(session->transport); + + status = gensec_client_start(session, &session->gensec, c->event_ctx, + io->in.gensec_settings); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start GENSEC client mode: %s\n", nt_errstr(status))); + return status; + } + + gensec_want_feature(session->gensec, GENSEC_FEATURE_SESSION_KEY); + + status = gensec_set_credentials(session->gensec, io->in.credentials); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start set GENSEC client credentials: %s\n", + nt_errstr(status))); + return status; + } + + status = gensec_set_target_hostname(session->gensec, session->transport->socket->hostname); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start set GENSEC target hostname: %s\n", + nt_errstr(status))); + return status; + } + + status = gensec_set_target_service(session->gensec, "cifs"); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start set GENSEC target service: %s\n", + nt_errstr(status))); + return status; + } + + if (session->transport->negotiate.secblob.length) { + chosen_oid = GENSEC_OID_SPNEGO; + status = gensec_start_mech_by_oid(session->gensec, chosen_oid); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start set GENSEC client mechanism %s: %s\n", + gensec_get_name_by_oid(session->gensec, chosen_oid), nt_errstr(status))); + chosen_oid = GENSEC_OID_NTLMSSP; + status = gensec_start_mech_by_oid(session->gensec, chosen_oid); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start set (fallback) GENSEC client mechanism %s: %s\n", + gensec_get_name_by_oid(session->gensec, chosen_oid), + nt_errstr(status))); + return status; + } + } + } else { + /* without a sec blob, means raw NTLMSSP */ + chosen_oid = GENSEC_OID_NTLMSSP; + status = gensec_start_mech_by_oid(session->gensec, chosen_oid); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to start set GENSEC client mechanism %s: %s\n", + gensec_get_name_by_oid(session->gensec, chosen_oid), nt_errstr(status))); + } + } + + if ((const void *)chosen_oid == (const void *)GENSEC_OID_SPNEGO) { + status = gensec_update(session->gensec, state, + session->transport->negotiate.secblob, + &state->setup.spnego.in.secblob); + } else { + status = gensec_update(session->gensec, state, + data_blob(NULL, 0), + &state->setup.spnego.in.secblob); + + } + + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && + !NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed initial gensec_update with mechanism %s: %s\n", + gensec_get_name_by_oid(session->gensec, chosen_oid), + nt_errstr(status))); + return status; + } + state->gensec_status = status; + + *req = smb_raw_sesssetup_send(session, &state->setup); + if (!*req) { + return NT_STATUS_NO_MEMORY; + } + + /* + * we need to check the signature ourself + * as the session key might be the acceptor subkey + * which comes within the response itself + */ + (*req)->sign_caller_checks = true; + + return (*req)->status; +} + + +/* + composite session setup function that hides the details of all the + different session setup varients, including the multi-pass nature of + the spnego varient +*/ +struct composite_context *smb_composite_sesssetup_send(struct smbcli_session *session, + struct smb_composite_sesssetup *io) +{ + struct composite_context *c; + struct sesssetup_state *state; + NTSTATUS status; + + c = composite_create(session, session->transport->socket->event.ctx); + if (c == NULL) return NULL; + + state = talloc_zero(c, struct sesssetup_state); + if (composite_nomem(state, c)) return c; + c->private_data = state; + + state->io = io; + + talloc_set_destructor(state, sesssetup_state_destructor); + + /* no session setup at all in earliest protocol varients */ + if (session->transport->negotiate.protocol < PROTOCOL_LANMAN1) { + ZERO_STRUCT(io->out); + composite_done(c); + return c; + } + + /* see what session setup interface we will use */ + if (session->transport->negotiate.protocol < PROTOCOL_NT1) { + status = session_setup_old(c, session, io, &state->req); + } else if (!session->transport->options.use_spnego || + !(io->in.capabilities & CAP_EXTENDED_SECURITY)) { + status = session_setup_nt1(c, session, io, &state->req); + } else { + status = session_setup_spnego(c, session, io, &state->req); + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) || + NT_STATUS_IS_OK(status)) { + composite_continue_smb(c, state->req, request_handler, c); + return c; + } + + composite_error(c, status); + return c; +} + + +/* + receive a composite session setup reply +*/ +NTSTATUS smb_composite_sesssetup_recv(struct composite_context *c) +{ + NTSTATUS status; + status = composite_wait(c); + talloc_free(c); + return status; +} + +/* + sync version of smb_composite_sesssetup +*/ +NTSTATUS smb_composite_sesssetup(struct smbcli_session *session, struct smb_composite_sesssetup *io) +{ + struct composite_context *c = smb_composite_sesssetup_send(session, io); + return smb_composite_sesssetup_recv(c); +} diff --git a/source4/libcli/smb_composite/smb2.c b/source4/libcli/smb_composite/smb2.c new file mode 100644 index 0000000000..d71708a974 --- /dev/null +++ b/source4/libcli/smb_composite/smb2.c @@ -0,0 +1,370 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Andrew Tridgell 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/>. +*/ +/* + a composite API for making SMB-like calls using SMB2. This is useful + as SMB2 often requires more than one requests where a single SMB + request would do. In converting code that uses SMB to use SMB2, + these routines make life a lot easier +*/ + + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/composite/composite.h" +#include "libcli/smb_composite/smb_composite.h" +#include "libcli/smb2/smb2_calls.h" + +/* + continue after a SMB2 close + */ +static void continue_close(struct smb2_request *req) +{ + struct composite_context *ctx = talloc_get_type(req->async.private_data, + struct composite_context); + NTSTATUS status; + struct smb2_close close_parm; + + status = smb2_close_recv(req, &close_parm); + composite_error(ctx, status); +} + +/* + continue after the create in a composite unlink + */ +static void continue_unlink(struct smb2_request *req) +{ + struct composite_context *ctx = talloc_get_type(req->async.private_data, + struct composite_context); + struct smb2_tree *tree = req->tree; + struct smb2_create create_parm; + struct smb2_close close_parm; + NTSTATUS status; + + status = smb2_create_recv(req, ctx, &create_parm); + if (!NT_STATUS_IS_OK(status)) { + composite_error(ctx, status); + return; + } + + ZERO_STRUCT(close_parm); + close_parm.in.file.handle = create_parm.out.file.handle; + + req = smb2_close_send(tree, &close_parm); + composite_continue_smb2(ctx, req, continue_close, ctx); +} + +/* + composite SMB2 unlink call +*/ +struct composite_context *smb2_composite_unlink_send(struct smb2_tree *tree, + union smb_unlink *io) +{ + struct composite_context *ctx; + struct smb2_create create_parm; + struct smb2_request *req; + + ctx = composite_create(tree, tree->session->transport->socket->event.ctx); + if (ctx == NULL) return NULL; + + /* check for wildcards - we could support these with a + search, but for now they aren't necessary */ + if (strpbrk(io->unlink.in.pattern, "*?<>") != NULL) { + composite_error(ctx, NT_STATUS_NOT_SUPPORTED); + return ctx; + } + + ZERO_STRUCT(create_parm); + create_parm.in.desired_access = SEC_STD_DELETE; + create_parm.in.create_disposition = NTCREATEX_DISP_OPEN; + create_parm.in.share_access = + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + create_parm.in.create_options = + NTCREATEX_OPTIONS_DELETE_ON_CLOSE | + NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; + create_parm.in.fname = io->unlink.in.pattern; + if (create_parm.in.fname[0] == '\\') { + create_parm.in.fname++; + } + + req = smb2_create_send(tree, &create_parm); + + composite_continue_smb2(ctx, req, continue_unlink, ctx); + return ctx; +} + + +/* + composite unlink call - sync interface +*/ +NTSTATUS smb2_composite_unlink(struct smb2_tree *tree, union smb_unlink *io) +{ + struct composite_context *c = smb2_composite_unlink_send(tree, io); + return composite_wait_free(c); +} + + + + +/* + continue after the create in a composite mkdir + */ +static void continue_mkdir(struct smb2_request *req) +{ + struct composite_context *ctx = talloc_get_type(req->async.private_data, + struct composite_context); + struct smb2_tree *tree = req->tree; + struct smb2_create create_parm; + struct smb2_close close_parm; + NTSTATUS status; + + status = smb2_create_recv(req, ctx, &create_parm); + if (!NT_STATUS_IS_OK(status)) { + composite_error(ctx, status); + return; + } + + ZERO_STRUCT(close_parm); + close_parm.in.file.handle = create_parm.out.file.handle; + + req = smb2_close_send(tree, &close_parm); + composite_continue_smb2(ctx, req, continue_close, ctx); +} + +/* + composite SMB2 mkdir call +*/ +struct composite_context *smb2_composite_mkdir_send(struct smb2_tree *tree, + union smb_mkdir *io) +{ + struct composite_context *ctx; + struct smb2_create create_parm; + struct smb2_request *req; + + ctx = composite_create(tree, tree->session->transport->socket->event.ctx); + if (ctx == NULL) return NULL; + + ZERO_STRUCT(create_parm); + + create_parm.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; + create_parm.in.share_access = + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + create_parm.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + create_parm.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + create_parm.in.create_disposition = NTCREATEX_DISP_CREATE; + create_parm.in.fname = io->mkdir.in.path; + if (create_parm.in.fname[0] == '\\') { + create_parm.in.fname++; + } + + req = smb2_create_send(tree, &create_parm); + + composite_continue_smb2(ctx, req, continue_mkdir, ctx); + + return ctx; +} + + +/* + composite mkdir call - sync interface +*/ +NTSTATUS smb2_composite_mkdir(struct smb2_tree *tree, union smb_mkdir *io) +{ + struct composite_context *c = smb2_composite_mkdir_send(tree, io); + return composite_wait_free(c); +} + + + +/* + continue after the create in a composite rmdir + */ +static void continue_rmdir(struct smb2_request *req) +{ + struct composite_context *ctx = talloc_get_type(req->async.private_data, + struct composite_context); + struct smb2_tree *tree = req->tree; + struct smb2_create create_parm; + struct smb2_close close_parm; + NTSTATUS status; + + status = smb2_create_recv(req, ctx, &create_parm); + if (!NT_STATUS_IS_OK(status)) { + composite_error(ctx, status); + return; + } + + ZERO_STRUCT(close_parm); + close_parm.in.file.handle = create_parm.out.file.handle; + + req = smb2_close_send(tree, &close_parm); + composite_continue_smb2(ctx, req, continue_close, ctx); +} + +/* + composite SMB2 rmdir call +*/ +struct composite_context *smb2_composite_rmdir_send(struct smb2_tree *tree, + struct smb_rmdir *io) +{ + struct composite_context *ctx; + struct smb2_create create_parm; + struct smb2_request *req; + + ctx = composite_create(tree, tree->session->transport->socket->event.ctx); + if (ctx == NULL) return NULL; + + ZERO_STRUCT(create_parm); + create_parm.in.desired_access = SEC_STD_DELETE; + create_parm.in.create_disposition = NTCREATEX_DISP_OPEN; + create_parm.in.share_access = + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + create_parm.in.create_options = + NTCREATEX_OPTIONS_DIRECTORY | + NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + create_parm.in.fname = io->in.path; + if (create_parm.in.fname[0] == '\\') { + create_parm.in.fname++; + } + + req = smb2_create_send(tree, &create_parm); + + composite_continue_smb2(ctx, req, continue_rmdir, ctx); + return ctx; +} + + +/* + composite rmdir call - sync interface +*/ +NTSTATUS smb2_composite_rmdir(struct smb2_tree *tree, struct smb_rmdir *io) +{ + struct composite_context *c = smb2_composite_rmdir_send(tree, io); + return composite_wait_free(c); +} + + +/* + continue after the setfileinfo in a composite setpathinfo + */ +static void continue_setpathinfo_close(struct smb2_request *req) +{ + struct composite_context *ctx = talloc_get_type(req->async.private_data, + struct composite_context); + struct smb2_tree *tree = req->tree; + struct smb2_close close_parm; + NTSTATUS status; + union smb_setfileinfo *io2 = talloc_get_type(ctx->private_data, + union smb_setfileinfo); + + status = smb2_setinfo_recv(req); + if (!NT_STATUS_IS_OK(status)) { + composite_error(ctx, status); + return; + } + + ZERO_STRUCT(close_parm); + close_parm.in.file.handle = io2->generic.in.file.handle; + + req = smb2_close_send(tree, &close_parm); + composite_continue_smb2(ctx, req, continue_close, ctx); +} + + +/* + continue after the create in a composite setpathinfo + */ +static void continue_setpathinfo(struct smb2_request *req) +{ + struct composite_context *ctx = talloc_get_type(req->async.private_data, + struct composite_context); + struct smb2_tree *tree = req->tree; + struct smb2_create create_parm; + NTSTATUS status; + union smb_setfileinfo *io2 = talloc_get_type(ctx->private_data, + union smb_setfileinfo); + + status = smb2_create_recv(req, ctx, &create_parm); + if (!NT_STATUS_IS_OK(status)) { + composite_error(ctx, status); + return; + } + + io2->generic.in.file.handle = create_parm.out.file.handle; + + req = smb2_setinfo_file_send(tree, io2); + composite_continue_smb2(ctx, req, continue_setpathinfo_close, ctx); +} + + +/* + composite SMB2 setpathinfo call +*/ +struct composite_context *smb2_composite_setpathinfo_send(struct smb2_tree *tree, + union smb_setfileinfo *io) +{ + struct composite_context *ctx; + struct smb2_create create_parm; + struct smb2_request *req; + union smb_setfileinfo *io2; + + ctx = composite_create(tree, tree->session->transport->socket->event.ctx); + if (ctx == NULL) return NULL; + + ZERO_STRUCT(create_parm); + create_parm.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; + create_parm.in.create_disposition = NTCREATEX_DISP_OPEN; + create_parm.in.share_access = + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + create_parm.in.create_options = 0; + create_parm.in.fname = io->generic.in.file.path; + if (create_parm.in.fname[0] == '\\') { + create_parm.in.fname++; + } + + req = smb2_create_send(tree, &create_parm); + + io2 = talloc(ctx, union smb_setfileinfo); + if (composite_nomem(io2, ctx)) { + return ctx; + } + *io2 = *io; + + ctx->private_data = io2; + + composite_continue_smb2(ctx, req, continue_setpathinfo, ctx); + return ctx; +} + + +/* + composite setpathinfo call + */ +NTSTATUS smb2_composite_setpathinfo(struct smb2_tree *tree, union smb_setfileinfo *io) +{ + struct composite_context *c = smb2_composite_setpathinfo_send(tree, io); + return composite_wait_free(c); +} diff --git a/source4/libcli/smb_composite/smb_composite.h b/source4/libcli/smb_composite/smb_composite.h new file mode 100644 index 0000000000..a1e1e99d7e --- /dev/null +++ b/source4/libcli/smb_composite/smb_composite.h @@ -0,0 +1,195 @@ +/* + Unix SMB/CIFS implementation. + + SMB composite request interfaces + + Copyright (C) Andrew Tridgell 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/>. +*/ + +/* + this defines the structures associated with "composite" + requests. Composite requests are libcli requests that are internally + implemented as multiple libcli/raw/ calls, but can be treated as a + single call via these composite calls. The composite calls are + particularly designed to be used in async applications +*/ + +#include "libcli/raw/signing.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/smb2/smb2.h" + + +/* + a composite open/read(s)/close request that loads a whole file + into memory. Used as a demo of the composite system. +*/ +struct smb_composite_loadfile { + struct { + const char *fname; + } in; + struct { + uint8_t *data; + uint32_t size; + } out; +}; + +struct smb_composite_fetchfile { + struct { + const char *dest_host; + const char **ports; + const char *called_name; + const char *service; + const char *service_type; + const char *socket_options; + struct cli_credentials *credentials; + const char *workgroup; + const char *filename; + struct smbcli_options options; + struct smbcli_session_options session_options; + struct resolve_context *resolve_ctx; + struct smb_iconv_convenience *iconv_convenience; + struct gensec_settings *gensec_settings; + } in; + struct { + uint8_t *data; + uint32_t size; + } out; +}; + +/* + a composite open/write(s)/close request that saves a whole file from + memory. Used as a demo of the composite system. +*/ +struct smb_composite_savefile { + struct { + const char *fname; + uint8_t *data; + uint32_t size; + } in; +}; + + +/* + a composite request for a full connection to a remote server. Includes + + - socket establishment + - session request + - negprot + - session setup (if credentials are not NULL) + - tree connect (if service is not NULL) +*/ +struct smb_composite_connect { + struct { + const char *dest_host; + const char **dest_ports; + const char *socket_options; + const char *called_name; + const char *service; + const char *service_type; + struct cli_credentials *credentials; + bool fallback_to_anonymous; + const char *workgroup; + struct smbcli_options options; + struct smbcli_session_options session_options; + struct smb_iconv_convenience *iconv_convenience; + struct gensec_settings *gensec_settings; + } in; + struct { + struct smbcli_tree *tree; + bool anonymous_fallback_done; + } out; +}; + + +/* + generic session setup interface that takes care of which + session setup varient to use +*/ +struct smb_composite_sesssetup { + struct { + uint32_t sesskey; + uint32_t capabilities; + struct cli_credentials *credentials; + const char *workgroup; + struct gensec_settings *gensec_settings; + } in; + struct { + uint16_t vuid; + } out; +}; + +/* + query file system info +*/ +struct smb_composite_fsinfo { + struct { + const char *dest_host; + const char **dest_ports; + const char *socket_options; + const char *called_name; + const char *service; + const char *service_type; + struct cli_credentials *credentials; + const char *workgroup; + enum smb_fsinfo_level level; + struct smb_iconv_convenience *iconv_convenience; + struct gensec_settings *gensec_settings; + } in; + + struct { + union smb_fsinfo *fsinfo; + } out; +}; + +/* + composite call for appending new acl to the file's security descriptor and get + new full acl +*/ + +struct smb_composite_appendacl { + struct { + const char *fname; + + const struct security_descriptor *sd; + } in; + + struct { + struct security_descriptor *sd; + } out; +}; + +/* + a composite API to fire connect() calls to multiple targets, picking the + first one. +*/ + +struct smb_composite_connectmulti { + struct { + int num_dests; + const char **hostnames; + const char **addresses; + int *ports; /* Either NULL for lp_smb_ports() per + * destination or a list of explicit ports */ + } in; + struct { + struct smbcli_socket *socket; + } out; +}; + +struct smbcli_session; +struct resolve_context; + +#include "libcli/smb_composite/proto.h" diff --git a/source4/libcli/smbc/README b/source4/libcli/smbc/README new file mode 100644 index 0000000000..66b0782722 --- /dev/null +++ b/source4/libcli/smbc/README @@ -0,0 +1 @@ +This is where the new samba4 libsmbclient will live. diff --git a/source4/libcli/util/clilsa.c b/source4/libcli/util/clilsa.c new file mode 100644 index 0000000000..16967d73b0 --- /dev/null +++ b/source4/libcli/util/clilsa.c @@ -0,0 +1,359 @@ +/* + Unix SMB/CIFS implementation. + + lsa calls for file sharing connections + + Copyright (C) Andrew Tridgell 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/>. +*/ + +/* + when dealing with ACLs the file sharing client code needs to + sometimes make LSA RPC calls. This code provides an easy interface + for doing those calls. +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/libcli.h" +#include "libcli/security/security.h" +#include "librpc/gen_ndr/ndr_lsa.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "libcli/util/clilsa.h" + +struct smblsa_state { + struct dcerpc_pipe *pipe; + struct smbcli_tree *ipc_tree; + struct policy_handle handle; +}; + +/* + establish the lsa pipe connection +*/ +static NTSTATUS smblsa_connect(struct smbcli_state *cli) +{ + struct smblsa_state *lsa; + NTSTATUS status; + struct lsa_OpenPolicy r; + uint16_t system_name = '\\'; + union smb_tcon tcon; + struct lsa_ObjectAttribute attr; + struct lsa_QosInfo qos; + + if (cli->lsa != NULL) { + return NT_STATUS_OK; + } + + lsa = talloc(cli, struct smblsa_state); + if (lsa == NULL) { + return NT_STATUS_NO_MEMORY; + } + + lsa->ipc_tree = smbcli_tree_init(cli->session, lsa, false); + if (lsa->ipc_tree == NULL) { + return NT_STATUS_NO_MEMORY; + } + + /* connect to IPC$ */ + tcon.generic.level = RAW_TCON_TCONX; + tcon.tconx.in.flags = 0; + tcon.tconx.in.password = data_blob(NULL, 0); + tcon.tconx.in.path = "ipc$"; + tcon.tconx.in.device = "IPC"; + status = smb_raw_tcon(lsa->ipc_tree, lsa, &tcon); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(lsa); + return status; + } + lsa->ipc_tree->tid = tcon.tconx.out.tid; + + lsa->pipe = dcerpc_pipe_init(lsa, cli->transport->socket->event.ctx, + cli->transport->iconv_convenience); + if (lsa->pipe == NULL) { + talloc_free(lsa); + return NT_STATUS_NO_MEMORY; + } + + /* open the LSA pipe */ + status = dcerpc_pipe_open_smb(lsa->pipe, lsa->ipc_tree, NDR_LSARPC_NAME); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(lsa); + return status; + } + + /* bind to the LSA pipe */ + status = dcerpc_bind_auth_none(lsa->pipe, &ndr_table_lsarpc); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(lsa); + return status; + } + + + /* open a lsa policy handle */ + qos.len = 0; + qos.impersonation_level = 2; + qos.context_mode = 1; + qos.effective_only = 0; + + attr.len = 0; + attr.root_dir = NULL; + attr.object_name = NULL; + attr.attributes = 0; + attr.sec_desc = NULL; + attr.sec_qos = &qos; + + r.in.system_name = &system_name; + r.in.attr = &attr; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = &lsa->handle; + + status = dcerpc_lsa_OpenPolicy(lsa->pipe, lsa, &r); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(lsa); + return status; + } + + cli->lsa = lsa; + + return NT_STATUS_OK; +} + + +/* + return the set of privileges for the given sid +*/ +NTSTATUS smblsa_sid_privileges(struct smbcli_state *cli, struct dom_sid *sid, + TALLOC_CTX *mem_ctx, + struct lsa_RightSet *rights) +{ + NTSTATUS status; + struct lsa_EnumAccountRights r; + + status = smblsa_connect(cli); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + r.in.handle = &cli->lsa->handle; + r.in.sid = sid; + r.out.rights = rights; + + return dcerpc_lsa_EnumAccountRights(cli->lsa->pipe, mem_ctx, &r); +} + + +/* + check if a named sid has a particular named privilege +*/ +NTSTATUS smblsa_sid_check_privilege(struct smbcli_state *cli, + const char *sid_str, + const char *privilege) +{ + struct lsa_RightSet rights; + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(cli); + struct dom_sid *sid; + unsigned i; + + sid = dom_sid_parse_talloc(mem_ctx, sid_str); + if (sid == NULL) { + talloc_free(mem_ctx); + return NT_STATUS_INVALID_SID; + } + + status = smblsa_sid_privileges(cli, sid, mem_ctx, &rights); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx); + return status; + } + + for (i=0;i<rights.count;i++) { + if (strcmp(rights.names[i].string, privilege) == 0) { + talloc_free(mem_ctx); + return NT_STATUS_OK; + } + } + + talloc_free(mem_ctx); + return NT_STATUS_NOT_FOUND; +} + + +/* + lookup a SID, returning its name +*/ +NTSTATUS smblsa_lookup_sid(struct smbcli_state *cli, + const char *sid_str, + TALLOC_CTX *mem_ctx, + const char **name) +{ + struct lsa_LookupSids r; + struct lsa_TransNameArray names; + struct lsa_SidArray sids; + struct lsa_RefDomainList *domains = NULL; + uint32_t count = 1; + NTSTATUS status; + struct dom_sid *sid; + TALLOC_CTX *mem_ctx2 = talloc_new(mem_ctx); + + status = smblsa_connect(cli); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + sid = dom_sid_parse_talloc(mem_ctx2, sid_str); + if (sid == NULL) { + return NT_STATUS_INVALID_SID; + } + + names.count = 0; + names.names = NULL; + + sids.num_sids = 1; + sids.sids = talloc(mem_ctx2, struct lsa_SidPtr); + sids.sids[0].sid = sid; + + r.in.handle = &cli->lsa->handle; + r.in.sids = &sids; + r.in.names = &names; + r.in.level = 1; + r.in.count = &count; + r.out.count = &count; + r.out.names = &names; + r.out.domains = &domains; + + status = dcerpc_lsa_LookupSids(cli->lsa->pipe, mem_ctx2, &r); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx2); + return status; + } + if (names.count != 1) { + talloc_free(mem_ctx2); + return NT_STATUS_UNSUCCESSFUL; + } + + (*name) = talloc_asprintf(mem_ctx, "%s\\%s", + domains->domains[0].name.string, + names.names[0].name.string); + + talloc_free(mem_ctx2); + + return NT_STATUS_OK; +} + +/* + lookup a name, returning its sid +*/ +NTSTATUS smblsa_lookup_name(struct smbcli_state *cli, + const char *name, + TALLOC_CTX *mem_ctx, + const char **sid_str) +{ + struct lsa_LookupNames r; + struct lsa_TransSidArray sids; + struct lsa_String names; + struct lsa_RefDomainList *domains = NULL; + uint32_t count = 1; + NTSTATUS status; + struct dom_sid *sid; + TALLOC_CTX *mem_ctx2 = talloc_new(mem_ctx); + uint32_t rid; + + status = smblsa_connect(cli); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + sids.count = 0; + sids.sids = NULL; + + names.string = name; + + r.in.handle = &cli->lsa->handle; + r.in.num_names = 1; + r.in.names = &names; + r.in.sids = &sids; + r.in.level = 1; + r.in.count = &count; + r.out.count = &count; + r.out.sids = &sids; + r.out.domains = &domains; + + status = dcerpc_lsa_LookupNames(cli->lsa->pipe, mem_ctx2, &r); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(mem_ctx2); + return status; + } + if (sids.count != 1) { + talloc_free(mem_ctx2); + return NT_STATUS_UNSUCCESSFUL; + } + + sid = domains->domains[0].sid; + rid = sids.sids[0].rid; + + (*sid_str) = talloc_asprintf(mem_ctx, "%s-%u", + dom_sid_string(mem_ctx2, sid), rid); + + talloc_free(mem_ctx2); + + return NT_STATUS_OK; +} + + +/* + add a set of privileges to the given sid +*/ +NTSTATUS smblsa_sid_add_privileges(struct smbcli_state *cli, struct dom_sid *sid, + TALLOC_CTX *mem_ctx, + struct lsa_RightSet *rights) +{ + NTSTATUS status; + struct lsa_AddAccountRights r; + + status = smblsa_connect(cli); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + r.in.handle = &cli->lsa->handle; + r.in.sid = sid; + r.in.rights = rights; + + return dcerpc_lsa_AddAccountRights(cli->lsa->pipe, mem_ctx, &r); +} + +/* + remove a set of privileges from the given sid +*/ +NTSTATUS smblsa_sid_del_privileges(struct smbcli_state *cli, struct dom_sid *sid, + TALLOC_CTX *mem_ctx, + struct lsa_RightSet *rights) +{ + NTSTATUS status; + struct lsa_RemoveAccountRights r; + + status = smblsa_connect(cli); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + r.in.handle = &cli->lsa->handle; + r.in.sid = sid; + r.in.remove_all = 0; + r.in.rights = rights; + + return dcerpc_lsa_RemoveAccountRights(cli->lsa->pipe, mem_ctx, &r); +} diff --git a/source4/libcli/util/errormap.c b/source4/libcli/util/errormap.c new file mode 100644 index 0000000000..930e45b214 --- /dev/null +++ b/source4/libcli/util/errormap.c @@ -0,0 +1,1408 @@ +/* + * 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" +#include "librpc/ndr/libndr.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_t dos_class; + uint32_t dos_code; + NTSTATUS ntstatus; +} ntstatus_to_dos_map[] = { + {ERRDOS, ERRnofiles, STATUS_NO_MORE_FILES}, + {ERRDOS, ERRnofiles, NT_STATUS_NO_MORE_ENTRIES}, + {ERRDOS, ERRgeneral, NT_STATUS_UNSUCCESSFUL}, + {ERRDOS, ERRbadfunc, NT_STATUS_NOT_IMPLEMENTED}, + {ERRDOS, 87, NT_STATUS_INVALID_INFO_CLASS}, + {ERRDOS, 24, NT_STATUS_INFO_LENGTH_MISMATCH}, + {ERRHRD, ERRgeneral, NT_STATUS_ACCESS_VIOLATION}, + {ERRHRD, ERRgeneral, NT_STATUS_IN_PAGE_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA}, + {ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_INITIAL_STACK}, + {ERRDOS, 193, NT_STATUS_BAD_INITIAL_PC}, + {ERRDOS, 87, NT_STATUS_INVALID_CID}, + {ERRHRD, ERRgeneral, NT_STATUS_TIMER_NOT_CANCELED}, + {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER}, + {ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_DEVICE}, + {ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_FILE}, + {ERRDOS, ERRbadfunc, NT_STATUS_INVALID_DEVICE_REQUEST}, + {ERRDOS, 38, NT_STATUS_END_OF_FILE}, + {ERRDOS, 34, NT_STATUS_WRONG_VOLUME}, + {ERRDOS, 21, NT_STATUS_NO_MEDIA_IN_DEVICE}, + {ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_MEDIA}, + {ERRDOS, 27, NT_STATUS_NONEXISTENT_SECTOR}, +/** Session setup succeeded. This shouldn't happen...*/ +/** Session setup succeeded. This shouldn't happen...*/ +/** NT error on DOS connection! (NT_STATUS_OK) */ +/* { This NT error code was 'sqashed' + from NT_STATUS_MORE_PROCESSING_REQUIRED to NT_STATUS_OK + during the session setup } +*/ +#if 0 + {SUCCESS, 0, NT_STATUS_OK}, +#endif + {ERRDOS, ERRnomem, NT_STATUS_NO_MEMORY}, + {ERRDOS, 487, NT_STATUS_CONFLICTING_ADDRESSES}, + {ERRDOS, 487, NT_STATUS_NOT_MAPPED_VIEW}, + {ERRDOS, 87, NT_STATUS_UNABLE_TO_FREE_VM}, + {ERRDOS, 87, NT_STATUS_UNABLE_TO_DELETE_SECTION}, + {ERRDOS, 2142, NT_STATUS_INVALID_SYSTEM_SERVICE}, + {ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_INSTRUCTION}, + {ERRDOS, ERRnoaccess, NT_STATUS_INVALID_LOCK_SEQUENCE}, + {ERRDOS, ERRnoaccess, NT_STATUS_INVALID_VIEW_SIZE}, + {ERRDOS, 193, NT_STATUS_INVALID_FILE_FOR_SECTION}, + {ERRDOS, ERRnoaccess, NT_STATUS_ALREADY_COMMITTED}, +/* { This NT error code was 'sqashed' + from NT_STATUS_ACCESS_DENIED to NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE + during the session setup } +*/ + {ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED}, + {ERRDOS, 111, NT_STATUS_BUFFER_TOO_SMALL}, + {ERRDOS, ERRbadfid, NT_STATUS_OBJECT_TYPE_MISMATCH}, + {ERRHRD, ERRgeneral, NT_STATUS_NONCONTINUABLE_EXCEPTION}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_DISPOSITION}, + {ERRHRD, ERRgeneral, NT_STATUS_UNWIND}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_STACK}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_UNWIND_TARGET}, + {ERRDOS, 158, NT_STATUS_NOT_LOCKED}, + {ERRHRD, ERRgeneral, NT_STATUS_PARITY_ERROR}, + {ERRDOS, 487, NT_STATUS_UNABLE_TO_DECOMMIT_VM}, + {ERRDOS, 487, NT_STATUS_NOT_COMMITTED}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_PORT_ATTRIBUTES}, + {ERRHRD, ERRgeneral, NT_STATUS_PORT_MESSAGE_TOO_LONG}, + {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_MIX}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_QUOTA_LOWER}, + {ERRHRD, ERRgeneral, NT_STATUS_DISK_CORRUPT_ERROR}, + {ERRDOS, ERRinvalidname, NT_STATUS_OBJECT_NAME_INVALID}, + {ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_NOT_FOUND}, + {ERRDOS, ERRfilexists, NT_STATUS_OBJECT_NAME_COLLISION}, + {ERRHRD, ERRgeneral, NT_STATUS_HANDLE_NOT_WAITABLE}, + {ERRDOS, ERRbadfid, NT_STATUS_PORT_DISCONNECTED}, + {ERRHRD, ERRgeneral, NT_STATUS_DEVICE_ALREADY_ATTACHED}, + {ERRDOS, 161, NT_STATUS_OBJECT_PATH_INVALID}, + {ERRDOS, ERRbadpath, NT_STATUS_OBJECT_PATH_NOT_FOUND}, + {ERRDOS, 161, NT_STATUS_OBJECT_PATH_SYNTAX_BAD}, + {ERRHRD, ERRgeneral, NT_STATUS_DATA_OVERRUN}, + {ERRHRD, ERRgeneral, NT_STATUS_DATA_LATE_ERROR}, + {ERRDOS, 23, NT_STATUS_DATA_ERROR}, + {ERRDOS, 23, NT_STATUS_CRC_ERROR}, + {ERRDOS, ERRnomem, NT_STATUS_SECTION_TOO_BIG}, + {ERRDOS, ERRnoaccess, NT_STATUS_PORT_CONNECTION_REFUSED}, + {ERRDOS, ERRbadfid, NT_STATUS_INVALID_PORT_HANDLE}, + {ERRDOS, ERRbadshare, NT_STATUS_SHARING_VIOLATION}, + {ERRHRD, ERRgeneral, NT_STATUS_QUOTA_EXCEEDED}, + {ERRDOS, 87, NT_STATUS_INVALID_PAGE_PROTECTION}, + {ERRDOS, 288, NT_STATUS_MUTANT_NOT_OWNED}, + {ERRDOS, 298, NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED}, + {ERRDOS, 87, NT_STATUS_PORT_ALREADY_SET}, + {ERRDOS, 87, NT_STATUS_SECTION_NOT_IMAGE}, + {ERRDOS, 156, NT_STATUS_SUSPEND_COUNT_EXCEEDED}, + {ERRDOS, ERRnoaccess, NT_STATUS_THREAD_IS_TERMINATING}, + {ERRDOS, 87, NT_STATUS_BAD_WORKING_SET_LIMIT}, + {ERRDOS, 87, NT_STATUS_INCOMPATIBLE_FILE_MAP}, + {ERRDOS, 87, NT_STATUS_SECTION_PROTECTION}, + {ERRDOS, 282, NT_STATUS_EAS_NOT_SUPPORTED}, + {ERRDOS, 255, NT_STATUS_EA_TOO_LARGE}, + {ERRHRD, ERRgeneral, NT_STATUS_NONEXISTENT_EA_ENTRY}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_EAS_ON_FILE}, + {ERRHRD, ERRgeneral, NT_STATUS_EA_CORRUPT_ERROR}, + {ERRDOS, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT}, + {ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED}, + {ERRDOS, ERRnoaccess, NT_STATUS_DELETE_PENDING}, + {ERRDOS, ERRunsup, NT_STATUS_CTL_FILE_NOT_SUPPORTED}, + {ERRHRD, ERRgeneral, NT_STATUS_UNKNOWN_REVISION}, + {ERRHRD, ERRgeneral, NT_STATUS_REVISION_MISMATCH}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_OWNER}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_PRIMARY_GROUP}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_IMPERSONATION_TOKEN}, + {ERRHRD, ERRgeneral, NT_STATUS_CANT_DISABLE_MANDATORY}, + {ERRDOS, 2215, NT_STATUS_NO_LOGON_SERVERS}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_LOGON_SESSION}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PRIVILEGE}, + {ERRDOS, ERRnoaccess, NT_STATUS_PRIVILEGE_NOT_HELD}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACCOUNT_NAME}, + {ERRHRD, ERRgeneral, NT_STATUS_USER_EXISTS}, +/* { This NT error code was 'sqashed' + from NT_STATUS_NO_SUCH_USER to NT_STATUS_LOGON_FAILURE + during the session setup } +*/ + {ERRDOS, ERRnoaccess, NT_STATUS_NO_SUCH_USER}, + {ERRHRD, ERRgeneral, NT_STATUS_GROUP_EXISTS}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_GROUP}, + {ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_GROUP}, + {ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_GROUP}, + {ERRHRD, ERRgeneral, NT_STATUS_LAST_ADMIN}, +/* { This NT error code was 'sqashed' + from NT_STATUS_WRONG_PASSWORD to NT_STATUS_LOGON_FAILURE + during the session setup } +*/ + {ERRSRV, ERRbadpw, NT_STATUS_WRONG_PASSWORD}, + {ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_PASSWORD}, + {ERRHRD, ERRgeneral, NT_STATUS_PASSWORD_RESTRICTION}, + {ERRDOS, ERRnoaccess, NT_STATUS_LOGON_FAILURE}, + {ERRHRD, ERRgeneral, NT_STATUS_ACCOUNT_RESTRICTION}, + {ERRSRV, 2241, NT_STATUS_INVALID_LOGON_HOURS}, + {ERRSRV, 2240, NT_STATUS_INVALID_WORKSTATION}, + {ERRSRV, 2242, NT_STATUS_PASSWORD_EXPIRED}, + {ERRSRV, 2239, NT_STATUS_ACCOUNT_DISABLED}, + {ERRHRD, ERRgeneral, NT_STATUS_NONE_MAPPED}, + {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_LUIDS_REQUESTED}, + {ERRHRD, ERRgeneral, NT_STATUS_LUIDS_EXHAUSTED}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_SUB_AUTHORITY}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ACL}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_SID}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_SECURITY_DESCR}, + {ERRDOS, 127, NT_STATUS_PROCEDURE_NOT_FOUND}, + {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_FORMAT}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_TOKEN}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_INHERITANCE_ACL}, + {ERRDOS, 158, NT_STATUS_RANGE_NOT_LOCKED}, + {ERRDOS, 112, NT_STATUS_DISK_FULL}, + {ERRHRD, ERRgeneral, NT_STATUS_SERVER_DISABLED}, + {ERRHRD, ERRgeneral, NT_STATUS_SERVER_NOT_DISABLED}, + {ERRDOS, 68, NT_STATUS_TOO_MANY_GUIDS_REQUESTED}, + {ERRDOS, 259, NT_STATUS_GUIDS_EXHAUSTED}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ID_AUTHORITY}, + {ERRDOS, 259, NT_STATUS_AGENTS_EXHAUSTED}, + {ERRDOS, 154, NT_STATUS_INVALID_VOLUME_LABEL}, + {ERRDOS, ERRres, NT_STATUS_SECTION_NOT_EXTENDED}, + {ERRDOS, 487, NT_STATUS_NOT_MAPPED_DATA}, + {ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_DATA_NOT_FOUND}, + {ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_TYPE_NOT_FOUND}, + {ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_NAME_NOT_FOUND}, + {ERRHRD, ERRgeneral, NT_STATUS_ARRAY_BOUNDS_EXCEEDED}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DENORMAL_OPERAND}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_DIVIDE_BY_ZERO}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INEXACT_RESULT}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_INVALID_OPERATION}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_OVERFLOW}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_STACK_CHECK}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOAT_UNDERFLOW}, + {ERRHRD, ERRgeneral, NT_STATUS_INTEGER_DIVIDE_BY_ZERO}, + {ERRDOS, 534, NT_STATUS_INTEGER_OVERFLOW}, + {ERRHRD, ERRgeneral, NT_STATUS_PRIVILEGED_INSTRUCTION}, + {ERRDOS, ERRnomem, NT_STATUS_TOO_MANY_PAGING_FILES}, + {ERRHRD, ERRgeneral, NT_STATUS_FILE_INVALID}, + {ERRHRD, ERRgeneral, NT_STATUS_ALLOTTED_SPACE_EXCEEDED}, +/* { This NT error code was 'sqashed' + from NT_STATUS_INSUFFICIENT_RESOURCES to NT_STATUS_INSUFF_SERVER_RESOURCES + during the session setup } +*/ + {ERRDOS, ERRnomem, NT_STATUS_INSUFFICIENT_RESOURCES}, + {ERRDOS, ERRbadpath, NT_STATUS_DFS_EXIT_PATH_FOUND}, + {ERRDOS, 23, NT_STATUS_DEVICE_DATA_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_CONNECTED}, + {ERRDOS, 21, NT_STATUS_DEVICE_POWER_FAILURE}, + {ERRDOS, 487, NT_STATUS_FREE_VM_NOT_AT_BASE}, + {ERRDOS, 487, NT_STATUS_MEMORY_NOT_ALLOCATED}, + {ERRHRD, ERRgeneral, NT_STATUS_WORKING_SET_QUOTA}, + {ERRDOS, 19, NT_STATUS_MEDIA_WRITE_PROTECTED}, + {ERRDOS, 21, NT_STATUS_DEVICE_NOT_READY}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_GROUP_ATTRIBUTES}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_IMPERSONATION_LEVEL}, + {ERRHRD, ERRgeneral, NT_STATUS_CANT_OPEN_ANONYMOUS}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_VALIDATION_CLASS}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_TOKEN_TYPE}, + {ERRDOS, 87, NT_STATUS_BAD_MASTER_BOOT_RECORD}, + {ERRHRD, ERRgeneral, NT_STATUS_INSTRUCTION_MISALIGNMENT}, + {ERRDOS, ERRpipebusy, NT_STATUS_INSTANCE_NOT_AVAILABLE}, + {ERRDOS, ERRpipebusy, NT_STATUS_PIPE_NOT_AVAILABLE}, + {ERRDOS, ERRbadpipe, NT_STATUS_INVALID_PIPE_STATE}, + {ERRDOS, ERRpipebusy, NT_STATUS_PIPE_BUSY}, + {ERRDOS, ERRbadfunc, NT_STATUS_ILLEGAL_FUNCTION}, + {ERRDOS, ERRnotconnected, NT_STATUS_PIPE_DISCONNECTED}, + {ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_CLOSING}, + {ERRHRD, ERRgeneral, NT_STATUS_PIPE_CONNECTED}, + {ERRHRD, ERRgeneral, NT_STATUS_PIPE_LISTENING}, + {ERRDOS, ERRbadpipe, NT_STATUS_INVALID_READ_MODE}, + {ERRDOS, 121, NT_STATUS_IO_TIMEOUT}, + {ERRDOS, 38, NT_STATUS_FILE_FORCED_CLOSED}, + {ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STARTED}, + {ERRHRD, ERRgeneral, NT_STATUS_PROFILING_NOT_STOPPED}, + {ERRHRD, ERRgeneral, NT_STATUS_COULD_NOT_INTERPRET}, + {ERRDOS, ERRnoaccess, NT_STATUS_FILE_IS_A_DIRECTORY}, + {ERRDOS, ERRunsup, NT_STATUS_NOT_SUPPORTED}, + {ERRDOS, 51, NT_STATUS_REMOTE_NOT_LISTENING}, + {ERRDOS, 52, NT_STATUS_DUPLICATE_NAME}, + {ERRDOS, 53, NT_STATUS_BAD_NETWORK_PATH}, + {ERRDOS, 54, NT_STATUS_NETWORK_BUSY}, + {ERRDOS, 55, NT_STATUS_DEVICE_DOES_NOT_EXIST}, + {ERRDOS, 56, NT_STATUS_TOO_MANY_COMMANDS}, + {ERRDOS, 57, NT_STATUS_ADAPTER_HARDWARE_ERROR}, + {ERRDOS, 58, NT_STATUS_INVALID_NETWORK_RESPONSE}, + {ERRDOS, 59, NT_STATUS_UNEXPECTED_NETWORK_ERROR}, + {ERRDOS, 60, NT_STATUS_BAD_REMOTE_ADAPTER}, + {ERRDOS, 61, NT_STATUS_PRINT_QUEUE_FULL}, + {ERRDOS, 62, NT_STATUS_NO_SPOOL_SPACE}, + {ERRDOS, 63, NT_STATUS_PRINT_CANCELLED}, + {ERRDOS, 64, NT_STATUS_NETWORK_NAME_DELETED}, + {ERRDOS, 65, NT_STATUS_NETWORK_ACCESS_DENIED}, + {ERRDOS, 66, NT_STATUS_BAD_DEVICE_TYPE}, + {ERRDOS, ERRnosuchshare, NT_STATUS_BAD_NETWORK_NAME}, + {ERRDOS, 68, NT_STATUS_TOO_MANY_NAMES}, + {ERRDOS, 69, NT_STATUS_TOO_MANY_SESSIONS}, + {ERRDOS, 70, NT_STATUS_SHARING_PAUSED}, + {ERRDOS, 71, NT_STATUS_REQUEST_NOT_ACCEPTED}, + {ERRDOS, 72, NT_STATUS_REDIRECTOR_PAUSED}, + {ERRDOS, 88, NT_STATUS_NET_WRITE_FAULT}, + {ERRHRD, ERRgeneral, NT_STATUS_PROFILING_AT_LIMIT}, + {ERRDOS, ERRdiffdevice, NT_STATUS_NOT_SAME_DEVICE}, + {ERRDOS, ERRnoaccess, NT_STATUS_FILE_RENAMED}, + {ERRDOS, 240, NT_STATUS_VIRTUAL_CIRCUIT_CLOSED}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_SECURITY_ON_OBJECT}, + {ERRHRD, ERRgeneral, NT_STATUS_CANT_WAIT}, + {ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_EMPTY}, + {ERRHRD, ERRgeneral, NT_STATUS_CANT_ACCESS_DOMAIN_INFO}, + {ERRHRD, ERRgeneral, NT_STATUS_CANT_TERMINATE_SELF}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_SERVER_STATE}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_STATE}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_DOMAIN_ROLE}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_DOMAIN}, + {ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_EXISTS}, + {ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_LIMIT_EXCEEDED}, + {ERRDOS, 300, NT_STATUS_OPLOCK_NOT_GRANTED}, + {ERRDOS, 301, NT_STATUS_INVALID_OPLOCK_PROTOCOL}, + {ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_CORRUPTION}, + {ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_GENERIC_NOT_MAPPED}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_DESCRIPTOR_FORMAT}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_USER_BUFFER}, + {ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_IO_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_CREATE_ERR}, + {ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_MAP_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_UNEXPECTED_MM_EXTEND_ERR}, + {ERRHRD, ERRgeneral, NT_STATUS_NOT_LOGON_PROCESS}, + {ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_EXISTS}, + {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_1}, + {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_2}, + {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_3}, + {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_4}, + {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_5}, + {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_6}, + {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_7}, + {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_8}, + {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_9}, + {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_10}, + {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_11}, + {ERRDOS, 87, NT_STATUS_INVALID_PARAMETER_12}, + {ERRDOS, ERRbadpath, NT_STATUS_REDIRECTOR_NOT_STARTED}, + {ERRHRD, ERRgeneral, NT_STATUS_REDIRECTOR_STARTED}, + {ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_PACKAGE}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_FUNCTION_TABLE}, + {ERRDOS, 203, NT_STATUS(0xc0000100)}, + {ERRDOS, 145, NT_STATUS_DIRECTORY_NOT_EMPTY}, + {ERRHRD, ERRgeneral, NT_STATUS_FILE_CORRUPT_ERROR}, + {ERRDOS, 267, NT_STATUS_NOT_A_DIRECTORY}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_LOGON_SESSION_STATE}, + {ERRHRD, ERRgeneral, NT_STATUS_LOGON_SESSION_COLLISION}, + {ERRDOS, 206, NT_STATUS_NAME_TOO_LONG}, + {ERRDOS, 2401, NT_STATUS_FILES_OPEN}, + {ERRDOS, 2404, NT_STATUS_CONNECTION_IN_USE}, + {ERRHRD, ERRgeneral, NT_STATUS_MESSAGE_NOT_FOUND}, + {ERRDOS, ERRnoaccess, NT_STATUS_PROCESS_IS_TERMINATING}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_LOGON_TYPE}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_GUID_TRANSLATION}, + {ERRHRD, ERRgeneral, NT_STATUS_CANNOT_IMPERSONATE}, + {ERRHRD, ERRgeneral, NT_STATUS_IMAGE_ALREADY_LOADED}, + {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_PRESENT}, + {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_NOT_EXIST}, + {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_LID_ALREADY_OWNED}, + {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_NOT_LID_OWNER}, + {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_COMMAND}, + {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_LID}, + {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE}, + {ERRHRD, ERRgeneral, NT_STATUS_ABIOS_INVALID_SELECTOR}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_LDT}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_SIZE}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_OFFSET}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_LDT_DESCRIPTOR}, + {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NE_FORMAT}, + {ERRHRD, ERRgeneral, NT_STATUS_RXACT_INVALID_STATE}, + {ERRHRD, ERRgeneral, NT_STATUS_RXACT_COMMIT_FAILURE}, + {ERRHRD, ERRgeneral, NT_STATUS_MAPPED_FILE_SIZE_ZERO}, + {ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES}, + {ERRHRD, ERRgeneral, NT_STATUS_CANCELLED}, + {ERRDOS, ERRnoaccess, NT_STATUS_CANNOT_DELETE}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_COMPUTER_NAME}, + {ERRDOS, ERRnoaccess, NT_STATUS_FILE_DELETED}, + {ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_ACCOUNT}, + {ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_GROUP}, + {ERRHRD, ERRgeneral, NT_STATUS_SPECIAL_USER}, + {ERRHRD, ERRgeneral, NT_STATUS_MEMBERS_PRIMARY_GROUP}, + {ERRDOS, ERRbadfid, NT_STATUS_FILE_CLOSED}, + {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_THREADS}, + {ERRHRD, ERRgeneral, NT_STATUS_THREAD_NOT_IN_PROCESS}, + {ERRHRD, ERRgeneral, NT_STATUS_TOKEN_ALREADY_IN_USE}, + {ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_QUOTA_EXCEEDED}, + {ERRHRD, ERRgeneral, NT_STATUS_COMMITMENT_LIMIT}, + {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_LE_FORMAT}, + {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_NOT_MZ}, + {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_PROTECT}, + {ERRDOS, 193, NT_STATUS_INVALID_IMAGE_WIN_16}, + {ERRHRD, ERRgeneral, NT_STATUS_LOGON_SERVER_CONFLICT}, + {ERRHRD, ERRgeneral, NT_STATUS_TIME_DIFFERENCE_AT_DC}, + {ERRHRD, ERRgeneral, NT_STATUS_SYNCHRONIZATION_REQUIRED}, + {ERRDOS, 126, NT_STATUS_DLL_NOT_FOUND}, + {ERRHRD, ERRgeneral, NT_STATUS_OPEN_FAILED}, + {ERRHRD, ERRgeneral, NT_STATUS_IO_PRIVILEGE_FAILED}, + {ERRDOS, 182, NT_STATUS_ORDINAL_NOT_FOUND}, + {ERRDOS, 127, NT_STATUS_ENTRYPOINT_NOT_FOUND}, + {ERRHRD, ERRgeneral, NT_STATUS_CONTROL_C_EXIT}, + {ERRDOS, 64, NT_STATUS_LOCAL_DISCONNECT}, + {ERRDOS, 64, NT_STATUS_REMOTE_DISCONNECT}, + {ERRDOS, 51, NT_STATUS_REMOTE_RESOURCES}, + {ERRDOS, 59, NT_STATUS_LINK_FAILED}, + {ERRDOS, 59, NT_STATUS_LINK_TIMEOUT}, + {ERRDOS, 59, NT_STATUS_INVALID_CONNECTION}, + {ERRDOS, 59, NT_STATUS_INVALID_ADDRESS}, + {ERRHRD, ERRgeneral, NT_STATUS_DLL_INIT_FAILED}, + {ERRHRD, ERRgeneral, NT_STATUS_MISSING_SYSTEMFILE}, + {ERRHRD, ERRgeneral, NT_STATUS_UNHANDLED_EXCEPTION}, + {ERRHRD, ERRgeneral, NT_STATUS_APP_INIT_FAILURE}, + {ERRHRD, ERRgeneral, NT_STATUS_PAGEFILE_CREATE_FAILED}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_PAGEFILE}, + {ERRDOS, 124, NT_STATUS_INVALID_LEVEL}, + {ERRDOS, 86, NT_STATUS_WRONG_PASSWORD_CORE}, + {ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_FLOAT_CONTEXT}, + {ERRDOS, 109, NT_STATUS_PIPE_BROKEN}, + {ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_CORRUPT}, + {ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_IO_FAILED}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_EVENT_PAIR}, + {ERRHRD, ERRgeneral, NT_STATUS_UNRECOGNIZED_VOLUME}, + {ERRHRD, ERRgeneral, NT_STATUS_SERIAL_NO_DEVICE_INITED}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_ALIAS}, + {ERRHRD, ERRgeneral, NT_STATUS_MEMBER_NOT_IN_ALIAS}, + {ERRHRD, ERRgeneral, NT_STATUS_MEMBER_IN_ALIAS}, + {ERRHRD, ERRgeneral, NT_STATUS_ALIAS_EXISTS}, + {ERRHRD, ERRgeneral, NT_STATUS_LOGON_NOT_GRANTED}, + {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SECRETS}, + {ERRHRD, ERRgeneral, NT_STATUS_SECRET_TOO_LONG}, + {ERRHRD, ERRgeneral, NT_STATUS_INTERNAL_DB_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_FULLSCREEN_MODE}, + {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_CONTEXT_IDS}, + {ERRDOS, ERRnoaccess, NT_STATUS_LOGON_TYPE_NOT_GRANTED}, + {ERRHRD, ERRgeneral, NT_STATUS_NOT_REGISTRY_FILE}, + {ERRHRD, ERRgeneral, NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED}, + {ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_FT_MISSING_MEMBER}, + {ERRHRD, ERRgeneral, NT_STATUS_ILL_FORMED_SERVICE_ENTRY}, + {ERRHRD, ERRgeneral, NT_STATUS_ILLEGAL_CHARACTER}, + {ERRHRD, ERRgeneral, NT_STATUS_UNMAPPABLE_CHARACTER}, + {ERRHRD, ERRgeneral, NT_STATUS_UNDEFINED_CHARACTER}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_VOLUME}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_WRONG_CYLINDER}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_UNKNOWN_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_FLOPPY_BAD_REGISTERS}, + {ERRHRD, ERRgeneral, NT_STATUS_DISK_RECALIBRATE_FAILED}, + {ERRHRD, ERRgeneral, NT_STATUS_DISK_OPERATION_FAILED}, + {ERRHRD, ERRgeneral, NT_STATUS_DISK_RESET_FAILED}, + {ERRHRD, ERRgeneral, NT_STATUS_SHARED_IRQ_BUSY}, + {ERRHRD, ERRgeneral, NT_STATUS_FT_ORPHANING}, + {ERRHRD, ERRgeneral, NT_STATUS(0xc000016e)}, + {ERRHRD, ERRgeneral, NT_STATUS(0xc000016f)}, + {ERRHRD, ERRgeneral, NT_STATUS(0xc0000170)}, + {ERRHRD, ERRgeneral, NT_STATUS(0xc0000171)}, + {ERRHRD, ERRgeneral, NT_STATUS_PARTITION_FAILURE}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_BLOCK_LENGTH}, + {ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_PARTITIONED}, + {ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_LOCK_MEDIA}, + {ERRHRD, ERRgeneral, NT_STATUS_UNABLE_TO_UNLOAD_MEDIA}, + {ERRHRD, ERRgeneral, NT_STATUS_EOM_OVERFLOW}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_MEDIA}, + {ERRHRD, ERRgeneral, NT_STATUS(0xc0000179)}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_SUCH_MEMBER}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_MEMBER}, + {ERRHRD, ERRgeneral, NT_STATUS_KEY_DELETED}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_LOG_SPACE}, + {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_SIDS}, + {ERRHRD, ERRgeneral, NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED}, + {ERRHRD, ERRgeneral, NT_STATUS_KEY_HAS_CHILDREN}, + {ERRHRD, ERRgeneral, NT_STATUS_CHILD_MUST_BE_VOLATILE}, + {ERRDOS, 87, NT_STATUS_DEVICE_CONFIGURATION_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_DRIVER_INTERNAL_ERROR}, + {ERRDOS, 22, NT_STATUS_INVALID_DEVICE_STATE}, + {ERRHRD, ERRgeneral, NT_STATUS_IO_DEVICE_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_DEVICE_PROTOCOL_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_BACKUP_CONTROLLER}, + {ERRHRD, ERRgeneral, NT_STATUS_LOG_FILE_FULL}, + {ERRDOS, 19, NT_STATUS_TOO_LATE}, + {ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_LSA_SECRET}, +/* { This NT error code was 'sqashed' + from NT_STATUS_NO_TRUST_SAM_ACCOUNT to NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE + during the session setup } +*/ + {ERRDOS, ERRnoaccess, NT_STATUS_NO_TRUST_SAM_ACCOUNT}, + {ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_DOMAIN_FAILURE}, + {ERRDOS, ERRnoaccess, NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE}, + {ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CORRUPT}, + {ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_CANT_START}, + {ERRDOS, ERRnoaccess, NT_STATUS_TRUST_FAILURE}, + {ERRHRD, ERRgeneral, NT_STATUS_MUTANT_LIMIT_EXCEEDED}, + {ERRDOS, ERRinvgroup, NT_STATUS_NETLOGON_NOT_STARTED}, + {ERRSRV, 2239, NT_STATUS_ACCOUNT_EXPIRED}, + {ERRHRD, ERRgeneral, NT_STATUS_POSSIBLE_DEADLOCK}, + {ERRHRD, ERRgeneral, NT_STATUS_NETWORK_CREDENTIAL_CONFLICT}, + {ERRHRD, ERRgeneral, NT_STATUS_REMOTE_SESSION_LIMIT}, + {ERRHRD, ERRgeneral, NT_STATUS_EVENTLOG_FILE_CHANGED}, + {ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT}, + {ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT}, + {ERRDOS, ERRnoaccess, NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT}, +/* { This NT error code was 'sqashed' + from NT_STATUS_DOMAIN_TRUST_INCONSISTENT to NT_STATUS_LOGON_FAILURE + during the session setup } +*/ + {ERRDOS, ERRnoaccess, NT_STATUS_DOMAIN_TRUST_INCONSISTENT}, + {ERRHRD, ERRgeneral, NT_STATUS_FS_DRIVER_REQUIRED}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_USER_SESSION_KEY}, + {ERRDOS, 59, NT_STATUS_USER_SESSION_DELETED}, + {ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_LANG_NOT_FOUND}, + {ERRDOS, ERRnomem, NT_STATUS_INSUFF_SERVER_RESOURCES}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_BUFFER_SIZE}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_COMPONENT}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_WILDCARD}, + {ERRDOS, 68, NT_STATUS_TOO_MANY_ADDRESSES}, + {ERRDOS, 52, NT_STATUS_ADDRESS_ALREADY_EXISTS}, + {ERRDOS, 64, NT_STATUS_ADDRESS_CLOSED}, + {ERRDOS, 64, NT_STATUS_CONNECTION_DISCONNECTED}, + {ERRDOS, 64, NT_STATUS_CONNECTION_RESET}, + {ERRDOS, 68, NT_STATUS_TOO_MANY_NODES}, + {ERRDOS, 59, NT_STATUS_TRANSACTION_ABORTED}, + {ERRDOS, 59, NT_STATUS_TRANSACTION_TIMED_OUT}, + {ERRDOS, 59, NT_STATUS_TRANSACTION_NO_RELEASE}, + {ERRDOS, 59, NT_STATUS_TRANSACTION_NO_MATCH}, + {ERRDOS, 59, NT_STATUS_TRANSACTION_RESPONDED}, + {ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_ID}, + {ERRDOS, 59, NT_STATUS_TRANSACTION_INVALID_TYPE}, + {ERRDOS, ERRunsup, NT_STATUS_NOT_SERVER_SESSION}, + {ERRDOS, ERRunsup, NT_STATUS_NOT_CLIENT_SESSION}, + {ERRHRD, ERRgeneral, NT_STATUS_CANNOT_LOAD_REGISTRY_FILE}, + {ERRHRD, ERRgeneral, NT_STATUS_DEBUG_ATTACH_FAILED}, + {ERRHRD, ERRgeneral, NT_STATUS_SYSTEM_PROCESS_TERMINATED}, + {ERRHRD, ERRgeneral, NT_STATUS_DATA_NOT_ACCEPTED}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_BROWSER_SERVERS_FOUND}, + {ERRHRD, ERRgeneral, NT_STATUS_VDM_HARD_ERROR}, + {ERRHRD, ERRgeneral, NT_STATUS_DRIVER_CANCEL_TIMEOUT}, + {ERRHRD, ERRgeneral, NT_STATUS_REPLY_MESSAGE_MISMATCH}, + {ERRHRD, ERRgeneral, NT_STATUS_MAPPED_ALIGNMENT}, + {ERRDOS, 193, NT_STATUS_IMAGE_CHECKSUM_MISMATCH}, + {ERRHRD, ERRgeneral, NT_STATUS_LOST_WRITEBEHIND_DATA}, + {ERRHRD, ERRgeneral, NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID}, + {ERRSRV, 2242, NT_STATUS_PASSWORD_MUST_CHANGE}, + {ERRHRD, ERRgeneral, NT_STATUS_NOT_FOUND}, + {ERRHRD, ERRgeneral, NT_STATUS_NOT_TINY_STREAM}, + {ERRHRD, ERRgeneral, NT_STATUS_RECOVERY_FAILURE}, + {ERRHRD, ERRgeneral, NT_STATUS_STACK_OVERFLOW_READ}, + {ERRHRD, ERRgeneral, NT_STATUS_FAIL_CHECK}, + {ERRHRD, ERRgeneral, NT_STATUS_DUPLICATE_OBJECTID}, + {ERRHRD, ERRgeneral, NT_STATUS_OBJECTID_EXISTS}, + {ERRHRD, ERRgeneral, NT_STATUS_CONVERT_TO_LARGE}, + {ERRHRD, ERRgeneral, NT_STATUS_RETRY}, + {ERRHRD, ERRgeneral, NT_STATUS_FOUND_OUT_OF_SCOPE}, + {ERRHRD, ERRgeneral, NT_STATUS_ALLOCATE_BUCKET}, + {ERRHRD, ERRgeneral, NT_STATUS_PROPSET_NOT_FOUND}, + {ERRHRD, ERRgeneral, NT_STATUS_MARSHALL_OVERFLOW}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_VARIANT}, + {ERRHRD, ERRgeneral, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND}, + {ERRDOS, ERRnoaccess, NT_STATUS_ACCOUNT_LOCKED_OUT}, + {ERRDOS, ERRbadfid, NT_STATUS_HANDLE_NOT_CLOSABLE}, + {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_REFUSED}, + {ERRHRD, ERRgeneral, NT_STATUS_GRACEFUL_DISCONNECT}, + {ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_ALREADY_ASSOCIATED}, + {ERRHRD, ERRgeneral, NT_STATUS_ADDRESS_NOT_ASSOCIATED}, + {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_INVALID}, + {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ACTIVE}, + {ERRHRD, ERRgeneral, NT_STATUS_NETWORK_UNREACHABLE}, + {ERRHRD, ERRgeneral, NT_STATUS_HOST_UNREACHABLE}, + {ERRHRD, ERRgeneral, NT_STATUS_PROTOCOL_UNREACHABLE}, + {ERRHRD, ERRgeneral, NT_STATUS_PORT_UNREACHABLE}, + {ERRHRD, ERRgeneral, NT_STATUS_REQUEST_ABORTED}, + {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_ABORTED}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_COMPRESSION_BUFFER}, + {ERRHRD, ERRgeneral, NT_STATUS_USER_MAPPED_FILE}, + {ERRHRD, ERRgeneral, NT_STATUS_AUDIT_FAILED}, + {ERRHRD, ERRgeneral, NT_STATUS_TIMER_RESOLUTION_NOT_SET}, + {ERRHRD, ERRgeneral, NT_STATUS_CONNECTION_COUNT_LIMIT}, + {ERRHRD, ERRgeneral, NT_STATUS_LOGIN_TIME_RESTRICTION}, + {ERRHRD, ERRgeneral, NT_STATUS_LOGIN_WKSTA_RESTRICTION}, + {ERRDOS, 193, NT_STATUS_IMAGE_MP_UP_MISMATCH}, + {ERRHRD, ERRgeneral, NT_STATUS(0xc000024a)}, + {ERRHRD, ERRgeneral, NT_STATUS(0xc000024b)}, + {ERRHRD, ERRgeneral, NT_STATUS(0xc000024c)}, + {ERRHRD, ERRgeneral, NT_STATUS(0xc000024d)}, + {ERRHRD, ERRgeneral, NT_STATUS(0xc000024e)}, + {ERRHRD, ERRgeneral, NT_STATUS(0xc000024f)}, + {ERRHRD, ERRgeneral, NT_STATUS_INSUFFICIENT_LOGON_INFO}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_DLL_ENTRYPOINT}, + {ERRHRD, ERRgeneral, NT_STATUS_BAD_SERVICE_ENTRYPOINT}, + {ERRHRD, ERRgeneral, NT_STATUS_LPC_REPLY_LOST}, + {ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT1}, + {ERRHRD, ERRgeneral, NT_STATUS_IP_ADDRESS_CONFLICT2}, + {ERRHRD, ERRgeneral, NT_STATUS_REGISTRY_QUOTA_LIMIT}, + {ERRSRV, ERRbadtype, NT_STATUS_PATH_NOT_COVERED}, + {ERRHRD, ERRgeneral, NT_STATUS_NO_CALLBACK_ACTIVE}, + {ERRHRD, ERRgeneral, NT_STATUS_LICENSE_QUOTA_EXCEEDED}, + {ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_SHORT}, + {ERRHRD, ERRgeneral, NT_STATUS_PWD_TOO_RECENT}, + {ERRHRD, ERRgeneral, NT_STATUS_PWD_HISTORY_CONFLICT}, + {ERRHRD, ERRgeneral, NT_STATUS(0xc000025d)}, + {ERRHRD, ERRgeneral, NT_STATUS_PLUGPLAY_NO_DEVICE}, + {ERRHRD, ERRgeneral, NT_STATUS_UNSUPPORTED_COMPRESSION}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_HW_PROFILE}, + {ERRHRD, ERRgeneral, NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH}, + {ERRDOS, 182, NT_STATUS_DRIVER_ORDINAL_NOT_FOUND}, + {ERRDOS, 127, NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND}, + {ERRDOS, 288, NT_STATUS_RESOURCE_NOT_OWNED}, + {ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_LINKS}, + {ERRHRD, ERRgeneral, NT_STATUS_QUOTA_LIST_INCONSISTENT}, + {ERRHRD, ERRgeneral, NT_STATUS_FILE_IS_OFFLINE}, + {ERRDOS, 21, NT_STATUS(0xc000026e)}, + {ERRDOS, 161, NT_STATUS(0xc0000281)}, + {ERRDOS, ERRnoaccess, NT_STATUS(0xc000028a)}, + {ERRDOS, ERRnoaccess, NT_STATUS(0xc000028b)}, + {ERRHRD, ERRgeneral, NT_STATUS(0xc000028c)}, + {ERRDOS, ERRnoaccess, NT_STATUS(0xc000028d)}, + {ERRDOS, ERRnoaccess, NT_STATUS(0xc000028e)}, + {ERRDOS, ERRnoaccess, NT_STATUS(0xc000028f)}, + {ERRDOS, ERRnoaccess, NT_STATUS(0xc0000290)}, + {ERRDOS, ERRbadfunc, NT_STATUS(0xc000029c)}, +}; + + +/* errmap NTSTATUS->Win32 */ +static const struct { + NTSTATUS ntstatus; + WERROR werror; +} ntstatus_to_werror_map[] = { + /* + * we add this manualy here, so that W_ERROR(0x5) + * gets mapped to NTSTATUS_ACCESS_DENIED + */ + {NT_STATUS_ACCESS_DENIED, WERR_ACCESS_DENIED}, + {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} +}; + +bool ntstatus_check_dos_mapping = true; + +/* + check if a DOS encoded NTSTATUS code maps to the given NTSTATUS code +*/ +bool ntstatus_dos_equal(NTSTATUS status1, NTSTATUS status2) +{ + /* when we negotiate nt status support, we don't want to consider + the mapping of dos codes, as we want to catch the cases where + a forced dos code is needed + */ + if (ntstatus_check_dos_mapping) { + return NT_STATUS_V(status1) == NT_STATUS_V(status2); + } + + /* otherwise check if the mapping comes out right. Note that it is important + that we do the mapping only from ntstatus -> dos and not from dos -> ntstatus, + as that is the mapping that servers must do */ + if (!NT_STATUS_IS_DOS(status1) && NT_STATUS_IS_DOS(status2)) { + uint8_t eclass; + uint32_t ecode; + ntstatus_to_dos(status1, &eclass, &ecode); + return eclass == NT_STATUS_DOS_CLASS(status2) && + ecode == NT_STATUS_DOS_CODE(status2); + } + if (NT_STATUS_IS_DOS(status1) && !NT_STATUS_IS_DOS(status2)) { + uint8_t eclass; + uint32_t ecode; + ntstatus_to_dos(status2, &eclass, &ecode); + return eclass == NT_STATUS_DOS_CLASS(status1) && + ecode == NT_STATUS_DOS_CODE(status1); + } + return NT_STATUS_V(status1) == NT_STATUS_V(status2); +} + +/***************************************************************************** +convert a NT status code to a dos class/code + *****************************************************************************/ +void ntstatus_to_dos(NTSTATUS ntstatus, uint8_t *eclass, uint32_t *ecode) +{ + int i; + if (NT_STATUS_IS_OK(ntstatus)) { + *eclass = 0; + *ecode = 0; + return; + } + if (NT_STATUS_IS_DOS(ntstatus)) { + *eclass = NT_STATUS_DOS_CLASS(ntstatus); + *ecode = NT_STATUS_DOS_CODE(ntstatus); + return; + } + for (i=0; NT_STATUS_V(ntstatus_to_dos_map[i].ntstatus); i++) { + if (NT_STATUS_V(ntstatus) == + NT_STATUS_V(ntstatus_to_dos_map[i].ntstatus)) { + *eclass = ntstatus_to_dos_map[i].dos_class; + *ecode = ntstatus_to_dos_map[i].dos_code; + return; + } + } + *eclass = ERRHRD; + *ecode = ERRgeneral; +} + + +/***************************************************************************** +convert a WERROR to a NT status32 code + *****************************************************************************/ +NTSTATUS werror_to_ntstatus(WERROR error) +{ + int i; + if (W_ERROR_IS_OK(error)) return NT_STATUS_OK; + for (i=0; NT_STATUS_V(ntstatus_to_werror_map[i].ntstatus); i++) { + if (W_ERROR_V(error) == + W_ERROR_V(ntstatus_to_werror_map[i].werror)) { + return ntstatus_to_werror_map[i].ntstatus; + } + } + + /* just guess ... */ + return NT_STATUS(W_ERROR_V(error) | 0xc0000000); +} + +/***************************************************************************** +convert a NTSTATUS to a WERROR + *****************************************************************************/ +WERROR ntstatus_to_werror(NTSTATUS error) +{ + int i; + if (NT_STATUS_IS_OK(error)) return WERR_OK; + for (i=0; NT_STATUS_V(ntstatus_to_werror_map[i].ntstatus); i++) { + if (NT_STATUS_V(error) == + NT_STATUS_V(ntstatus_to_werror_map[i].ntstatus)) { + return ntstatus_to_werror_map[i].werror; + } + } + + /* a lame guess */ + return W_ERROR(NT_STATUS_V(error) & 0xffff); +} + +/* Mapping between Unix, DOS and NT error numbers */ + +struct unix_error_map { + int unix_error; + NTSTATUS nt_error; +}; + +const struct unix_error_map unix_nt_errmap[] = { + { EAGAIN, STATUS_MORE_ENTRIES }, + { EINTR, STATUS_MORE_ENTRIES }, + { ENOBUFS, STATUS_MORE_ENTRIES }, +#ifdef EWOULDBLOCK + { EWOULDBLOCK, STATUS_MORE_ENTRIES }, +#endif + { EINPROGRESS, NT_STATUS_MORE_PROCESSING_REQUIRED }, + { EPERM, NT_STATUS_ACCESS_DENIED }, + { EACCES, NT_STATUS_ACCESS_DENIED }, + { ENOENT, NT_STATUS_OBJECT_NAME_NOT_FOUND }, + { ENOTDIR, NT_STATUS_NOT_A_DIRECTORY }, + { EIO, NT_STATUS_IO_DEVICE_ERROR }, + { EBADF, NT_STATUS_INVALID_HANDLE }, + { EINVAL, NT_STATUS_INVALID_PARAMETER }, + { EEXIST, NT_STATUS_OBJECT_NAME_COLLISION}, + { ENFILE, NT_STATUS_TOO_MANY_OPENED_FILES }, + { EMFILE, NT_STATUS_TOO_MANY_OPENED_FILES }, + { ENOSPC, NT_STATUS_DISK_FULL }, + { EISDIR, NT_STATUS_FILE_IS_A_DIRECTORY }, + { ENOTSOCK, NT_STATUS_INVALID_HANDLE }, + { EFAULT, NT_STATUS_INVALID_PARAMETER }, + { EMSGSIZE, NT_STATUS_INVALID_BUFFER_SIZE }, + { ENOMEM, NT_STATUS_NO_MEMORY }, + { EPIPE, NT_STATUS_CONNECTION_DISCONNECTED }, + { ECONNREFUSED, NT_STATUS_CONNECTION_REFUSED }, +#ifdef ECONNRESET + { ECONNRESET, NT_STATUS_CONNECTION_RESET }, +#endif + { EBUSY, NT_STATUS_SHARING_VIOLATION }, +#ifdef ENOTSUP + { ENOTSUP, NT_STATUS_NOT_SUPPORTED}, +#endif +#ifdef EOPNOTSUPP + { EOPNOTSUPP, NT_STATUS_NOT_SUPPORTED}, +#endif +#ifdef EHOSTUNREACH + { EHOSTUNREACH, NT_STATUS_HOST_UNREACHABLE }, +#endif +#ifdef ENETUNREACH + { ENETUNREACH, NT_STATUS_NETWORK_UNREACHABLE }, +#endif +#ifdef ETIMEDOUT + { ETIMEDOUT, NT_STATUS_IO_TIMEOUT }, +#endif +#ifdef EADDRINUSE + { EADDRINUSE, NT_STATUS_ADDRESS_ALREADY_ASSOCIATED }, +#endif +#ifdef ENOATTR + { ENOATTR, NT_STATUS_NOT_FOUND }, +#endif +#ifdef ENODATA + { ENODATA, NT_STATUS_NOT_FOUND }, +#endif +#ifdef EDQUOT + { EDQUOT, NT_STATUS_QUOTA_EXCEEDED }, +#endif +#ifdef ENOTEMPTY + { ENOTEMPTY, NT_STATUS_DIRECTORY_NOT_EMPTY }, +#endif +#ifdef EXDEV + { EXDEV, NT_STATUS_NOT_SAME_DEVICE }, +#endif +#ifdef EROFS + { EROFS, NT_STATUS_MEDIA_WRITE_PROTECTED }, +#endif +#ifdef ENAMETOOLONG + { ENAMETOOLONG, NT_STATUS_NAME_TOO_LONG }, +#endif +#ifdef EFBIG + { EFBIG, NT_STATUS_DISK_FULL }, +#endif +#ifdef EADDRNOTAVAIL + { EADDRNOTAVAIL,NT_STATUS_ADDRESS_NOT_ASSOCIATED }, +#endif +#ifdef ESOCKTNOSUPPORT + { ESOCKTNOSUPPORT,NT_STATUS_INVALID_PARAMETER_MIX }, +#endif +#ifdef EAFNOSUPPORT + { EAFNOSUPPORT, NT_STATUS_INVALID_PARAMETER_MIX }, +#endif +#ifdef ENOPROTOOPT + { ENOPROTOOPT, NT_STATUS_INVALID_PARAMETER_MIX }, +#endif +#ifdef ENODEV + { ENODEV, NT_STATUS_NO_SUCH_DEVICE }, +#endif +#ifdef ENOSYS + { ENOSYS, NT_STATUS_INVALID_SYSTEM_SERVICE }, +#endif +#ifdef ECANCELED + { ECANCELED, NT_STATUS_CANCELLED }, +#endif + + { 0, NT_STATUS_UNSUCCESSFUL } +}; + + +/********************************************************************* + Map an NT error code from a Unix error code. +*********************************************************************/ +NTSTATUS map_nt_error_from_unix(int unix_error) +{ + int i; + + /* Look through list */ + for (i=0;i<ARRAY_SIZE(unix_nt_errmap);i++) { + if (unix_nt_errmap[i].unix_error == unix_error) { + return unix_nt_errmap[i].nt_error; + } + } + + /* Default return */ + return NT_STATUS_UNSUCCESSFUL; +} + +NTSTATUS ndr_map_error2ntstatus(enum ndr_err_code ndr_err) +{ + switch (ndr_err) { + case NDR_ERR_SUCCESS: + return NT_STATUS_OK; + case NDR_ERR_BUFSIZE: + return NT_STATUS_BUFFER_TOO_SMALL; + case NDR_ERR_TOKEN: + return NT_STATUS_INTERNAL_ERROR; + case NDR_ERR_ALLOC: + return NT_STATUS_NO_MEMORY; + case NDR_ERR_ARRAY_SIZE: + return NT_STATUS_ARRAY_BOUNDS_EXCEEDED; + case NDR_ERR_INVALID_POINTER: + return NT_STATUS_INVALID_PARAMETER_MIX; + case NDR_ERR_UNREAD_BYTES: + return NT_STATUS_PORT_MESSAGE_TOO_LONG; + default: + break; + } + + /* we should map all error codes to different status codes */ + return NT_STATUS_INVALID_PARAMETER; +} diff --git a/source4/libcli/util/nterr.c b/source4/libcli/util/nterr.c new file mode 100644 index 0000000000..4e7cdf5c3a --- /dev/null +++ b/source4/libcli/util/nterr.c @@ -0,0 +1,898 @@ +/* + * 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" +#include "../libcli/ldap/ldap_errors.h" +#undef strcasecmp + +typedef struct +{ + const char *nt_errstr; + NTSTATUS nt_errcode; +} nt_err_code_struct; + +#define DOS_CODE(class, code) { #class ":" #code, NT_STATUS_DOS(class, code) } +#define LDAP_CODE(code) { #code, NT_STATUS_LDAP(code) } + +static const nt_err_code_struct nt_errs[] = +{ + { "NT_STATUS_OK", NT_STATUS_OK }, + { "STATUS_NO_MORE_FILES", STATUS_NO_MORE_FILES }, + { "STATUS_NO_MORE_EAS", STATUS_NO_MORE_EAS }, + { "STATUS_INVALID_EA_NAME", STATUS_INVALID_EA_NAME }, + { "STATUS_EA_LIST_INCONSISTENT", STATUS_EA_LIST_INCONSISTENT }, + { "STATUS_INVALID_EA_FLAG", STATUS_INVALID_EA_FLAG }, + { "NT_STATUS_UNSUCCESSFUL", NT_STATUS_UNSUCCESSFUL }, + { "NT_STATUS_NOT_IMPLEMENTED", NT_STATUS_NOT_IMPLEMENTED }, + { "NT_STATUS_INVALID_INFO_CLASS", NT_STATUS_INVALID_INFO_CLASS }, + { "NT_STATUS_INFO_LENGTH_MISMATCH", NT_STATUS_INFO_LENGTH_MISMATCH }, + { "NT_STATUS_ACCESS_VIOLATION", NT_STATUS_ACCESS_VIOLATION }, + { "STATUS_BUFFER_OVERFLOW", STATUS_BUFFER_OVERFLOW }, + { "NT_STATUS_IN_PAGE_ERROR", NT_STATUS_IN_PAGE_ERROR }, + { "NT_STATUS_PAGEFILE_QUOTA", NT_STATUS_PAGEFILE_QUOTA }, + { "NT_STATUS_INVALID_HANDLE", NT_STATUS_INVALID_HANDLE }, + { "NT_STATUS_BAD_INITIAL_STACK", NT_STATUS_BAD_INITIAL_STACK }, + { "NT_STATUS_BAD_INITIAL_PC", NT_STATUS_BAD_INITIAL_PC }, + { "NT_STATUS_INVALID_CID", NT_STATUS_INVALID_CID }, + { "NT_STATUS_TIMER_NOT_CANCELED", NT_STATUS_TIMER_NOT_CANCELED }, + { "NT_STATUS_INVALID_PARAMETER", NT_STATUS_INVALID_PARAMETER }, + { "NT_STATUS_NO_SUCH_DEVICE", NT_STATUS_NO_SUCH_DEVICE }, + { "NT_STATUS_NO_SUCH_FILE", NT_STATUS_NO_SUCH_FILE }, + { "NT_STATUS_INVALID_DEVICE_REQUEST", NT_STATUS_INVALID_DEVICE_REQUEST }, + { "NT_STATUS_END_OF_FILE", NT_STATUS_END_OF_FILE }, + { "NT_STATUS_WRONG_VOLUME", NT_STATUS_WRONG_VOLUME }, + { "NT_STATUS_NO_MEDIA_IN_DEVICE", NT_STATUS_NO_MEDIA_IN_DEVICE }, + { "NT_STATUS_UNRECOGNIZED_MEDIA", NT_STATUS_UNRECOGNIZED_MEDIA }, + { "NT_STATUS_NONEXISTENT_SECTOR", NT_STATUS_NONEXISTENT_SECTOR }, + { "NT_STATUS_MORE_PROCESSING_REQUIRED", NT_STATUS_MORE_PROCESSING_REQUIRED }, + { "NT_STATUS_NO_MEMORY", NT_STATUS_NO_MEMORY }, + { "NT_STATUS_CONFLICTING_ADDRESSES", NT_STATUS_CONFLICTING_ADDRESSES }, + { "NT_STATUS_NOT_MAPPED_VIEW", NT_STATUS_NOT_MAPPED_VIEW }, + { "NT_STATUS_UNABLE_TO_FREE_VM", NT_STATUS_UNABLE_TO_FREE_VM }, + { "NT_STATUS_UNABLE_TO_DELETE_SECTION", NT_STATUS_UNABLE_TO_DELETE_SECTION }, + { "NT_STATUS_INVALID_SYSTEM_SERVICE", NT_STATUS_INVALID_SYSTEM_SERVICE }, + { "NT_STATUS_ILLEGAL_INSTRUCTION", NT_STATUS_ILLEGAL_INSTRUCTION }, + { "NT_STATUS_INVALID_LOCK_SEQUENCE", NT_STATUS_INVALID_LOCK_SEQUENCE }, + { "NT_STATUS_INVALID_VIEW_SIZE", NT_STATUS_INVALID_VIEW_SIZE }, + { "NT_STATUS_INVALID_FILE_FOR_SECTION", NT_STATUS_INVALID_FILE_FOR_SECTION }, + { "NT_STATUS_ALREADY_COMMITTED", NT_STATUS_ALREADY_COMMITTED }, + { "NT_STATUS_ACCESS_DENIED", NT_STATUS_ACCESS_DENIED }, + { "NT_STATUS_BUFFER_TOO_SMALL", NT_STATUS_BUFFER_TOO_SMALL }, + { "NT_STATUS_OBJECT_TYPE_MISMATCH", NT_STATUS_OBJECT_TYPE_MISMATCH }, + { "NT_STATUS_NONCONTINUABLE_EXCEPTION", NT_STATUS_NONCONTINUABLE_EXCEPTION }, + { "NT_STATUS_INVALID_DISPOSITION", NT_STATUS_INVALID_DISPOSITION }, + { "NT_STATUS_UNWIND", NT_STATUS_UNWIND }, + { "NT_STATUS_BAD_STACK", NT_STATUS_BAD_STACK }, + { "NT_STATUS_INVALID_UNWIND_TARGET", NT_STATUS_INVALID_UNWIND_TARGET }, + { "NT_STATUS_NOT_LOCKED", NT_STATUS_NOT_LOCKED }, + { "NT_STATUS_PARITY_ERROR", NT_STATUS_PARITY_ERROR }, + { "NT_STATUS_UNABLE_TO_DECOMMIT_VM", NT_STATUS_UNABLE_TO_DECOMMIT_VM }, + { "NT_STATUS_NOT_COMMITTED", NT_STATUS_NOT_COMMITTED }, + { "NT_STATUS_INVALID_PORT_ATTRIBUTES", NT_STATUS_INVALID_PORT_ATTRIBUTES }, + { "NT_STATUS_PORT_MESSAGE_TOO_LONG", NT_STATUS_PORT_MESSAGE_TOO_LONG }, + { "NT_STATUS_INVALID_PARAMETER_MIX", NT_STATUS_INVALID_PARAMETER_MIX }, + { "NT_STATUS_INVALID_QUOTA_LOWER", NT_STATUS_INVALID_QUOTA_LOWER }, + { "NT_STATUS_DISK_CORRUPT_ERROR", NT_STATUS_DISK_CORRUPT_ERROR }, + { "NT_STATUS_OBJECT_NAME_INVALID", NT_STATUS_OBJECT_NAME_INVALID }, + { "NT_STATUS_OBJECT_NAME_NOT_FOUND", NT_STATUS_OBJECT_NAME_NOT_FOUND }, + { "NT_STATUS_OBJECT_NAME_COLLISION", NT_STATUS_OBJECT_NAME_COLLISION }, + { "NT_STATUS_HANDLE_NOT_WAITABLE", NT_STATUS_HANDLE_NOT_WAITABLE }, + { "NT_STATUS_PORT_DISCONNECTED", NT_STATUS_PORT_DISCONNECTED }, + { "NT_STATUS_DEVICE_ALREADY_ATTACHED", NT_STATUS_DEVICE_ALREADY_ATTACHED }, + { "NT_STATUS_OBJECT_PATH_INVALID", NT_STATUS_OBJECT_PATH_INVALID }, + { "NT_STATUS_OBJECT_PATH_NOT_FOUND", NT_STATUS_OBJECT_PATH_NOT_FOUND }, + { "NT_STATUS_OBJECT_PATH_SYNTAX_BAD", NT_STATUS_OBJECT_PATH_SYNTAX_BAD }, + { "NT_STATUS_DATA_OVERRUN", NT_STATUS_DATA_OVERRUN }, + { "NT_STATUS_DATA_LATE_ERROR", NT_STATUS_DATA_LATE_ERROR }, + { "NT_STATUS_DATA_ERROR", NT_STATUS_DATA_ERROR }, + { "NT_STATUS_CRC_ERROR", NT_STATUS_CRC_ERROR }, + { "NT_STATUS_SECTION_TOO_BIG", NT_STATUS_SECTION_TOO_BIG }, + { "NT_STATUS_PORT_CONNECTION_REFUSED", NT_STATUS_PORT_CONNECTION_REFUSED }, + { "NT_STATUS_INVALID_PORT_HANDLE", NT_STATUS_INVALID_PORT_HANDLE }, + { "NT_STATUS_SHARING_VIOLATION", NT_STATUS_SHARING_VIOLATION }, + { "NT_STATUS_QUOTA_EXCEEDED", NT_STATUS_QUOTA_EXCEEDED }, + { "NT_STATUS_INVALID_PAGE_PROTECTION", NT_STATUS_INVALID_PAGE_PROTECTION }, + { "NT_STATUS_MUTANT_NOT_OWNED", NT_STATUS_MUTANT_NOT_OWNED }, + { "NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED", NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED }, + { "NT_STATUS_PORT_ALREADY_SET", NT_STATUS_PORT_ALREADY_SET }, + { "NT_STATUS_SECTION_NOT_IMAGE", NT_STATUS_SECTION_NOT_IMAGE }, + { "NT_STATUS_SUSPEND_COUNT_EXCEEDED", NT_STATUS_SUSPEND_COUNT_EXCEEDED }, + { "NT_STATUS_THREAD_IS_TERMINATING", NT_STATUS_THREAD_IS_TERMINATING }, + { "NT_STATUS_BAD_WORKING_SET_LIMIT", NT_STATUS_BAD_WORKING_SET_LIMIT }, + { "NT_STATUS_INCOMPATIBLE_FILE_MAP", NT_STATUS_INCOMPATIBLE_FILE_MAP }, + { "NT_STATUS_SECTION_PROTECTION", NT_STATUS_SECTION_PROTECTION }, + { "NT_STATUS_EAS_NOT_SUPPORTED", NT_STATUS_EAS_NOT_SUPPORTED }, + { "NT_STATUS_EA_TOO_LARGE", NT_STATUS_EA_TOO_LARGE }, + { "NT_STATUS_NONEXISTENT_EA_ENTRY", NT_STATUS_NONEXISTENT_EA_ENTRY }, + { "NT_STATUS_NO_EAS_ON_FILE", NT_STATUS_NO_EAS_ON_FILE }, + { "NT_STATUS_EA_CORRUPT_ERROR", NT_STATUS_EA_CORRUPT_ERROR }, + { "NT_STATUS_FILE_LOCK_CONFLICT", NT_STATUS_FILE_LOCK_CONFLICT }, + { "NT_STATUS_LOCK_NOT_GRANTED", NT_STATUS_LOCK_NOT_GRANTED }, + { "NT_STATUS_DELETE_PENDING", NT_STATUS_DELETE_PENDING }, + { "NT_STATUS_CTL_FILE_NOT_SUPPORTED", NT_STATUS_CTL_FILE_NOT_SUPPORTED }, + { "NT_STATUS_UNKNOWN_REVISION", NT_STATUS_UNKNOWN_REVISION }, + { "NT_STATUS_REVISION_MISMATCH", NT_STATUS_REVISION_MISMATCH }, + { "NT_STATUS_INVALID_OWNER", NT_STATUS_INVALID_OWNER }, + { "NT_STATUS_INVALID_PRIMARY_GROUP", NT_STATUS_INVALID_PRIMARY_GROUP }, + { "NT_STATUS_NO_IMPERSONATION_TOKEN", NT_STATUS_NO_IMPERSONATION_TOKEN }, + { "NT_STATUS_CANT_DISABLE_MANDATORY", NT_STATUS_CANT_DISABLE_MANDATORY }, + { "NT_STATUS_NO_LOGON_SERVERS", NT_STATUS_NO_LOGON_SERVERS }, + { "NT_STATUS_NO_SUCH_LOGON_SESSION", NT_STATUS_NO_SUCH_LOGON_SESSION }, + { "NT_STATUS_NO_SUCH_PRIVILEGE", NT_STATUS_NO_SUCH_PRIVILEGE }, + { "NT_STATUS_PRIVILEGE_NOT_HELD", NT_STATUS_PRIVILEGE_NOT_HELD }, + { "NT_STATUS_INVALID_ACCOUNT_NAME", NT_STATUS_INVALID_ACCOUNT_NAME }, + { "NT_STATUS_USER_EXISTS", NT_STATUS_USER_EXISTS }, + { "NT_STATUS_NO_SUCH_USER", NT_STATUS_NO_SUCH_USER }, + { "NT_STATUS_GROUP_EXISTS", NT_STATUS_GROUP_EXISTS }, + { "NT_STATUS_NO_SUCH_GROUP", NT_STATUS_NO_SUCH_GROUP }, + { "NT_STATUS_MEMBER_IN_GROUP", NT_STATUS_MEMBER_IN_GROUP }, + { "NT_STATUS_MEMBER_NOT_IN_GROUP", NT_STATUS_MEMBER_NOT_IN_GROUP }, + { "NT_STATUS_LAST_ADMIN", NT_STATUS_LAST_ADMIN }, + { "NT_STATUS_WRONG_PASSWORD", NT_STATUS_WRONG_PASSWORD }, + { "NT_STATUS_ILL_FORMED_PASSWORD", NT_STATUS_ILL_FORMED_PASSWORD }, + { "NT_STATUS_PASSWORD_RESTRICTION", NT_STATUS_PASSWORD_RESTRICTION }, + { "NT_STATUS_LOGON_FAILURE", NT_STATUS_LOGON_FAILURE }, + { "NT_STATUS_ACCOUNT_RESTRICTION", NT_STATUS_ACCOUNT_RESTRICTION }, + { "NT_STATUS_INVALID_LOGON_HOURS", NT_STATUS_INVALID_LOGON_HOURS }, + { "NT_STATUS_INVALID_WORKSTATION", NT_STATUS_INVALID_WORKSTATION }, + { "NT_STATUS_PASSWORD_EXPIRED", NT_STATUS_PASSWORD_EXPIRED }, + { "NT_STATUS_ACCOUNT_DISABLED", NT_STATUS_ACCOUNT_DISABLED }, + { "NT_STATUS_NONE_MAPPED", NT_STATUS_NONE_MAPPED }, + { "NT_STATUS_TOO_MANY_LUIDS_REQUESTED", NT_STATUS_TOO_MANY_LUIDS_REQUESTED }, + { "NT_STATUS_LUIDS_EXHAUSTED", NT_STATUS_LUIDS_EXHAUSTED }, + { "NT_STATUS_INVALID_SUB_AUTHORITY", NT_STATUS_INVALID_SUB_AUTHORITY }, + { "NT_STATUS_INVALID_ACL", NT_STATUS_INVALID_ACL }, + { "NT_STATUS_INVALID_SID", NT_STATUS_INVALID_SID }, + { "NT_STATUS_INVALID_SECURITY_DESCR", NT_STATUS_INVALID_SECURITY_DESCR }, + { "NT_STATUS_PROCEDURE_NOT_FOUND", NT_STATUS_PROCEDURE_NOT_FOUND }, + { "NT_STATUS_INVALID_IMAGE_FORMAT", NT_STATUS_INVALID_IMAGE_FORMAT }, + { "NT_STATUS_NO_TOKEN", NT_STATUS_NO_TOKEN }, + { "NT_STATUS_BAD_INHERITANCE_ACL", NT_STATUS_BAD_INHERITANCE_ACL }, + { "NT_STATUS_RANGE_NOT_LOCKED", NT_STATUS_RANGE_NOT_LOCKED }, + { "NT_STATUS_DISK_FULL", NT_STATUS_DISK_FULL }, + { "NT_STATUS_SERVER_DISABLED", NT_STATUS_SERVER_DISABLED }, + { "NT_STATUS_SERVER_NOT_DISABLED", NT_STATUS_SERVER_NOT_DISABLED }, + { "NT_STATUS_TOO_MANY_GUIDS_REQUESTED", NT_STATUS_TOO_MANY_GUIDS_REQUESTED }, + { "NT_STATUS_GUIDS_EXHAUSTED", NT_STATUS_GUIDS_EXHAUSTED }, + { "NT_STATUS_INVALID_ID_AUTHORITY", NT_STATUS_INVALID_ID_AUTHORITY }, + { "NT_STATUS_AGENTS_EXHAUSTED", NT_STATUS_AGENTS_EXHAUSTED }, + { "NT_STATUS_INVALID_VOLUME_LABEL", NT_STATUS_INVALID_VOLUME_LABEL }, + { "NT_STATUS_SECTION_NOT_EXTENDED", NT_STATUS_SECTION_NOT_EXTENDED }, + { "NT_STATUS_NOT_MAPPED_DATA", NT_STATUS_NOT_MAPPED_DATA }, + { "NT_STATUS_RESOURCE_DATA_NOT_FOUND", NT_STATUS_RESOURCE_DATA_NOT_FOUND }, + { "NT_STATUS_RESOURCE_TYPE_NOT_FOUND", NT_STATUS_RESOURCE_TYPE_NOT_FOUND }, + { "NT_STATUS_RESOURCE_NAME_NOT_FOUND", NT_STATUS_RESOURCE_NAME_NOT_FOUND }, + { "NT_STATUS_ARRAY_BOUNDS_EXCEEDED", NT_STATUS_ARRAY_BOUNDS_EXCEEDED }, + { "NT_STATUS_FLOAT_DENORMAL_OPERAND", NT_STATUS_FLOAT_DENORMAL_OPERAND }, + { "NT_STATUS_FLOAT_DIVIDE_BY_ZERO", NT_STATUS_FLOAT_DIVIDE_BY_ZERO }, + { "NT_STATUS_FLOAT_INEXACT_RESULT", NT_STATUS_FLOAT_INEXACT_RESULT }, + { "NT_STATUS_FLOAT_INVALID_OPERATION", NT_STATUS_FLOAT_INVALID_OPERATION }, + { "NT_STATUS_FLOAT_OVERFLOW", NT_STATUS_FLOAT_OVERFLOW }, + { "NT_STATUS_FLOAT_STACK_CHECK", NT_STATUS_FLOAT_STACK_CHECK }, + { "NT_STATUS_FLOAT_UNDERFLOW", NT_STATUS_FLOAT_UNDERFLOW }, + { "NT_STATUS_INTEGER_DIVIDE_BY_ZERO", NT_STATUS_INTEGER_DIVIDE_BY_ZERO }, + { "NT_STATUS_INTEGER_OVERFLOW", NT_STATUS_INTEGER_OVERFLOW }, + { "NT_STATUS_PRIVILEGED_INSTRUCTION", NT_STATUS_PRIVILEGED_INSTRUCTION }, + { "NT_STATUS_TOO_MANY_PAGING_FILES", NT_STATUS_TOO_MANY_PAGING_FILES }, + { "NT_STATUS_FILE_INVALID", NT_STATUS_FILE_INVALID }, + { "NT_STATUS_ALLOTTED_SPACE_EXCEEDED", NT_STATUS_ALLOTTED_SPACE_EXCEEDED }, + { "NT_STATUS_INSUFFICIENT_RESOURCES", NT_STATUS_INSUFFICIENT_RESOURCES }, + { "NT_STATUS_DFS_EXIT_PATH_FOUND", NT_STATUS_DFS_EXIT_PATH_FOUND }, + { "NT_STATUS_DEVICE_DATA_ERROR", NT_STATUS_DEVICE_DATA_ERROR }, + { "NT_STATUS_DEVICE_NOT_CONNECTED", NT_STATUS_DEVICE_NOT_CONNECTED }, + { "NT_STATUS_DEVICE_POWER_FAILURE", NT_STATUS_DEVICE_POWER_FAILURE }, + { "NT_STATUS_FREE_VM_NOT_AT_BASE", NT_STATUS_FREE_VM_NOT_AT_BASE }, + { "NT_STATUS_MEMORY_NOT_ALLOCATED", NT_STATUS_MEMORY_NOT_ALLOCATED }, + { "NT_STATUS_WORKING_SET_QUOTA", NT_STATUS_WORKING_SET_QUOTA }, + { "NT_STATUS_MEDIA_WRITE_PROTECTED", NT_STATUS_MEDIA_WRITE_PROTECTED }, + { "NT_STATUS_DEVICE_NOT_READY", NT_STATUS_DEVICE_NOT_READY }, + { "NT_STATUS_INVALID_GROUP_ATTRIBUTES", NT_STATUS_INVALID_GROUP_ATTRIBUTES }, + { "NT_STATUS_BAD_IMPERSONATION_LEVEL", NT_STATUS_BAD_IMPERSONATION_LEVEL }, + { "NT_STATUS_CANT_OPEN_ANONYMOUS", NT_STATUS_CANT_OPEN_ANONYMOUS }, + { "NT_STATUS_BAD_VALIDATION_CLASS", NT_STATUS_BAD_VALIDATION_CLASS }, + { "NT_STATUS_BAD_TOKEN_TYPE", NT_STATUS_BAD_TOKEN_TYPE }, + { "NT_STATUS_BAD_MASTER_BOOT_RECORD", NT_STATUS_BAD_MASTER_BOOT_RECORD }, + { "NT_STATUS_INSTRUCTION_MISALIGNMENT", NT_STATUS_INSTRUCTION_MISALIGNMENT }, + { "NT_STATUS_INSTANCE_NOT_AVAILABLE", NT_STATUS_INSTANCE_NOT_AVAILABLE }, + { "NT_STATUS_PIPE_NOT_AVAILABLE", NT_STATUS_PIPE_NOT_AVAILABLE }, + { "NT_STATUS_INVALID_PIPE_STATE", NT_STATUS_INVALID_PIPE_STATE }, + { "NT_STATUS_PIPE_BUSY", NT_STATUS_PIPE_BUSY }, + { "NT_STATUS_ILLEGAL_FUNCTION", NT_STATUS_ILLEGAL_FUNCTION }, + { "NT_STATUS_PIPE_DISCONNECTED", NT_STATUS_PIPE_DISCONNECTED }, + { "NT_STATUS_PIPE_CLOSING", NT_STATUS_PIPE_CLOSING }, + { "NT_STATUS_PIPE_CONNECTED", NT_STATUS_PIPE_CONNECTED }, + { "NT_STATUS_PIPE_LISTENING", NT_STATUS_PIPE_LISTENING }, + { "NT_STATUS_INVALID_READ_MODE", NT_STATUS_INVALID_READ_MODE }, + { "NT_STATUS_IO_TIMEOUT", NT_STATUS_IO_TIMEOUT }, + { "NT_STATUS_FILE_FORCED_CLOSED", NT_STATUS_FILE_FORCED_CLOSED }, + { "NT_STATUS_PROFILING_NOT_STARTED", NT_STATUS_PROFILING_NOT_STARTED }, + { "NT_STATUS_PROFILING_NOT_STOPPED", NT_STATUS_PROFILING_NOT_STOPPED }, + { "NT_STATUS_COULD_NOT_INTERPRET", NT_STATUS_COULD_NOT_INTERPRET }, + { "NT_STATUS_FILE_IS_A_DIRECTORY", NT_STATUS_FILE_IS_A_DIRECTORY }, + { "NT_STATUS_NOT_SUPPORTED", NT_STATUS_NOT_SUPPORTED }, + { "NT_STATUS_REMOTE_NOT_LISTENING", NT_STATUS_REMOTE_NOT_LISTENING }, + { "NT_STATUS_DUPLICATE_NAME", NT_STATUS_DUPLICATE_NAME }, + { "NT_STATUS_BAD_NETWORK_PATH", NT_STATUS_BAD_NETWORK_PATH }, + { "NT_STATUS_NETWORK_BUSY", NT_STATUS_NETWORK_BUSY }, + { "NT_STATUS_DEVICE_DOES_NOT_EXIST", NT_STATUS_DEVICE_DOES_NOT_EXIST }, + { "NT_STATUS_TOO_MANY_COMMANDS", NT_STATUS_TOO_MANY_COMMANDS }, + { "NT_STATUS_ADAPTER_HARDWARE_ERROR", NT_STATUS_ADAPTER_HARDWARE_ERROR }, + { "NT_STATUS_INVALID_NETWORK_RESPONSE", NT_STATUS_INVALID_NETWORK_RESPONSE }, + { "NT_STATUS_UNEXPECTED_NETWORK_ERROR", NT_STATUS_UNEXPECTED_NETWORK_ERROR }, + { "NT_STATUS_BAD_REMOTE_ADAPTER", NT_STATUS_BAD_REMOTE_ADAPTER }, + { "NT_STATUS_PRINT_QUEUE_FULL", NT_STATUS_PRINT_QUEUE_FULL }, + { "NT_STATUS_NO_SPOOL_SPACE", NT_STATUS_NO_SPOOL_SPACE }, + { "NT_STATUS_PRINT_CANCELLED", NT_STATUS_PRINT_CANCELLED }, + { "NT_STATUS_NETWORK_NAME_DELETED", NT_STATUS_NETWORK_NAME_DELETED }, + { "NT_STATUS_NETWORK_ACCESS_DENIED", NT_STATUS_NETWORK_ACCESS_DENIED }, + { "NT_STATUS_BAD_DEVICE_TYPE", NT_STATUS_BAD_DEVICE_TYPE }, + { "NT_STATUS_BAD_NETWORK_NAME", NT_STATUS_BAD_NETWORK_NAME }, + { "NT_STATUS_TOO_MANY_NAMES", NT_STATUS_TOO_MANY_NAMES }, + { "NT_STATUS_TOO_MANY_SESSIONS", NT_STATUS_TOO_MANY_SESSIONS }, + { "NT_STATUS_SHARING_PAUSED", NT_STATUS_SHARING_PAUSED }, + { "NT_STATUS_REQUEST_NOT_ACCEPTED", NT_STATUS_REQUEST_NOT_ACCEPTED }, + { "NT_STATUS_REDIRECTOR_PAUSED", NT_STATUS_REDIRECTOR_PAUSED }, + { "NT_STATUS_NET_WRITE_FAULT", NT_STATUS_NET_WRITE_FAULT }, + { "NT_STATUS_PROFILING_AT_LIMIT", NT_STATUS_PROFILING_AT_LIMIT }, + { "NT_STATUS_NOT_SAME_DEVICE", NT_STATUS_NOT_SAME_DEVICE }, + { "NT_STATUS_FILE_RENAMED", NT_STATUS_FILE_RENAMED }, + { "NT_STATUS_VIRTUAL_CIRCUIT_CLOSED", NT_STATUS_VIRTUAL_CIRCUIT_CLOSED }, + { "NT_STATUS_NO_SECURITY_ON_OBJECT", NT_STATUS_NO_SECURITY_ON_OBJECT }, + { "NT_STATUS_CANT_WAIT", NT_STATUS_CANT_WAIT }, + { "NT_STATUS_PIPE_EMPTY", NT_STATUS_PIPE_EMPTY }, + { "NT_STATUS_CANT_ACCESS_DOMAIN_INFO", NT_STATUS_CANT_ACCESS_DOMAIN_INFO }, + { "NT_STATUS_CANT_TERMINATE_SELF", NT_STATUS_CANT_TERMINATE_SELF }, + { "NT_STATUS_INVALID_SERVER_STATE", NT_STATUS_INVALID_SERVER_STATE }, + { "NT_STATUS_INVALID_DOMAIN_STATE", NT_STATUS_INVALID_DOMAIN_STATE }, + { "NT_STATUS_INVALID_DOMAIN_ROLE", NT_STATUS_INVALID_DOMAIN_ROLE }, + { "NT_STATUS_NO_SUCH_DOMAIN", NT_STATUS_NO_SUCH_DOMAIN }, + { "NT_STATUS_DOMAIN_EXISTS", NT_STATUS_DOMAIN_EXISTS }, + { "NT_STATUS_DOMAIN_LIMIT_EXCEEDED", NT_STATUS_DOMAIN_LIMIT_EXCEEDED }, + { "NT_STATUS_OPLOCK_NOT_GRANTED", NT_STATUS_OPLOCK_NOT_GRANTED }, + { "NT_STATUS_INVALID_OPLOCK_PROTOCOL", NT_STATUS_INVALID_OPLOCK_PROTOCOL }, + { "NT_STATUS_INTERNAL_DB_CORRUPTION", NT_STATUS_INTERNAL_DB_CORRUPTION }, + { "NT_STATUS_INTERNAL_ERROR", NT_STATUS_INTERNAL_ERROR }, + { "NT_STATUS_GENERIC_NOT_MAPPED", NT_STATUS_GENERIC_NOT_MAPPED }, + { "NT_STATUS_BAD_DESCRIPTOR_FORMAT", NT_STATUS_BAD_DESCRIPTOR_FORMAT }, + { "NT_STATUS_INVALID_USER_BUFFER", NT_STATUS_INVALID_USER_BUFFER }, + { "NT_STATUS_UNEXPECTED_IO_ERROR", NT_STATUS_UNEXPECTED_IO_ERROR }, + { "NT_STATUS_UNEXPECTED_MM_CREATE_ERR", NT_STATUS_UNEXPECTED_MM_CREATE_ERR }, + { "NT_STATUS_UNEXPECTED_MM_MAP_ERROR", NT_STATUS_UNEXPECTED_MM_MAP_ERROR }, + { "NT_STATUS_UNEXPECTED_MM_EXTEND_ERR", NT_STATUS_UNEXPECTED_MM_EXTEND_ERR }, + { "NT_STATUS_NOT_LOGON_PROCESS", NT_STATUS_NOT_LOGON_PROCESS }, + { "NT_STATUS_LOGON_SESSION_EXISTS", NT_STATUS_LOGON_SESSION_EXISTS }, + { "NT_STATUS_INVALID_PARAMETER_1", NT_STATUS_INVALID_PARAMETER_1 }, + { "NT_STATUS_INVALID_PARAMETER_2", NT_STATUS_INVALID_PARAMETER_2 }, + { "NT_STATUS_INVALID_PARAMETER_3", NT_STATUS_INVALID_PARAMETER_3 }, + { "NT_STATUS_INVALID_PARAMETER_4", NT_STATUS_INVALID_PARAMETER_4 }, + { "NT_STATUS_INVALID_PARAMETER_5", NT_STATUS_INVALID_PARAMETER_5 }, + { "NT_STATUS_INVALID_PARAMETER_6", NT_STATUS_INVALID_PARAMETER_6 }, + { "NT_STATUS_INVALID_PARAMETER_7", NT_STATUS_INVALID_PARAMETER_7 }, + { "NT_STATUS_INVALID_PARAMETER_8", NT_STATUS_INVALID_PARAMETER_8 }, + { "NT_STATUS_INVALID_PARAMETER_9", NT_STATUS_INVALID_PARAMETER_9 }, + { "NT_STATUS_INVALID_PARAMETER_10", NT_STATUS_INVALID_PARAMETER_10 }, + { "NT_STATUS_INVALID_PARAMETER_11", NT_STATUS_INVALID_PARAMETER_11 }, + { "NT_STATUS_INVALID_PARAMETER_12", NT_STATUS_INVALID_PARAMETER_12 }, + { "NT_STATUS_REDIRECTOR_NOT_STARTED", NT_STATUS_REDIRECTOR_NOT_STARTED }, + { "NT_STATUS_REDIRECTOR_STARTED", NT_STATUS_REDIRECTOR_STARTED }, + { "NT_STATUS_STACK_OVERFLOW", NT_STATUS_STACK_OVERFLOW }, + { "NT_STATUS_NO_SUCH_PACKAGE", NT_STATUS_NO_SUCH_PACKAGE }, + { "NT_STATUS_BAD_FUNCTION_TABLE", NT_STATUS_BAD_FUNCTION_TABLE }, + { "NT_STATUS_DIRECTORY_NOT_EMPTY", NT_STATUS_DIRECTORY_NOT_EMPTY }, + { "NT_STATUS_FILE_CORRUPT_ERROR", NT_STATUS_FILE_CORRUPT_ERROR }, + { "NT_STATUS_NOT_A_DIRECTORY", NT_STATUS_NOT_A_DIRECTORY }, + { "NT_STATUS_BAD_LOGON_SESSION_STATE", NT_STATUS_BAD_LOGON_SESSION_STATE }, + { "NT_STATUS_LOGON_SESSION_COLLISION", NT_STATUS_LOGON_SESSION_COLLISION }, + { "NT_STATUS_NAME_TOO_LONG", NT_STATUS_NAME_TOO_LONG }, + { "NT_STATUS_FILES_OPEN", NT_STATUS_FILES_OPEN }, + { "NT_STATUS_CONNECTION_IN_USE", NT_STATUS_CONNECTION_IN_USE }, + { "NT_STATUS_MESSAGE_NOT_FOUND", NT_STATUS_MESSAGE_NOT_FOUND }, + { "NT_STATUS_PROCESS_IS_TERMINATING", NT_STATUS_PROCESS_IS_TERMINATING }, + { "NT_STATUS_INVALID_LOGON_TYPE", NT_STATUS_INVALID_LOGON_TYPE }, + { "NT_STATUS_NO_GUID_TRANSLATION", NT_STATUS_NO_GUID_TRANSLATION }, + { "NT_STATUS_CANNOT_IMPERSONATE", NT_STATUS_CANNOT_IMPERSONATE }, + { "NT_STATUS_IMAGE_ALREADY_LOADED", NT_STATUS_IMAGE_ALREADY_LOADED }, + { "NT_STATUS_ABIOS_NOT_PRESENT", NT_STATUS_ABIOS_NOT_PRESENT }, + { "NT_STATUS_ABIOS_LID_NOT_EXIST", NT_STATUS_ABIOS_LID_NOT_EXIST }, + { "NT_STATUS_ABIOS_LID_ALREADY_OWNED", NT_STATUS_ABIOS_LID_ALREADY_OWNED }, + { "NT_STATUS_ABIOS_NOT_LID_OWNER", NT_STATUS_ABIOS_NOT_LID_OWNER }, + { "NT_STATUS_ABIOS_INVALID_COMMAND", NT_STATUS_ABIOS_INVALID_COMMAND }, + { "NT_STATUS_ABIOS_INVALID_LID", NT_STATUS_ABIOS_INVALID_LID }, + { "NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE", NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE }, + { "NT_STATUS_ABIOS_INVALID_SELECTOR", NT_STATUS_ABIOS_INVALID_SELECTOR }, + { "NT_STATUS_NO_LDT", NT_STATUS_NO_LDT }, + { "NT_STATUS_INVALID_LDT_SIZE", NT_STATUS_INVALID_LDT_SIZE }, + { "NT_STATUS_INVALID_LDT_OFFSET", NT_STATUS_INVALID_LDT_OFFSET }, + { "NT_STATUS_INVALID_LDT_DESCRIPTOR", NT_STATUS_INVALID_LDT_DESCRIPTOR }, + { "NT_STATUS_INVALID_IMAGE_NE_FORMAT", NT_STATUS_INVALID_IMAGE_NE_FORMAT }, + { "NT_STATUS_RXACT_INVALID_STATE", NT_STATUS_RXACT_INVALID_STATE }, + { "NT_STATUS_RXACT_COMMIT_FAILURE", NT_STATUS_RXACT_COMMIT_FAILURE }, + { "NT_STATUS_MAPPED_FILE_SIZE_ZERO", NT_STATUS_MAPPED_FILE_SIZE_ZERO }, + { "NT_STATUS_TOO_MANY_OPENED_FILES", NT_STATUS_TOO_MANY_OPENED_FILES }, + { "NT_STATUS_CANCELLED", NT_STATUS_CANCELLED }, + { "NT_STATUS_CANNOT_DELETE", NT_STATUS_CANNOT_DELETE }, + { "NT_STATUS_INVALID_COMPUTER_NAME", NT_STATUS_INVALID_COMPUTER_NAME }, + { "NT_STATUS_FILE_DELETED", NT_STATUS_FILE_DELETED }, + { "NT_STATUS_SPECIAL_ACCOUNT", NT_STATUS_SPECIAL_ACCOUNT }, + { "NT_STATUS_SPECIAL_GROUP", NT_STATUS_SPECIAL_GROUP }, + { "NT_STATUS_SPECIAL_USER", NT_STATUS_SPECIAL_USER }, + { "NT_STATUS_MEMBERS_PRIMARY_GROUP", NT_STATUS_MEMBERS_PRIMARY_GROUP }, + { "NT_STATUS_FILE_CLOSED", NT_STATUS_FILE_CLOSED }, + { "NT_STATUS_TOO_MANY_THREADS", NT_STATUS_TOO_MANY_THREADS }, + { "NT_STATUS_THREAD_NOT_IN_PROCESS", NT_STATUS_THREAD_NOT_IN_PROCESS }, + { "NT_STATUS_TOKEN_ALREADY_IN_USE", NT_STATUS_TOKEN_ALREADY_IN_USE }, + { "NT_STATUS_PAGEFILE_QUOTA_EXCEEDED", NT_STATUS_PAGEFILE_QUOTA_EXCEEDED }, + { "NT_STATUS_COMMITMENT_LIMIT", NT_STATUS_COMMITMENT_LIMIT }, + { "NT_STATUS_INVALID_IMAGE_LE_FORMAT", NT_STATUS_INVALID_IMAGE_LE_FORMAT }, + { "NT_STATUS_INVALID_IMAGE_NOT_MZ", NT_STATUS_INVALID_IMAGE_NOT_MZ }, + { "NT_STATUS_INVALID_IMAGE_PROTECT", NT_STATUS_INVALID_IMAGE_PROTECT }, + { "NT_STATUS_INVALID_IMAGE_WIN_16", NT_STATUS_INVALID_IMAGE_WIN_16 }, + { "NT_STATUS_LOGON_SERVER_CONFLICT", NT_STATUS_LOGON_SERVER_CONFLICT }, + { "NT_STATUS_TIME_DIFFERENCE_AT_DC", NT_STATUS_TIME_DIFFERENCE_AT_DC }, + { "NT_STATUS_SYNCHRONIZATION_REQUIRED", NT_STATUS_SYNCHRONIZATION_REQUIRED }, + { "NT_STATUS_DLL_NOT_FOUND", NT_STATUS_DLL_NOT_FOUND }, + { "NT_STATUS_OPEN_FAILED", NT_STATUS_OPEN_FAILED }, + { "NT_STATUS_IO_PRIVILEGE_FAILED", NT_STATUS_IO_PRIVILEGE_FAILED }, + { "NT_STATUS_ORDINAL_NOT_FOUND", NT_STATUS_ORDINAL_NOT_FOUND }, + { "NT_STATUS_ENTRYPOINT_NOT_FOUND", NT_STATUS_ENTRYPOINT_NOT_FOUND }, + { "NT_STATUS_CONTROL_C_EXIT", NT_STATUS_CONTROL_C_EXIT }, + { "NT_STATUS_LOCAL_DISCONNECT", NT_STATUS_LOCAL_DISCONNECT }, + { "NT_STATUS_REMOTE_DISCONNECT", NT_STATUS_REMOTE_DISCONNECT }, + { "NT_STATUS_REMOTE_RESOURCES", NT_STATUS_REMOTE_RESOURCES }, + { "NT_STATUS_LINK_FAILED", NT_STATUS_LINK_FAILED }, + { "NT_STATUS_LINK_TIMEOUT", NT_STATUS_LINK_TIMEOUT }, + { "NT_STATUS_INVALID_CONNECTION", NT_STATUS_INVALID_CONNECTION }, + { "NT_STATUS_INVALID_ADDRESS", NT_STATUS_INVALID_ADDRESS }, + { "NT_STATUS_DLL_INIT_FAILED", NT_STATUS_DLL_INIT_FAILED }, + { "NT_STATUS_MISSING_SYSTEMFILE", NT_STATUS_MISSING_SYSTEMFILE }, + { "NT_STATUS_UNHANDLED_EXCEPTION", NT_STATUS_UNHANDLED_EXCEPTION }, + { "NT_STATUS_APP_INIT_FAILURE", NT_STATUS_APP_INIT_FAILURE }, + { "NT_STATUS_PAGEFILE_CREATE_FAILED", NT_STATUS_PAGEFILE_CREATE_FAILED }, + { "NT_STATUS_NO_PAGEFILE", NT_STATUS_NO_PAGEFILE }, + { "NT_STATUS_INVALID_LEVEL", NT_STATUS_INVALID_LEVEL }, + { "NT_STATUS_WRONG_PASSWORD_CORE", NT_STATUS_WRONG_PASSWORD_CORE }, + { "NT_STATUS_ILLEGAL_FLOAT_CONTEXT", NT_STATUS_ILLEGAL_FLOAT_CONTEXT }, + { "NT_STATUS_PIPE_BROKEN", NT_STATUS_PIPE_BROKEN }, + { "NT_STATUS_REGISTRY_CORRUPT", NT_STATUS_REGISTRY_CORRUPT }, + { "NT_STATUS_REGISTRY_IO_FAILED", NT_STATUS_REGISTRY_IO_FAILED }, + { "NT_STATUS_NO_EVENT_PAIR", NT_STATUS_NO_EVENT_PAIR }, + { "NT_STATUS_UNRECOGNIZED_VOLUME", NT_STATUS_UNRECOGNIZED_VOLUME }, + { "NT_STATUS_SERIAL_NO_DEVICE_INITED", NT_STATUS_SERIAL_NO_DEVICE_INITED }, + { "NT_STATUS_NO_SUCH_ALIAS", NT_STATUS_NO_SUCH_ALIAS }, + { "NT_STATUS_MEMBER_NOT_IN_ALIAS", NT_STATUS_MEMBER_NOT_IN_ALIAS }, + { "NT_STATUS_MEMBER_IN_ALIAS", NT_STATUS_MEMBER_IN_ALIAS }, + { "NT_STATUS_ALIAS_EXISTS", NT_STATUS_ALIAS_EXISTS }, + { "NT_STATUS_LOGON_NOT_GRANTED", NT_STATUS_LOGON_NOT_GRANTED }, + { "NT_STATUS_TOO_MANY_SECRETS", NT_STATUS_TOO_MANY_SECRETS }, + { "NT_STATUS_SECRET_TOO_LONG", NT_STATUS_SECRET_TOO_LONG }, + { "NT_STATUS_INTERNAL_DB_ERROR", NT_STATUS_INTERNAL_DB_ERROR }, + { "NT_STATUS_FULLSCREEN_MODE", NT_STATUS_FULLSCREEN_MODE }, + { "NT_STATUS_TOO_MANY_CONTEXT_IDS", NT_STATUS_TOO_MANY_CONTEXT_IDS }, + { "NT_STATUS_LOGON_TYPE_NOT_GRANTED", NT_STATUS_LOGON_TYPE_NOT_GRANTED }, + { "NT_STATUS_NOT_REGISTRY_FILE", NT_STATUS_NOT_REGISTRY_FILE }, + { "NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED", NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED }, + { "NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR", NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR }, + { "NT_STATUS_FT_MISSING_MEMBER", NT_STATUS_FT_MISSING_MEMBER }, + { "NT_STATUS_ILL_FORMED_SERVICE_ENTRY", NT_STATUS_ILL_FORMED_SERVICE_ENTRY }, + { "NT_STATUS_ILLEGAL_CHARACTER", NT_STATUS_ILLEGAL_CHARACTER }, + { "NT_STATUS_UNMAPPABLE_CHARACTER", NT_STATUS_UNMAPPABLE_CHARACTER }, + { "NT_STATUS_UNDEFINED_CHARACTER", NT_STATUS_UNDEFINED_CHARACTER }, + { "NT_STATUS_FLOPPY_VOLUME", NT_STATUS_FLOPPY_VOLUME }, + { "NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND", NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND }, + { "NT_STATUS_FLOPPY_WRONG_CYLINDER", NT_STATUS_FLOPPY_WRONG_CYLINDER }, + { "NT_STATUS_FLOPPY_UNKNOWN_ERROR", NT_STATUS_FLOPPY_UNKNOWN_ERROR }, + { "NT_STATUS_FLOPPY_BAD_REGISTERS", NT_STATUS_FLOPPY_BAD_REGISTERS }, + { "NT_STATUS_DISK_RECALIBRATE_FAILED", NT_STATUS_DISK_RECALIBRATE_FAILED }, + { "NT_STATUS_DISK_OPERATION_FAILED", NT_STATUS_DISK_OPERATION_FAILED }, + { "NT_STATUS_DISK_RESET_FAILED", NT_STATUS_DISK_RESET_FAILED }, + { "NT_STATUS_SHARED_IRQ_BUSY", NT_STATUS_SHARED_IRQ_BUSY }, + { "NT_STATUS_FT_ORPHANING", NT_STATUS_FT_ORPHANING }, + { "NT_STATUS_PARTITION_FAILURE", NT_STATUS_PARTITION_FAILURE }, + { "NT_STATUS_INVALID_BLOCK_LENGTH", NT_STATUS_INVALID_BLOCK_LENGTH }, + { "NT_STATUS_DEVICE_NOT_PARTITIONED", NT_STATUS_DEVICE_NOT_PARTITIONED }, + { "NT_STATUS_UNABLE_TO_LOCK_MEDIA", NT_STATUS_UNABLE_TO_LOCK_MEDIA }, + { "NT_STATUS_UNABLE_TO_UNLOAD_MEDIA", NT_STATUS_UNABLE_TO_UNLOAD_MEDIA }, + { "NT_STATUS_EOM_OVERFLOW", NT_STATUS_EOM_OVERFLOW }, + { "NT_STATUS_NO_MEDIA", NT_STATUS_NO_MEDIA }, + { "NT_STATUS_NO_SUCH_MEMBER", NT_STATUS_NO_SUCH_MEMBER }, + { "NT_STATUS_INVALID_MEMBER", NT_STATUS_INVALID_MEMBER }, + { "NT_STATUS_KEY_DELETED", NT_STATUS_KEY_DELETED }, + { "NT_STATUS_NO_LOG_SPACE", NT_STATUS_NO_LOG_SPACE }, + { "NT_STATUS_TOO_MANY_SIDS", NT_STATUS_TOO_MANY_SIDS }, + { "NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED", NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED }, + { "NT_STATUS_KEY_HAS_CHILDREN", NT_STATUS_KEY_HAS_CHILDREN }, + { "NT_STATUS_CHILD_MUST_BE_VOLATILE", NT_STATUS_CHILD_MUST_BE_VOLATILE }, + { "NT_STATUS_DEVICE_CONFIGURATION_ERROR", NT_STATUS_DEVICE_CONFIGURATION_ERROR }, + { "NT_STATUS_DRIVER_INTERNAL_ERROR", NT_STATUS_DRIVER_INTERNAL_ERROR }, + { "NT_STATUS_INVALID_DEVICE_STATE", NT_STATUS_INVALID_DEVICE_STATE }, + { "NT_STATUS_IO_DEVICE_ERROR", NT_STATUS_IO_DEVICE_ERROR }, + { "NT_STATUS_DEVICE_PROTOCOL_ERROR", NT_STATUS_DEVICE_PROTOCOL_ERROR }, + { "NT_STATUS_BACKUP_CONTROLLER", NT_STATUS_BACKUP_CONTROLLER }, + { "NT_STATUS_LOG_FILE_FULL", NT_STATUS_LOG_FILE_FULL }, + { "NT_STATUS_TOO_LATE", NT_STATUS_TOO_LATE }, + { "NT_STATUS_NO_TRUST_LSA_SECRET", NT_STATUS_NO_TRUST_LSA_SECRET }, + { "NT_STATUS_NO_TRUST_SAM_ACCOUNT", NT_STATUS_NO_TRUST_SAM_ACCOUNT }, + { "NT_STATUS_TRUSTED_DOMAIN_FAILURE", NT_STATUS_TRUSTED_DOMAIN_FAILURE }, + { "NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE", NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE }, + { "NT_STATUS_EVENTLOG_FILE_CORRUPT", NT_STATUS_EVENTLOG_FILE_CORRUPT }, + { "NT_STATUS_EVENTLOG_CANT_START", NT_STATUS_EVENTLOG_CANT_START }, + { "NT_STATUS_TRUST_FAILURE", NT_STATUS_TRUST_FAILURE }, + { "NT_STATUS_MUTANT_LIMIT_EXCEEDED", NT_STATUS_MUTANT_LIMIT_EXCEEDED }, + { "NT_STATUS_NETLOGON_NOT_STARTED", NT_STATUS_NETLOGON_NOT_STARTED }, + { "NT_STATUS_ACCOUNT_EXPIRED", NT_STATUS_ACCOUNT_EXPIRED }, + { "NT_STATUS_POSSIBLE_DEADLOCK", NT_STATUS_POSSIBLE_DEADLOCK }, + { "NT_STATUS_NETWORK_CREDENTIAL_CONFLICT", NT_STATUS_NETWORK_CREDENTIAL_CONFLICT }, + { "NT_STATUS_REMOTE_SESSION_LIMIT", NT_STATUS_REMOTE_SESSION_LIMIT }, + { "NT_STATUS_EVENTLOG_FILE_CHANGED", NT_STATUS_EVENTLOG_FILE_CHANGED }, + { "NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT", NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT }, + { "NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT", NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT }, + { "NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT", NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT }, + { "NT_STATUS_DOMAIN_TRUST_INCONSISTENT", NT_STATUS_DOMAIN_TRUST_INCONSISTENT }, + { "NT_STATUS_FS_DRIVER_REQUIRED", NT_STATUS_FS_DRIVER_REQUIRED }, + { "NT_STATUS_NO_USER_SESSION_KEY", NT_STATUS_NO_USER_SESSION_KEY }, + { "NT_STATUS_USER_SESSION_DELETED", NT_STATUS_USER_SESSION_DELETED }, + { "NT_STATUS_RESOURCE_LANG_NOT_FOUND", NT_STATUS_RESOURCE_LANG_NOT_FOUND }, + { "NT_STATUS_INSUFF_SERVER_RESOURCES", NT_STATUS_INSUFF_SERVER_RESOURCES }, + { "NT_STATUS_INVALID_BUFFER_SIZE", NT_STATUS_INVALID_BUFFER_SIZE }, + { "NT_STATUS_INVALID_ADDRESS_COMPONENT", NT_STATUS_INVALID_ADDRESS_COMPONENT }, + { "NT_STATUS_INVALID_ADDRESS_WILDCARD", NT_STATUS_INVALID_ADDRESS_WILDCARD }, + { "NT_STATUS_TOO_MANY_ADDRESSES", NT_STATUS_TOO_MANY_ADDRESSES }, + { "NT_STATUS_ADDRESS_ALREADY_EXISTS", NT_STATUS_ADDRESS_ALREADY_EXISTS }, + { "NT_STATUS_ADDRESS_CLOSED", NT_STATUS_ADDRESS_CLOSED }, + { "NT_STATUS_CONNECTION_DISCONNECTED", NT_STATUS_CONNECTION_DISCONNECTED }, + { "NT_STATUS_CONNECTION_RESET", NT_STATUS_CONNECTION_RESET }, + { "NT_STATUS_TOO_MANY_NODES", NT_STATUS_TOO_MANY_NODES }, + { "NT_STATUS_TRANSACTION_ABORTED", NT_STATUS_TRANSACTION_ABORTED }, + { "NT_STATUS_TRANSACTION_TIMED_OUT", NT_STATUS_TRANSACTION_TIMED_OUT }, + { "NT_STATUS_TRANSACTION_NO_RELEASE", NT_STATUS_TRANSACTION_NO_RELEASE }, + { "NT_STATUS_TRANSACTION_NO_MATCH", NT_STATUS_TRANSACTION_NO_MATCH }, + { "NT_STATUS_TRANSACTION_RESPONDED", NT_STATUS_TRANSACTION_RESPONDED }, + { "NT_STATUS_TRANSACTION_INVALID_ID", NT_STATUS_TRANSACTION_INVALID_ID }, + { "NT_STATUS_TRANSACTION_INVALID_TYPE", NT_STATUS_TRANSACTION_INVALID_TYPE }, + { "NT_STATUS_NOT_SERVER_SESSION", NT_STATUS_NOT_SERVER_SESSION }, + { "NT_STATUS_NOT_CLIENT_SESSION", NT_STATUS_NOT_CLIENT_SESSION }, + { "NT_STATUS_CANNOT_LOAD_REGISTRY_FILE", NT_STATUS_CANNOT_LOAD_REGISTRY_FILE }, + { "NT_STATUS_DEBUG_ATTACH_FAILED", NT_STATUS_DEBUG_ATTACH_FAILED }, + { "NT_STATUS_SYSTEM_PROCESS_TERMINATED", NT_STATUS_SYSTEM_PROCESS_TERMINATED }, + { "NT_STATUS_DATA_NOT_ACCEPTED", NT_STATUS_DATA_NOT_ACCEPTED }, + { "NT_STATUS_NO_BROWSER_SERVERS_FOUND", NT_STATUS_NO_BROWSER_SERVERS_FOUND }, + { "NT_STATUS_VDM_HARD_ERROR", NT_STATUS_VDM_HARD_ERROR }, + { "NT_STATUS_DRIVER_CANCEL_TIMEOUT", NT_STATUS_DRIVER_CANCEL_TIMEOUT }, + { "NT_STATUS_REPLY_MESSAGE_MISMATCH", NT_STATUS_REPLY_MESSAGE_MISMATCH }, + { "NT_STATUS_MAPPED_ALIGNMENT", NT_STATUS_MAPPED_ALIGNMENT }, + { "NT_STATUS_IMAGE_CHECKSUM_MISMATCH", NT_STATUS_IMAGE_CHECKSUM_MISMATCH }, + { "NT_STATUS_LOST_WRITEBEHIND_DATA", NT_STATUS_LOST_WRITEBEHIND_DATA }, + { "NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID", NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID }, + { "NT_STATUS_PASSWORD_MUST_CHANGE", NT_STATUS_PASSWORD_MUST_CHANGE }, + { "NT_STATUS_NOT_FOUND", NT_STATUS_NOT_FOUND }, + { "NT_STATUS_NOT_TINY_STREAM", NT_STATUS_NOT_TINY_STREAM }, + { "NT_STATUS_RECOVERY_FAILURE", NT_STATUS_RECOVERY_FAILURE }, + { "NT_STATUS_STACK_OVERFLOW_READ", NT_STATUS_STACK_OVERFLOW_READ }, + { "NT_STATUS_FAIL_CHECK", NT_STATUS_FAIL_CHECK }, + { "NT_STATUS_DUPLICATE_OBJECTID", NT_STATUS_DUPLICATE_OBJECTID }, + { "NT_STATUS_OBJECTID_EXISTS", NT_STATUS_OBJECTID_EXISTS }, + { "NT_STATUS_CONVERT_TO_LARGE", NT_STATUS_CONVERT_TO_LARGE }, + { "NT_STATUS_RETRY", NT_STATUS_RETRY }, + { "NT_STATUS_FOUND_OUT_OF_SCOPE", NT_STATUS_FOUND_OUT_OF_SCOPE }, + { "NT_STATUS_ALLOCATE_BUCKET", NT_STATUS_ALLOCATE_BUCKET }, + { "NT_STATUS_PROPSET_NOT_FOUND", NT_STATUS_PROPSET_NOT_FOUND }, + { "NT_STATUS_MARSHALL_OVERFLOW", NT_STATUS_MARSHALL_OVERFLOW }, + { "NT_STATUS_INVALID_VARIANT", NT_STATUS_INVALID_VARIANT }, + { "NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND", NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND }, + { "NT_STATUS_ACCOUNT_LOCKED_OUT", NT_STATUS_ACCOUNT_LOCKED_OUT }, + { "NT_STATUS_HANDLE_NOT_CLOSABLE", NT_STATUS_HANDLE_NOT_CLOSABLE }, + { "NT_STATUS_CONNECTION_REFUSED", NT_STATUS_CONNECTION_REFUSED }, + { "NT_STATUS_GRACEFUL_DISCONNECT", NT_STATUS_GRACEFUL_DISCONNECT }, + { "NT_STATUS_ADDRESS_ALREADY_ASSOCIATED", NT_STATUS_ADDRESS_ALREADY_ASSOCIATED }, + { "NT_STATUS_ADDRESS_NOT_ASSOCIATED", NT_STATUS_ADDRESS_NOT_ASSOCIATED }, + { "NT_STATUS_CONNECTION_INVALID", NT_STATUS_CONNECTION_INVALID }, + { "NT_STATUS_CONNECTION_ACTIVE", NT_STATUS_CONNECTION_ACTIVE }, + { "NT_STATUS_NETWORK_UNREACHABLE", NT_STATUS_NETWORK_UNREACHABLE }, + { "NT_STATUS_HOST_UNREACHABLE", NT_STATUS_HOST_UNREACHABLE }, + { "NT_STATUS_PROTOCOL_UNREACHABLE", NT_STATUS_PROTOCOL_UNREACHABLE }, + { "NT_STATUS_PORT_UNREACHABLE", NT_STATUS_PORT_UNREACHABLE }, + { "NT_STATUS_REQUEST_ABORTED", NT_STATUS_REQUEST_ABORTED }, + { "NT_STATUS_CONNECTION_ABORTED", NT_STATUS_CONNECTION_ABORTED }, + { "NT_STATUS_BAD_COMPRESSION_BUFFER", NT_STATUS_BAD_COMPRESSION_BUFFER }, + { "NT_STATUS_USER_MAPPED_FILE", NT_STATUS_USER_MAPPED_FILE }, + { "NT_STATUS_AUDIT_FAILED", NT_STATUS_AUDIT_FAILED }, + { "NT_STATUS_TIMER_RESOLUTION_NOT_SET", NT_STATUS_TIMER_RESOLUTION_NOT_SET }, + { "NT_STATUS_CONNECTION_COUNT_LIMIT", NT_STATUS_CONNECTION_COUNT_LIMIT }, + { "NT_STATUS_LOGIN_TIME_RESTRICTION", NT_STATUS_LOGIN_TIME_RESTRICTION }, + { "NT_STATUS_LOGIN_WKSTA_RESTRICTION", NT_STATUS_LOGIN_WKSTA_RESTRICTION }, + { "NT_STATUS_IMAGE_MP_UP_MISMATCH", NT_STATUS_IMAGE_MP_UP_MISMATCH }, + { "NT_STATUS_INSUFFICIENT_LOGON_INFO", NT_STATUS_INSUFFICIENT_LOGON_INFO }, + { "NT_STATUS_BAD_DLL_ENTRYPOINT", NT_STATUS_BAD_DLL_ENTRYPOINT }, + { "NT_STATUS_BAD_SERVICE_ENTRYPOINT", NT_STATUS_BAD_SERVICE_ENTRYPOINT }, + { "NT_STATUS_LPC_REPLY_LOST", NT_STATUS_LPC_REPLY_LOST }, + { "NT_STATUS_IP_ADDRESS_CONFLICT1", NT_STATUS_IP_ADDRESS_CONFLICT1 }, + { "NT_STATUS_IP_ADDRESS_CONFLICT2", NT_STATUS_IP_ADDRESS_CONFLICT2 }, + { "NT_STATUS_REGISTRY_QUOTA_LIMIT", NT_STATUS_REGISTRY_QUOTA_LIMIT }, + { "NT_STATUS_PATH_NOT_COVERED", NT_STATUS_PATH_NOT_COVERED }, + { "NT_STATUS_NO_CALLBACK_ACTIVE", NT_STATUS_NO_CALLBACK_ACTIVE }, + { "NT_STATUS_LICENSE_QUOTA_EXCEEDED", NT_STATUS_LICENSE_QUOTA_EXCEEDED }, + { "NT_STATUS_PWD_TOO_SHORT", NT_STATUS_PWD_TOO_SHORT }, + { "NT_STATUS_PWD_TOO_RECENT", NT_STATUS_PWD_TOO_RECENT }, + { "NT_STATUS_PWD_HISTORY_CONFLICT", NT_STATUS_PWD_HISTORY_CONFLICT }, + { "NT_STATUS_PLUGPLAY_NO_DEVICE", NT_STATUS_PLUGPLAY_NO_DEVICE }, + { "NT_STATUS_UNSUPPORTED_COMPRESSION", NT_STATUS_UNSUPPORTED_COMPRESSION }, + { "NT_STATUS_INVALID_HW_PROFILE", NT_STATUS_INVALID_HW_PROFILE }, + { "NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH", NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH }, + { "NT_STATUS_DRIVER_ORDINAL_NOT_FOUND", NT_STATUS_DRIVER_ORDINAL_NOT_FOUND }, + { "NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND", NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND }, + { "NT_STATUS_RESOURCE_NOT_OWNED", NT_STATUS_RESOURCE_NOT_OWNED }, + { "NT_STATUS_TOO_MANY_LINKS", NT_STATUS_TOO_MANY_LINKS }, + { "NT_STATUS_QUOTA_LIST_INCONSISTENT", NT_STATUS_QUOTA_LIST_INCONSISTENT }, + { "NT_STATUS_FILE_IS_OFFLINE", NT_STATUS_FILE_IS_OFFLINE }, + { "NT_STATUS_NO_MORE_ENTRIES", NT_STATUS_NO_MORE_ENTRIES }, + { "NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED", NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED }, + { "NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX", NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX }, + { "NT_STATUS_CURRENT_DOMAIN_NOT_ALLOWED", NT_STATUS_CURRENT_DOMAIN_NOT_ALLOWED }, + { "NT_STATUS_OBJECTID_NOT_FOUND", NT_STATUS_OBJECTID_NOT_FOUND }, + { "NT_STATUS_DOWNGRADE_DETECTED", NT_STATUS_DOWNGRADE_DETECTED }, + { "NT_STATUS_DS_BUSY", NT_STATUS_DS_BUSY }, + { "STATUS_MORE_ENTRIES", STATUS_MORE_ENTRIES }, + { "STATUS_SOME_UNMAPPED", STATUS_SOME_UNMAPPED }, + { "STATUS_NOTIFY_CLEANUP", STATUS_NOTIFY_CLEANUP }, + { "STATUS_NOTIFY_ENUM_DIR", STATUS_NOTIFY_ENUM_DIR }, + + DOS_CODE(ERRDOS, ERRsuccess), + DOS_CODE(ERRDOS, ERRbadfunc), + DOS_CODE(ERRDOS, ERRbadfile), + DOS_CODE(ERRDOS, ERRbadpath), + DOS_CODE(ERRDOS, ERRnofids), + DOS_CODE(ERRDOS, ERRnoaccess), + DOS_CODE(ERRDOS, ERRbadfid), + DOS_CODE(ERRDOS, ERRbadmcb), + DOS_CODE(ERRDOS, ERRnomem), + DOS_CODE(ERRDOS, ERRbadmem), + DOS_CODE(ERRDOS, ERRbadenv), + DOS_CODE(ERRDOS, ERRbadaccess), + DOS_CODE(ERRDOS, ERRbaddata), + DOS_CODE(ERRDOS, ERRres), + DOS_CODE(ERRDOS, ERRbaddrive), + DOS_CODE(ERRDOS, ERRremcd), + DOS_CODE(ERRDOS, ERRdiffdevice), + DOS_CODE(ERRDOS, ERRnofiles), + DOS_CODE(ERRDOS, ERRgeneral), + DOS_CODE(ERRDOS, ERRbadshare), + DOS_CODE(ERRDOS, ERRlock), + DOS_CODE(ERRDOS, ERRunsup), + DOS_CODE(ERRDOS, ERRnetnamedel), + DOS_CODE(ERRDOS, ERRnosuchshare), + DOS_CODE(ERRDOS, ERRfilexists), + DOS_CODE(ERRDOS, ERRinvalidparam), + DOS_CODE(ERRDOS, ERRcannotopen), + DOS_CODE(ERRDOS, ERRinsufficientbuffer), + DOS_CODE(ERRDOS, ERRinvalidname), + DOS_CODE(ERRDOS, ERRunknownlevel), + DOS_CODE(ERRDOS, ERRnotlocked), + DOS_CODE(ERRDOS, ERRinvalidpath), + DOS_CODE(ERRDOS, ERRcancelviolation), + DOS_CODE(ERRDOS, ERRnoatomiclocks), + DOS_CODE(ERRDOS, ERRrename), + DOS_CODE(ERRDOS, ERRbadpipe), + DOS_CODE(ERRDOS, ERRpipebusy), + DOS_CODE(ERRDOS, ERRpipeclosing), + DOS_CODE(ERRDOS, ERRnotconnected), + DOS_CODE(ERRDOS, ERRmoredata), + DOS_CODE(ERRDOS, ERRnomoreitems), + DOS_CODE(ERRDOS, ERRbaddirectory), + DOS_CODE(ERRDOS, ERReasnotsupported), + DOS_CODE(ERRDOS, ERRlogonfailure), + DOS_CODE(ERRDOS, ERRbuftoosmall), + DOS_CODE(ERRDOS, ERRunknownipc), + DOS_CODE(ERRDOS, ERRnosuchprintjob), + DOS_CODE(ERRDOS, ERRinvgroup), + DOS_CODE(ERRDOS, ERRnoipc), + DOS_CODE(ERRDOS, ERRdriveralreadyinstalled), + DOS_CODE(ERRDOS, ERRunknownprinterport), + DOS_CODE(ERRDOS, ERRunknownprinterdriver), + DOS_CODE(ERRDOS, ERRunknownprintprocessor), + DOS_CODE(ERRDOS, ERRinvalidseparatorfile), + DOS_CODE(ERRDOS, ERRinvalidjobpriority), + DOS_CODE(ERRDOS, ERRinvalidprintername), + DOS_CODE(ERRDOS, ERRprinteralreadyexists), + DOS_CODE(ERRDOS, ERRinvalidprintercommand), + DOS_CODE(ERRDOS, ERRinvaliddatatype), + DOS_CODE(ERRDOS, ERRinvalidenvironment), + DOS_CODE(ERRDOS, ERRunknownprintmonitor), + DOS_CODE(ERRDOS, ERRprinterdriverinuse), + DOS_CODE(ERRDOS, ERRspoolfilenotfound), + DOS_CODE(ERRDOS, ERRnostartdoc), + DOS_CODE(ERRDOS, ERRnoaddjob), + DOS_CODE(ERRDOS, ERRprintprocessoralreadyinstalled), + DOS_CODE(ERRDOS, ERRprintmonitoralreadyinstalled), + DOS_CODE(ERRDOS, ERRinvalidprintmonitor), + DOS_CODE(ERRDOS, ERRprintmonitorinuse), + DOS_CODE(ERRDOS, ERRprinterhasjobsqueued), + DOS_CODE(ERRDOS, ERReainconsistent), + + DOS_CODE(ERRSRV, ERRerror), + DOS_CODE(ERRSRV, ERRbadpw), + DOS_CODE(ERRSRV, ERRbadtype), + DOS_CODE(ERRSRV, ERRaccess), + DOS_CODE(ERRSRV, ERRinvnid), + DOS_CODE(ERRSRV, ERRinvnetname), + DOS_CODE(ERRSRV, ERRinvdevice), + DOS_CODE(ERRSRV, ERRqfull), + DOS_CODE(ERRSRV, ERRqtoobig), + DOS_CODE(ERRSRV, ERRinvpfid), + DOS_CODE(ERRSRV, ERRsmbcmd), + DOS_CODE(ERRSRV, ERRsrverror), + DOS_CODE(ERRSRV, ERRfilespecs), + DOS_CODE(ERRSRV, ERRbadlink), + DOS_CODE(ERRSRV, ERRbadpermits), + DOS_CODE(ERRSRV, ERRbadpid), + DOS_CODE(ERRSRV, ERRsetattrmode), + DOS_CODE(ERRSRV, ERRpaused), + DOS_CODE(ERRSRV, ERRmsgoff), + DOS_CODE(ERRSRV, ERRnoroom), + DOS_CODE(ERRSRV, ERRrmuns), + DOS_CODE(ERRSRV, ERRtimeout), + DOS_CODE(ERRSRV, ERRnoresource), + DOS_CODE(ERRSRV, ERRtoomanyuids), + DOS_CODE(ERRSRV, ERRbaduid), + DOS_CODE(ERRSRV, ERRuseMPX), + DOS_CODE(ERRSRV, ERRuseSTD), + DOS_CODE(ERRSRV, ERRcontMPX), + DOS_CODE(ERRSRV, ERRnosupport), + DOS_CODE(ERRSRV, ERRunknownsmb), + + DOS_CODE(ERRHRD, ERRnowrite), + DOS_CODE(ERRHRD, ERRbadunit), + DOS_CODE(ERRHRD, ERRnotready), + DOS_CODE(ERRHRD, ERRbadcmd), + DOS_CODE(ERRHRD, ERRdata), + DOS_CODE(ERRHRD, ERRbadreq), + DOS_CODE(ERRHRD, ERRseek), + DOS_CODE(ERRHRD, ERRbadmedia), + DOS_CODE(ERRHRD, ERRbadsector), + DOS_CODE(ERRHRD, ERRnopaper), + DOS_CODE(ERRHRD, ERRwrite), + DOS_CODE(ERRHRD, ERRread), + DOS_CODE(ERRHRD, ERRgeneral), + DOS_CODE(ERRHRD, ERRwrongdisk), + DOS_CODE(ERRHRD, ERRFCBunavail), + DOS_CODE(ERRHRD, ERRsharebufexc), + DOS_CODE(ERRHRD, ERRdiskfull), + + LDAP_CODE(LDAP_SUCCESS), + LDAP_CODE(LDAP_OPERATIONS_ERROR), + LDAP_CODE(LDAP_PROTOCOL_ERROR), + LDAP_CODE(LDAP_TIME_LIMIT_EXCEEDED), + LDAP_CODE(LDAP_SIZE_LIMIT_EXCEEDED), + LDAP_CODE(LDAP_COMPARE_FALSE), + LDAP_CODE(LDAP_COMPARE_TRUE), + LDAP_CODE(LDAP_AUTH_METHOD_NOT_SUPPORTED), + LDAP_CODE(LDAP_STRONG_AUTH_REQUIRED), + LDAP_CODE(LDAP_REFERRAL), + LDAP_CODE(LDAP_ADMIN_LIMIT_EXCEEDED), + LDAP_CODE(LDAP_UNAVAILABLE_CRITICAL_EXTENSION), + LDAP_CODE(LDAP_CONFIDENTIALITY_REQUIRED), + LDAP_CODE(LDAP_SASL_BIND_IN_PROGRESS), + LDAP_CODE(LDAP_NO_SUCH_ATTRIBUTE), + LDAP_CODE(LDAP_UNDEFINED_ATTRIBUTE_TYPE), + LDAP_CODE(LDAP_INAPPROPRIATE_MATCHING), + LDAP_CODE(LDAP_CONSTRAINT_VIOLATION), + LDAP_CODE(LDAP_ATTRIBUTE_OR_VALUE_EXISTS), + LDAP_CODE(LDAP_INVALID_ATTRIBUTE_SYNTAX), + LDAP_CODE(LDAP_NO_SUCH_OBJECT), + LDAP_CODE(LDAP_ALIAS_PROBLEM), + LDAP_CODE(LDAP_INVALID_DN_SYNTAX), + LDAP_CODE(LDAP_ALIAS_DEREFERENCING_PROBLEM), + LDAP_CODE(LDAP_INAPPROPRIATE_AUTHENTICATION), + LDAP_CODE(LDAP_INVALID_CREDENTIALS), + LDAP_CODE(LDAP_INSUFFICIENT_ACCESS_RIGHTS), + LDAP_CODE(LDAP_BUSY), + LDAP_CODE(LDAP_UNAVAILABLE), + LDAP_CODE(LDAP_UNWILLING_TO_PERFORM), + LDAP_CODE(LDAP_LOOP_DETECT), + LDAP_CODE(LDAP_NAMING_VIOLATION), + LDAP_CODE(LDAP_OBJECT_CLASS_VIOLATION), + LDAP_CODE(LDAP_NOT_ALLOWED_ON_NON_LEAF), + LDAP_CODE(LDAP_NOT_ALLOWED_ON_RDN), + LDAP_CODE(LDAP_ENTRY_ALREADY_EXISTS), + LDAP_CODE(LDAP_OBJECT_CLASS_MODS_PROHIBITED), + LDAP_CODE(LDAP_AFFECTS_MULTIPLE_DSAS), + LDAP_CODE(LDAP_OTHER), + + { NULL, NT_STATUS(0) } +}; + +static const 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 }, + { "Unexpected information received", NT_STATUS_INVALID_PARAMETER }, + { "Memory allocation error", NT_STATUS_NO_MEMORY }, + { "No domain controllers located", NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND }, + { "Account locked out", NT_STATUS_ACCOUNT_LOCKED_OUT }, + { "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 logon servers", NT_STATUS_NO_LOGON_SERVERS }, + { "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 }, + { "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 }, + + { NULL, NT_STATUS(0) } +}; + +/***************************************************************************** + returns an NT error message. not amazingly helpful, but better than a number. + *****************************************************************************/ +const char *nt_errstr(NTSTATUS nt_code) +{ + static char msg[40]; + 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++; + } + + if (NT_STATUS_IS_LDAP(nt_code)) { + slprintf(msg, sizeof(msg), "LDAP code %u", NT_STATUS_LDAP_CODE(nt_code)); + return msg; + } + + slprintf(msg, sizeof(msg), "NT code 0x%08x", NT_STATUS_V(nt_code)); + + return msg; +} + +/************************************************************************ + 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) +{ + static char out[40]; + int idx = 0; + + while (nt_errs[idx].nt_errstr != NULL) { + if (NT_STATUS_V(nt_errs[idx].nt_errcode) == + NT_STATUS_V(nt_code)) { + return nt_errs[idx].nt_errstr; + } + idx++; + } + + slprintf(out, sizeof(out), "NT_STATUS(0x%08x)", NT_STATUS_V(nt_code)); + + return out; +} + +/***************************************************************************** + returns the NT_STATUS constant matching the string supplied (as an NTSTATUS) + *****************************************************************************/ +NTSTATUS nt_status_string_to_code(const char *nt_status_str) +{ + int idx = 0; + + while (nt_errs[idx].nt_errstr != NULL) { + if (strcasecmp(nt_errs[idx].nt_errstr, nt_status_str) == 0) { + return nt_errs[idx].nt_errcode; + } + idx++; + } + return NT_STATUS_UNSUCCESSFUL; +} diff --git a/source4/libcli/util/pyerrors.h b/source4/libcli/util/pyerrors.h new file mode 100644 index 0000000000..66823f4a41 --- /dev/null +++ b/source4/libcli/util/pyerrors.h @@ -0,0 +1,45 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Jelmer Vernooij <jelmer@samba.org> 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/>. +*/ + +#ifndef __PYERRORS_H__ +#define __PYERRORS_H__ + +#define PyErr_FromWERROR(err) Py_BuildValue("(i,s)", W_ERROR_V(err), discard_const_p(char, win_errstr(err))) + +#define PyErr_FromNTSTATUS(status) Py_BuildValue("(i,s)", NT_STATUS_V(status), discard_const_p(char, get_friendly_nt_error_msg(status))) + +#define PyErr_SetWERROR(err) \ + PyErr_SetObject(PyExc_RuntimeError, PyErr_FromWERROR(err)) + +#define PyErr_SetNTSTATUS(status) \ + PyErr_SetObject(PyExc_RuntimeError, PyErr_FromNTSTATUS(status)) + +#define PyErr_NTSTATUS_IS_ERR_RAISE(status) \ + if (NT_STATUS_IS_ERR(status)) { \ + PyErr_SetNTSTATUS(status); \ + return NULL; \ + } + +#define PyErr_WERROR_IS_ERR_RAISE(status) \ + if (!W_ERROR_IS_OK(status)) { \ + PyErr_SetWERROR(status); \ + return NULL; \ + } + +#endif /* __PYERRORS_H__ */ diff --git a/source4/libcli/wbclient/config.mk b/source4/libcli/wbclient/config.mk new file mode 100644 index 0000000000..00df5dbb22 --- /dev/null +++ b/source4/libcli/wbclient/config.mk @@ -0,0 +1,5 @@ +[SUBSYSTEM::LIBWBCLIENT] +PUBLIC_DEPENDENCIES = LIBSAMBA-ERRORS LIBEVENTS +PRIVATE_DEPENDENCIES = NDR_WINBIND MESSAGING + +LIBWBCLIENT_OBJ_FILES = $(libclisrcdir)/wbclient/wbclient.o diff --git a/source4/libcli/wbclient/wbclient.c b/source4/libcli/wbclient/wbclient.c new file mode 100644 index 0000000000..da7d678da9 --- /dev/null +++ b/source4/libcli/wbclient/wbclient.c @@ -0,0 +1,210 @@ +/* + Unix SMB/CIFS implementation. + + Winbind client library. + + Copyright (C) 2008 Kai Blin <kai@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/>. +*/ + +#include "includes.h" +#include "libcli/wbclient/wbclient.h" + +/** + * Get the server_id of the winbind task. + * + * \param[in] msg_ctx message context to use + * \param[in] mem_ctx talloc context to use + * \param[out] ids array of server_id structs containing the winbind id + * \return NT_STATUS_OK on success, NT_STATUS_INTERNAL_ERROR on failure + */ +static NTSTATUS get_server_id(struct messaging_context *msg_ctx, + TALLOC_CTX *mem_ctx, struct server_id **ids) +{ + *ids = irpc_servers_byname(msg_ctx, mem_ctx, "winbind_server"); + if (*ids == NULL || (*ids)[0].id == 0) { + DEBUG(0, ("Geting the winbind server ID failed.\n")); + return NT_STATUS_INTERNAL_ERROR; + } + return NT_STATUS_OK; +} + +/** + * Initialize the wbclient context, talloc_free() when done. + * + * \param mem_ctx talloc context to allocate memory from + * \param msg_ctx message context to use + * \param + */ +struct wbc_context *wbc_init(TALLOC_CTX *mem_ctx, + struct messaging_context *msg_ctx, + struct tevent_context *event_ctx) +{ + struct wbc_context *ctx; + NTSTATUS status; + + ctx = talloc(mem_ctx, struct wbc_context); + if (ctx == NULL) return NULL; + + status = get_server_id(msg_ctx, mem_ctx, &ctx->ids); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(ctx); + return NULL; + } + + ctx->msg_ctx = msg_ctx; + ctx->event_ctx = event_ctx; + + return ctx; +} + +struct wbc_idmap_state { + struct composite_context *ctx; + struct winbind_get_idmap *req; + struct irpc_request *irpc_req; + struct id_mapping *ids; +}; + +static void sids_to_xids_recv_ids(struct irpc_request *req); + +struct composite_context *wbc_sids_to_xids_send(struct wbc_context *wbc_ctx, + TALLOC_CTX *mem_ctx, + uint32_t count, + struct id_mapping *ids) +{ + struct composite_context *ctx; + struct wbc_idmap_state *state; + + DEBUG(5, ("wbc_sids_to_xids called\n")); + + ctx = composite_create(mem_ctx, wbc_ctx->event_ctx); + if (ctx == NULL) return NULL; + + state = talloc(ctx, struct wbc_idmap_state); + if (composite_nomem(state, ctx)) return ctx; + ctx->private_data = state; + + state->req = talloc(state, struct winbind_get_idmap); + if (composite_nomem(state->req, ctx)) return ctx; + + state->req->in.count = count; + state->req->in.level = WINBIND_IDMAP_LEVEL_SIDS_TO_XIDS; + state->req->in.ids = ids; + state->ctx = ctx; + + state->irpc_req = IRPC_CALL_SEND(wbc_ctx->msg_ctx, wbc_ctx->ids[0], + winbind, WINBIND_GET_IDMAP, state->req, + state); + if (composite_nomem(state->irpc_req, ctx)) return ctx; + + composite_continue_irpc(ctx, state->irpc_req, sids_to_xids_recv_ids, + state); + return ctx; +} + +static void sids_to_xids_recv_ids(struct irpc_request *req) +{ + struct wbc_idmap_state *state = talloc_get_type_abort( + req->async.private_data, + struct wbc_idmap_state); + + state->ctx->status = irpc_call_recv(state->irpc_req); + if (!composite_is_ok(state->ctx)) return; + + state->ids = state->req->out.ids; + composite_done(state->ctx); +} + +NTSTATUS wbc_sids_to_xids_recv(struct composite_context *ctx, + struct id_mapping **ids) +{ + NTSTATUS status = composite_wait(ctx); + DEBUG(5, ("wbc_sids_to_xids_recv called\n")); + if (NT_STATUS_IS_OK(status)) { + struct wbc_idmap_state *state = talloc_get_type_abort( + ctx->private_data, + struct wbc_idmap_state); + *ids = state->ids; + } + + return status; +} + +static void xids_to_sids_recv_ids(struct irpc_request *req); + +struct composite_context *wbc_xids_to_sids_send(struct wbc_context *wbc_ctx, + TALLOC_CTX *mem_ctx, + uint32_t count, + struct id_mapping *ids) +{ + struct composite_context *ctx; + struct wbc_idmap_state *state; + + DEBUG(5, ("wbc_xids_to_sids called\n")); + + ctx = composite_create(mem_ctx, wbc_ctx->event_ctx); + if (ctx == NULL) return NULL; + + state = talloc(ctx, struct wbc_idmap_state); + if (composite_nomem(state, ctx)) return ctx; + ctx->private_data = state; + + state->req = talloc(state, struct winbind_get_idmap); + if (composite_nomem(state->req, ctx)) return ctx; + + state->req->in.count = count; + state->req->in.level = WINBIND_IDMAP_LEVEL_XIDS_TO_SIDS; + state->req->in.ids = ids; + state->ctx = ctx; + + state->irpc_req = IRPC_CALL_SEND(wbc_ctx->msg_ctx, wbc_ctx->ids[0], + winbind, WINBIND_GET_IDMAP, state->req, + state); + if (composite_nomem(state->irpc_req, ctx)) return ctx; + + composite_continue_irpc(ctx, state->irpc_req, xids_to_sids_recv_ids, + state); + + return ctx; +} + +static void xids_to_sids_recv_ids(struct irpc_request *req) +{ + struct wbc_idmap_state *state = talloc_get_type_abort( + req->async.private_data, + struct wbc_idmap_state); + + state->ctx->status = irpc_call_recv(state->irpc_req); + if (!composite_is_ok(state->ctx)) return; + + state->ids = state->req->out.ids; + composite_done(state->ctx); +} + +NTSTATUS wbc_xids_to_sids_recv(struct composite_context *ctx, + struct id_mapping **ids) +{ + NTSTATUS status = composite_wait(ctx); + DEBUG(5, ("wbc_xids_to_sids_recv called\n")); + if (NT_STATUS_IS_OK(status)) { + struct wbc_idmap_state *state = talloc_get_type_abort( + ctx->private_data, + struct wbc_idmap_state); + *ids = state->ids; + } + + return status; +} + diff --git a/source4/libcli/wbclient/wbclient.h b/source4/libcli/wbclient/wbclient.h new file mode 100644 index 0000000000..416dc78324 --- /dev/null +++ b/source4/libcli/wbclient/wbclient.h @@ -0,0 +1,50 @@ +/* + Unix SMB/CIFS implementation. + + Winbind client library. + + Copyright (C) 2008 Kai Blin <kai@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/>. +*/ +#include "lib/messaging/irpc.h" +#include "libcli/composite/composite.h" +#include "librpc/gen_ndr/ndr_winbind.h" + +struct wbc_context { + struct messaging_context *msg_ctx; + struct tevent_context *event_ctx; + struct server_id *ids; +}; + +struct wbc_context *wbc_init(TALLOC_CTX *mem_ctx, + struct messaging_context *msg_ctx, + struct tevent_context *event_ctx); + +struct composite_context *wbc_sids_to_xids_send(struct wbc_context *wbc_ctx, + TALLOC_CTX *mem_ctx, + uint32_t count, + struct id_mapping *ids); + +NTSTATUS wbc_sids_to_xids_recv(struct composite_context *ctx, + struct id_mapping **ids); + +struct composite_context *wbc_xids_to_sids_send(struct wbc_context *wbc_ctx, + TALLOC_CTX *mem_ctx, + uint32_t count, + struct id_mapping *ids); + +NTSTATUS wbc_xids_to_sids_recv(struct composite_context *ctx, + struct id_mapping **ids); + diff --git a/source4/libcli/wrepl/winsrepl.c b/source4/libcli/wrepl/winsrepl.c new file mode 100644 index 0000000000..48a6abba9d --- /dev/null +++ b/source4/libcli/wrepl/winsrepl.c @@ -0,0 +1,871 @@ +/* + Unix SMB/CIFS implementation. + + low level WINS replication client code + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/events/events.h" +#include "../lib/util/dlinklist.h" +#include "lib/socket/socket.h" +#include "libcli/wrepl/winsrepl.h" +#include "librpc/gen_ndr/ndr_winsrepl.h" +#include "lib/stream/packet.h" +#include "libcli/composite/composite.h" +#include "system/network.h" +#include "lib/socket/netif.h" +#include "param/param.h" + +static struct wrepl_request *wrepl_request_finished(struct wrepl_request *req, NTSTATUS status); + +/* + mark all pending requests as dead - called when a socket error happens +*/ +static void wrepl_socket_dead(struct wrepl_socket *wrepl_socket, NTSTATUS status) +{ + wrepl_socket->dead = true; + + if (wrepl_socket->packet) { + packet_recv_disable(wrepl_socket->packet); + packet_set_fde(wrepl_socket->packet, NULL); + packet_set_socket(wrepl_socket->packet, NULL); + } + + if (wrepl_socket->event.fde) { + talloc_free(wrepl_socket->event.fde); + wrepl_socket->event.fde = NULL; + } + + if (wrepl_socket->sock) { + talloc_free(wrepl_socket->sock); + wrepl_socket->sock = NULL; + } + + if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) { + status = NT_STATUS_UNEXPECTED_NETWORK_ERROR; + } + while (wrepl_socket->recv_queue) { + struct wrepl_request *req = wrepl_socket->recv_queue; + DLIST_REMOVE(wrepl_socket->recv_queue, req); + wrepl_request_finished(req, status); + } + + talloc_set_destructor(wrepl_socket, NULL); + if (wrepl_socket->free_skipped) { + talloc_free(wrepl_socket); + } +} + +static void wrepl_request_timeout_handler(struct tevent_context *ev, struct tevent_timer *te, + struct timeval t, void *ptr) +{ + struct wrepl_request *req = talloc_get_type(ptr, struct wrepl_request); + wrepl_socket_dead(req->wrepl_socket, NT_STATUS_IO_TIMEOUT); +} + +/* + handle recv events +*/ +static NTSTATUS wrepl_finish_recv(void *private_data, DATA_BLOB packet_blob_in) +{ + struct wrepl_socket *wrepl_socket = talloc_get_type(private_data, struct wrepl_socket); + struct wrepl_request *req = wrepl_socket->recv_queue; + DATA_BLOB blob; + enum ndr_err_code ndr_err; + + if (!req) { + DEBUG(1,("Received unexpected WINS packet of length %u!\n", + (unsigned)packet_blob_in.length)); + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + req->packet = talloc(req, struct wrepl_packet); + NT_STATUS_HAVE_NO_MEMORY(req->packet); + + blob.data = packet_blob_in.data + 4; + blob.length = packet_blob_in.length - 4; + + /* we have a full request - parse it */ + ndr_err = ndr_pull_struct_blob(&blob, req->packet, wrepl_socket->iconv_convenience, req->packet, + (ndr_pull_flags_fn_t)ndr_pull_wrepl_packet); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + NTSTATUS status = ndr_map_error2ntstatus(ndr_err); + wrepl_request_finished(req, status); + return NT_STATUS_OK; + } + + if (DEBUGLVL(10)) { + DEBUG(10,("Received WINS packet of length %u\n", + (unsigned)packet_blob_in.length)); + NDR_PRINT_DEBUG(wrepl_packet, req->packet); + } + + wrepl_request_finished(req, NT_STATUS_OK); + return NT_STATUS_OK; +} + +/* + handler for winrepl events +*/ +static void wrepl_handler(struct tevent_context *ev, struct tevent_fd *fde, + uint16_t flags, void *private_data) +{ + struct wrepl_socket *wrepl_socket = talloc_get_type(private_data, + struct wrepl_socket); + if (flags & EVENT_FD_READ) { + packet_recv(wrepl_socket->packet); + return; + } + if (flags & EVENT_FD_WRITE) { + packet_queue_run(wrepl_socket->packet); + } +} + +static void wrepl_error(void *private_data, NTSTATUS status) +{ + struct wrepl_socket *wrepl_socket = talloc_get_type(private_data, + struct wrepl_socket); + wrepl_socket_dead(wrepl_socket, status); +} + + +/* + destroy a wrepl_socket destructor +*/ +static int wrepl_socket_destructor(struct wrepl_socket *sock) +{ + if (sock->dead) { + sock->free_skipped = true; + return -1; + } + wrepl_socket_dead(sock, NT_STATUS_LOCAL_DISCONNECT); + return 0; +} + +/* + initialise a wrepl_socket. The event_ctx is optional, if provided then + operations will use that event context +*/ +struct wrepl_socket *wrepl_socket_init(TALLOC_CTX *mem_ctx, + struct tevent_context *event_ctx, + struct smb_iconv_convenience *iconv_convenience) +{ + struct wrepl_socket *wrepl_socket; + NTSTATUS status; + + wrepl_socket = talloc_zero(mem_ctx, struct wrepl_socket); + if (!wrepl_socket) return NULL; + + wrepl_socket->event.ctx = talloc_reference(wrepl_socket, event_ctx); + if (!wrepl_socket->event.ctx) goto failed; + + wrepl_socket->iconv_convenience = iconv_convenience; + + status = socket_create("ip", SOCKET_TYPE_STREAM, &wrepl_socket->sock, 0); + if (!NT_STATUS_IS_OK(status)) goto failed; + + talloc_steal(wrepl_socket, wrepl_socket->sock); + + wrepl_socket->request_timeout = WREPL_SOCKET_REQUEST_TIMEOUT; + + talloc_set_destructor(wrepl_socket, wrepl_socket_destructor); + + return wrepl_socket; + +failed: + talloc_free(wrepl_socket); + return NULL; +} + +/* + initialise a wrepl_socket from an already existing connection +*/ +struct wrepl_socket *wrepl_socket_merge(TALLOC_CTX *mem_ctx, + struct tevent_context *event_ctx, + struct socket_context *sock, + struct packet_context *pack) +{ + struct wrepl_socket *wrepl_socket; + + wrepl_socket = talloc_zero(mem_ctx, struct wrepl_socket); + if (wrepl_socket == NULL) goto failed; + + wrepl_socket->event.ctx = talloc_reference(wrepl_socket, event_ctx); + if (wrepl_socket->event.ctx == NULL) goto failed; + + wrepl_socket->sock = sock; + talloc_steal(wrepl_socket, wrepl_socket->sock); + + + wrepl_socket->request_timeout = WREPL_SOCKET_REQUEST_TIMEOUT; + + wrepl_socket->event.fde = event_add_fd(wrepl_socket->event.ctx, wrepl_socket, + socket_get_fd(wrepl_socket->sock), + EVENT_FD_READ, + wrepl_handler, wrepl_socket); + if (wrepl_socket->event.fde == NULL) { + goto failed; + } + + wrepl_socket->packet = pack; + talloc_steal(wrepl_socket, wrepl_socket->packet); + packet_set_private(wrepl_socket->packet, wrepl_socket); + packet_set_socket(wrepl_socket->packet, wrepl_socket->sock); + packet_set_callback(wrepl_socket->packet, wrepl_finish_recv); + packet_set_full_request(wrepl_socket->packet, packet_full_request_u32); + packet_set_error_handler(wrepl_socket->packet, wrepl_error); + packet_set_event_context(wrepl_socket->packet, wrepl_socket->event.ctx); + packet_set_fde(wrepl_socket->packet, wrepl_socket->event.fde); + packet_set_serialise(wrepl_socket->packet); + + talloc_set_destructor(wrepl_socket, wrepl_socket_destructor); + + return wrepl_socket; + +failed: + talloc_free(wrepl_socket); + return NULL; +} + +/* + destroy a wrepl_request +*/ +static int wrepl_request_destructor(struct wrepl_request *req) +{ + if (req->state == WREPL_REQUEST_RECV) { + DLIST_REMOVE(req->wrepl_socket->recv_queue, req); + } + req->state = WREPL_REQUEST_ERROR; + return 0; +} + +/* + wait for a request to complete +*/ +static NTSTATUS wrepl_request_wait(struct wrepl_request *req) +{ + NT_STATUS_HAVE_NO_MEMORY(req); + while (req->state < WREPL_REQUEST_DONE) { + event_loop_once(req->wrepl_socket->event.ctx); + } + return req->status; +} + +struct wrepl_connect_state { + struct composite_context *result; + struct wrepl_socket *wrepl_socket; + struct composite_context *creq; +}; + +/* + handler for winrepl connection completion +*/ +static void wrepl_connect_handler(struct composite_context *creq) +{ + struct wrepl_connect_state *state = talloc_get_type(creq->async.private_data, + struct wrepl_connect_state); + struct wrepl_socket *wrepl_socket = state->wrepl_socket; + struct composite_context *result = state->result; + + result->status = socket_connect_recv(state->creq); + if (!composite_is_ok(result)) return; + + wrepl_socket->event.fde = event_add_fd(wrepl_socket->event.ctx, wrepl_socket, + socket_get_fd(wrepl_socket->sock), + EVENT_FD_READ, + wrepl_handler, wrepl_socket); + if (composite_nomem(wrepl_socket->event.fde, result)) return; + + /* setup the stream -> packet parser */ + wrepl_socket->packet = packet_init(wrepl_socket); + if (composite_nomem(wrepl_socket->packet, result)) return; + packet_set_private(wrepl_socket->packet, wrepl_socket); + packet_set_socket(wrepl_socket->packet, wrepl_socket->sock); + packet_set_callback(wrepl_socket->packet, wrepl_finish_recv); + packet_set_full_request(wrepl_socket->packet, packet_full_request_u32); + packet_set_error_handler(wrepl_socket->packet, wrepl_error); + packet_set_event_context(wrepl_socket->packet, wrepl_socket->event.ctx); + packet_set_fde(wrepl_socket->packet, wrepl_socket->event.fde); + packet_set_serialise(wrepl_socket->packet); + + composite_done(result); +} + +const char *wrepl_best_ip(struct loadparm_context *lp_ctx, const char *peer_ip) +{ + struct interface *ifaces; + load_interfaces(lp_ctx, lp_interfaces(lp_ctx), &ifaces); + return iface_best_ip(ifaces, peer_ip); +} + + +/* + connect a wrepl_socket to a WINS server +*/ +struct composite_context *wrepl_connect_send(struct wrepl_socket *wrepl_socket, + const char *our_ip, const char *peer_ip) +{ + struct composite_context *result; + struct wrepl_connect_state *state; + struct socket_address *peer, *us; + + result = talloc_zero(wrepl_socket, struct composite_context); + if (!result) return NULL; + + result->state = COMPOSITE_STATE_IN_PROGRESS; + result->event_ctx = wrepl_socket->event.ctx; + + state = talloc_zero(result, struct wrepl_connect_state); + if (composite_nomem(state, result)) return result; + result->private_data = state; + state->result = result; + state->wrepl_socket = wrepl_socket; + + us = socket_address_from_strings(state, wrepl_socket->sock->backend_name, + our_ip, 0); + if (composite_nomem(us, result)) return result; + + peer = socket_address_from_strings(state, wrepl_socket->sock->backend_name, + peer_ip, WINS_REPLICATION_PORT); + if (composite_nomem(peer, result)) return result; + + state->creq = socket_connect_send(wrepl_socket->sock, us, peer, + 0, wrepl_socket->event.ctx); + composite_continue(result, state->creq, wrepl_connect_handler, state); + return result; +} + +/* + connect a wrepl_socket to a WINS server - recv side +*/ +NTSTATUS wrepl_connect_recv(struct composite_context *result) +{ + struct wrepl_connect_state *state = talloc_get_type(result->private_data, + struct wrepl_connect_state); + struct wrepl_socket *wrepl_socket = state->wrepl_socket; + NTSTATUS status = composite_wait(result); + + if (!NT_STATUS_IS_OK(status)) { + wrepl_socket_dead(wrepl_socket, status); + } + + talloc_free(result); + return status; +} + +/* + connect a wrepl_socket to a WINS server - sync API +*/ +NTSTATUS wrepl_connect(struct wrepl_socket *wrepl_socket, + const char *our_ip, const char *peer_ip) +{ + struct composite_context *c_req = wrepl_connect_send(wrepl_socket, our_ip, peer_ip); + return wrepl_connect_recv(c_req); +} + +/* + callback from wrepl_request_trigger() +*/ +static void wrepl_request_trigger_handler(struct tevent_context *ev, struct tevent_timer *te, + struct timeval t, void *ptr) +{ + struct wrepl_request *req = talloc_get_type(ptr, struct wrepl_request); + if (req->async.fn) { + req->async.fn(req); + } +} + +/* + trigger an immediate event on a wrepl_request + the return value should only be used in wrepl_request_send() + this is the only place where req->trigger is true +*/ +static struct wrepl_request *wrepl_request_finished(struct wrepl_request *req, NTSTATUS status) +{ + struct tevent_timer *te; + + if (req->state == WREPL_REQUEST_RECV) { + DLIST_REMOVE(req->wrepl_socket->recv_queue, req); + } + + if (!NT_STATUS_IS_OK(status)) { + req->state = WREPL_REQUEST_ERROR; + } else { + req->state = WREPL_REQUEST_DONE; + } + + req->status = status; + + if (req->trigger) { + req->trigger = false; + /* a zero timeout means immediate */ + te = event_add_timed(req->wrepl_socket->event.ctx, + req, timeval_zero(), + wrepl_request_trigger_handler, req); + if (!te) { + talloc_free(req); + return NULL; + } + return req; + } + + if (req->async.fn) { + req->async.fn(req); + } + return NULL; +} + +struct wrepl_send_ctrl_state { + struct wrepl_send_ctrl ctrl; + struct wrepl_request *req; + struct wrepl_socket *wrepl_sock; +}; + +static int wrepl_send_ctrl_destructor(struct wrepl_send_ctrl_state *s) +{ + struct wrepl_request *req = s->wrepl_sock->recv_queue; + + /* check if the request is still in WREPL_STATE_RECV, + * we need this here because the caller has may called + * talloc_free(req) and wrepl_send_ctrl_state isn't + * a talloc child of the request, so our s->req pointer + * is maybe invalid! + */ + for (; req; req = req->next) { + if (req == s->req) break; + } + if (!req) return 0; + + /* here, we need to make sure the async request handler is called + * later in the next event_loop and now now + */ + req->trigger = true; + wrepl_request_finished(req, NT_STATUS_OK); + + if (s->ctrl.disconnect_after_send) { + wrepl_socket_dead(s->wrepl_sock, NT_STATUS_LOCAL_DISCONNECT); + } + + return 0; +} + +/* + send a generic wins replication request +*/ +struct wrepl_request *wrepl_request_send(struct wrepl_socket *wrepl_socket, + struct wrepl_packet *packet, + struct wrepl_send_ctrl *ctrl) +{ + struct wrepl_request *req; + struct wrepl_wrap wrap; + DATA_BLOB blob; + NTSTATUS status; + enum ndr_err_code ndr_err; + + req = talloc_zero(wrepl_socket, struct wrepl_request); + if (!req) return NULL; + req->wrepl_socket = wrepl_socket; + req->state = WREPL_REQUEST_RECV; + req->trigger = true; + + DLIST_ADD_END(wrepl_socket->recv_queue, req, struct wrepl_request *); + talloc_set_destructor(req, wrepl_request_destructor); + + if (wrepl_socket->dead) { + return wrepl_request_finished(req, NT_STATUS_INVALID_CONNECTION); + } + + wrap.packet = *packet; + ndr_err = ndr_push_struct_blob(&blob, req, wrepl_socket->iconv_convenience, &wrap, + (ndr_push_flags_fn_t)ndr_push_wrepl_wrap); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + status = ndr_map_error2ntstatus(ndr_err); + return wrepl_request_finished(req, status); + } + + if (DEBUGLVL(10)) { + DEBUG(10,("Sending WINS packet of length %u\n", + (unsigned)blob.length)); + NDR_PRINT_DEBUG(wrepl_packet, &wrap.packet); + } + + if (wrepl_socket->request_timeout > 0) { + req->te = event_add_timed(wrepl_socket->event.ctx, req, + timeval_current_ofs(wrepl_socket->request_timeout, 0), + wrepl_request_timeout_handler, req); + if (!req->te) return wrepl_request_finished(req, NT_STATUS_NO_MEMORY); + } + + if (ctrl && (ctrl->send_only || ctrl->disconnect_after_send)) { + struct wrepl_send_ctrl_state *s = talloc(blob.data, struct wrepl_send_ctrl_state); + if (!s) return wrepl_request_finished(req, NT_STATUS_NO_MEMORY); + s->ctrl = *ctrl; + s->req = req; + s->wrepl_sock = wrepl_socket; + talloc_set_destructor(s, wrepl_send_ctrl_destructor); + } + + status = packet_send(wrepl_socket->packet, blob); + if (!NT_STATUS_IS_OK(status)) { + return wrepl_request_finished(req, status); + } + + req->trigger = false; + return req; +} + +/* + receive a generic WINS replication reply +*/ +NTSTATUS wrepl_request_recv(struct wrepl_request *req, + TALLOC_CTX *mem_ctx, + struct wrepl_packet **packet) +{ + NTSTATUS status = wrepl_request_wait(req); + if (NT_STATUS_IS_OK(status) && packet) { + *packet = talloc_steal(mem_ctx, req->packet); + } + talloc_free(req); + return status; +} + +/* + a full WINS replication request/response +*/ +NTSTATUS wrepl_request(struct wrepl_socket *wrepl_socket, + TALLOC_CTX *mem_ctx, + struct wrepl_packet *req_packet, + struct wrepl_packet **reply_packet) +{ + struct wrepl_request *req = wrepl_request_send(wrepl_socket, req_packet, NULL); + return wrepl_request_recv(req, mem_ctx, reply_packet); +} + + +/* + setup an association - send +*/ +struct wrepl_request *wrepl_associate_send(struct wrepl_socket *wrepl_socket, + struct wrepl_associate *io) +{ + struct wrepl_packet *packet; + struct wrepl_request *req; + + packet = talloc_zero(wrepl_socket, struct wrepl_packet); + if (packet == NULL) return NULL; + + packet->opcode = WREPL_OPCODE_BITS; + packet->mess_type = WREPL_START_ASSOCIATION; + packet->message.start.minor_version = 2; + packet->message.start.major_version = 5; + + /* + * nt4 uses 41 bytes for the start_association call + * so do it the same and as we don't know th emeanings of this bytes + * we just send zeros and nt4, w2k and w2k3 seems to be happy with this + * + * if we don't do this nt4 uses an old version of the wins replication protocol + * and that would break nt4 <-> samba replication + */ + packet->padding = data_blob_talloc(packet, NULL, 21); + if (packet->padding.data == NULL) { + talloc_free(packet); + return NULL; + } + memset(packet->padding.data, 0, packet->padding.length); + + req = wrepl_request_send(wrepl_socket, packet, NULL); + + talloc_free(packet); + + return req; +} + +/* + setup an association - recv +*/ +NTSTATUS wrepl_associate_recv(struct wrepl_request *req, + struct wrepl_associate *io) +{ + struct wrepl_packet *packet=NULL; + NTSTATUS status; + status = wrepl_request_recv(req, req->wrepl_socket, &packet); + NT_STATUS_NOT_OK_RETURN(status); + if (packet->mess_type != WREPL_START_ASSOCIATION_REPLY) { + status = NT_STATUS_UNEXPECTED_NETWORK_ERROR; + } + if (NT_STATUS_IS_OK(status)) { + io->out.assoc_ctx = packet->message.start_reply.assoc_ctx; + io->out.major_version = packet->message.start_reply.major_version; + } + talloc_free(packet); + return status; +} + +/* + setup an association - sync api +*/ +NTSTATUS wrepl_associate(struct wrepl_socket *wrepl_socket, + struct wrepl_associate *io) +{ + struct wrepl_request *req = wrepl_associate_send(wrepl_socket, io); + return wrepl_associate_recv(req, io); +} + + +/* + stop an association - send +*/ +struct wrepl_request *wrepl_associate_stop_send(struct wrepl_socket *wrepl_socket, + struct wrepl_associate_stop *io) +{ + struct wrepl_packet *packet; + struct wrepl_request *req; + struct wrepl_send_ctrl ctrl; + + packet = talloc_zero(wrepl_socket, struct wrepl_packet); + if (packet == NULL) return NULL; + + packet->opcode = WREPL_OPCODE_BITS; + packet->assoc_ctx = io->in.assoc_ctx; + packet->mess_type = WREPL_STOP_ASSOCIATION; + packet->message.stop.reason = io->in.reason; + + ZERO_STRUCT(ctrl); + if (io->in.reason == 0) { + ctrl.send_only = true; + ctrl.disconnect_after_send = true; + } + + req = wrepl_request_send(wrepl_socket, packet, &ctrl); + + talloc_free(packet); + + return req; +} + +/* + stop an association - recv +*/ +NTSTATUS wrepl_associate_stop_recv(struct wrepl_request *req, + struct wrepl_associate_stop *io) +{ + struct wrepl_packet *packet=NULL; + NTSTATUS status; + status = wrepl_request_recv(req, req->wrepl_socket, &packet); + NT_STATUS_NOT_OK_RETURN(status); + talloc_free(packet); + return status; +} + +/* + setup an association - sync api +*/ +NTSTATUS wrepl_associate_stop(struct wrepl_socket *wrepl_socket, + struct wrepl_associate_stop *io) +{ + struct wrepl_request *req = wrepl_associate_stop_send(wrepl_socket, io); + return wrepl_associate_stop_recv(req, io); +} + +/* + fetch the partner tables - send +*/ +struct wrepl_request *wrepl_pull_table_send(struct wrepl_socket *wrepl_socket, + struct wrepl_pull_table *io) +{ + struct wrepl_packet *packet; + struct wrepl_request *req; + + packet = talloc_zero(wrepl_socket, struct wrepl_packet); + if (packet == NULL) return NULL; + + packet->opcode = WREPL_OPCODE_BITS; + packet->assoc_ctx = io->in.assoc_ctx; + packet->mess_type = WREPL_REPLICATION; + packet->message.replication.command = WREPL_REPL_TABLE_QUERY; + + req = wrepl_request_send(wrepl_socket, packet, NULL); + + talloc_free(packet); + + return req; +} + + +/* + fetch the partner tables - recv +*/ +NTSTATUS wrepl_pull_table_recv(struct wrepl_request *req, + TALLOC_CTX *mem_ctx, + struct wrepl_pull_table *io) +{ + struct wrepl_packet *packet=NULL; + NTSTATUS status; + struct wrepl_table *table; + int i; + + status = wrepl_request_recv(req, req->wrepl_socket, &packet); + NT_STATUS_NOT_OK_RETURN(status); + if (packet->mess_type != WREPL_REPLICATION) { + status = NT_STATUS_NETWORK_ACCESS_DENIED; + } else if (packet->message.replication.command != WREPL_REPL_TABLE_REPLY) { + status = NT_STATUS_UNEXPECTED_NETWORK_ERROR; + } + if (!NT_STATUS_IS_OK(status)) goto failed; + + table = &packet->message.replication.info.table; + io->out.num_partners = table->partner_count; + io->out.partners = talloc_steal(mem_ctx, table->partners); + for (i=0;i<io->out.num_partners;i++) { + talloc_steal(io->out.partners, io->out.partners[i].address); + } + +failed: + talloc_free(packet); + return status; +} + + +/* + fetch the partner table - sync api +*/ +NTSTATUS wrepl_pull_table(struct wrepl_socket *wrepl_socket, + TALLOC_CTX *mem_ctx, + struct wrepl_pull_table *io) +{ + struct wrepl_request *req = wrepl_pull_table_send(wrepl_socket, io); + return wrepl_pull_table_recv(req, mem_ctx, io); +} + + +/* + fetch the names for a WINS partner - send +*/ +struct wrepl_request *wrepl_pull_names_send(struct wrepl_socket *wrepl_socket, + struct wrepl_pull_names *io) +{ + struct wrepl_packet *packet; + struct wrepl_request *req; + + packet = talloc_zero(wrepl_socket, struct wrepl_packet); + if (packet == NULL) return NULL; + + packet->opcode = WREPL_OPCODE_BITS; + packet->assoc_ctx = io->in.assoc_ctx; + packet->mess_type = WREPL_REPLICATION; + packet->message.replication.command = WREPL_REPL_SEND_REQUEST; + packet->message.replication.info.owner = io->in.partner; + + req = wrepl_request_send(wrepl_socket, packet, NULL); + + talloc_free(packet); + + return req; +} + +/* + fetch the names for a WINS partner - recv +*/ +NTSTATUS wrepl_pull_names_recv(struct wrepl_request *req, + TALLOC_CTX *mem_ctx, + struct wrepl_pull_names *io) +{ + struct wrepl_packet *packet=NULL; + NTSTATUS status; + int i; + + status = wrepl_request_recv(req, req->wrepl_socket, &packet); + NT_STATUS_NOT_OK_RETURN(status); + if (packet->mess_type != WREPL_REPLICATION || + packet->message.replication.command != WREPL_REPL_SEND_REPLY) { + status = NT_STATUS_UNEXPECTED_NETWORK_ERROR; + } + if (!NT_STATUS_IS_OK(status)) goto failed; + + io->out.num_names = packet->message.replication.info.reply.num_names; + + io->out.names = talloc_array(packet, struct wrepl_name, io->out.num_names); + if (io->out.names == NULL) goto nomem; + + /* convert the list of names and addresses to a sane format */ + for (i=0;i<io->out.num_names;i++) { + struct wrepl_wins_name *wname = &packet->message.replication.info.reply.names[i]; + struct wrepl_name *name = &io->out.names[i]; + + name->name = *wname->name; + talloc_steal(io->out.names, wname->name); + name->type = WREPL_NAME_TYPE(wname->flags); + name->state = WREPL_NAME_STATE(wname->flags); + name->node = WREPL_NAME_NODE(wname->flags); + name->is_static = WREPL_NAME_IS_STATIC(wname->flags); + name->raw_flags = wname->flags; + name->version_id= wname->id; + name->owner = talloc_strdup(io->out.names, io->in.partner.address); + if (name->owner == NULL) goto nomem; + + /* trying to save 1 or 2 bytes on the wire isn't a good idea */ + if (wname->flags & 2) { + int j; + + name->num_addresses = wname->addresses.addresses.num_ips; + name->addresses = talloc_array(io->out.names, + struct wrepl_address, + name->num_addresses); + if (name->addresses == NULL) goto nomem; + for (j=0;j<name->num_addresses;j++) { + name->addresses[j].owner = + talloc_steal(name->addresses, + wname->addresses.addresses.ips[j].owner); + name->addresses[j].address = + talloc_steal(name->addresses, + wname->addresses.addresses.ips[j].ip); + } + } else { + name->num_addresses = 1; + name->addresses = talloc(io->out.names, struct wrepl_address); + if (name->addresses == NULL) goto nomem; + name->addresses[0].owner = talloc_strdup(name->addresses,io->in.partner.address); + if (name->addresses[0].owner == NULL) goto nomem; + name->addresses[0].address = talloc_steal(name->addresses, + wname->addresses.ip); + } + } + + talloc_steal(mem_ctx, io->out.names); + talloc_free(packet); + return NT_STATUS_OK; +nomem: + status = NT_STATUS_NO_MEMORY; +failed: + talloc_free(packet); + return status; +} + + + +/* + fetch the names for a WINS partner - sync api +*/ +NTSTATUS wrepl_pull_names(struct wrepl_socket *wrepl_socket, + TALLOC_CTX *mem_ctx, + struct wrepl_pull_names *io) +{ + struct wrepl_request *req = wrepl_pull_names_send(wrepl_socket, io); + return wrepl_pull_names_recv(req, mem_ctx, io); +} diff --git a/source4/libcli/wrepl/winsrepl.h b/source4/libcli/wrepl/winsrepl.h new file mode 100644 index 0000000000..ec1fb6bb59 --- /dev/null +++ b/source4/libcli/wrepl/winsrepl.h @@ -0,0 +1,162 @@ +/* + Unix SMB/CIFS implementation. + + structures for WINS replication client library + + Copyright (C) Andrew Tridgell 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 "librpc/gen_ndr/nbt.h" +#include "librpc/gen_ndr/winsrepl.h" + +/* + main context structure for the wins replication client library +*/ +struct wrepl_socket { + struct socket_context *sock; + struct packet_context *packet; + + struct { + struct tevent_context *ctx; + struct tevent_fd *fde; + } event; + + /* a queue of replies waiting to be received */ + struct wrepl_request *recv_queue; + + /* the default timeout for requests, 0 means no timeout */ +#define WREPL_SOCKET_REQUEST_TIMEOUT (60) + uint32_t request_timeout; + + /* counter for request timeouts, after 2 timeouts the socket is marked as dead */ + uint32_t timeout_count; + + /* remember is the socket is dead */ + bool dead; + + /* remember if we need to free the wrepl_socket at the end of wrepl_socket_dead() */ + bool free_skipped; + + struct smb_iconv_convenience *iconv_convenience; +}; + +struct wrepl_send_ctrl { + bool send_only; + bool disconnect_after_send; +}; + +enum wrepl_request_state { + WREPL_REQUEST_INIT = 0, + WREPL_REQUEST_RECV = 1, + WREPL_REQUEST_DONE = 2, + WREPL_REQUEST_ERROR = 3 +}; + +/* + a WINS replication request +*/ +struct wrepl_request { + struct wrepl_request *next, *prev; + struct wrepl_socket *wrepl_socket; + + enum wrepl_request_state state; + bool trigger; + NTSTATUS status; + + struct tevent_timer *te; + + struct wrepl_packet *packet; + + struct { + void (*fn)(struct wrepl_request *); + void *private_data; + } async; +}; + + +/* + setup an association +*/ +struct wrepl_associate { + struct { + uint32_t assoc_ctx; + uint16_t major_version; + } out; +}; + +/* + setup an association +*/ +struct wrepl_associate_stop { + struct { + uint32_t assoc_ctx; + uint32_t reason; + } in; +}; + +/* + pull the partner table +*/ +struct wrepl_pull_table { + struct { + uint32_t assoc_ctx; + } in; + struct { + uint32_t num_partners; + struct wrepl_wins_owner *partners; + } out; +}; + +#define WREPL_NAME_TYPE(flags) (flags & WREPL_FLAGS_RECORD_TYPE) +#define WREPL_NAME_STATE(flags) ((flags & WREPL_FLAGS_RECORD_STATE)>>2) +#define WREPL_NAME_NODE(flags) ((flags & WREPL_FLAGS_NODE_TYPE)>>5) +#define WREPL_NAME_IS_STATIC(flags) ((flags & WREPL_FLAGS_IS_STATIC)?true:false) + +#define WREPL_NAME_FLAGS(type, state, node, is_static) \ + (type | (state << 2) | (node << 5) | \ + (is_static ? WREPL_FLAGS_IS_STATIC : 0)) + +/* + a full pull replication +*/ +struct wrepl_pull_names { + struct { + uint32_t assoc_ctx; + struct wrepl_wins_owner partner; + } in; + struct { + uint32_t num_names; + struct wrepl_name { + struct nbt_name name; + enum wrepl_name_type type; + enum wrepl_name_state state; + enum wrepl_name_node node; + bool is_static; + uint32_t raw_flags; + uint64_t version_id; + const char *owner; + uint32_t num_addresses; + struct wrepl_address { + const char *owner; + const char *address; + } *addresses; + } *names; + } out; +}; + +struct resolve_context; + +#include "libcli/wrepl/winsrepl_proto.h" |