wmi-1.3.16 from opsview.com
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
#################################
|
||||
# Start SUBSYSTEM LIBCLI_AUTH
|
||||
[SUBSYSTEM::LIBCLI_AUTH]
|
||||
PUBLIC_HEADERS = credentials.h
|
||||
PRIVATE_PROTO_HEADER = proto.h
|
||||
OBJ_FILES = credentials.o \
|
||||
session.o \
|
||||
smbencrypt.o \
|
||||
smbdes.o
|
||||
PUBLIC_DEPENDENCIES = \
|
||||
SCHANNELDB MSRPC_PARSE
|
||||
# End SUBSYSTEM LIBCLI_AUTH
|
||||
#################################
|
||||
@@ -0,0 +1,376 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "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)
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#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__ */
|
||||
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "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;
|
||||
}
|
||||
@@ -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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
/* NOTES:
|
||||
|
||||
This code makes no attempt to be fast! In fact, it is a very
|
||||
slow implementation
|
||||
|
||||
This code is NOT a complete DES implementation. It implements only
|
||||
the minimum necessary for SMB authentication, as used by all SMB
|
||||
products (including every copy of Microsoft Windows95 ever sold)
|
||||
|
||||
In particular, it can only do a unchained forward DES pass. This
|
||||
means it is not possible to use this code for encryption/decryption
|
||||
of data, instead it is only useful as a "hash" algorithm.
|
||||
|
||||
There is no entry point into this code that allows normal DES operation.
|
||||
|
||||
I believe this means that this code does not come under ITAR
|
||||
regulations but this is NOT a legal opinion. If you are concerned
|
||||
about the applicability of ITAR regulations to this code then you
|
||||
should confirm it for yourself (and maybe let me know if you come
|
||||
up with a different answer to the one above)
|
||||
*/
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,536 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "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"
|
||||
#include "pstring.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
|
||||
*/
|
||||
|
||||
_PUBLIC_ BOOL E_md4hash(const char *passwd, uint8_t p16[16])
|
||||
{
|
||||
int len;
|
||||
void *wpwd;
|
||||
|
||||
len = push_ucs2_talloc(NULL, &wpwd, passwd);
|
||||
if (len < 2) {
|
||||
/* We don't want to return fixed data, as most callers
|
||||
* don't check */
|
||||
mdfour(p16, passwd, strlen(passwd));
|
||||
return False;
|
||||
}
|
||||
|
||||
len -= 2;
|
||||
mdfour(p16, 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
|
||||
*/
|
||||
|
||||
_PUBLIC_ BOOL E_deshash(const char *passwd, uint8_t p16[16])
|
||||
{
|
||||
BOOL ret = True;
|
||||
fstring dospwd;
|
||||
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 fisrt 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])
|
||||
{
|
||||
void *user;
|
||||
void *domain;
|
||||
size_t user_byte_len;
|
||||
size_t domain_byte_len;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
user_byte_len = push_ucs2_talloc(mem_ctx, &user, user_in);
|
||||
if (user_byte_len == (ssize_t)-1) {
|
||||
DEBUG(0, ("push_uss2_talloc() for user returned -1 (probably talloc() failure)\n"));
|
||||
talloc_free(mem_ctx);
|
||||
return False;
|
||||
}
|
||||
|
||||
domain_byte_len = push_ucs2_talloc(mem_ctx, &domain, domain_in);
|
||||
if (domain_byte_len == (ssize_t)-1) {
|
||||
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(user, user_byte_len, &ctx);
|
||||
hmac_md5_update(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, user, user_byte_len);
|
||||
dump_data(100, 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, uint32_t *new_pw_len,
|
||||
int string_flags)
|
||||
{
|
||||
int byte_len=0;
|
||||
|
||||
/* the incoming buffer can be any alignment. */
|
||||
string_flags |= STR_NOALIGN;
|
||||
|
||||
/*
|
||||
Warning !!! : This function is called from some rpc call.
|
||||
The password IN the buffer may be a UNICODE string.
|
||||
The password IN new_pwrd is an ASCII string
|
||||
If you reuse that code somewhere else check first.
|
||||
*/
|
||||
|
||||
/* The length of the new password is in the last 4 bytes of the data buffer. */
|
||||
|
||||
byte_len = IVAL(in_buffer, 512);
|
||||
|
||||
#ifdef DEBUG_PASSWORD
|
||||
dump_data(100, in_buffer, 516);
|
||||
#endif
|
||||
|
||||
/* Password cannot be longer than the size of the password buffer */
|
||||
if ( (byte_len < 0) || (byte_len > 512)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
/* decode into the return buffer. Buffer length supplied */
|
||||
*new_pw_len = pull_string(new_pwrd, &in_buffer[512 - byte_len], new_pwrd_size,
|
||||
byte_len, string_flags);
|
||||
|
||||
#ifdef DEBUG_PASSWORD
|
||||
DEBUG(100,("decode_pw_buffer: new_pwrd: "));
|
||||
dump_data(100, (const uint8_t *)new_pwrd, *new_pw_len);
|
||||
DEBUG(100,("multibyte len:%d\n", *new_pw_len));
|
||||
DEBUG(100,("original char len:%d\n", byte_len/2));
|
||||
#endif
|
||||
|
||||
return True;
|
||||
}
|
||||
@@ -0,0 +1,717 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
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/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;
|
||||
struct ldap_message *ldap_msg;
|
||||
struct cldap_request *req;
|
||||
|
||||
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;
|
||||
}
|
||||
talloc_steal(tmp_ctx, asn1.data);
|
||||
|
||||
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 */
|
||||
if (!ldap_decode(&asn1, ldap_msg)) {
|
||||
DEBUG(2,("Failed to decode ldap message\n"));
|
||||
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 = asn1;
|
||||
talloc_steal(req, asn1.data);
|
||||
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 event_context *event_ctx,
|
||||
struct timed_event *te, struct timeval t,
|
||||
void *private)
|
||||
{
|
||||
struct cldap_request *req = talloc_get_type(private, 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_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(3,("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);
|
||||
talloc_free(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 event_context *ev, struct fd_event *fde,
|
||||
uint16_t flags, void *private)
|
||||
{
|
||||
struct cldap_socket *cldap = talloc_get_type(private, 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 event_context *event_ctx)
|
||||
{
|
||||
struct cldap_socket *cldap;
|
||||
NTSTATUS status;
|
||||
|
||||
cldap = talloc(mem_ctx, struct cldap_socket);
|
||||
if (cldap == NULL) goto failed;
|
||||
|
||||
if (event_ctx == NULL) {
|
||||
cldap->event_ctx = event_context_init(cldap);
|
||||
} else {
|
||||
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;
|
||||
|
||||
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)
|
||||
{
|
||||
cldap->incoming.handler = handler;
|
||||
cldap->incoming.private = private;
|
||||
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->dest = socket_address_from_strings(req, cldap->sock->backend_name,
|
||||
io->in.dest_address, lp_cldap_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, &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->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, &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, &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;
|
||||
|
||||
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_TIMEOUT) {
|
||||
talloc_free(req);
|
||||
return NT_STATUS_IO_TIMEOUT;
|
||||
}
|
||||
|
||||
ldap_msg = talloc(mem_ctx, struct ldap_message);
|
||||
NT_STATUS_HAVE_NO_MEMORY(ldap_msg);
|
||||
|
||||
if (!ldap_decode(&req->asn1, ldap_msg)) {
|
||||
talloc_free(req);
|
||||
return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
|
||||
}
|
||||
|
||||
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 */
|
||||
if (!ldap_decode(&req->asn1, ldap_msg)) {
|
||||
talloc_free(req);
|
||||
return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
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(filter, "(User=%s)", io->in.user);
|
||||
if (filter == NULL) goto failed;
|
||||
}
|
||||
if (io->in.host) {
|
||||
filter = talloc_asprintf_append(filter, "(Host=%s)", io->in.host);
|
||||
if (filter == NULL) goto failed;
|
||||
}
|
||||
if (io->in.realm) {
|
||||
filter = talloc_asprintf_append(filter, "(DnsDomain=%s)", io->in.realm);
|
||||
if (filter == NULL) goto failed;
|
||||
}
|
||||
if (io->in.acct_control != -1) {
|
||||
filter = talloc_asprintf_append(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(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(filter, "(DomainGuid=%s)",
|
||||
ldap_encode_ndr_GUID(tmp_ctx, &guid));
|
||||
if (filter == NULL) goto failed;
|
||||
}
|
||||
filter = talloc_asprintf_append(filter, ")");
|
||||
if (filter == NULL) goto failed;
|
||||
|
||||
search.in.dest_address = io->in.dest_address;
|
||||
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;
|
||||
DATA_BLOB *data;
|
||||
|
||||
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 = ndr_pull_union_blob(data, mem_ctx, &io->out.netlogon,
|
||||
io->in.version & 0xF,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_nbt_cldap_netlogon);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(2,("cldap failed to parse netlogon response of type 0x%02x\n",
|
||||
SVAL(data->data, 0)));
|
||||
dump_data(10, data->data, data->length);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
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,
|
||||
union nbt_cldap_netlogon *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 = ndr_push_union_blob(&blob, tmp_ctx, netlogon, version & 0xF,
|
||||
(ndr_push_flags_fn_t)ndr_push_nbt_cldap_netlogon);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "libcli/util/asn_1.h"
|
||||
#include "librpc/gen_ndr/nbt.h"
|
||||
|
||||
struct ldap_message;
|
||||
|
||||
enum cldap_request_state {CLDAP_REQUEST_SEND,
|
||||
CLDAP_REQUEST_WAIT,
|
||||
CLDAP_REQUEST_DONE,
|
||||
CLDAP_REQUEST_TIMEOUT};
|
||||
|
||||
/*
|
||||
a cldap request packet
|
||||
*/
|
||||
struct cldap_request {
|
||||
struct cldap_request *next, *prev;
|
||||
|
||||
struct cldap_socket *cldap;
|
||||
|
||||
enum cldap_request_state state;
|
||||
|
||||
/* 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 timed_event *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;
|
||||
} async;
|
||||
};
|
||||
|
||||
/*
|
||||
context structure for operations on cldap packets
|
||||
*/
|
||||
struct cldap_socket {
|
||||
struct socket_context *sock;
|
||||
struct event_context *event_ctx;
|
||||
|
||||
/* the fd event */
|
||||
struct fd_event *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;
|
||||
} incoming;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
a general cldap search request
|
||||
*/
|
||||
struct cldap_search {
|
||||
struct {
|
||||
const char *dest_address;
|
||||
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 event_context *event_ctx);
|
||||
NTSTATUS cldap_set_incoming_handler(struct cldap_socket *cldap,
|
||||
void (*handler)(struct cldap_socket *, struct ldap_message *,
|
||||
struct socket_address *),
|
||||
void *private);
|
||||
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;
|
||||
const char *realm;
|
||||
const char *host;
|
||||
const char *user;
|
||||
const char *domain_guid;
|
||||
const char *domain_sid;
|
||||
int acct_control;
|
||||
uint32_t version;
|
||||
} in;
|
||||
struct {
|
||||
union nbt_cldap_netlogon 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,
|
||||
union nbt_cldap_netlogon *netlogon);
|
||||
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/libcli.h"
|
||||
#include "libcli/raw/libcliraw.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)
|
||||
{
|
||||
struct smbcli_socket *sock;
|
||||
|
||||
sock = smbcli_sock_connect_byname(server, 0, NULL, NULL);
|
||||
|
||||
if (sock == NULL) return False;
|
||||
|
||||
cli->transport = smbcli_transport_init(sock, cli, True);
|
||||
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)
|
||||
{
|
||||
return smb_raw_negotiate(cli->transport, lp_cli_maxprotocol());
|
||||
}
|
||||
|
||||
/* wrapper around smb_raw_sesssetup() */
|
||||
NTSTATUS smbcli_session_setup(struct smbcli_state *cli,
|
||||
struct cli_credentials *credentials)
|
||||
{
|
||||
struct smb_composite_sesssetup setup;
|
||||
NTSTATUS status;
|
||||
|
||||
cli->session = smbcli_session_init(cli->transport, cli, True);
|
||||
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 = lp_workgroup();
|
||||
|
||||
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 *sharename,
|
||||
const char *devtype,
|
||||
struct cli_credentials *credentials,
|
||||
struct event_context *ev)
|
||||
{
|
||||
struct smbcli_tree *tree;
|
||||
NTSTATUS status;
|
||||
|
||||
*ret_cli = NULL;
|
||||
|
||||
status = smbcli_tree_full_connection(parent_ctx,
|
||||
&tree, host, 0, sharename, devtype,
|
||||
credentials, ev);
|
||||
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 && *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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "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 = 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;
|
||||
|
||||
dstate.tree = tree;
|
||||
dstate.total_deleted = 0;
|
||||
dstate.failed = False;
|
||||
|
||||
/* it might be a file */
|
||||
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)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
if (NT_STATUS_IS_ERR(smbcli_rmdir(dstate.tree, dname))) {
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,701 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "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, int *bsize, int *total, int *avail)
|
||||
{
|
||||
union smb_fsinfo fsinfo_parms;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
NTSTATUS status;
|
||||
|
||||
mem_ctx = talloc_init("smbcli_dskattr");
|
||||
|
||||
fsinfo_parms.dskattr.level = RAW_QFS_DSKATTR;
|
||||
status = smb_raw_fsinfo(tree, mem_ctx, &fsinfo_parms);
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
*bsize = fsinfo_parms.dskattr.out.block_size;
|
||||
*total = fsinfo_parms.dskattr.out.units_total;
|
||||
*avail = fsinfo_parms.dskattr.out.units_free;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,354 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/libcli.h"
|
||||
#include "libcli/raw/libcliraw.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,
|
||||
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, union smb_search_data *file)
|
||||
{
|
||||
struct search_private *state = (struct search_private*) private;
|
||||
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_new(state.mem_ctx);
|
||||
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,
|
||||
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, union smb_search_data *file)
|
||||
{
|
||||
struct search_private *state = (struct search_private*) private;
|
||||
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_new(state.mem_ctx);
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.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 = _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 = _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 = _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;
|
||||
}
|
||||
@@ -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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.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;
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
/*
|
||||
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 event_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 = ev;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Some composite helpers that are handy if you write larger composite
|
||||
* functions.
|
||||
*/
|
||||
_PUBLIC_ BOOL composite_is_ok(struct composite_context *ctx)
|
||||
{
|
||||
if (NT_STATUS_IS_OK(ctx->status)) {
|
||||
return True;
|
||||
}
|
||||
ctx->state = COMPOSITE_STATE_ERROR;
|
||||
if (ctx->async.fn != NULL) {
|
||||
ctx->async.fn(ctx);
|
||||
}
|
||||
return False;
|
||||
}
|
||||
|
||||
/*
|
||||
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) 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 event_context *ev, struct timed_event *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)
|
||||
{
|
||||
if (!ctx->used_wait && !ctx->async.fn) {
|
||||
event_add_timed(ctx->event_ctx, ctx, timeval_zero(), composite_trigger, ctx);
|
||||
}
|
||||
ctx->status = status;
|
||||
SMB_ASSERT(!composite_is_ok(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_ 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 = 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 = 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 = 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 = 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 = private_data;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "libcli/raw/interfaces.h"
|
||||
|
||||
/*
|
||||
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 event_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;
|
||||
|
||||
#include "libcli/composite/proto.h"
|
||||
@@ -0,0 +1,31 @@
|
||||
AC_MSG_CHECKING([for Python (libcli_nbt)])
|
||||
|
||||
PYTHON=
|
||||
|
||||
AC_ARG_WITH(python,
|
||||
[ --with-python=PYTHONNAME build Python libraries],
|
||||
[ case "${withval-python}" in
|
||||
yes)
|
||||
PYTHON=python
|
||||
;;
|
||||
no)
|
||||
PYTHON=
|
||||
;;
|
||||
*)
|
||||
PYTHON=${withval-python}
|
||||
;;
|
||||
esac ])
|
||||
|
||||
if test x"$PYTHON" != "x"; then
|
||||
incdir=`python -c 'import sys; print "%s/include/python%d.%d" % (sys.prefix, sys.version_info[[0]], sys.version_info[[1]])'`
|
||||
CPPFLAGS="$CPPFLAGS -I $incdir"
|
||||
fi
|
||||
|
||||
if test x"$PYTHON" != "x"; then
|
||||
AC_MSG_RESULT([${withval-python}])
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
SMB_ENABLE(swig_libcli_nbt, NO)
|
||||
fi
|
||||
|
||||
AC_SUBST(PYTHON)
|
||||
@@ -0,0 +1,152 @@
|
||||
include auth/config.mk
|
||||
include ldap/config.mk
|
||||
include security/config.mk
|
||||
|
||||
[SUBSYSTEM::LIBSAMBA-ERRORS]
|
||||
PUBLIC_PROTO_HEADER = util/proto.h
|
||||
PUBLIC_HEADERS = util/error.h util/nterr.h util/doserr.h util/nt_status.h
|
||||
OBJ_FILES = util/doserr.o \
|
||||
util/errormap.o \
|
||||
util/clierror.o \
|
||||
util/nterr.o \
|
||||
|
||||
[SUBSYSTEM::ASN1_UTIL]
|
||||
PUBLIC_PROTO_HEADER = util/asn1_proto.h
|
||||
PUBLIC_HEADERS = util/asn_1.h
|
||||
OBJ_FILES = util/asn1.o
|
||||
|
||||
[SUBSYSTEM::LIBCLI_LSA]
|
||||
PRIVATE_PROTO_HEADER = util/clilsa.h
|
||||
OBJ_FILES = util/clilsa.o
|
||||
PUBLIC_DEPENDENCIES = RPC_NDR_LSA
|
||||
|
||||
[SUBSYSTEM::LIBCLI_COMPOSITE]
|
||||
PRIVATE_PROTO_HEADER = composite/proto.h
|
||||
OBJ_FILES = \
|
||||
composite/composite.o
|
||||
PUBLIC_DEPENDENCIES = LIBEVENTS
|
||||
|
||||
[SUBSYSTEM::LIBCLI_SMB_COMPOSITE]
|
||||
PRIVATE_PROTO_HEADER = smb_composite/proto.h
|
||||
OBJ_FILES = \
|
||||
smb_composite/loadfile.o \
|
||||
smb_composite/savefile.o \
|
||||
smb_composite/connect.o \
|
||||
smb_composite/sesssetup.o \
|
||||
smb_composite/fetchfile.o \
|
||||
smb_composite/appendacl.o \
|
||||
smb_composite/fsinfo.o
|
||||
PUBLIC_DEPENDENCIES = LIBCLI_COMPOSITE CREDENTIALS
|
||||
|
||||
[SUBSYSTEM::NDR_NBT_BUF]
|
||||
PRIVATE_PROTO_HEADER = nbt/nbtname.h
|
||||
OBJ_FILES = nbt/nbtname.o
|
||||
|
||||
[SUBSYSTEM::LIBCLI_NBT]
|
||||
#VERSION = 0.0.1
|
||||
#SO_VERSION = 0
|
||||
#DESCRIPTION = NetBios over TCP/IP client library
|
||||
PRIVATE_PROTO_HEADER = nbt/nbt_proto.h
|
||||
OBJ_FILES = \
|
||||
nbt/nbtsocket.o \
|
||||
nbt/namequery.o \
|
||||
nbt/nameregister.o \
|
||||
nbt/namerefresh.o \
|
||||
nbt/namerelease.o
|
||||
PUBLIC_DEPENDENCIES = LIBNDR NDR_NBT LIBCLI_COMPOSITE LIBEVENTS \
|
||||
NDR_SECURITY samba-socket LIBSAMBA-UTIL
|
||||
|
||||
[LIBRARY::swig_libcli_nbt]
|
||||
LIBRARY_REALNAME = swig/_libcli_nbt.$(SHLIBEXT)
|
||||
OBJ_FILES = swig/libcli_nbt_wrap.o
|
||||
PUBLIC_DEPENDENCIES = LIBCLI_NBT DYNCONFIG LIBSAMBA-CONFIG
|
||||
|
||||
[SUBSYSTEM::LIBCLI_DGRAM]
|
||||
OBJ_FILES = \
|
||||
dgram/dgramsocket.o \
|
||||
dgram/mailslot.o \
|
||||
dgram/netlogon.o \
|
||||
dgram/ntlogon.o \
|
||||
dgram/browse.o
|
||||
PUBLIC_DEPENDENCIES = LIBCLI_NBT
|
||||
|
||||
[LIBRARY::LIBCLI_CLDAP]
|
||||
VERSION = 0.0.1
|
||||
SO_VERSION = 0
|
||||
DESCRIPTION = CLDAP client library
|
||||
OBJ_FILES = cldap/cldap.o
|
||||
PUBLIC_HEADERS = cldap/cldap.h
|
||||
PUBLIC_DEPENDENCIES = LIBCLI_LDAP
|
||||
PRIVATE_DEPENDENCIES = LIBSAMBA-UTIL ldb
|
||||
|
||||
[LIBRARY::LIBCLI_WREPL]
|
||||
PRIVATE_PROTO_HEADER = wrepl/winsrepl_proto.h
|
||||
VERSION = 0.0.1
|
||||
SO_VERSION = 0
|
||||
DESCRIPTION = WINS Replication client library
|
||||
OBJ_FILES = \
|
||||
wrepl/winsrepl.o
|
||||
PUBLIC_DEPENDENCIES = NDR_WINSREPL samba-socket LIBCLI_RESOLVE LIBEVENTS LIBPACKET
|
||||
|
||||
[SUBSYSTEM::LIBCLI_RESOLVE]
|
||||
PRIVATE_PROTO_HEADER = resolve/proto.h
|
||||
OBJ_FILES = \
|
||||
resolve/resolve.o \
|
||||
resolve/bcast.o \
|
||||
resolve/nbtlist.o \
|
||||
resolve/wins.o \
|
||||
resolve/host.o
|
||||
PUBLIC_DEPENDENCIES = LIBNETIF
|
||||
PRIVATE_DEPENDENCIES = LIBCLI_NBT
|
||||
|
||||
[SUBSYSTEM::LIBCLI_FINDDCS]
|
||||
PRIVATE_PROTO_HEADER = finddcs.h
|
||||
OBJ_FILES = \
|
||||
finddcs.o
|
||||
PUBLIC_DEPENDENCIES = LIBCLI_NBT MESSAGING
|
||||
|
||||
[LIBRARY::LIBCLI_SMB]
|
||||
PUBLIC_HEADERS = libcli.h
|
||||
PUBLIC_PROTO_HEADER = libcli_proto.h
|
||||
VERSION = 0.0.1
|
||||
SO_VERSION = 0
|
||||
DESCRIPTION = SMB/CIFS client library
|
||||
OBJ_FILES = clireadwrite.o \
|
||||
cliconnect.o \
|
||||
clifile.o \
|
||||
clilist.o \
|
||||
clitrans2.o \
|
||||
climessage.o \
|
||||
clideltree.o
|
||||
PUBLIC_DEPENDENCIES = LIBCLI_RAW LIBSAMBA-ERRORS LIBCLI_AUTH \
|
||||
LIBCLI_SMB_COMPOSITE LIBCLI_NBT LIBSECURITY LIBCLI_RESOLVE \
|
||||
LIBCLI_DGRAM LIBCLI_SMB2 LIBCLI_FINDDCS samba-socket
|
||||
|
||||
[SUBSYSTEM::LIBCLI_RAW]
|
||||
PRIVATE_PROTO_HEADER = raw/raw_proto.h
|
||||
PRIVATE_DEPENDENCIES = LIBCLI_COMPOSITE
|
||||
PUBLIC_DEPENDENCIES = samba-socket LIBPACKET gensec LIBCRYPTO
|
||||
LDFLAGS = $(SUBSYSTEM_LIBCLI_SMB_COMPOSITE_OUTPUT)
|
||||
OBJ_FILES = raw/rawfile.o \
|
||||
raw/smb_signing.o \
|
||||
raw/clisocket.o \
|
||||
raw/clitransport.o \
|
||||
raw/clisession.o \
|
||||
raw/clitree.o \
|
||||
raw/rawrequest.o \
|
||||
raw/rawreadwrite.o \
|
||||
raw/rawsearch.o \
|
||||
raw/rawsetfileinfo.o \
|
||||
raw/raweas.o \
|
||||
raw/rawtrans.o \
|
||||
raw/clioplock.o \
|
||||
raw/rawnegotiate.o \
|
||||
raw/rawfsinfo.o \
|
||||
raw/rawfileinfo.o \
|
||||
raw/rawnotify.o \
|
||||
raw/rawioctl.o \
|
||||
raw/rawacl.o \
|
||||
raw/rawdate.o \
|
||||
raw/rawlpq.o
|
||||
|
||||
include smb2/config.mk
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/dgram/libdgram.h"
|
||||
#include "lib/socket/socket.h"
|
||||
#include "libcli/resolve/resolve.h"
|
||||
#include "librpc/gen_ndr/ndr_nbt.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;
|
||||
DATA_BLOB blob;
|
||||
TALLOC_CTX *tmp_ctx = talloc_new(dgmsock);
|
||||
|
||||
status = ndr_push_struct_blob(&blob, tmp_ctx, request,
|
||||
(ndr_push_flags_fn_t)ndr_push_nbt_browse_packet);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return status;
|
||||
}
|
||||
|
||||
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,
|
||||
struct nbt_browse_packet *reply)
|
||||
{
|
||||
NTSTATUS status;
|
||||
DATA_BLOB blob;
|
||||
TALLOC_CTX *tmp_ctx = talloc_new(dgmsock);
|
||||
struct nbt_name myname;
|
||||
struct socket_address *dest;
|
||||
|
||||
status = ndr_push_struct_blob(&blob, tmp_ctx, reply,
|
||||
(ndr_push_flags_fn_t)ndr_push_nbt_browse_packet);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return status;
|
||||
}
|
||||
|
||||
make_nbt_name_client(&myname, lp_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);
|
||||
NTSTATUS status;
|
||||
|
||||
status = ndr_pull_struct_blob(&data, mem_ctx, pkt,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_nbt_browse_packet);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("Failed to parse browse packet of length %d\n",
|
||||
(int)data.length));
|
||||
if (DEBUGLVL(10)) {
|
||||
file_save("browse.dat", data.data, data.length);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "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;
|
||||
|
||||
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 */
|
||||
status = ndr_pull_struct_blob(&blob, packet, packet,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_nbt_dgram_packet);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
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 event_context *ev, struct fd_event *fde,
|
||||
uint16_t flags, void *private)
|
||||
{
|
||||
struct nbt_dgram_socket *dgmsock = talloc_get_type(private,
|
||||
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 event_context *event_ctx)
|
||||
{
|
||||
struct nbt_dgram_socket *dgmsock;
|
||||
NTSTATUS status;
|
||||
|
||||
dgmsock = talloc(mem_ctx, struct nbt_dgram_socket);
|
||||
if (dgmsock == NULL) goto failed;
|
||||
|
||||
if (event_ctx == NULL) {
|
||||
dgmsock->event_ctx = event_context_init(dgmsock);
|
||||
} else {
|
||||
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;
|
||||
|
||||
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)
|
||||
{
|
||||
dgmsock->incoming.handler = handler;
|
||||
dgmsock->incoming.private = private;
|
||||
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;
|
||||
|
||||
req = talloc(dgmsock, struct nbt_dgram_request);
|
||||
if (req == NULL) goto failed;
|
||||
|
||||
req->dest = dest;
|
||||
if (talloc_reference(req, dest) == NULL) goto failed;
|
||||
|
||||
status = ndr_push_struct_blob(&req->encoded, req, packet,
|
||||
(ndr_push_flags_fn_t)ndr_push_nbt_dgram_packet);
|
||||
if (!NT_STATUS_IS_OK(status)) 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;
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "librpc/gen_ndr/nbt.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 event_context *event_ctx;
|
||||
|
||||
/* the fd event */
|
||||
struct fd_event *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;
|
||||
} 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;
|
||||
};
|
||||
|
||||
|
||||
/* 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);
|
||||
struct nbt_dgram_socket *nbt_dgram_socket_init(TALLOC_CTX *mem_ctx,
|
||||
struct event_context *event_ctx);
|
||||
|
||||
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);
|
||||
struct dgram_mailslot_handler *dgram_mailslot_temp(struct nbt_dgram_socket *dgmsock,
|
||||
const char *mailslot_name,
|
||||
dgram_mailslot_handler_t handler,
|
||||
void *private);
|
||||
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,
|
||||
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 *mailslot_name,
|
||||
struct nbt_netlogon_packet *reply);
|
||||
NTSTATUS dgram_mailslot_netlogon_parse(struct dgram_mailslot_handler *dgmslot,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct nbt_dgram_packet *dgram,
|
||||
struct nbt_netlogon_packet *netlogon);
|
||||
|
||||
NTSTATUS dgram_mailslot_ntlogon_send(struct nbt_dgram_socket *dgmsock,
|
||||
enum dgram_msg_type msg_type,
|
||||
struct nbt_name *dest_name,
|
||||
struct socket_address *dest,
|
||||
struct nbt_name *src_name,
|
||||
struct nbt_ntlogon_packet *request);
|
||||
NTSTATUS dgram_mailslot_ntlogon_reply(struct nbt_dgram_socket *dgmsock,
|
||||
struct nbt_dgram_packet *request,
|
||||
const char *mailslot_name,
|
||||
struct nbt_ntlogon_packet *reply);
|
||||
NTSTATUS dgram_mailslot_ntlogon_parse(struct dgram_mailslot_handler *dgmslot,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct nbt_dgram_packet *dgram,
|
||||
struct nbt_ntlogon_packet *ntlogon);
|
||||
|
||||
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,
|
||||
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);
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
This 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)
|
||||
{
|
||||
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 = private;
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
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 socket_address *dest;
|
||||
struct dgram_message *msg;
|
||||
struct dgram_smb_packet *smb;
|
||||
struct smb_trans_body *trans;
|
||||
struct socket_address *src;
|
||||
NTSTATUS status;
|
||||
|
||||
if (_dest->port == 0) {
|
||||
dest = socket_address_from_strings(tmp_ctx, _dest->family,
|
||||
_dest->addr, lp_dgram_port());
|
||||
} else {
|
||||
dest = _dest;
|
||||
}
|
||||
if (!dest) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "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,
|
||||
struct nbt_name *src_name,
|
||||
struct nbt_netlogon_packet *request)
|
||||
{
|
||||
NTSTATUS status;
|
||||
DATA_BLOB blob;
|
||||
TALLOC_CTX *tmp_ctx = talloc_new(dgmsock);
|
||||
|
||||
status = ndr_push_struct_blob(&blob, tmp_ctx, request,
|
||||
(ndr_push_flags_fn_t)ndr_push_nbt_netlogon_packet);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
status = dgram_mailslot_send(dgmsock, DGRAM_DIRECT_UNIQUE,
|
||||
NBT_MAILSLOT_NETLOGON,
|
||||
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 *mailslot_name,
|
||||
struct nbt_netlogon_packet *reply)
|
||||
{
|
||||
NTSTATUS status;
|
||||
DATA_BLOB blob;
|
||||
TALLOC_CTX *tmp_ctx = talloc_new(dgmsock);
|
||||
struct nbt_name myname;
|
||||
struct socket_address *dest;
|
||||
|
||||
status = ndr_push_struct_blob(&blob, tmp_ctx, reply,
|
||||
(ndr_push_flags_fn_t)ndr_push_nbt_netlogon_packet);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return status;
|
||||
}
|
||||
|
||||
make_nbt_name_client(&myname, lp_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(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);
|
||||
NTSTATUS status;
|
||||
|
||||
status = ndr_pull_struct_blob(&data, mem_ctx, netlogon,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_nbt_netlogon_packet);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("Failed to parse netlogon packet of length %d\n",
|
||||
(int)data.length));
|
||||
if (DEBUGLVL(10)) {
|
||||
file_save("netlogon.dat", data.data, data.length);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
handling for ntlogon 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/dgram/libdgram.h"
|
||||
#include "lib/socket/socket.h"
|
||||
#include "libcli/resolve/resolve.h"
|
||||
#include "librpc/gen_ndr/ndr_nbt.h"
|
||||
|
||||
/*
|
||||
send a ntlogon mailslot request
|
||||
*/
|
||||
NTSTATUS dgram_mailslot_ntlogon_send(struct nbt_dgram_socket *dgmsock,
|
||||
enum dgram_msg_type msg_type,
|
||||
struct nbt_name *dest_name,
|
||||
struct socket_address *dest,
|
||||
struct nbt_name *src_name,
|
||||
struct nbt_ntlogon_packet *request)
|
||||
{
|
||||
NTSTATUS status;
|
||||
DATA_BLOB blob;
|
||||
TALLOC_CTX *tmp_ctx = talloc_new(dgmsock);
|
||||
|
||||
status = ndr_push_struct_blob(&blob, tmp_ctx, request,
|
||||
(ndr_push_flags_fn_t)ndr_push_nbt_ntlogon_packet);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
status = dgram_mailslot_send(dgmsock, msg_type,
|
||||
NBT_MAILSLOT_NTLOGON,
|
||||
dest_name, dest,
|
||||
src_name, &blob);
|
||||
talloc_free(tmp_ctx);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
send a ntlogon mailslot reply
|
||||
*/
|
||||
NTSTATUS dgram_mailslot_ntlogon_reply(struct nbt_dgram_socket *dgmsock,
|
||||
struct nbt_dgram_packet *request,
|
||||
const char *mailslot_name,
|
||||
struct nbt_ntlogon_packet *reply)
|
||||
{
|
||||
NTSTATUS status;
|
||||
DATA_BLOB blob;
|
||||
TALLOC_CTX *tmp_ctx = talloc_new(dgmsock);
|
||||
struct nbt_name myname;
|
||||
struct socket_address *dest;
|
||||
|
||||
status = ndr_push_struct_blob(&blob, tmp_ctx, reply,
|
||||
(ndr_push_flags_fn_t)ndr_push_nbt_ntlogon_packet);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return status;
|
||||
}
|
||||
|
||||
make_nbt_name_client(&myname, lp_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 ntlogon response. The packet must be a valid mailslot packet
|
||||
*/
|
||||
NTSTATUS dgram_mailslot_ntlogon_parse(struct dgram_mailslot_handler *dgmslot,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct nbt_dgram_packet *dgram,
|
||||
struct nbt_ntlogon_packet *ntlogon)
|
||||
{
|
||||
DATA_BLOB data = dgram_mailslot_data(dgram);
|
||||
NTSTATUS status;
|
||||
|
||||
status = ndr_pull_struct_blob(&data, mem_ctx, ntlogon,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_nbt_ntlogon_packet);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(0,("Failed to parse ntlogon packet of length %d\n",
|
||||
(int)data.length));
|
||||
if (DEBUGLVL(10)) {
|
||||
file_save("ntlogon.dat", data.data, data.length);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "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"
|
||||
|
||||
struct finddcs_state {
|
||||
struct composite_context *ctx;
|
||||
struct messaging_context *msg_ctx;
|
||||
|
||||
const char *domain_name;
|
||||
struct dom_sid *domain_sid;
|
||||
|
||||
struct nbtd_getdcname r;
|
||||
struct nbt_name_status node_status;
|
||||
|
||||
int num_dcs;
|
||||
struct nbt_dc_name *dcs;
|
||||
};
|
||||
|
||||
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 *domain_name,
|
||||
int name_type,
|
||||
struct dom_sid *domain_sid,
|
||||
const char **methods,
|
||||
struct event_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->domain_name = talloc_strdup(state, domain_name);
|
||||
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(&name, event_ctx,
|
||||
methods);
|
||||
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;
|
||||
uint32_t *nbt_servers;
|
||||
const char *address;
|
||||
|
||||
state->ctx->status = resolve_name_recv(ctx, state, &address);
|
||||
if (!composite_is_ok(state->ctx)) return;
|
||||
|
||||
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, "nbt_server");
|
||||
if ((nbt_servers == NULL) || (nbt_servers[0] == 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 = lp_netbios_name();
|
||||
state->r.in.my_accountname = talloc_asprintf(state, "%s$",
|
||||
lp_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],
|
||||
&dcerpc_table_irpc, DCERPC_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, 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 availible (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.timeout = 1;
|
||||
state->node_status.in.retries = 2;
|
||||
|
||||
nbtsock = nbt_name_socket_init(state, state->ctx->event_ctx);
|
||||
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, 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 *domain_name, int name_type,
|
||||
struct dom_sid *domain_sid,
|
||||
const char **methods,
|
||||
struct event_context *event_ctx,
|
||||
struct messaging_context *msg_ctx,
|
||||
int *num_dcs, struct nbt_dc_name **dcs)
|
||||
{
|
||||
struct composite_context *c = finddcs_send(mem_ctx,
|
||||
domain_name, name_type,
|
||||
domain_sid, methods,
|
||||
event_ctx, msg_ctx);
|
||||
return finddcs_recv(c, mem_ctx, num_dcs, dcs);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#################################
|
||||
# Start SUBSYSTEM LIBCLI_LDAP
|
||||
[SUBSYSTEM::LIBCLI_LDAP]
|
||||
PUBLIC_PROTO_HEADER = ldap_proto.h
|
||||
PUBLIC_HEADERS = ldap.h
|
||||
OBJ_FILES = ldap.o \
|
||||
ldap_client.o \
|
||||
ldap_bind.o \
|
||||
ldap_msg.o \
|
||||
ldap_ndr.o \
|
||||
ldap_ildap.o \
|
||||
ldap_controls.o
|
||||
PUBLIC_DEPENDENCIES = LIBSAMBA-ERRORS LIBEVENTS LIBPACKET
|
||||
PRIVATE_DEPENDENCIES = LIBCLI_COMPOSITE samba-socket LIBCLI_RESOLVE NDR_SAMR LIBTLS ASN1_UTIL GENSEC_SOCKET
|
||||
#PRIVATE_DEPENDENCIES = gensec
|
||||
# End SUBSYSTEM LIBCLI_LDAP
|
||||
#################################
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _SMB_LDAP_H
|
||||
#define _SMB_LDAP_H
|
||||
|
||||
#include "lib/ldb/include/ldb.h"
|
||||
|
||||
enum ldap_request_tag {
|
||||
LDAP_TAG_BindRequest = 0,
|
||||
LDAP_TAG_BindResponse = 1,
|
||||
LDAP_TAG_UnbindRequest = 2,
|
||||
LDAP_TAG_SearchRequest = 3,
|
||||
LDAP_TAG_SearchResultEntry = 4,
|
||||
LDAP_TAG_SearchResultDone = 5,
|
||||
LDAP_TAG_ModifyRequest = 6,
|
||||
LDAP_TAG_ModifyResponse = 7,
|
||||
LDAP_TAG_AddRequest = 8,
|
||||
LDAP_TAG_AddResponse = 9,
|
||||
LDAP_TAG_DelRequest = 10,
|
||||
LDAP_TAG_DelResponse = 11,
|
||||
LDAP_TAG_ModifyDNRequest = 12,
|
||||
LDAP_TAG_ModifyDNResponse = 13,
|
||||
LDAP_TAG_CompareRequest = 14,
|
||||
LDAP_TAG_CompareResponse = 15,
|
||||
LDAP_TAG_AbandonRequest = 16,
|
||||
LDAP_TAG_SearchResultReference = 19,
|
||||
LDAP_TAG_ExtendedRequest = 23,
|
||||
LDAP_TAG_ExtendedResponse = 24
|
||||
};
|
||||
|
||||
enum ldap_auth_mechanism {
|
||||
LDAP_AUTH_MECH_SIMPLE = 0,
|
||||
LDAP_AUTH_MECH_SASL = 3
|
||||
};
|
||||
|
||||
enum ldap_result_code {
|
||||
LDAP_SUCCESS = 0,
|
||||
LDAP_OPERATIONS_ERROR = 1,
|
||||
LDAP_PROTOCOL_ERROR = 2,
|
||||
LDAP_TIME_LIMIT_EXCEEDED = 3,
|
||||
LDAP_SIZE_LIMIT_EXCEEDED = 4,
|
||||
LDAP_COMPARE_FALSE = 5,
|
||||
LDAP_COMPARE_TRUE = 6,
|
||||
LDAP_AUTH_METHOD_NOT_SUPPORTED = 7,
|
||||
LDAP_STRONG_AUTH_REQUIRED = 8,
|
||||
LDAP_REFERRAL = 10,
|
||||
LDAP_ADMIN_LIMIT_EXCEEDED = 11,
|
||||
LDAP_UNAVAILABLE_CRITICAL_EXTENSION = 12,
|
||||
LDAP_CONFIDENTIALITY_REQUIRED = 13,
|
||||
LDAP_SASL_BIND_IN_PROGRESS = 14,
|
||||
LDAP_NO_SUCH_ATTRIBUTE = 16,
|
||||
LDAP_UNDEFINED_ATTRIBUTE_TYPE = 17,
|
||||
LDAP_INAPPROPRIATE_MATCHING = 18,
|
||||
LDAP_CONSTRAINT_VIOLATION = 19,
|
||||
LDAP_ATTRIBUTE_OR_VALUE_EXISTS = 20,
|
||||
LDAP_INVALID_ATTRIBUTE_SYNTAX = 21,
|
||||
LDAP_NO_SUCH_OBJECT = 32,
|
||||
LDAP_ALIAS_PROBLEM = 33,
|
||||
LDAP_INVALID_DN_SYNTAX = 34,
|
||||
LDAP_ALIAS_DEREFERENCING_PROBLEM = 36,
|
||||
LDAP_INAPPROPRIATE_AUTHENTICATION = 48,
|
||||
LDAP_INVALID_CREDENTIALS = 49,
|
||||
LDAP_INSUFFICIENT_ACCESS_RIGHTs = 50,
|
||||
LDAP_BUSY = 51,
|
||||
LDAP_UNAVAILABLE = 52,
|
||||
LDAP_UNWILLING_TO_PERFORM = 53,
|
||||
LDAP_LOOP_DETECT = 54,
|
||||
LDAP_NAMING_VIOLATION = 64,
|
||||
LDAP_OBJECT_CLASS_VIOLATION = 65,
|
||||
LDAP_NOT_ALLOWED_ON_NON_LEAF = 66,
|
||||
LDAP_NOT_ALLOWED_ON_RDN = 67,
|
||||
LDAP_ENTRY_ALREADY_EXISTS = 68,
|
||||
LDAP_OBJECT_CLASS_MODS_PROHIBITED = 69,
|
||||
LDAP_AFFECTS_MULTIPLE_DSAS = 71,
|
||||
LDAP_OTHER = 80
|
||||
};
|
||||
|
||||
struct ldap_Result {
|
||||
int resultcode;
|
||||
const char *dn;
|
||||
const char *errormessage;
|
||||
const char *referral;
|
||||
};
|
||||
|
||||
struct ldap_BindRequest {
|
||||
int version;
|
||||
const char *dn;
|
||||
enum ldap_auth_mechanism mechanism;
|
||||
union {
|
||||
const char *password;
|
||||
struct {
|
||||
const char *mechanism;
|
||||
DATA_BLOB *secblob;/* optional */
|
||||
} SASL;
|
||||
} creds;
|
||||
};
|
||||
|
||||
struct ldap_BindResponse {
|
||||
struct ldap_Result response;
|
||||
union {
|
||||
DATA_BLOB *secblob;/* optional */
|
||||
} SASL;
|
||||
};
|
||||
|
||||
struct ldap_UnbindRequest {
|
||||
uint8_t __dummy;
|
||||
};
|
||||
|
||||
enum ldap_scope {
|
||||
LDAP_SEARCH_SCOPE_BASE = 0,
|
||||
LDAP_SEARCH_SCOPE_SINGLE = 1,
|
||||
LDAP_SEARCH_SCOPE_SUB = 2
|
||||
};
|
||||
|
||||
enum ldap_deref {
|
||||
LDAP_DEREFERENCE_NEVER = 0,
|
||||
LDAP_DEREFERENCE_IN_SEARCHING = 1,
|
||||
LDAP_DEREFERENCE_FINDING_BASE = 2,
|
||||
LDAP_DEREFERENCE_ALWAYS
|
||||
};
|
||||
|
||||
struct ldap_SearchRequest {
|
||||
const char *basedn;
|
||||
enum ldap_scope scope;
|
||||
enum ldap_deref deref;
|
||||
uint32_t timelimit;
|
||||
uint32_t sizelimit;
|
||||
BOOL attributesonly;
|
||||
struct ldb_parse_tree *tree;
|
||||
int num_attributes;
|
||||
const char **attributes;
|
||||
};
|
||||
|
||||
struct ldap_SearchResEntry {
|
||||
const char *dn;
|
||||
int num_attributes;
|
||||
struct ldb_message_element *attributes;
|
||||
};
|
||||
|
||||
struct ldap_SearchResRef {
|
||||
const char *referral;
|
||||
};
|
||||
|
||||
enum ldap_modify_type {
|
||||
LDAP_MODIFY_NONE = -1,
|
||||
LDAP_MODIFY_ADD = 0,
|
||||
LDAP_MODIFY_DELETE = 1,
|
||||
LDAP_MODIFY_REPLACE = 2
|
||||
};
|
||||
|
||||
struct ldap_mod {
|
||||
enum ldap_modify_type type;
|
||||
struct ldb_message_element attrib;
|
||||
};
|
||||
|
||||
struct ldap_ModifyRequest {
|
||||
const char *dn;
|
||||
int num_mods;
|
||||
struct ldap_mod *mods;
|
||||
};
|
||||
|
||||
struct ldap_AddRequest {
|
||||
const char *dn;
|
||||
int num_attributes;
|
||||
struct ldb_message_element *attributes;
|
||||
};
|
||||
|
||||
struct ldap_DelRequest {
|
||||
const char *dn;
|
||||
};
|
||||
|
||||
struct ldap_ModifyDNRequest {
|
||||
const char *dn;
|
||||
const char *newrdn;
|
||||
BOOL deleteolddn;
|
||||
const char *newsuperior;/* optional */
|
||||
};
|
||||
|
||||
struct ldap_CompareRequest {
|
||||
const char *dn;
|
||||
const char *attribute;
|
||||
DATA_BLOB value;
|
||||
};
|
||||
|
||||
struct ldap_AbandonRequest {
|
||||
uint32_t messageid;
|
||||
};
|
||||
|
||||
struct ldap_ExtendedRequest {
|
||||
const char *oid;
|
||||
DATA_BLOB *value;/* optional */
|
||||
};
|
||||
|
||||
struct ldap_ExtendedResponse {
|
||||
struct ldap_Result response;
|
||||
const char *oid;/* optional */
|
||||
DATA_BLOB *value;/* optional */
|
||||
};
|
||||
|
||||
union ldap_Request {
|
||||
struct ldap_Result GeneralResult;
|
||||
struct ldap_BindRequest BindRequest;
|
||||
struct ldap_BindResponse BindResponse;
|
||||
struct ldap_UnbindRequest UnbindRequest;
|
||||
struct ldap_SearchRequest SearchRequest;
|
||||
struct ldap_SearchResEntry SearchResultEntry;
|
||||
struct ldap_Result SearchResultDone;
|
||||
struct ldap_SearchResRef SearchResultReference;
|
||||
struct ldap_ModifyRequest ModifyRequest;
|
||||
struct ldap_Result ModifyResponse;
|
||||
struct ldap_AddRequest AddRequest;
|
||||
struct ldap_Result AddResponse;
|
||||
struct ldap_DelRequest DelRequest;
|
||||
struct ldap_Result DelResponse;
|
||||
struct ldap_ModifyDNRequest ModifyDNRequest;
|
||||
struct ldap_Result ModifyDNResponse;
|
||||
struct ldap_CompareRequest CompareRequest;
|
||||
struct ldap_Result CompareResponse;
|
||||
struct ldap_AbandonRequest AbandonRequest;
|
||||
struct ldap_ExtendedRequest ExtendedRequest;
|
||||
struct ldap_ExtendedResponse ExtendedResponse;
|
||||
};
|
||||
|
||||
struct ldap_message {
|
||||
int messageid;
|
||||
enum ldap_request_tag type;
|
||||
union ldap_Request r;
|
||||
struct ldb_control **controls;
|
||||
};
|
||||
|
||||
struct event_context;
|
||||
struct cli_credentials;
|
||||
struct dom_sid;
|
||||
struct asn1_data;
|
||||
|
||||
#include "libcli/ldap/ldap_proto.h"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,400 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/ldap/ldap.h"
|
||||
#include "libcli/ldap/ldap_client.h"
|
||||
#include "lib/tls/tls.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
#include "auth/gensec/socket.h"
|
||||
#include "lib/stream/packet.h"
|
||||
|
||||
struct ldap_simple_creds {
|
||||
const char *dn;
|
||||
const char *pw;
|
||||
};
|
||||
|
||||
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);
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
NTSTATUS ldap_bind_sasl(struct ldap_connection *conn, struct cli_credentials *creds)
|
||||
{
|
||||
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;
|
||||
|
||||
static const char *supported_sasl_mech_attrs[] = {
|
||||
"supportedSASLMechanisms",
|
||||
NULL
|
||||
};
|
||||
|
||||
status = gensec_client_start(conn, &conn->gensec, NULL);
|
||||
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 */
|
||||
if (!tls_enabled(conn->sock)) {
|
||||
gensec_want_feature(conn->gensec, 0 | GENSEC_FEATURE_SIGN | GENSEC_FEATURE_SEAL);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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\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->sock,
|
||||
conn->event.event_ctx,
|
||||
ldap_read_io_handler,
|
||||
conn,
|
||||
&sasl_socket);
|
||||
if (!NT_STATUS_IS_OK(status)) goto failed;
|
||||
|
||||
talloc_steal(conn->sock, sasl_socket);
|
||||
talloc_unlink(conn, conn->sock);
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,782 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/util/asn_1.h"
|
||||
#include "lib/util/dlinklist.h"
|
||||
#include "lib/events/events.h"
|
||||
#include "lib/socket/socket.h"
|
||||
#include "libcli/ldap/ldap.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"
|
||||
|
||||
|
||||
/*
|
||||
create a new ldap_connection stucture. The event context is optional
|
||||
*/
|
||||
struct ldap_connection *ldap4_new_connection(TALLOC_CTX *mem_ctx,
|
||||
struct event_context *ev)
|
||||
{
|
||||
struct ldap_connection *conn;
|
||||
|
||||
conn = talloc_zero(mem_ctx, struct ldap_connection);
|
||||
if (conn == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ev == NULL) {
|
||||
ev = event_context_init(conn);
|
||||
if (ev == NULL) {
|
||||
talloc_free(conn);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
conn->next_messageid = 1;
|
||||
conn->event.event_ctx = ev;
|
||||
|
||||
/* 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;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
talloc_free(conn->sock); /* this will also free event.fde */
|
||||
talloc_free(conn->packet);
|
||||
conn->sock = NULL;
|
||||
conn->event.fde = NULL;
|
||||
conn->packet = NULL;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
struct asn1_data asn1;
|
||||
struct ldap_connection *conn = talloc_get_type(private_data,
|
||||
struct ldap_connection);
|
||||
struct ldap_message *msg = talloc(conn, struct ldap_message);
|
||||
|
||||
if (msg == NULL) {
|
||||
return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
|
||||
}
|
||||
|
||||
if (!asn1_load(&asn1, blob)) {
|
||||
return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
|
||||
}
|
||||
|
||||
if (!ldap_decode(&asn1, msg)) {
|
||||
return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
|
||||
}
|
||||
|
||||
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 event_context *ev, struct fd_event *fde,
|
||||
uint16_t flags, void *private_data)
|
||||
{
|
||||
struct ldap_connection *conn = talloc_get_type(private_data,
|
||||
struct ldap_connection);
|
||||
if (flags & EVENT_FD_WRITE) {
|
||||
packet_queue_run(conn->packet);
|
||||
if (!tls_enabled(conn->sock)) return;
|
||||
}
|
||||
if (flags & EVENT_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);
|
||||
|
||||
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(NULL, 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);
|
||||
|
||||
/* 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,
|
||||
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 = event_add_fd(conn->event.event_ctx, conn->sock,
|
||||
socket_get_fd(conn->sock),
|
||||
EVENT_FD_READ, ldap_io_handler, conn);
|
||||
if (conn->event.fde == NULL) {
|
||||
composite_error(ctx, NT_STATUS_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
talloc_steal(conn, conn->sock);
|
||||
if (conn->ldaps) {
|
||||
struct socket_context *tls_socket = tls_init_client(conn->sock, conn->event.fde);
|
||||
if (tls_socket == NULL) {
|
||||
talloc_free(conn->sock);
|
||||
return;
|
||||
}
|
||||
talloc_unlink(conn, conn->sock);
|
||||
conn->sock = tls_socket;
|
||||
talloc_steal(conn, conn->sock);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 */
|
||||
|
||||
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 event_context *ev, struct timed_event *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 event_context *ev, struct timed_event *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
|
||||
*/
|
||||
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, &req->data, req)) {
|
||||
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 */
|
||||
event_add_timed(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 = event_add_timed(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;
|
||||
event_add_timed(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
|
||||
*/
|
||||
NTSTATUS ldap_request_wait(struct ldap_request *req)
|
||||
{
|
||||
while (req->state < LDAP_REQUEST_DONE) {
|
||||
if (event_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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
const char *ldap_errstr(struct ldap_connection *conn, NTSTATUS status)
|
||||
{
|
||||
if (NT_STATUS_IS_LDAP(status) && conn->last_error != NULL) {
|
||||
return conn->last_error;
|
||||
}
|
||||
return nt_errstr(status);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
return the Nth result message, waiting if necessary
|
||||
*/
|
||||
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 (event_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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
|
||||
#include "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 timed_event *time_event;
|
||||
};
|
||||
|
||||
|
||||
/* main context for a ldap client connection */
|
||||
struct ldap_connection {
|
||||
struct socket_context *sock;
|
||||
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 event_context *event_ctx;
|
||||
struct fd_event *fde;
|
||||
} event;
|
||||
|
||||
struct packet_context *packet;
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/ldap/ldap.h"
|
||||
#include "libcli/ldap/ldap_client.h"
|
||||
|
||||
/*
|
||||
delete a record
|
||||
*/
|
||||
NTSTATUS ildap_delete(struct ldap_connection *conn, const char *dn)
|
||||
{
|
||||
struct ldap_message *msg;
|
||||
NTSTATUS status;
|
||||
|
||||
msg = new_ldap_message(conn);
|
||||
NT_STATUS_HAVE_NO_MEMORY(msg);
|
||||
|
||||
msg->type = LDAP_TAG_DelRequest;
|
||||
msg->r.DelRequest.dn = dn;
|
||||
|
||||
status = ldap_transaction(conn, msg);
|
||||
|
||||
talloc_free(msg);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
add a record
|
||||
*/
|
||||
NTSTATUS ildap_add(struct ldap_connection *conn, const char *dn, struct ldap_mod **mods)
|
||||
{
|
||||
struct ldap_message *msg;
|
||||
int n, i;
|
||||
NTSTATUS status;
|
||||
|
||||
msg = new_ldap_message(conn);
|
||||
NT_STATUS_HAVE_NO_MEMORY(msg);
|
||||
|
||||
for (n=0;mods[n];n++) /* noop */ ;
|
||||
|
||||
msg->type = LDAP_TAG_AddRequest;
|
||||
msg->r.AddRequest.dn = dn;
|
||||
msg->r.AddRequest.num_attributes = n;
|
||||
msg->r.AddRequest.attributes = talloc_array(msg, struct ldb_message_element, n);
|
||||
if (msg->r.AddRequest.attributes == NULL) {
|
||||
talloc_free(msg);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
for (i=0;i<n;i++) {
|
||||
msg->r.AddRequest.attributes[i] = mods[i]->attrib;
|
||||
}
|
||||
|
||||
status = ldap_transaction(conn, msg);
|
||||
|
||||
talloc_free(msg);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
modify a record
|
||||
*/
|
||||
NTSTATUS ildap_modify(struct ldap_connection *conn, const char *dn, struct ldap_mod **mods)
|
||||
{
|
||||
struct ldap_message *msg;
|
||||
int n, i;
|
||||
NTSTATUS status;
|
||||
|
||||
msg = new_ldap_message(conn);
|
||||
NT_STATUS_HAVE_NO_MEMORY(msg);
|
||||
|
||||
for (n=0;mods[n];n++) /* noop */ ;
|
||||
|
||||
msg->type = LDAP_TAG_ModifyRequest;
|
||||
msg->r.ModifyRequest.dn = dn;
|
||||
msg->r.ModifyRequest.num_mods = n;
|
||||
msg->r.ModifyRequest.mods = talloc_array(msg, struct ldap_mod, n);
|
||||
if (msg->r.ModifyRequest.mods == NULL) {
|
||||
talloc_free(msg);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
for (i=0;i<n;i++) {
|
||||
msg->r.ModifyRequest.mods[i] = *mods[i];
|
||||
}
|
||||
|
||||
status = ldap_transaction(conn, msg);
|
||||
|
||||
talloc_free(msg);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
rename a record
|
||||
*/
|
||||
NTSTATUS ildap_rename(struct ldap_connection *conn, const char *dn, const char *newrdn,
|
||||
const char *parentdn, BOOL deleteolddn)
|
||||
{
|
||||
struct ldap_message *msg;
|
||||
NTSTATUS status;
|
||||
|
||||
msg = new_ldap_message(conn);
|
||||
NT_STATUS_HAVE_NO_MEMORY(msg);
|
||||
|
||||
msg->type = LDAP_TAG_ModifyDNRequest;
|
||||
msg->r.ModifyDNRequest.dn = dn;
|
||||
msg->r.ModifyDNRequest.newrdn = newrdn;
|
||||
msg->r.ModifyDNRequest.deleteolddn = deleteolddn;
|
||||
msg->r.ModifyDNRequest.newsuperior = parentdn;
|
||||
|
||||
status = ldap_transaction(conn, msg);
|
||||
|
||||
talloc_free(msg);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
count the returned search entries
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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 = discard_const(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
|
||||
*/
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
Unix SMB/CIFS mplementation.
|
||||
|
||||
LDAP protocol helper functions for SAMBA
|
||||
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/ldap/ldap.h"
|
||||
#include "libcli/ldap/ldap_client.h"
|
||||
|
||||
|
||||
struct ldap_message *new_ldap_message(TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
return talloc_zero(mem_ctx, struct ldap_message);
|
||||
}
|
||||
|
||||
|
||||
BOOL add_value_to_attrib(TALLOC_CTX *mem_ctx, struct ldb_val *value,
|
||||
struct ldb_message_element *attrib)
|
||||
{
|
||||
attrib->values = talloc_realloc(mem_ctx,
|
||||
attrib->values,
|
||||
DATA_BLOB,
|
||||
attrib->num_values+1);
|
||||
if (attrib->values == NULL)
|
||||
return False;
|
||||
|
||||
attrib->values[attrib->num_values].data = talloc_steal(attrib->values,
|
||||
value->data);
|
||||
attrib->values[attrib->num_values].length = value->length;
|
||||
attrib->num_values += 1;
|
||||
return True;
|
||||
}
|
||||
|
||||
BOOL add_attrib_to_array_talloc(TALLOC_CTX *mem_ctx,
|
||||
const struct ldb_message_element *attrib,
|
||||
struct ldb_message_element **attribs,
|
||||
int *num_attribs)
|
||||
{
|
||||
*attribs = talloc_realloc(mem_ctx,
|
||||
*attribs,
|
||||
struct ldb_message_element,
|
||||
*num_attribs+1);
|
||||
|
||||
if (*attribs == NULL)
|
||||
return False;
|
||||
|
||||
(*attribs)[*num_attribs] = *attrib;
|
||||
talloc_steal(*attribs, attrib->values);
|
||||
talloc_steal(*attribs, attrib->name);
|
||||
*num_attribs += 1;
|
||||
return True;
|
||||
}
|
||||
|
||||
BOOL add_mod_to_array_talloc(TALLOC_CTX *mem_ctx,
|
||||
struct ldap_mod *mod,
|
||||
struct ldap_mod **mods,
|
||||
int *num_mods)
|
||||
{
|
||||
*mods = talloc_realloc(mem_ctx, *mods, struct ldap_mod, (*num_mods)+1);
|
||||
|
||||
if (*mods == NULL)
|
||||
return False;
|
||||
|
||||
(*mods)[*num_mods] = *mod;
|
||||
*num_mods += 1;
|
||||
return True;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
Unix SMB/CIFS mplementation.
|
||||
|
||||
wrap/unwrap NDR encoded elements for ldap 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/ldap/ldap.h"
|
||||
#include "librpc/gen_ndr/ndr_security.h"
|
||||
#include "librpc/gen_ndr/ndr_misc.h"
|
||||
|
||||
/*
|
||||
encode a NDR uint32 as a ldap filter element
|
||||
*/
|
||||
char *ldap_encode_ndr_uint32(TALLOC_CTX *mem_ctx, uint32_t value)
|
||||
{
|
||||
uint8_t buf[4];
|
||||
struct ldb_val val;
|
||||
SIVAL(buf, 0, value);
|
||||
val.data = buf;
|
||||
val.length = 4;
|
||||
return ldb_binary_encode(mem_ctx, val);
|
||||
}
|
||||
|
||||
/*
|
||||
encode a NDR dom_sid as a ldap filter element
|
||||
*/
|
||||
char *ldap_encode_ndr_dom_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid)
|
||||
{
|
||||
DATA_BLOB blob;
|
||||
NTSTATUS status;
|
||||
char *ret;
|
||||
status = ndr_push_struct_blob(&blob, mem_ctx, sid,
|
||||
(ndr_push_flags_fn_t)ndr_push_dom_sid);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return NULL;
|
||||
}
|
||||
ret = ldb_binary_encode(mem_ctx, blob);
|
||||
data_blob_free(&blob);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
encode a NDR GUID as a ldap filter element
|
||||
*/
|
||||
char *ldap_encode_ndr_GUID(TALLOC_CTX *mem_ctx, struct GUID *guid)
|
||||
{
|
||||
DATA_BLOB blob;
|
||||
NTSTATUS status;
|
||||
char *ret;
|
||||
status = ndr_push_struct_blob(&blob, mem_ctx, guid,
|
||||
(ndr_push_flags_fn_t)ndr_push_GUID);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return NULL;
|
||||
}
|
||||
ret = ldb_binary_encode(mem_ctx, blob);
|
||||
data_blob_free(&blob);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
decode a NDR GUID from a ldap filter element
|
||||
*/
|
||||
NTSTATUS ldap_decode_ndr_GUID(TALLOC_CTX *mem_ctx, struct ldb_val val, struct GUID *guid)
|
||||
{
|
||||
DATA_BLOB blob;
|
||||
NTSTATUS status;
|
||||
|
||||
blob.data = val.data;
|
||||
blob.length = val.length;
|
||||
status = ndr_pull_struct_blob(&blob, mem_ctx, guid,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_GUID);
|
||||
talloc_free(val.data);
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef __LIBCLI_H__
|
||||
#define __LIBCLI_H__
|
||||
|
||||
#include "core.h"
|
||||
#include "librpc/gen_ndr/nbt.h"
|
||||
|
||||
/*
|
||||
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 event_context;
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "libcli/libcli_proto.h"
|
||||
|
||||
#endif /* __LIBCLI_H__ */
|
||||
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
a raw async NBT 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef __LIBNBT_H__
|
||||
#define __LIBNBT_H__
|
||||
|
||||
#include "librpc/gen_ndr/nbt.h"
|
||||
|
||||
/*
|
||||
possible states for pending requests
|
||||
*/
|
||||
enum nbt_request_state {NBT_REQUEST_SEND,
|
||||
NBT_REQUEST_WAIT,
|
||||
NBT_REQUEST_DONE,
|
||||
NBT_REQUEST_TIMEOUT,
|
||||
NBT_REQUEST_ERROR};
|
||||
|
||||
/*
|
||||
a nbt name request
|
||||
*/
|
||||
struct nbt_name_request {
|
||||
struct nbt_name_request *next, *prev;
|
||||
|
||||
enum nbt_request_state state;
|
||||
|
||||
NTSTATUS status;
|
||||
|
||||
/* the socket this was on */
|
||||
struct nbt_name_socket *nbtsock;
|
||||
|
||||
/* where to send the request */
|
||||
struct socket_address *dest;
|
||||
|
||||
/* timeout between retries */
|
||||
int timeout;
|
||||
|
||||
/* how many retries to send on timeout */
|
||||
int num_retries;
|
||||
|
||||
/* whether we have received a WACK */
|
||||
BOOL received_wack;
|
||||
|
||||
/* the timeout event */
|
||||
struct timed_event *te;
|
||||
|
||||
/* the name transaction id */
|
||||
uint16_t name_trn_id;
|
||||
|
||||
/* is it a reply? */
|
||||
BOOL is_reply;
|
||||
|
||||
/* the encoded request */
|
||||
DATA_BLOB encoded;
|
||||
|
||||
/* shall we allow multiple replies? */
|
||||
BOOL allow_multiple_replies;
|
||||
|
||||
unsigned int num_replies;
|
||||
struct nbt_name_reply {
|
||||
struct nbt_name_packet *packet;
|
||||
struct socket_address *dest;
|
||||
} *replies;
|
||||
|
||||
/* information on what to do on completion */
|
||||
struct {
|
||||
void (*fn)(struct nbt_name_request *);
|
||||
void *private;
|
||||
} async;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
context structure for operations on name queries
|
||||
*/
|
||||
struct nbt_name_socket {
|
||||
struct socket_context *sock;
|
||||
struct event_context *event_ctx;
|
||||
|
||||
/* a queue of requests pending to be sent */
|
||||
struct nbt_name_request *send_queue;
|
||||
|
||||
/* the fd event */
|
||||
struct fd_event *fde;
|
||||
|
||||
/* mapping from name_trn_id to pending event */
|
||||
struct idr_context *idr;
|
||||
|
||||
/* how many requests are waiting for a reply */
|
||||
uint16_t num_pending;
|
||||
|
||||
/* what to do with incoming request packets */
|
||||
struct {
|
||||
void (*handler)(struct nbt_name_socket *, struct nbt_name_packet *,
|
||||
struct socket_address *);
|
||||
void *private;
|
||||
} incoming;
|
||||
|
||||
/* what to do with unexpected replies */
|
||||
struct {
|
||||
void (*handler)(struct nbt_name_socket *, struct nbt_name_packet *,
|
||||
struct socket_address *);
|
||||
void *private;
|
||||
} unexpected;
|
||||
};
|
||||
|
||||
|
||||
/* a simple name query */
|
||||
struct nbt_name_query {
|
||||
struct {
|
||||
struct nbt_name name;
|
||||
const char *dest_addr;
|
||||
BOOL broadcast;
|
||||
BOOL wins_lookup;
|
||||
int timeout; /* in seconds */
|
||||
int retries;
|
||||
} in;
|
||||
struct {
|
||||
const char *reply_from;
|
||||
struct nbt_name name;
|
||||
int16_t num_addrs;
|
||||
const char **reply_addrs;
|
||||
} out;
|
||||
};
|
||||
|
||||
/* a simple name status query */
|
||||
struct nbt_name_status {
|
||||
struct {
|
||||
struct nbt_name name;
|
||||
const char *dest_addr;
|
||||
int timeout; /* in seconds */
|
||||
int retries;
|
||||
} in;
|
||||
struct {
|
||||
const char *reply_from;
|
||||
struct nbt_name name;
|
||||
struct nbt_rdata_status status;
|
||||
} out;
|
||||
};
|
||||
|
||||
/* a name registration request */
|
||||
struct nbt_name_register {
|
||||
struct {
|
||||
struct nbt_name name;
|
||||
const char *dest_addr;
|
||||
const char *address;
|
||||
uint16_t nb_flags;
|
||||
BOOL register_demand;
|
||||
BOOL broadcast;
|
||||
BOOL multi_homed;
|
||||
uint32_t ttl;
|
||||
int timeout; /* in seconds */
|
||||
int retries;
|
||||
} in;
|
||||
struct {
|
||||
const char *reply_from;
|
||||
struct nbt_name name;
|
||||
const char *reply_addr;
|
||||
uint8_t rcode;
|
||||
} out;
|
||||
};
|
||||
|
||||
/* a send 3 times then demand name broadcast name registration */
|
||||
struct nbt_name_register_bcast {
|
||||
struct {
|
||||
struct nbt_name name;
|
||||
const char *dest_addr;
|
||||
const char *address;
|
||||
uint16_t nb_flags;
|
||||
uint32_t ttl;
|
||||
} in;
|
||||
};
|
||||
|
||||
|
||||
/* wins name register with multiple wins servers to try and multiple
|
||||
addresses to register */
|
||||
struct nbt_name_register_wins {
|
||||
struct {
|
||||
struct nbt_name name;
|
||||
const char **wins_servers;
|
||||
const char **addresses;
|
||||
uint16_t nb_flags;
|
||||
uint32_t ttl;
|
||||
} in;
|
||||
struct {
|
||||
const char *wins_server;
|
||||
uint8_t rcode;
|
||||
} out;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* a name refresh request */
|
||||
struct nbt_name_refresh {
|
||||
struct {
|
||||
struct nbt_name name;
|
||||
const char *dest_addr;
|
||||
const char *address;
|
||||
uint16_t nb_flags;
|
||||
BOOL broadcast;
|
||||
uint32_t ttl;
|
||||
int timeout; /* in seconds */
|
||||
int retries;
|
||||
} in;
|
||||
struct {
|
||||
const char *reply_from;
|
||||
struct nbt_name name;
|
||||
const char *reply_addr;
|
||||
uint8_t rcode;
|
||||
} out;
|
||||
};
|
||||
|
||||
/* wins name refresh with multiple wins servers to try and multiple
|
||||
addresses to register */
|
||||
struct nbt_name_refresh_wins {
|
||||
struct {
|
||||
struct nbt_name name;
|
||||
const char **wins_servers;
|
||||
const char **addresses;
|
||||
uint16_t nb_flags;
|
||||
uint32_t ttl;
|
||||
} in;
|
||||
struct {
|
||||
const char *wins_server;
|
||||
uint8_t rcode;
|
||||
} out;
|
||||
};
|
||||
|
||||
|
||||
/* a name release request */
|
||||
struct nbt_name_release {
|
||||
struct {
|
||||
struct nbt_name name;
|
||||
const char *dest_addr;
|
||||
const char *address;
|
||||
uint16_t nb_flags;
|
||||
BOOL broadcast;
|
||||
int timeout; /* in seconds */
|
||||
int retries;
|
||||
} in;
|
||||
struct {
|
||||
const char *reply_from;
|
||||
struct nbt_name name;
|
||||
const char *reply_addr;
|
||||
uint8_t rcode;
|
||||
} out;
|
||||
};
|
||||
|
||||
#include "libcli/nbt/nbt_proto.h"
|
||||
|
||||
#endif /* __LIBNBT_H__ */
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
make nbt name query 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/nbt/libnbt.h"
|
||||
#include "lib/socket/socket.h"
|
||||
|
||||
/**
|
||||
send a nbt name query
|
||||
*/
|
||||
_PUBLIC_ struct nbt_name_request *nbt_name_query_send(struct nbt_name_socket *nbtsock,
|
||||
struct nbt_name_query *io)
|
||||
{
|
||||
struct nbt_name_request *req;
|
||||
struct nbt_name_packet *packet;
|
||||
struct socket_address *dest;
|
||||
|
||||
packet = talloc_zero(nbtsock, struct nbt_name_packet);
|
||||
if (packet == NULL) return NULL;
|
||||
|
||||
packet->qdcount = 1;
|
||||
packet->operation = NBT_OPCODE_QUERY;
|
||||
if (io->in.broadcast) {
|
||||
packet->operation |= NBT_FLAG_BROADCAST;
|
||||
}
|
||||
if (io->in.wins_lookup) {
|
||||
packet->operation |= NBT_FLAG_RECURSION_DESIRED;
|
||||
}
|
||||
|
||||
packet->questions = talloc_array(packet, struct nbt_name_question, 1);
|
||||
if (packet->questions == NULL) goto failed;
|
||||
|
||||
packet->questions[0].name = io->in.name;
|
||||
packet->questions[0].question_type = NBT_QTYPE_NETBIOS;
|
||||
packet->questions[0].question_class = NBT_QCLASS_IP;
|
||||
|
||||
dest = socket_address_from_strings(packet, nbtsock->sock->backend_name,
|
||||
io->in.dest_addr, lp_nbt_port());
|
||||
if (dest == NULL) goto failed;
|
||||
req = nbt_name_request_send(nbtsock, dest, packet,
|
||||
io->in.timeout, io->in.retries, False);
|
||||
if (req == NULL) goto failed;
|
||||
|
||||
talloc_free(packet);
|
||||
return req;
|
||||
|
||||
failed:
|
||||
talloc_free(packet);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
wait for a name query reply
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS nbt_name_query_recv(struct nbt_name_request *req,
|
||||
TALLOC_CTX *mem_ctx, struct nbt_name_query *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct nbt_name_packet *packet;
|
||||
int i;
|
||||
|
||||
status = nbt_name_request_recv(req);
|
||||
if (!NT_STATUS_IS_OK(status) ||
|
||||
req->num_replies == 0) {
|
||||
talloc_free(req);
|
||||
return status;
|
||||
}
|
||||
|
||||
packet = req->replies[0].packet;
|
||||
io->out.reply_from = talloc_steal(mem_ctx, req->replies[0].dest->addr);
|
||||
|
||||
if ((packet->operation & NBT_RCODE) != 0) {
|
||||
status = nbt_rcode_to_ntstatus(packet->operation & NBT_RCODE);
|
||||
talloc_free(req);
|
||||
return status;
|
||||
}
|
||||
|
||||
if (packet->ancount != 1 ||
|
||||
packet->answers[0].rr_type != NBT_QTYPE_NETBIOS ||
|
||||
packet->answers[0].rr_class != NBT_QCLASS_IP) {
|
||||
talloc_free(req);
|
||||
return status;
|
||||
}
|
||||
|
||||
io->out.name = packet->answers[0].name;
|
||||
io->out.num_addrs = packet->answers[0].rdata.netbios.length / 6;
|
||||
io->out.reply_addrs = talloc_array(mem_ctx, const char *, io->out.num_addrs+1);
|
||||
if (io->out.reply_addrs == NULL) {
|
||||
talloc_free(req);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
for (i=0;i<io->out.num_addrs;i++) {
|
||||
io->out.reply_addrs[i] = talloc_steal(io->out.reply_addrs,
|
||||
packet->answers[0].rdata.netbios.addresses[i].ipaddr);
|
||||
}
|
||||
io->out.reply_addrs[i] = NULL;
|
||||
|
||||
talloc_steal(mem_ctx, io->out.name.name);
|
||||
talloc_steal(mem_ctx, io->out.name.scope);
|
||||
|
||||
talloc_free(req);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
wait for a name query reply
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS nbt_name_query(struct nbt_name_socket *nbtsock,
|
||||
TALLOC_CTX *mem_ctx, struct nbt_name_query *io)
|
||||
{
|
||||
struct nbt_name_request *req = nbt_name_query_send(nbtsock, io);
|
||||
return nbt_name_query_recv(req, mem_ctx, io);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
send a nbt name status
|
||||
*/
|
||||
_PUBLIC_ struct nbt_name_request *nbt_name_status_send(struct nbt_name_socket *nbtsock,
|
||||
struct nbt_name_status *io)
|
||||
{
|
||||
struct nbt_name_request *req;
|
||||
struct nbt_name_packet *packet;
|
||||
struct socket_address *dest;
|
||||
|
||||
packet = talloc_zero(nbtsock, struct nbt_name_packet);
|
||||
if (packet == NULL) return NULL;
|
||||
|
||||
packet->qdcount = 1;
|
||||
packet->operation = NBT_OPCODE_QUERY;
|
||||
|
||||
packet->questions = talloc_array(packet, struct nbt_name_question, 1);
|
||||
if (packet->questions == NULL) goto failed;
|
||||
|
||||
packet->questions[0].name = io->in.name;
|
||||
packet->questions[0].question_type = NBT_QTYPE_STATUS;
|
||||
packet->questions[0].question_class = NBT_QCLASS_IP;
|
||||
|
||||
dest = socket_address_from_strings(packet, nbtsock->sock->backend_name,
|
||||
io->in.dest_addr, lp_nbt_port());
|
||||
if (dest == NULL) goto failed;
|
||||
req = nbt_name_request_send(nbtsock, dest, packet,
|
||||
io->in.timeout, io->in.retries, False);
|
||||
if (req == NULL) goto failed;
|
||||
|
||||
talloc_free(packet);
|
||||
return req;
|
||||
|
||||
failed:
|
||||
talloc_free(packet);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
wait for a name status reply
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS nbt_name_status_recv(struct nbt_name_request *req,
|
||||
TALLOC_CTX *mem_ctx, struct nbt_name_status *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct nbt_name_packet *packet;
|
||||
int i;
|
||||
|
||||
status = nbt_name_request_recv(req);
|
||||
if (!NT_STATUS_IS_OK(status) ||
|
||||
req->num_replies == 0) {
|
||||
talloc_free(req);
|
||||
return status;
|
||||
}
|
||||
|
||||
packet = req->replies[0].packet;
|
||||
io->out.reply_from = talloc_steal(mem_ctx, req->replies[0].dest->addr);
|
||||
|
||||
if ((packet->operation & NBT_RCODE) != 0) {
|
||||
status = nbt_rcode_to_ntstatus(packet->operation & NBT_RCODE);
|
||||
talloc_free(req);
|
||||
return status;
|
||||
}
|
||||
|
||||
if (packet->ancount != 1 ||
|
||||
packet->answers[0].rr_type != NBT_QTYPE_STATUS ||
|
||||
packet->answers[0].rr_class != NBT_QCLASS_IP) {
|
||||
talloc_free(req);
|
||||
return NT_STATUS_INVALID_NETWORK_RESPONSE;
|
||||
}
|
||||
|
||||
io->out.name = packet->answers[0].name;
|
||||
talloc_steal(mem_ctx, io->out.name.name);
|
||||
talloc_steal(mem_ctx, io->out.name.scope);
|
||||
|
||||
io->out.status = packet->answers[0].rdata.status;
|
||||
talloc_steal(mem_ctx, io->out.status.names);
|
||||
for (i=0;i<io->out.status.num_names;i++) {
|
||||
talloc_steal(io->out.status.names, io->out.status.names[i].name);
|
||||
}
|
||||
|
||||
|
||||
talloc_free(req);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
wait for a name status reply
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS nbt_name_status(struct nbt_name_socket *nbtsock,
|
||||
TALLOC_CTX *mem_ctx, struct nbt_name_status *io)
|
||||
{
|
||||
struct nbt_name_request *req = nbt_name_status_send(nbtsock, io);
|
||||
return nbt_name_status_recv(req, mem_ctx, io);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,296 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
send out a name refresh request
|
||||
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/nbt/libnbt.h"
|
||||
#include "libcli/composite/composite.h"
|
||||
#include "lib/socket/socket.h"
|
||||
|
||||
/*
|
||||
send a nbt name refresh request
|
||||
*/
|
||||
struct nbt_name_request *nbt_name_refresh_send(struct nbt_name_socket *nbtsock,
|
||||
struct nbt_name_refresh *io)
|
||||
{
|
||||
struct nbt_name_request *req;
|
||||
struct nbt_name_packet *packet;
|
||||
struct socket_address *dest;
|
||||
|
||||
packet = talloc_zero(nbtsock, struct nbt_name_packet);
|
||||
if (packet == NULL) return NULL;
|
||||
|
||||
packet->qdcount = 1;
|
||||
packet->arcount = 1;
|
||||
packet->operation = NBT_OPCODE_REFRESH;
|
||||
if (io->in.broadcast) {
|
||||
packet->operation |= NBT_FLAG_BROADCAST;
|
||||
}
|
||||
|
||||
packet->questions = talloc_array(packet, struct nbt_name_question, 1);
|
||||
if (packet->questions == NULL) goto failed;
|
||||
|
||||
packet->questions[0].name = io->in.name;
|
||||
packet->questions[0].question_type = NBT_QTYPE_NETBIOS;
|
||||
packet->questions[0].question_class = NBT_QCLASS_IP;
|
||||
|
||||
packet->additional = talloc_array(packet, struct nbt_res_rec, 1);
|
||||
if (packet->additional == NULL) goto failed;
|
||||
|
||||
packet->additional[0].name = io->in.name;
|
||||
packet->additional[0].rr_type = NBT_QTYPE_NETBIOS;
|
||||
packet->additional[0].rr_class = NBT_QCLASS_IP;
|
||||
packet->additional[0].ttl = io->in.ttl;
|
||||
packet->additional[0].rdata.netbios.length = 6;
|
||||
packet->additional[0].rdata.netbios.addresses = talloc_array(packet->additional,
|
||||
struct nbt_rdata_address, 1);
|
||||
if (packet->additional[0].rdata.netbios.addresses == NULL) goto failed;
|
||||
packet->additional[0].rdata.netbios.addresses[0].nb_flags = io->in.nb_flags;
|
||||
packet->additional[0].rdata.netbios.addresses[0].ipaddr =
|
||||
talloc_strdup(packet->additional, io->in.address);
|
||||
|
||||
dest = socket_address_from_strings(nbtsock, nbtsock->sock->backend_name,
|
||||
io->in.dest_addr, lp_nbt_port());
|
||||
if (dest == NULL) goto failed;
|
||||
req = nbt_name_request_send(nbtsock, dest, packet,
|
||||
io->in.timeout, io->in.retries, False);
|
||||
if (req == NULL) goto failed;
|
||||
|
||||
talloc_free(packet);
|
||||
return req;
|
||||
|
||||
failed:
|
||||
talloc_free(packet);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
wait for a refresh reply
|
||||
*/
|
||||
NTSTATUS nbt_name_refresh_recv(struct nbt_name_request *req,
|
||||
TALLOC_CTX *mem_ctx, struct nbt_name_refresh *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct nbt_name_packet *packet;
|
||||
|
||||
status = nbt_name_request_recv(req);
|
||||
if (!NT_STATUS_IS_OK(status) ||
|
||||
req->num_replies == 0) {
|
||||
talloc_free(req);
|
||||
return status;
|
||||
}
|
||||
|
||||
packet = req->replies[0].packet;
|
||||
io->out.reply_from = talloc_steal(mem_ctx, req->replies[0].dest->addr);
|
||||
|
||||
if (packet->ancount != 1 ||
|
||||
packet->answers[0].rr_type != NBT_QTYPE_NETBIOS ||
|
||||
packet->answers[0].rr_class != NBT_QCLASS_IP) {
|
||||
talloc_free(req);
|
||||
return NT_STATUS_INVALID_NETWORK_RESPONSE;
|
||||
}
|
||||
|
||||
io->out.rcode = packet->operation & NBT_RCODE;
|
||||
io->out.name = packet->answers[0].name;
|
||||
if (packet->answers[0].rdata.netbios.length < 6) {
|
||||
talloc_free(req);
|
||||
return NT_STATUS_INVALID_NETWORK_RESPONSE;
|
||||
}
|
||||
io->out.reply_addr = talloc_steal(mem_ctx,
|
||||
packet->answers[0].rdata.netbios.addresses[0].ipaddr);
|
||||
talloc_steal(mem_ctx, io->out.name.name);
|
||||
talloc_steal(mem_ctx, io->out.name.scope);
|
||||
|
||||
talloc_free(req);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
synchronous name refresh request
|
||||
*/
|
||||
NTSTATUS nbt_name_refresh(struct nbt_name_socket *nbtsock,
|
||||
TALLOC_CTX *mem_ctx, struct nbt_name_refresh *io)
|
||||
{
|
||||
struct nbt_name_request *req = nbt_name_refresh_send(nbtsock, io);
|
||||
return nbt_name_refresh_recv(req, mem_ctx, io);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
a wins name refresh with multiple WINS servers and multiple
|
||||
addresses to refresh. Try each WINS server in turn, until we get a
|
||||
reply for each address
|
||||
*/
|
||||
struct refresh_wins_state {
|
||||
struct nbt_name_socket *nbtsock;
|
||||
struct nbt_name_refresh *io;
|
||||
const char **wins_servers;
|
||||
const char **addresses;
|
||||
int address_idx;
|
||||
struct nbt_name_request *req;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
state handler for WINS multi-homed multi-server name refresh
|
||||
*/
|
||||
static void name_refresh_wins_handler(struct nbt_name_request *req)
|
||||
{
|
||||
struct composite_context *c = talloc_get_type(req->async.private,
|
||||
struct composite_context);
|
||||
struct refresh_wins_state *state = talloc_get_type(c->private_data,
|
||||
struct refresh_wins_state);
|
||||
NTSTATUS status;
|
||||
|
||||
status = nbt_name_refresh_recv(state->req, state, state->io);
|
||||
if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
|
||||
/* the refresh timed out - try the next WINS server */
|
||||
state->wins_servers++;
|
||||
state->address_idx = 0;
|
||||
if (state->wins_servers[0] == NULL) {
|
||||
c->state = COMPOSITE_STATE_ERROR;
|
||||
c->status = status;
|
||||
goto done;
|
||||
}
|
||||
state->io->in.dest_addr = state->wins_servers[0];
|
||||
state->io->in.address = state->addresses[0];
|
||||
state->req = nbt_name_refresh_send(state->nbtsock, state->io);
|
||||
if (state->req == NULL) {
|
||||
c->state = COMPOSITE_STATE_ERROR;
|
||||
c->status = NT_STATUS_NO_MEMORY;
|
||||
} else {
|
||||
state->req->async.fn = name_refresh_wins_handler;
|
||||
state->req->async.private = c;
|
||||
}
|
||||
} else if (!NT_STATUS_IS_OK(status)) {
|
||||
c->state = COMPOSITE_STATE_ERROR;
|
||||
c->status = status;
|
||||
} else {
|
||||
if (state->io->out.rcode == 0 &&
|
||||
state->addresses[state->address_idx+1] != NULL) {
|
||||
/* refresh our next address */
|
||||
state->io->in.address = state->addresses[++(state->address_idx)];
|
||||
state->req = nbt_name_refresh_send(state->nbtsock, state->io);
|
||||
if (state->req == NULL) {
|
||||
c->state = COMPOSITE_STATE_ERROR;
|
||||
c->status = NT_STATUS_NO_MEMORY;
|
||||
} else {
|
||||
state->req->async.fn = name_refresh_wins_handler;
|
||||
state->req->async.private = c;
|
||||
}
|
||||
} else {
|
||||
c->state = COMPOSITE_STATE_DONE;
|
||||
c->status = NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
if (c->state >= COMPOSITE_STATE_DONE &&
|
||||
c->async.fn) {
|
||||
c->async.fn(c);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
the async send call for a multi-server WINS refresh
|
||||
*/
|
||||
struct composite_context *nbt_name_refresh_wins_send(struct nbt_name_socket *nbtsock,
|
||||
struct nbt_name_refresh_wins *io)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct refresh_wins_state *state;
|
||||
|
||||
c = talloc_zero(nbtsock, struct composite_context);
|
||||
if (c == NULL) goto failed;
|
||||
|
||||
state = talloc(c, struct refresh_wins_state);
|
||||
if (state == NULL) goto failed;
|
||||
|
||||
state->io = talloc(state, struct nbt_name_refresh);
|
||||
if (state->io == NULL) goto failed;
|
||||
|
||||
state->wins_servers = str_list_copy(state, io->in.wins_servers);
|
||||
if (state->wins_servers == NULL ||
|
||||
state->wins_servers[0] == NULL) goto failed;
|
||||
|
||||
state->addresses = str_list_copy(state, io->in.addresses);
|
||||
if (state->addresses == NULL ||
|
||||
state->addresses[0] == NULL) goto failed;
|
||||
|
||||
state->io->in.name = io->in.name;
|
||||
state->io->in.dest_addr = state->wins_servers[0];
|
||||
state->io->in.address = io->in.addresses[0];
|
||||
state->io->in.nb_flags = io->in.nb_flags;
|
||||
state->io->in.broadcast = False;
|
||||
state->io->in.ttl = io->in.ttl;
|
||||
state->io->in.timeout = 2;
|
||||
state->io->in.retries = 2;
|
||||
|
||||
state->nbtsock = nbtsock;
|
||||
state->address_idx = 0;
|
||||
|
||||
state->req = nbt_name_refresh_send(nbtsock, state->io);
|
||||
if (state->req == NULL) goto failed;
|
||||
|
||||
state->req->async.fn = name_refresh_wins_handler;
|
||||
state->req->async.private = c;
|
||||
|
||||
c->private_data = state;
|
||||
c->state = COMPOSITE_STATE_IN_PROGRESS;
|
||||
c->event_ctx = nbtsock->event_ctx;
|
||||
|
||||
return c;
|
||||
|
||||
failed:
|
||||
talloc_free(c);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
multi-homed WINS name refresh - recv side
|
||||
*/
|
||||
NTSTATUS nbt_name_refresh_wins_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
|
||||
struct nbt_name_refresh_wins *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
status = composite_wait(c);
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
struct refresh_wins_state *state =
|
||||
talloc_get_type(c->private_data, struct refresh_wins_state);
|
||||
io->out.wins_server = talloc_steal(mem_ctx, state->wins_servers[0]);
|
||||
io->out.rcode = state->io->out.rcode;
|
||||
}
|
||||
talloc_free(c);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
multi-homed WINS refresh - sync interface
|
||||
*/
|
||||
NTSTATUS nbt_name_refresh_wins(struct nbt_name_socket *nbtsock,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct nbt_name_refresh_wins *io)
|
||||
{
|
||||
struct composite_context *c = nbt_name_refresh_wins_send(nbtsock, io);
|
||||
return nbt_name_refresh_wins_recv(c, mem_ctx, io);
|
||||
}
|
||||
@@ -0,0 +1,436 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
send out a name registration request
|
||||
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/nbt/libnbt.h"
|
||||
#include "libcli/composite/composite.h"
|
||||
#include "lib/socket/socket.h"
|
||||
#include "librpc/gen_ndr/ndr_nbt.h"
|
||||
|
||||
/*
|
||||
send a nbt name registration request
|
||||
*/
|
||||
struct nbt_name_request *nbt_name_register_send(struct nbt_name_socket *nbtsock,
|
||||
struct nbt_name_register *io)
|
||||
{
|
||||
struct nbt_name_request *req;
|
||||
struct nbt_name_packet *packet;
|
||||
struct socket_address *dest;
|
||||
|
||||
packet = talloc_zero(nbtsock, struct nbt_name_packet);
|
||||
if (packet == NULL) return NULL;
|
||||
|
||||
packet->qdcount = 1;
|
||||
packet->arcount = 1;
|
||||
if (io->in.multi_homed) {
|
||||
packet->operation = NBT_OPCODE_MULTI_HOME_REG;
|
||||
} else {
|
||||
packet->operation = NBT_OPCODE_REGISTER;
|
||||
}
|
||||
if (io->in.broadcast) {
|
||||
packet->operation |= NBT_FLAG_BROADCAST;
|
||||
}
|
||||
if (io->in.register_demand) {
|
||||
packet->operation |= NBT_FLAG_RECURSION_DESIRED;
|
||||
}
|
||||
|
||||
packet->questions = talloc_array(packet, struct nbt_name_question, 1);
|
||||
if (packet->questions == NULL) goto failed;
|
||||
|
||||
packet->questions[0].name = io->in.name;
|
||||
packet->questions[0].question_type = NBT_QTYPE_NETBIOS;
|
||||
packet->questions[0].question_class = NBT_QCLASS_IP;
|
||||
|
||||
packet->additional = talloc_array(packet, struct nbt_res_rec, 1);
|
||||
if (packet->additional == NULL) goto failed;
|
||||
|
||||
packet->additional[0].name = io->in.name;
|
||||
packet->additional[0].rr_type = NBT_QTYPE_NETBIOS;
|
||||
packet->additional[0].rr_class = NBT_QCLASS_IP;
|
||||
packet->additional[0].ttl = io->in.ttl;
|
||||
packet->additional[0].rdata.netbios.length = 6;
|
||||
packet->additional[0].rdata.netbios.addresses = talloc_array(packet->additional,
|
||||
struct nbt_rdata_address, 1);
|
||||
if (packet->additional[0].rdata.netbios.addresses == NULL) goto failed;
|
||||
packet->additional[0].rdata.netbios.addresses[0].nb_flags = io->in.nb_flags;
|
||||
packet->additional[0].rdata.netbios.addresses[0].ipaddr =
|
||||
talloc_strdup(packet->additional, io->in.address);
|
||||
if (packet->additional[0].rdata.netbios.addresses[0].ipaddr == NULL) goto failed;
|
||||
|
||||
dest = socket_address_from_strings(packet, nbtsock->sock->backend_name,
|
||||
io->in.dest_addr, lp_nbt_port());
|
||||
if (dest == NULL) goto failed;
|
||||
req = nbt_name_request_send(nbtsock, dest, packet,
|
||||
io->in.timeout, io->in.retries, False);
|
||||
if (req == NULL) goto failed;
|
||||
|
||||
talloc_free(packet);
|
||||
return req;
|
||||
|
||||
failed:
|
||||
talloc_free(packet);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
wait for a registration reply
|
||||
*/
|
||||
NTSTATUS nbt_name_register_recv(struct nbt_name_request *req,
|
||||
TALLOC_CTX *mem_ctx, struct nbt_name_register *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct nbt_name_packet *packet;
|
||||
|
||||
status = nbt_name_request_recv(req);
|
||||
if (!NT_STATUS_IS_OK(status) ||
|
||||
req->num_replies == 0) {
|
||||
talloc_free(req);
|
||||
return status;
|
||||
}
|
||||
|
||||
packet = req->replies[0].packet;
|
||||
io->out.reply_from = talloc_steal(mem_ctx, req->replies[0].dest->addr);
|
||||
|
||||
if (packet->ancount != 1 ||
|
||||
packet->answers[0].rr_type != NBT_QTYPE_NETBIOS ||
|
||||
packet->answers[0].rr_class != NBT_QCLASS_IP) {
|
||||
talloc_free(req);
|
||||
return NT_STATUS_INVALID_NETWORK_RESPONSE;
|
||||
}
|
||||
|
||||
io->out.rcode = packet->operation & NBT_RCODE;
|
||||
io->out.name = packet->answers[0].name;
|
||||
if (packet->answers[0].rdata.netbios.length < 6) {
|
||||
talloc_free(req);
|
||||
return NT_STATUS_INVALID_NETWORK_RESPONSE;
|
||||
}
|
||||
io->out.reply_addr = talloc_steal(mem_ctx,
|
||||
packet->answers[0].rdata.netbios.addresses[0].ipaddr);
|
||||
talloc_steal(mem_ctx, io->out.name.name);
|
||||
talloc_steal(mem_ctx, io->out.name.scope);
|
||||
|
||||
talloc_free(req);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
synchronous name registration request
|
||||
*/
|
||||
NTSTATUS nbt_name_register(struct nbt_name_socket *nbtsock,
|
||||
TALLOC_CTX *mem_ctx, struct nbt_name_register *io)
|
||||
{
|
||||
struct nbt_name_request *req = nbt_name_register_send(nbtsock, io);
|
||||
return nbt_name_register_recv(req, mem_ctx, io);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
a 4 step broadcast registration. 3 lots of name registration requests, followed by
|
||||
a name registration demand
|
||||
*/
|
||||
struct register_bcast_state {
|
||||
struct nbt_name_socket *nbtsock;
|
||||
struct nbt_name_register *io;
|
||||
struct nbt_name_request *req;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
state handler for 4 stage name registration
|
||||
*/
|
||||
static void name_register_bcast_handler(struct nbt_name_request *req)
|
||||
{
|
||||
struct composite_context *c = talloc_get_type(req->async.private, struct composite_context);
|
||||
struct register_bcast_state *state = talloc_get_type(c->private_data, struct register_bcast_state);
|
||||
NTSTATUS status;
|
||||
|
||||
status = nbt_name_register_recv(state->req, state, state->io);
|
||||
if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
|
||||
if (state->io->in.register_demand == True) {
|
||||
/* all done */
|
||||
c->state = COMPOSITE_STATE_DONE;
|
||||
c->status = NT_STATUS_OK;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* the registration timed out - good, send the demand */
|
||||
state->io->in.register_demand = True;
|
||||
state->io->in.retries = 0;
|
||||
state->req = nbt_name_register_send(state->nbtsock, state->io);
|
||||
if (state->req == NULL) {
|
||||
c->state = COMPOSITE_STATE_ERROR;
|
||||
c->status = NT_STATUS_NO_MEMORY;
|
||||
} else {
|
||||
state->req->async.fn = name_register_bcast_handler;
|
||||
state->req->async.private = c;
|
||||
}
|
||||
} else if (!NT_STATUS_IS_OK(status)) {
|
||||
c->state = COMPOSITE_STATE_ERROR;
|
||||
c->status = status;
|
||||
} else {
|
||||
c->state = COMPOSITE_STATE_ERROR;
|
||||
c->status = NT_STATUS_CONFLICTING_ADDRESSES;
|
||||
DEBUG(3,("Name registration conflict from %s for %s with ip %s - rcode %d\n",
|
||||
state->io->out.reply_from,
|
||||
nbt_name_string(state, &state->io->out.name),
|
||||
state->io->out.reply_addr,
|
||||
state->io->out.rcode));
|
||||
}
|
||||
|
||||
done:
|
||||
if (c->state >= COMPOSITE_STATE_DONE &&
|
||||
c->async.fn) {
|
||||
c->async.fn(c);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
the async send call for a 4 stage name registration
|
||||
*/
|
||||
struct composite_context *nbt_name_register_bcast_send(struct nbt_name_socket *nbtsock,
|
||||
struct nbt_name_register_bcast *io)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct register_bcast_state *state;
|
||||
|
||||
c = talloc_zero(nbtsock, struct composite_context);
|
||||
if (c == NULL) goto failed;
|
||||
|
||||
state = talloc(c, struct register_bcast_state);
|
||||
if (state == NULL) goto failed;
|
||||
|
||||
state->io = talloc(state, struct nbt_name_register);
|
||||
if (state->io == NULL) goto failed;
|
||||
|
||||
state->io->in.name = io->in.name;
|
||||
state->io->in.dest_addr = io->in.dest_addr;
|
||||
state->io->in.address = io->in.address;
|
||||
state->io->in.nb_flags = io->in.nb_flags;
|
||||
state->io->in.register_demand = False;
|
||||
state->io->in.broadcast = True;
|
||||
state->io->in.multi_homed = False;
|
||||
state->io->in.ttl = io->in.ttl;
|
||||
state->io->in.timeout = 1;
|
||||
state->io->in.retries = 2;
|
||||
|
||||
state->nbtsock = nbtsock;
|
||||
|
||||
state->req = nbt_name_register_send(nbtsock, state->io);
|
||||
if (state->req == NULL) goto failed;
|
||||
|
||||
state->req->async.fn = name_register_bcast_handler;
|
||||
state->req->async.private = c;
|
||||
|
||||
c->private_data = state;
|
||||
c->state = COMPOSITE_STATE_IN_PROGRESS;
|
||||
c->event_ctx = nbtsock->event_ctx;
|
||||
|
||||
return c;
|
||||
|
||||
failed:
|
||||
talloc_free(c);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
broadcast 4 part name register - recv
|
||||
*/
|
||||
NTSTATUS nbt_name_register_bcast_recv(struct composite_context *c)
|
||||
{
|
||||
NTSTATUS status;
|
||||
status = composite_wait(c);
|
||||
talloc_free(c);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
broadcast 4 part name register - sync interface
|
||||
*/
|
||||
NTSTATUS nbt_name_register_bcast(struct nbt_name_socket *nbtsock,
|
||||
struct nbt_name_register_bcast *io)
|
||||
{
|
||||
struct composite_context *c = nbt_name_register_bcast_send(nbtsock, io);
|
||||
return nbt_name_register_bcast_recv(c);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
a wins name register with multiple WINS servers and multiple
|
||||
addresses to register. Try each WINS server in turn, until we get a
|
||||
reply for each address
|
||||
*/
|
||||
struct register_wins_state {
|
||||
struct nbt_name_socket *nbtsock;
|
||||
struct nbt_name_register *io;
|
||||
const char **wins_servers;
|
||||
const char **addresses;
|
||||
int address_idx;
|
||||
struct nbt_name_request *req;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
state handler for WINS multi-homed multi-server name register
|
||||
*/
|
||||
static void name_register_wins_handler(struct nbt_name_request *req)
|
||||
{
|
||||
struct composite_context *c = talloc_get_type(req->async.private,
|
||||
struct composite_context);
|
||||
struct register_wins_state *state = talloc_get_type(c->private_data,
|
||||
struct register_wins_state);
|
||||
NTSTATUS status;
|
||||
|
||||
status = nbt_name_register_recv(state->req, state, state->io);
|
||||
if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
|
||||
/* the register timed out - try the next WINS server */
|
||||
state->wins_servers++;
|
||||
state->address_idx = 0;
|
||||
if (state->wins_servers[0] == NULL) {
|
||||
c->state = COMPOSITE_STATE_ERROR;
|
||||
c->status = status;
|
||||
goto done;
|
||||
}
|
||||
state->io->in.dest_addr = state->wins_servers[0];
|
||||
state->io->in.address = state->addresses[0];
|
||||
state->req = nbt_name_register_send(state->nbtsock, state->io);
|
||||
if (state->req == NULL) {
|
||||
c->state = COMPOSITE_STATE_ERROR;
|
||||
c->status = NT_STATUS_NO_MEMORY;
|
||||
} else {
|
||||
state->req->async.fn = name_register_wins_handler;
|
||||
state->req->async.private = c;
|
||||
}
|
||||
} else if (!NT_STATUS_IS_OK(status)) {
|
||||
c->state = COMPOSITE_STATE_ERROR;
|
||||
c->status = status;
|
||||
} else {
|
||||
if (state->io->out.rcode == 0 &&
|
||||
state->addresses[state->address_idx+1] != NULL) {
|
||||
/* register our next address */
|
||||
state->io->in.address = state->addresses[++(state->address_idx)];
|
||||
state->req = nbt_name_register_send(state->nbtsock, state->io);
|
||||
if (state->req == NULL) {
|
||||
c->state = COMPOSITE_STATE_ERROR;
|
||||
c->status = NT_STATUS_NO_MEMORY;
|
||||
} else {
|
||||
state->req->async.fn = name_register_wins_handler;
|
||||
state->req->async.private = c;
|
||||
}
|
||||
} else {
|
||||
c->state = COMPOSITE_STATE_DONE;
|
||||
c->status = NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
if (c->state >= COMPOSITE_STATE_DONE &&
|
||||
c->async.fn) {
|
||||
c->async.fn(c);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
the async send call for a multi-server WINS register
|
||||
*/
|
||||
struct composite_context *nbt_name_register_wins_send(struct nbt_name_socket *nbtsock,
|
||||
struct nbt_name_register_wins *io)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct register_wins_state *state;
|
||||
|
||||
c = talloc_zero(nbtsock, struct composite_context);
|
||||
if (c == NULL) goto failed;
|
||||
|
||||
state = talloc(c, struct register_wins_state);
|
||||
if (state == NULL) goto failed;
|
||||
|
||||
state->io = talloc(state, struct nbt_name_register);
|
||||
if (state->io == NULL) goto failed;
|
||||
|
||||
state->wins_servers = str_list_copy(state, io->in.wins_servers);
|
||||
if (state->wins_servers == NULL ||
|
||||
state->wins_servers[0] == NULL) goto failed;
|
||||
|
||||
state->addresses = str_list_copy(state, io->in.addresses);
|
||||
if (state->addresses == NULL ||
|
||||
state->addresses[0] == NULL) goto failed;
|
||||
|
||||
state->io->in.name = io->in.name;
|
||||
state->io->in.dest_addr = state->wins_servers[0];
|
||||
state->io->in.address = io->in.addresses[0];
|
||||
state->io->in.nb_flags = io->in.nb_flags;
|
||||
state->io->in.broadcast = False;
|
||||
state->io->in.register_demand = False;
|
||||
state->io->in.multi_homed = (io->in.nb_flags & NBT_NM_GROUP)?False:True;
|
||||
state->io->in.ttl = io->in.ttl;
|
||||
state->io->in.timeout = 3;
|
||||
state->io->in.retries = 2;
|
||||
|
||||
state->nbtsock = nbtsock;
|
||||
state->address_idx = 0;
|
||||
|
||||
state->req = nbt_name_register_send(nbtsock, state->io);
|
||||
if (state->req == NULL) goto failed;
|
||||
|
||||
state->req->async.fn = name_register_wins_handler;
|
||||
state->req->async.private = c;
|
||||
|
||||
c->private_data = state;
|
||||
c->state = COMPOSITE_STATE_IN_PROGRESS;
|
||||
c->event_ctx = nbtsock->event_ctx;
|
||||
|
||||
return c;
|
||||
|
||||
failed:
|
||||
talloc_free(c);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
multi-homed WINS name register - recv side
|
||||
*/
|
||||
NTSTATUS nbt_name_register_wins_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
|
||||
struct nbt_name_register_wins *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
status = composite_wait(c);
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
struct register_wins_state *state =
|
||||
talloc_get_type(c->private_data, struct register_wins_state);
|
||||
io->out.wins_server = talloc_steal(mem_ctx, state->wins_servers[0]);
|
||||
io->out.rcode = state->io->out.rcode;
|
||||
}
|
||||
talloc_free(c);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
multi-homed WINS register - sync interface
|
||||
*/
|
||||
NTSTATUS nbt_name_register_wins(struct nbt_name_socket *nbtsock,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct nbt_name_register_wins *io)
|
||||
{
|
||||
struct composite_context *c = nbt_name_register_wins_send(nbtsock, io);
|
||||
return nbt_name_register_wins_recv(c, mem_ctx, io);
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
send out a name release request
|
||||
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/nbt/libnbt.h"
|
||||
#include "lib/socket/socket.h"
|
||||
|
||||
/*
|
||||
send a nbt name release request
|
||||
*/
|
||||
struct nbt_name_request *nbt_name_release_send(struct nbt_name_socket *nbtsock,
|
||||
struct nbt_name_release *io)
|
||||
{
|
||||
struct nbt_name_request *req;
|
||||
struct nbt_name_packet *packet;
|
||||
struct socket_address *dest;
|
||||
|
||||
packet = talloc_zero(nbtsock, struct nbt_name_packet);
|
||||
if (packet == NULL) return NULL;
|
||||
|
||||
packet->qdcount = 1;
|
||||
packet->arcount = 1;
|
||||
packet->operation = NBT_OPCODE_RELEASE;
|
||||
if (io->in.broadcast) {
|
||||
packet->operation |= NBT_FLAG_BROADCAST;
|
||||
}
|
||||
|
||||
packet->questions = talloc_array(packet, struct nbt_name_question, 1);
|
||||
if (packet->questions == NULL) goto failed;
|
||||
|
||||
packet->questions[0].name = io->in.name;
|
||||
packet->questions[0].question_type = NBT_QTYPE_NETBIOS;
|
||||
packet->questions[0].question_class = NBT_QCLASS_IP;
|
||||
|
||||
packet->additional = talloc_array(packet, struct nbt_res_rec, 1);
|
||||
if (packet->additional == NULL) goto failed;
|
||||
|
||||
packet->additional[0].name = io->in.name;
|
||||
packet->additional[0].rr_type = NBT_QTYPE_NETBIOS;
|
||||
packet->additional[0].rr_class = NBT_QCLASS_IP;
|
||||
packet->additional[0].ttl = 0;
|
||||
packet->additional[0].rdata.netbios.length = 6;
|
||||
packet->additional[0].rdata.netbios.addresses = talloc_array(packet->additional,
|
||||
struct nbt_rdata_address, 1);
|
||||
if (packet->additional[0].rdata.netbios.addresses == NULL) goto failed;
|
||||
packet->additional[0].rdata.netbios.addresses[0].nb_flags = io->in.nb_flags;
|
||||
packet->additional[0].rdata.netbios.addresses[0].ipaddr =
|
||||
talloc_strdup(packet->additional, io->in.address);
|
||||
|
||||
dest = socket_address_from_strings(packet, nbtsock->sock->backend_name,
|
||||
io->in.dest_addr, lp_nbt_port());
|
||||
if (dest == NULL) goto failed;
|
||||
req = nbt_name_request_send(nbtsock, dest, packet,
|
||||
io->in.timeout, io->in.retries, False);
|
||||
if (req == NULL) goto failed;
|
||||
|
||||
talloc_free(packet);
|
||||
return req;
|
||||
|
||||
failed:
|
||||
talloc_free(packet);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
wait for a release reply
|
||||
*/
|
||||
NTSTATUS nbt_name_release_recv(struct nbt_name_request *req,
|
||||
TALLOC_CTX *mem_ctx, struct nbt_name_release *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct nbt_name_packet *packet;
|
||||
|
||||
status = nbt_name_request_recv(req);
|
||||
if (!NT_STATUS_IS_OK(status) ||
|
||||
req->num_replies == 0) {
|
||||
talloc_free(req);
|
||||
return status;
|
||||
}
|
||||
|
||||
packet = req->replies[0].packet;
|
||||
io->out.reply_from = talloc_steal(mem_ctx, req->replies[0].dest->addr);
|
||||
|
||||
if (packet->ancount != 1 ||
|
||||
packet->answers[0].rr_type != NBT_QTYPE_NETBIOS ||
|
||||
packet->answers[0].rr_class != NBT_QCLASS_IP) {
|
||||
talloc_free(req);
|
||||
return NT_STATUS_INVALID_NETWORK_RESPONSE;
|
||||
}
|
||||
|
||||
io->out.rcode = packet->operation & NBT_RCODE;
|
||||
io->out.name = packet->answers[0].name;
|
||||
if (packet->answers[0].rdata.netbios.length < 6) {
|
||||
talloc_free(req);
|
||||
return NT_STATUS_INVALID_NETWORK_RESPONSE;
|
||||
}
|
||||
io->out.reply_addr = talloc_steal(mem_ctx,
|
||||
packet->answers[0].rdata.netbios.addresses[0].ipaddr);
|
||||
talloc_steal(mem_ctx, io->out.name.name);
|
||||
talloc_steal(mem_ctx, io->out.name.scope);
|
||||
|
||||
talloc_free(req);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
synchronous name release request
|
||||
*/
|
||||
NTSTATUS nbt_name_release(struct nbt_name_socket *nbtsock,
|
||||
TALLOC_CTX *mem_ctx, struct nbt_name_release *io)
|
||||
{
|
||||
struct nbt_name_request *req = nbt_name_release_send(nbtsock, io);
|
||||
return nbt_name_release_recv(req, mem_ctx, io);
|
||||
}
|
||||
@@ -0,0 +1,595 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
manipulate nbt name structures
|
||||
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
see rfc1002 for the detailed format of compressed names
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "librpc/gen_ndr/ndr_nbt.h"
|
||||
#include "librpc/gen_ndr/ndr_misc.h"
|
||||
#include "system/locale.h"
|
||||
|
||||
/* don't allow an unlimited number of name components */
|
||||
#define MAX_COMPONENTS 10
|
||||
|
||||
/**
|
||||
print a nbt string
|
||||
*/
|
||||
_PUBLIC_ void ndr_print_nbt_string(struct ndr_print *ndr, const char *name, const char *s)
|
||||
{
|
||||
ndr_print_string(ndr, name, s);
|
||||
}
|
||||
|
||||
/*
|
||||
pull one component of a nbt_string
|
||||
*/
|
||||
static NTSTATUS ndr_pull_component(struct ndr_pull *ndr, uint8_t **component,
|
||||
uint32_t *offset, uint32_t *max_offset)
|
||||
{
|
||||
uint8_t len;
|
||||
uint_t loops = 0;
|
||||
while (loops < 5) {
|
||||
if (*offset >= ndr->data_size) {
|
||||
return NT_STATUS_BAD_NETWORK_NAME;
|
||||
}
|
||||
len = ndr->data[*offset];
|
||||
if (len == 0) {
|
||||
*offset += 1;
|
||||
*max_offset = MAX(*max_offset, *offset);
|
||||
*component = NULL;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
if ((len & 0xC0) == 0xC0) {
|
||||
/* its a label pointer */
|
||||
if (1 + *offset >= ndr->data_size) {
|
||||
return NT_STATUS_BAD_NETWORK_NAME;
|
||||
}
|
||||
*max_offset = MAX(*max_offset, *offset + 2);
|
||||
*offset = ((len&0x3F)<<8) | ndr->data[1 + *offset];
|
||||
*max_offset = MAX(*max_offset, *offset);
|
||||
loops++;
|
||||
continue;
|
||||
}
|
||||
if ((len & 0xC0) != 0) {
|
||||
/* its a reserved length field */
|
||||
return NT_STATUS_BAD_NETWORK_NAME;
|
||||
}
|
||||
if (*offset + len + 2 > ndr->data_size) {
|
||||
return NT_STATUS_BAD_NETWORK_NAME;
|
||||
}
|
||||
*component = (uint8_t*)talloc_strndup(ndr, (const char *)&ndr->data[1 + *offset], len);
|
||||
NT_STATUS_HAVE_NO_MEMORY(*component);
|
||||
*offset += len + 1;
|
||||
*max_offset = MAX(*max_offset, *offset);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* too many pointers */
|
||||
return NT_STATUS_BAD_NETWORK_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
pull a nbt_string from the wire
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS ndr_pull_nbt_string(struct ndr_pull *ndr, int ndr_flags, const char **s)
|
||||
{
|
||||
NTSTATUS status;
|
||||
uint32_t offset = ndr->offset;
|
||||
uint32_t max_offset = offset;
|
||||
unsigned num_components;
|
||||
char *name;
|
||||
|
||||
if (!(ndr_flags & NDR_SCALARS)) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
name = NULL;
|
||||
|
||||
/* break up name into a list of components */
|
||||
for (num_components=0;num_components<MAX_COMPONENTS;num_components++) {
|
||||
uint8_t *component;
|
||||
status = ndr_pull_component(ndr, &component, &offset, &max_offset);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
if (component == NULL) break;
|
||||
if (name) {
|
||||
name = talloc_asprintf_append(name, ".%s", component);
|
||||
NT_STATUS_HAVE_NO_MEMORY(name);
|
||||
} else {
|
||||
name = (char *)component;
|
||||
}
|
||||
}
|
||||
if (num_components == MAX_COMPONENTS) {
|
||||
return NT_STATUS_BAD_NETWORK_NAME;
|
||||
}
|
||||
if (num_components == 0) {
|
||||
name = talloc_strdup(ndr, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(name);
|
||||
}
|
||||
|
||||
(*s) = name;
|
||||
ndr->offset = max_offset;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
push a nbt string to the wire
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS ndr_push_nbt_string(struct ndr_push *ndr, int ndr_flags, const char *s)
|
||||
{
|
||||
if (!(ndr_flags & NDR_SCALARS)) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
while (s && *s) {
|
||||
NTSTATUS status;
|
||||
char *compname;
|
||||
size_t complen;
|
||||
uint32_t offset;
|
||||
|
||||
/* see if we have pushed the remaing string allready,
|
||||
* if so we use a label pointer to this string
|
||||
*/
|
||||
status = ndr_token_retrieve_cmp_fn(&ndr->nbt_string_list, s, &offset, (comparison_fn_t)strcmp, False);
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
uint8_t b[2];
|
||||
|
||||
if (offset > 0x3FFF) {
|
||||
return ndr_push_error(ndr, NDR_ERR_STRING,
|
||||
"offset for nbt string label pointer %u[%08X] > 0x00003FFF",
|
||||
offset, offset);
|
||||
}
|
||||
|
||||
b[0] = 0xC0 | (offset>>8);
|
||||
b[1] = (offset & 0xFF);
|
||||
|
||||
return ndr_push_bytes(ndr, b, 2);
|
||||
}
|
||||
|
||||
complen = strcspn(s, ".");
|
||||
|
||||
/* we need to make sure the length fits into 6 bytes */
|
||||
if (complen >= 0x3F) {
|
||||
return ndr_push_error(ndr, NDR_ERR_STRING,
|
||||
"component length %u[%08X] > 0x00003F",
|
||||
(unsigned)complen, (unsigned)complen);
|
||||
}
|
||||
|
||||
compname = talloc_asprintf(ndr, "%c%*.*s",
|
||||
(unsigned char)complen,
|
||||
(unsigned char)complen,
|
||||
(unsigned char)complen, s);
|
||||
NT_STATUS_HAVE_NO_MEMORY(compname);
|
||||
|
||||
/* remember the current componemt + the rest of the string
|
||||
* so it can be reused later
|
||||
*/
|
||||
NDR_CHECK(ndr_token_store(ndr, &ndr->nbt_string_list, s, ndr->offset));
|
||||
|
||||
/* push just this component into the blob */
|
||||
NDR_CHECK(ndr_push_bytes(ndr, (const uint8_t *)compname, complen+1));
|
||||
talloc_free(compname);
|
||||
|
||||
s += complen;
|
||||
if (*s == '.') s++;
|
||||
}
|
||||
|
||||
/* if we reach the end of the string and have pushed the last component
|
||||
* without using a label pointer, we need to terminate the string
|
||||
*/
|
||||
return ndr_push_bytes(ndr, (const uint8_t *)"", 1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
decompress a 'compressed' name component
|
||||
*/
|
||||
static NTSTATUS decompress_name(char *name, enum nbt_name_type *type)
|
||||
{
|
||||
int i;
|
||||
for (i=0;name[2*i];i++) {
|
||||
uint8_t c1 = name[2*i];
|
||||
uint8_t c2 = name[1+(2*i)];
|
||||
if (c1 < 'A' || c1 > 'P' ||
|
||||
c2 < 'A' || c2 > 'P') {
|
||||
return NT_STATUS_BAD_NETWORK_NAME;
|
||||
}
|
||||
name[i] = ((c1-'A')<<4) | (c2-'A');
|
||||
}
|
||||
name[i] = 0;
|
||||
if (i == 16) {
|
||||
*type = (enum nbt_name_type)(name[15]);
|
||||
name[15] = 0;
|
||||
i--;
|
||||
} else {
|
||||
*type = NBT_NAME_CLIENT;
|
||||
}
|
||||
|
||||
/* trim trailing spaces */
|
||||
for (;i>0 && name[i-1]==' ';i--) {
|
||||
name[i-1] = 0;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
compress a name component
|
||||
*/
|
||||
static uint8_t *compress_name(TALLOC_CTX *mem_ctx,
|
||||
const uint8_t *name, enum nbt_name_type type)
|
||||
{
|
||||
uint8_t *cname;
|
||||
int i;
|
||||
uint8_t pad_char;
|
||||
|
||||
if (strlen((const char *)name) > 15) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cname = talloc_array(mem_ctx, uint8_t, 33);
|
||||
if (cname == NULL) return NULL;
|
||||
|
||||
for (i=0;name[i];i++) {
|
||||
cname[2*i] = 'A' + (name[i]>>4);
|
||||
cname[1+2*i] = 'A' + (name[i]&0xF);
|
||||
}
|
||||
if (strcmp((const char *)name, "*") == 0) {
|
||||
pad_char = 0;
|
||||
} else {
|
||||
pad_char = ' ';
|
||||
}
|
||||
for (;i<15;i++) {
|
||||
cname[2*i] = 'A' + (pad_char>>4);
|
||||
cname[1+2*i] = 'A' + (pad_char&0xF);
|
||||
}
|
||||
|
||||
pad_char = type;
|
||||
cname[2*i] = 'A' + (pad_char>>4);
|
||||
cname[1+2*i] = 'A' + (pad_char&0xF);
|
||||
|
||||
cname[32] = 0;
|
||||
return cname;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
pull a nbt name from the wire
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS ndr_pull_nbt_name(struct ndr_pull *ndr, int ndr_flags, struct nbt_name *r)
|
||||
{
|
||||
NTSTATUS status;
|
||||
uint8_t *scope;
|
||||
char *cname;
|
||||
const char *s;
|
||||
|
||||
if (!(ndr_flags & NDR_SCALARS)) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
status = ndr_pull_nbt_string(ndr, ndr_flags, &s);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
scope = (uint8_t *)strchr(s, '.');
|
||||
if (scope) {
|
||||
*scope = 0;
|
||||
r->scope = talloc_strdup(ndr->current_mem_ctx, (const char *)&scope[1]);
|
||||
NT_STATUS_HAVE_NO_MEMORY(r->scope);
|
||||
} else {
|
||||
r->scope = NULL;
|
||||
}
|
||||
|
||||
cname = discard_const_p(char, s);
|
||||
|
||||
/* the first component is limited to 16 bytes in the DOS charset,
|
||||
which is 32 in the 'compressed' form */
|
||||
if (strlen(cname) > 32) {
|
||||
return NT_STATUS_BAD_NETWORK_NAME;
|
||||
}
|
||||
|
||||
/* decompress the first component */
|
||||
status = decompress_name(cname, &r->type);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
r->name = talloc_strdup(ndr->current_mem_ctx, cname);
|
||||
NT_STATUS_HAVE_NO_MEMORY(r->name);
|
||||
|
||||
talloc_free(cname);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
push a nbt name to the wire
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS ndr_push_nbt_name(struct ndr_push *ndr, int ndr_flags, const struct nbt_name *r)
|
||||
{
|
||||
uint8_t *cname, *fullname;
|
||||
NTSTATUS status;
|
||||
|
||||
if (!(ndr_flags & NDR_SCALARS)) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
cname = compress_name(ndr, (const uint8_t *)r->name, r->type);
|
||||
NT_STATUS_HAVE_NO_MEMORY(cname);
|
||||
|
||||
if (r->scope) {
|
||||
fullname = (uint8_t *)talloc_asprintf(ndr, "%s.%s", cname, r->scope);
|
||||
NT_STATUS_HAVE_NO_MEMORY(fullname);
|
||||
talloc_free(cname);
|
||||
} else {
|
||||
fullname = cname;
|
||||
}
|
||||
|
||||
status = ndr_push_nbt_string(ndr, ndr_flags, (const char *)fullname);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
copy a nbt name structure
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS nbt_name_dup(TALLOC_CTX *mem_ctx, struct nbt_name *name, struct nbt_name *newname)
|
||||
{
|
||||
*newname = *name;
|
||||
newname->name = talloc_strdup(mem_ctx, newname->name);
|
||||
NT_STATUS_HAVE_NO_MEMORY(newname->name);
|
||||
newname->scope = talloc_strdup(mem_ctx, newname->scope);
|
||||
if (name->scope) {
|
||||
NT_STATUS_HAVE_NO_MEMORY(newname->scope);
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
push a nbt name into a blob
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS nbt_name_to_blob(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, struct nbt_name *name)
|
||||
{
|
||||
return ndr_push_struct_blob(blob, mem_ctx, name,
|
||||
(ndr_push_flags_fn_t)ndr_push_nbt_name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
pull a nbt name from a blob
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS nbt_name_from_blob(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, struct nbt_name *name)
|
||||
{
|
||||
return ndr_pull_struct_blob(blob, mem_ctx, name,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_nbt_name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
choose a name to use when calling a server in a NBT session request.
|
||||
we use heuristics to see if the name we have been given is a IP
|
||||
address, or a too-long name. If it is then use *SMBSERVER, or a
|
||||
truncated name
|
||||
*/
|
||||
_PUBLIC_ void nbt_choose_called_name(TALLOC_CTX *mem_ctx,
|
||||
struct nbt_name *n, const char *name, int type)
|
||||
{
|
||||
n->scope = NULL;
|
||||
n->type = type;
|
||||
|
||||
if (is_ipaddress(name)) {
|
||||
n->name = "*SMBSERVER";
|
||||
return;
|
||||
}
|
||||
if (strlen(name) > 15) {
|
||||
const char *p = strchr(name, '.');
|
||||
char *s;
|
||||
if (p - name > 15) {
|
||||
n->name = "*SMBSERVER";
|
||||
return;
|
||||
}
|
||||
s = talloc_strndup(mem_ctx, name, PTR_DIFF(p, name));
|
||||
n->name = strupper_talloc(mem_ctx, s);
|
||||
return;
|
||||
}
|
||||
|
||||
n->name = strupper_talloc(mem_ctx, name);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
escape a string into a form containing only a small set of characters,
|
||||
the rest is hex encoded. This is similar to URL encoding
|
||||
*/
|
||||
static const char *nbt_hex_encode(TALLOC_CTX *mem_ctx, const char *s)
|
||||
{
|
||||
int i, len;
|
||||
char *ret;
|
||||
const char *valid_chars = "_-.$@ ";
|
||||
#define NBT_CHAR_ALLOW(c) (isalnum((unsigned char)c) || strchr(valid_chars, c))
|
||||
|
||||
for (len=i=0;s[i];i++,len++) {
|
||||
if (!NBT_CHAR_ALLOW(s[i])) {
|
||||
len += 2;
|
||||
}
|
||||
}
|
||||
|
||||
ret = talloc_array(mem_ctx, char, len+1);
|
||||
if (ret == NULL) return NULL;
|
||||
|
||||
for (len=i=0;s[i];i++) {
|
||||
if (NBT_CHAR_ALLOW(s[i])) {
|
||||
ret[len++] = s[i];
|
||||
} else {
|
||||
snprintf(&ret[len], 4, "%%%02x", (unsigned char)s[i]);
|
||||
len += 3;
|
||||
}
|
||||
}
|
||||
ret[len] = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
form a string for a NBT name
|
||||
*/
|
||||
_PUBLIC_ char *nbt_name_string(TALLOC_CTX *mem_ctx, const struct nbt_name *name)
|
||||
{
|
||||
TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
|
||||
char *ret;
|
||||
if (name->scope) {
|
||||
ret = talloc_asprintf(mem_ctx, "%s<%02x>-%s",
|
||||
nbt_hex_encode(tmp_ctx, name->name),
|
||||
name->type,
|
||||
nbt_hex_encode(tmp_ctx, name->scope));
|
||||
} else {
|
||||
ret = talloc_asprintf(mem_ctx, "%s<%02x>",
|
||||
nbt_hex_encode(tmp_ctx, name->name),
|
||||
name->type);
|
||||
}
|
||||
talloc_free(tmp_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
pull a nbt name, WINS Replication uses another on wire format for nbt name
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS ndr_pull_wrepl_nbt_name(struct ndr_pull *ndr, int ndr_flags, struct nbt_name **_r)
|
||||
{
|
||||
struct nbt_name *r;
|
||||
uint8_t *namebuf;
|
||||
uint32_t namebuf_len;
|
||||
|
||||
if (!(ndr_flags & NDR_SCALARS)) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NDR_CHECK(ndr_pull_align(ndr, 4));
|
||||
NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &namebuf_len));
|
||||
if (namebuf_len < 1 || namebuf_len > 255) {
|
||||
return ndr_pull_error(ndr, NDR_ERR_ALLOC, "value out of range");
|
||||
}
|
||||
NDR_PULL_ALLOC_N(ndr, namebuf, namebuf_len);
|
||||
NDR_CHECK(ndr_pull_array_uint8(ndr, NDR_SCALARS, namebuf, namebuf_len));
|
||||
|
||||
NDR_PULL_ALLOC(ndr, r);
|
||||
|
||||
/* oh wow, what a nasty bug in windows ... */
|
||||
if (namebuf[0] == 0x1b && namebuf_len >= 16) {
|
||||
namebuf[0] = namebuf[15];
|
||||
namebuf[15] = 0x1b;
|
||||
}
|
||||
|
||||
if (namebuf_len < 17) {
|
||||
r->type = 0x00;
|
||||
|
||||
r->name = talloc_strndup(r, (char *)namebuf, namebuf_len);
|
||||
if (!r->name) return ndr_pull_error(ndr, NDR_ERR_ALLOC, "out of memory");
|
||||
|
||||
r->scope= NULL;
|
||||
|
||||
talloc_free(namebuf);
|
||||
*_r = r;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
r->type = namebuf[15];
|
||||
|
||||
namebuf[15] = '\0';
|
||||
trim_string((char *)namebuf, NULL, " ");
|
||||
r->name = talloc_strdup(r, (char *)namebuf);
|
||||
if (!r->name) return ndr_pull_error(ndr, NDR_ERR_ALLOC, "out of memory");
|
||||
|
||||
if (namebuf_len > 18) {
|
||||
r->scope = talloc_strndup(r, (char *)(namebuf+17), namebuf_len-17);
|
||||
if (!r->scope) return ndr_pull_error(ndr, NDR_ERR_ALLOC, "out of memory");
|
||||
} else {
|
||||
r->scope = NULL;
|
||||
}
|
||||
|
||||
talloc_free(namebuf);
|
||||
*_r = r;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
push a nbt name, WINS Replication uses another on wire format for nbt name
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS ndr_push_wrepl_nbt_name(struct ndr_push *ndr, int ndr_flags, const struct nbt_name *r)
|
||||
{
|
||||
uint8_t *namebuf;
|
||||
uint32_t namebuf_len;
|
||||
uint32_t name_len;
|
||||
uint32_t scope_len = 0;
|
||||
|
||||
if (r == NULL) return NT_STATUS_INVALID_PARAMETER_MIX;
|
||||
|
||||
if (!(ndr_flags & NDR_SCALARS)) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
name_len = strlen(r->name);
|
||||
if (name_len > 15) {
|
||||
return NT_STATUS_INVALID_PARAMETER_MIX;
|
||||
}
|
||||
|
||||
if (r->scope) {
|
||||
scope_len = strlen(r->scope);
|
||||
}
|
||||
if (scope_len > 238) {
|
||||
return NT_STATUS_INVALID_PARAMETER_MIX;
|
||||
}
|
||||
|
||||
namebuf = (uint8_t *)talloc_asprintf(ndr, "%-15s%c%s",
|
||||
r->name, 'X',
|
||||
(r->scope?r->scope:""));
|
||||
if (!namebuf) return ndr_push_error(ndr, NDR_ERR_ALLOC, "out of memory");
|
||||
|
||||
namebuf_len = strlen((char *)namebuf) + 1;
|
||||
|
||||
/*
|
||||
* we need to set the type here, and use a place-holder in the talloc_asprintf()
|
||||
* as the type can be 0x00, and then the namebuf_len = strlen(namebuf); would give wrong results
|
||||
*/
|
||||
namebuf[15] = r->type;
|
||||
|
||||
/* oh wow, what a nasty bug in windows ... */
|
||||
if (r->type == 0x1b) {
|
||||
namebuf[15] = namebuf[0];
|
||||
namebuf[0] = 0x1b;
|
||||
}
|
||||
|
||||
NDR_CHECK(ndr_push_align(ndr, 4));
|
||||
NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, namebuf_len));
|
||||
NDR_CHECK(ndr_push_array_uint8(ndr, NDR_SCALARS, namebuf, namebuf_len));
|
||||
|
||||
talloc_free(namebuf);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
_PUBLIC_ void ndr_print_wrepl_nbt_name(struct ndr_print *ndr, const char *name, const struct nbt_name *r)
|
||||
{
|
||||
char *s = nbt_name_string(ndr, r);
|
||||
ndr_print_string(ndr, name, s);
|
||||
talloc_free(s);
|
||||
}
|
||||
@@ -0,0 +1,526 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
low level socket handling for nbt 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "lib/events/events.h"
|
||||
#include "lib/util/dlinklist.h"
|
||||
#include "libcli/nbt/libnbt.h"
|
||||
#include "lib/socket/socket.h"
|
||||
#include "librpc/gen_ndr/ndr_nbt.h"
|
||||
|
||||
#define NBT_MAX_REPLIES 1000
|
||||
|
||||
/*
|
||||
destroy a pending request
|
||||
*/
|
||||
static int nbt_name_request_destructor(struct nbt_name_request *req)
|
||||
{
|
||||
if (req->state == NBT_REQUEST_SEND) {
|
||||
DLIST_REMOVE(req->nbtsock->send_queue, req);
|
||||
}
|
||||
if (req->state == NBT_REQUEST_WAIT) {
|
||||
req->nbtsock->num_pending--;
|
||||
}
|
||||
if (req->name_trn_id != 0 && !req->is_reply) {
|
||||
idr_remove(req->nbtsock->idr, req->name_trn_id);
|
||||
req->name_trn_id = 0;
|
||||
}
|
||||
if (req->te) {
|
||||
req->te = NULL;
|
||||
}
|
||||
if (req->nbtsock->send_queue == NULL) {
|
||||
EVENT_FD_NOT_WRITEABLE(req->nbtsock->fde);
|
||||
}
|
||||
if (req->nbtsock->num_pending == 0 &&
|
||||
req->nbtsock->incoming.handler == NULL) {
|
||||
EVENT_FD_NOT_READABLE(req->nbtsock->fde);
|
||||
}
|
||||
|
||||
/* once this has been called for this req, don't call again */
|
||||
talloc_set_destructor(req, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
handle send events on a nbt name socket
|
||||
*/
|
||||
static void nbt_name_socket_send(struct nbt_name_socket *nbtsock)
|
||||
{
|
||||
struct nbt_name_request *req = nbtsock->send_queue;
|
||||
TALLOC_CTX *tmp_ctx = talloc_new(nbtsock);
|
||||
NTSTATUS status;
|
||||
|
||||
while ((req = nbtsock->send_queue)) {
|
||||
size_t len;
|
||||
|
||||
len = req->encoded.length;
|
||||
status = socket_sendto(nbtsock->sock, &req->encoded, &len,
|
||||
req->dest);
|
||||
if (NT_STATUS_IS_ERR(status)) goto failed;
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
DLIST_REMOVE(nbtsock->send_queue, req);
|
||||
req->state = NBT_REQUEST_WAIT;
|
||||
if (req->is_reply) {
|
||||
talloc_free(req);
|
||||
} else {
|
||||
EVENT_FD_READABLE(nbtsock->fde);
|
||||
nbtsock->num_pending++;
|
||||
}
|
||||
}
|
||||
|
||||
EVENT_FD_NOT_WRITEABLE(nbtsock->fde);
|
||||
talloc_free(tmp_ctx);
|
||||
return;
|
||||
|
||||
failed:
|
||||
DLIST_REMOVE(nbtsock->send_queue, req);
|
||||
nbt_name_request_destructor(req);
|
||||
req->status = status;
|
||||
req->state = NBT_REQUEST_ERROR;
|
||||
talloc_free(tmp_ctx);
|
||||
if (req->async.fn) {
|
||||
req->async.fn(req);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
handle a request timeout
|
||||
*/
|
||||
static void nbt_name_socket_timeout(struct event_context *ev, struct timed_event *te,
|
||||
struct timeval t, void *private)
|
||||
{
|
||||
struct nbt_name_request *req = talloc_get_type(private,
|
||||
struct nbt_name_request);
|
||||
|
||||
if (req->num_retries != 0) {
|
||||
req->num_retries--;
|
||||
req->te = event_add_timed(req->nbtsock->event_ctx, req,
|
||||
timeval_add(&t, req->timeout, 0),
|
||||
nbt_name_socket_timeout, req);
|
||||
if (req->state != NBT_REQUEST_SEND) {
|
||||
req->state = NBT_REQUEST_SEND;
|
||||
DLIST_ADD_END(req->nbtsock->send_queue, req,
|
||||
struct nbt_name_request *);
|
||||
}
|
||||
EVENT_FD_WRITEABLE(req->nbtsock->fde);
|
||||
return;
|
||||
}
|
||||
|
||||
nbt_name_request_destructor(req);
|
||||
if (req->num_replies == 0) {
|
||||
req->state = NBT_REQUEST_TIMEOUT;
|
||||
req->status = NT_STATUS_IO_TIMEOUT;
|
||||
} else {
|
||||
req->state = NBT_REQUEST_DONE;
|
||||
req->status = NT_STATUS_OK;
|
||||
}
|
||||
if (req->async.fn) {
|
||||
req->async.fn(req);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
handle recv events on a nbt name socket
|
||||
*/
|
||||
static void nbt_name_socket_recv(struct nbt_name_socket *nbtsock)
|
||||
{
|
||||
TALLOC_CTX *tmp_ctx = talloc_new(nbtsock);
|
||||
NTSTATUS status;
|
||||
struct socket_address *src;
|
||||
DATA_BLOB blob;
|
||||
size_t nread, dsize;
|
||||
struct nbt_name_packet *packet;
|
||||
struct nbt_name_request *req;
|
||||
|
||||
status = socket_pending(nbtsock->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(nbtsock->sock, blob.data, blob.length, &nread,
|
||||
tmp_ctx, &src);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
packet = talloc(tmp_ctx, struct nbt_name_packet);
|
||||
if (packet == NULL) {
|
||||
talloc_free(tmp_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
/* parse the request */
|
||||
status = ndr_pull_struct_blob(&blob, packet, packet,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_nbt_name_packet);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(2,("Failed to parse incoming NBT name packet - %s\n",
|
||||
nt_errstr(status)));
|
||||
talloc_free(tmp_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUGLVL(10)) {
|
||||
DEBUG(10,("Received nbt packet of length %d from %s:%d\n",
|
||||
(int)blob.length, src->addr, src->port));
|
||||
NDR_PRINT_DEBUG(nbt_name_packet, packet);
|
||||
}
|
||||
|
||||
/* if its not a reply then pass it off to the incoming request
|
||||
handler, if any */
|
||||
if (!(packet->operation & NBT_FLAG_REPLY)) {
|
||||
if (nbtsock->incoming.handler) {
|
||||
nbtsock->incoming.handler(nbtsock, packet, src);
|
||||
}
|
||||
talloc_free(tmp_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
/* find the matching request */
|
||||
req = idr_find(nbtsock->idr, packet->name_trn_id);
|
||||
if (req == NULL) {
|
||||
if (nbtsock->unexpected.handler) {
|
||||
nbtsock->unexpected.handler(nbtsock, packet, src);
|
||||
} else {
|
||||
DEBUG(2,("Failed to match request for incoming name packet id 0x%04x on %p\n",
|
||||
packet->name_trn_id, nbtsock));
|
||||
}
|
||||
talloc_free(tmp_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
/* if this is a WACK response, this we need to go back to waiting,
|
||||
but perhaps increase the timeout */
|
||||
if ((packet->operation & NBT_OPCODE) == NBT_OPCODE_WACK) {
|
||||
if (req->received_wack || packet->ancount < 1) {
|
||||
nbt_name_request_destructor(req);
|
||||
req->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
|
||||
req->state = NBT_REQUEST_ERROR;
|
||||
goto done;
|
||||
}
|
||||
talloc_free(req->te);
|
||||
/* we know we won't need any more retries - the server
|
||||
has received our request */
|
||||
req->num_retries = 0;
|
||||
req->received_wack = True;
|
||||
/* although there can be a timeout in the packet, w2k3 screws it up,
|
||||
so better to set it ourselves */
|
||||
req->timeout = lp_parm_int(-1, "nbt", "wack_timeout", 30);
|
||||
req->te = event_add_timed(req->nbtsock->event_ctx, req,
|
||||
timeval_current_ofs(req->timeout, 0),
|
||||
nbt_name_socket_timeout, req);
|
||||
talloc_free(tmp_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
req->replies = talloc_realloc(req, req->replies, struct nbt_name_reply, req->num_replies+1);
|
||||
if (req->replies == NULL) {
|
||||
nbt_name_request_destructor(req);
|
||||
req->state = NBT_REQUEST_ERROR;
|
||||
req->status = NT_STATUS_NO_MEMORY;
|
||||
goto done;
|
||||
}
|
||||
|
||||
talloc_steal(req, src);
|
||||
req->replies[req->num_replies].dest = src;
|
||||
talloc_steal(req, packet);
|
||||
req->replies[req->num_replies].packet = packet;
|
||||
req->num_replies++;
|
||||
|
||||
/* if we don't want multiple replies then we are done */
|
||||
if (req->allow_multiple_replies &&
|
||||
req->num_replies < NBT_MAX_REPLIES) {
|
||||
talloc_free(tmp_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
nbt_name_request_destructor(req);
|
||||
req->state = NBT_REQUEST_DONE;
|
||||
req->status = NT_STATUS_OK;
|
||||
|
||||
done:
|
||||
if (DEBUGLVL(9)) {
|
||||
talloc_report(tmp_ctx, stdout);
|
||||
talloc_report(req, stdout);
|
||||
}
|
||||
if (req->async.fn) {
|
||||
req->async.fn(req);
|
||||
}
|
||||
talloc_free(tmp_ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
handle fd events on a nbt_name_socket
|
||||
*/
|
||||
static void nbt_name_socket_handler(struct event_context *ev, struct fd_event *fde,
|
||||
uint16_t flags, void *private)
|
||||
{
|
||||
struct nbt_name_socket *nbtsock = talloc_get_type(private,
|
||||
struct nbt_name_socket);
|
||||
if (nbtsock != NULL) {
|
||||
if (flags & EVENT_FD_WRITE) {
|
||||
nbt_name_socket_send(nbtsock);
|
||||
}
|
||||
if (flags & EVENT_FD_READ) {
|
||||
nbt_name_socket_recv(nbtsock);
|
||||
}
|
||||
}
|
||||
else {
|
||||
DEBUG(9, ("Error condition in nbt_name_socket_handler: nbtsock was not of type struct nbt_name_socket"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
initialise a nbt_name_socket. The event_ctx is optional, if provided
|
||||
then operations will use that event context
|
||||
*/
|
||||
_PUBLIC_ struct nbt_name_socket *nbt_name_socket_init(TALLOC_CTX *mem_ctx,
|
||||
struct event_context *event_ctx)
|
||||
{
|
||||
struct nbt_name_socket *nbtsock;
|
||||
NTSTATUS status;
|
||||
|
||||
nbtsock = talloc(mem_ctx, struct nbt_name_socket);
|
||||
if (nbtsock == NULL) goto failed;
|
||||
|
||||
if (event_ctx == NULL) {
|
||||
nbtsock->event_ctx = event_context_init(nbtsock);
|
||||
} else {
|
||||
nbtsock->event_ctx = talloc_reference(nbtsock, event_ctx);
|
||||
}
|
||||
if (nbtsock->event_ctx == NULL) goto failed;
|
||||
|
||||
status = socket_create("ip", SOCKET_TYPE_DGRAM, &nbtsock->sock, 0);
|
||||
if (!NT_STATUS_IS_OK(status)) goto failed;
|
||||
|
||||
socket_set_option(nbtsock->sock, "SO_BROADCAST", "1");
|
||||
|
||||
talloc_steal(nbtsock, nbtsock->sock);
|
||||
|
||||
nbtsock->idr = idr_init(nbtsock);
|
||||
if (nbtsock->idr == NULL) goto failed;
|
||||
|
||||
nbtsock->send_queue = NULL;
|
||||
nbtsock->num_pending = 0;
|
||||
nbtsock->incoming.handler = NULL;
|
||||
nbtsock->unexpected.handler = NULL;
|
||||
|
||||
nbtsock->fde = event_add_fd(nbtsock->event_ctx, nbtsock,
|
||||
socket_get_fd(nbtsock->sock), 0,
|
||||
nbt_name_socket_handler, nbtsock);
|
||||
|
||||
return nbtsock;
|
||||
|
||||
failed:
|
||||
talloc_free(nbtsock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
send off a nbt name request
|
||||
*/
|
||||
struct nbt_name_request *nbt_name_request_send(struct nbt_name_socket *nbtsock,
|
||||
struct socket_address *dest,
|
||||
struct nbt_name_packet *request,
|
||||
int timeout, int retries,
|
||||
BOOL allow_multiple_replies)
|
||||
{
|
||||
struct nbt_name_request *req;
|
||||
int id;
|
||||
NTSTATUS status;
|
||||
|
||||
req = talloc_zero(nbtsock, struct nbt_name_request);
|
||||
if (req == NULL) goto failed;
|
||||
|
||||
req->nbtsock = nbtsock;
|
||||
req->allow_multiple_replies = allow_multiple_replies;
|
||||
req->state = NBT_REQUEST_SEND;
|
||||
req->is_reply = False;
|
||||
req->timeout = timeout;
|
||||
req->num_retries = retries;
|
||||
req->dest = dest;
|
||||
if (talloc_reference(req, dest) == NULL) goto failed;
|
||||
|
||||
/* we select a random transaction id unless the user supplied one */
|
||||
if (request->name_trn_id == 0) {
|
||||
id = idr_get_new_random(req->nbtsock->idr, req, UINT16_MAX);
|
||||
} else {
|
||||
if (idr_find(req->nbtsock->idr, request->name_trn_id)) goto failed;
|
||||
id = idr_get_new_above(req->nbtsock->idr, req, request->name_trn_id,
|
||||
UINT16_MAX);
|
||||
}
|
||||
if (id == -1) goto failed;
|
||||
|
||||
request->name_trn_id = id;
|
||||
req->name_trn_id = id;
|
||||
|
||||
req->te = event_add_timed(nbtsock->event_ctx, req,
|
||||
timeval_current_ofs(req->timeout, 0),
|
||||
nbt_name_socket_timeout, req);
|
||||
|
||||
talloc_set_destructor(req, nbt_name_request_destructor);
|
||||
|
||||
status = ndr_push_struct_blob(&req->encoded, req, request,
|
||||
(ndr_push_flags_fn_t)ndr_push_nbt_name_packet);
|
||||
if (!NT_STATUS_IS_OK(status)) goto failed;
|
||||
|
||||
DLIST_ADD_END(nbtsock->send_queue, req, struct nbt_name_request *);
|
||||
|
||||
if (DEBUGLVL(10)) {
|
||||
DEBUG(10,("Queueing nbt packet to %s:%d\n",
|
||||
req->dest->addr, req->dest->port));
|
||||
NDR_PRINT_DEBUG(nbt_name_packet, request);
|
||||
}
|
||||
|
||||
EVENT_FD_WRITEABLE(nbtsock->fde);
|
||||
|
||||
return req;
|
||||
|
||||
failed:
|
||||
talloc_free(req);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
send off a nbt name reply
|
||||
*/
|
||||
NTSTATUS nbt_name_reply_send(struct nbt_name_socket *nbtsock,
|
||||
struct socket_address *dest,
|
||||
struct nbt_name_packet *request)
|
||||
{
|
||||
struct nbt_name_request *req;
|
||||
NTSTATUS status;
|
||||
|
||||
req = talloc_zero(nbtsock, struct nbt_name_request);
|
||||
NT_STATUS_HAVE_NO_MEMORY(req);
|
||||
|
||||
req->nbtsock = nbtsock;
|
||||
req->dest = dest;
|
||||
if (talloc_reference(req, dest) == NULL) goto failed;
|
||||
req->state = NBT_REQUEST_SEND;
|
||||
req->is_reply = True;
|
||||
|
||||
talloc_set_destructor(req, nbt_name_request_destructor);
|
||||
|
||||
if (DEBUGLVL(10)) {
|
||||
NDR_PRINT_DEBUG(nbt_name_packet, request);
|
||||
}
|
||||
|
||||
status = ndr_push_struct_blob(&req->encoded, req, request,
|
||||
(ndr_push_flags_fn_t)ndr_push_nbt_name_packet);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(req);
|
||||
return status;
|
||||
}
|
||||
|
||||
DLIST_ADD_END(nbtsock->send_queue, req, struct nbt_name_request *);
|
||||
|
||||
EVENT_FD_WRITEABLE(nbtsock->fde);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
|
||||
failed:
|
||||
talloc_free(req);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/*
|
||||
wait for a nbt request to complete
|
||||
*/
|
||||
NTSTATUS nbt_name_request_recv(struct nbt_name_request *req)
|
||||
{
|
||||
if (!req) return NT_STATUS_NO_MEMORY;
|
||||
|
||||
while (req->state < NBT_REQUEST_DONE) {
|
||||
if (event_loop_once(req->nbtsock->event_ctx) != 0) {
|
||||
req->state = NBT_REQUEST_ERROR;
|
||||
req->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
|
||||
if (req->async.fn) {
|
||||
req->async.fn(req);
|
||||
}
|
||||
}
|
||||
}
|
||||
return req->status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
setup a handler for incoming requests
|
||||
*/
|
||||
NTSTATUS nbt_set_incoming_handler(struct nbt_name_socket *nbtsock,
|
||||
void (*handler)(struct nbt_name_socket *, struct nbt_name_packet *,
|
||||
struct socket_address *),
|
||||
void *private)
|
||||
{
|
||||
nbtsock->incoming.handler = handler;
|
||||
nbtsock->incoming.private = private;
|
||||
EVENT_FD_READABLE(nbtsock->fde);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
turn a NBT rcode into a NTSTATUS
|
||||
*/
|
||||
NTSTATUS nbt_rcode_to_ntstatus(uint8_t rcode)
|
||||
{
|
||||
int i;
|
||||
struct {
|
||||
enum nbt_rcode rcode;
|
||||
NTSTATUS status;
|
||||
} map[] = {
|
||||
{ NBT_RCODE_FMT, NT_STATUS_INVALID_PARAMETER },
|
||||
{ NBT_RCODE_SVR, NT_STATUS_SERVER_DISABLED },
|
||||
{ NBT_RCODE_NAM, NT_STATUS_OBJECT_NAME_NOT_FOUND },
|
||||
{ NBT_RCODE_IMP, NT_STATUS_NOT_SUPPORTED },
|
||||
{ NBT_RCODE_RFS, NT_STATUS_ACCESS_DENIED },
|
||||
{ NBT_RCODE_ACT, NT_STATUS_ADDRESS_ALREADY_EXISTS },
|
||||
{ NBT_RCODE_CFT, NT_STATUS_CONFLICTING_ADDRESSES }
|
||||
};
|
||||
for (i=0;i<ARRAY_SIZE(map);i++) {
|
||||
if (map[i].rcode == rcode) {
|
||||
return map[i].status;
|
||||
}
|
||||
}
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#define 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;
|
||||
};
|
||||
@@ -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
|
||||
@@ -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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.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)
|
||||
{
|
||||
transport->oplock.handler = handler;
|
||||
transport->oplock.private = private;
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.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 *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;
|
||||
|
||||
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, mem_ctx, &parms->old.out.os, p, -1, STR_TERMINATE);
|
||||
p += smbcli_req_pull_string(req, mem_ctx, &parms->old.out.lanman, p, -1, STR_TERMINATE);
|
||||
p += smbcli_req_pull_string(req, 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, mem_ctx, &parms->nt1.out.os, p, -1, STR_TERMINATE);
|
||||
p += smbcli_req_pull_string(req, 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, 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, mem_ctx, p, len);
|
||||
p += parms->spnego.out.secblob.length;
|
||||
p += smbcli_req_pull_string(req, mem_ctx, &parms->spnego.out.os, p, -1, STR_TERMINATE);
|
||||
p += smbcli_req_pull_string(req, mem_ctx, &parms->spnego.out.lanman, p, -1, STR_TERMINATE);
|
||||
p += smbcli_req_pull_string(req, 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)
|
||||
*****************************************************************************/
|
||||
NTSTATUS smb_raw_exit(struct smbcli_session *session)
|
||||
{
|
||||
struct smbcli_request *req = smb_raw_exit_send(session);
|
||||
return smbcli_request_simple_recv(req);
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "lib/events/events.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "libcli/composite/composite.h"
|
||||
#include "lib/socket/socket.h"
|
||||
#include "libcli/resolve/resolve.h"
|
||||
|
||||
struct sock_connect_state {
|
||||
struct composite_context *ctx;
|
||||
const char *host_name;
|
||||
int num_ports;
|
||||
uint16_t *ports;
|
||||
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,
|
||||
int port,
|
||||
const char *host_name,
|
||||
struct event_context *event_ctx)
|
||||
{
|
||||
struct composite_context *result, *ctx;
|
||||
struct sock_connect_state *state;
|
||||
int reason = 0;
|
||||
char* reasons[] = {
|
||||
"could not allocate composite_context",
|
||||
"no event context/failed init",
|
||||
"could not allocate sock_connect_state",
|
||||
"no hostname/could not allocate copy",
|
||||
"ports setup error",
|
||||
"failed socket_connect_multi_send",
|
||||
"",
|
||||
"",
|
||||
""
|
||||
};
|
||||
DEBUG_FN_ENTER;
|
||||
|
||||
result = talloc_zero(mem_ctx, struct composite_context);
|
||||
if (result == NULL) goto failed;
|
||||
reason++;
|
||||
result->state = COMPOSITE_STATE_IN_PROGRESS;
|
||||
|
||||
if (event_ctx != NULL) {
|
||||
result->event_ctx = talloc_reference(result, event_ctx);
|
||||
} else {
|
||||
result->event_ctx = event_context_init(result);
|
||||
}
|
||||
|
||||
if (result->event_ctx == NULL) goto failed;
|
||||
reason++;
|
||||
|
||||
state = talloc(result, struct sock_connect_state);
|
||||
if (state == NULL) goto failed;
|
||||
reason++;
|
||||
state->ctx = result;
|
||||
result->private_data = state;
|
||||
|
||||
state->host_name = talloc_strdup(state, host_name);
|
||||
if (state->host_name == NULL) goto failed;
|
||||
reason++;
|
||||
|
||||
if (port == 0) {
|
||||
const char **ports = lp_smb_ports();
|
||||
int i;
|
||||
|
||||
for (i=0;ports[i];i++) /* noop */ ;
|
||||
if (i == 0) {
|
||||
DEBUG(3, ("no smb ports defined\n"));
|
||||
goto failed;
|
||||
}
|
||||
state->num_ports = i;
|
||||
state->ports = talloc_array(state, uint16_t, i);
|
||||
if (state->ports == NULL) goto failed;
|
||||
for (i=0;ports[i];i++) {
|
||||
state->ports[i] = atoi(ports[i]);
|
||||
}
|
||||
} else {
|
||||
state->ports = talloc_array(state, uint16_t, 1);
|
||||
if (state->ports == NULL) goto failed;
|
||||
state->num_ports = 1;
|
||||
state->ports[0] = port;
|
||||
}
|
||||
reason++;
|
||||
|
||||
ctx = socket_connect_multi_send(state, host_addr,
|
||||
state->num_ports, state->ports,
|
||||
state->ctx->event_ctx);
|
||||
if (ctx == NULL) goto failed;
|
||||
reason++;
|
||||
ctx->async.fn = smbcli_sock_connect_recv_conn;
|
||||
ctx->async.private_data = state;
|
||||
|
||||
DEBUG_FN_EXIT;
|
||||
|
||||
return result;
|
||||
|
||||
failed:
|
||||
talloc_free(result);
|
||||
|
||||
DEBUG_FN_FAIL(reasons[reason]);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void smbcli_sock_connect_recv_conn(struct composite_context *ctx)
|
||||
{
|
||||
DEBUG_FN_ENTER;
|
||||
|
||||
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, lp_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);
|
||||
DEBUG_FN_EXIT;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
finish a smbcli_sock_connect_send() operation
|
||||
*/
|
||||
NTSTATUS smbcli_sock_connect_recv(struct composite_context *c,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct smbcli_socket **result)
|
||||
{
|
||||
DEBUG_FN_ENTER;
|
||||
|
||||
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);
|
||||
DEBUG_FN_EXIT;
|
||||
|
||||
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, int port,
|
||||
const char *host_name,
|
||||
struct event_context *event_ctx,
|
||||
struct smbcli_socket **result)
|
||||
{
|
||||
struct composite_context *c =
|
||||
smbcli_sock_connect_send(mem_ctx, host_addr, port, host_name,
|
||||
event_ctx);
|
||||
return smbcli_sock_connect_recv(c, mem_ctx, result);
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
mark the socket as dead
|
||||
****************************************************************************/
|
||||
void smbcli_sock_dead(struct smbcli_socket *sock)
|
||||
{
|
||||
DEBUG_FN_ENTER;
|
||||
|
||||
talloc_free(sock->event.fde);
|
||||
sock->event.fde = NULL;
|
||||
talloc_free(sock->sock);
|
||||
sock->sock = NULL;
|
||||
|
||||
DEBUG_FN_EXIT;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
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
|
||||
****************************************************************************/
|
||||
struct smbcli_socket *smbcli_sock_connect_byname(const char *host, int port,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct event_context *event_ctx)
|
||||
{
|
||||
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 (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;
|
||||
}
|
||||
|
||||
if (event_ctx == NULL) {
|
||||
event_ctx = event_context_init(mem_ctx);
|
||||
}
|
||||
|
||||
if (event_ctx == NULL) {
|
||||
DEBUG(0, ("event_context_init 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(&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, port, name, event_ctx,
|
||||
&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;
|
||||
}
|
||||
@@ -0,0 +1,675 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.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"
|
||||
|
||||
static int smbcli_request_deny_destructor(struct smbcli_request *req);
|
||||
|
||||
/*
|
||||
an event has happened on the socket
|
||||
*/
|
||||
static void smbcli_transport_event_handler(struct event_context *ev,
|
||||
struct fd_event *fde,
|
||||
uint16_t flags, void *private)
|
||||
{
|
||||
struct smbcli_transport *transport = talloc_get_type(private,
|
||||
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)
|
||||
{
|
||||
DEBUG_FN_ENTER;
|
||||
|
||||
smbcli_transport_dead(transport, NT_STATUS_LOCAL_DISCONNECT);
|
||||
DEBUG_FN_EXIT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
handle receive errors
|
||||
*/
|
||||
static void smbcli_transport_error(void *private, NTSTATUS status)
|
||||
{
|
||||
struct smbcli_transport *transport = talloc_get_type(private, struct smbcli_transport);
|
||||
DEBUG_FN_ENTER;
|
||||
|
||||
smbcli_transport_dead(transport, status);
|
||||
DEBUG_FN_EXIT;
|
||||
|
||||
}
|
||||
|
||||
static NTSTATUS smbcli_transport_finish_recv(void *private, 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_transport *transport;
|
||||
|
||||
DEBUG_FN_ENTER;
|
||||
|
||||
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.use_spnego = lp_use_spnego() && lp_nt_status_support();
|
||||
transport->options.max_xmit = lp_max_xmit();
|
||||
transport->options.max_mux = lp_maxmux();
|
||||
transport->options.request_timeout = SMB_REQUEST_TIMEOUT;
|
||||
|
||||
transport->negotiate.max_xmit = transport->options.max_xmit;
|
||||
|
||||
/* setup the stream -> packet parser */
|
||||
transport->packet = packet_init(transport);
|
||||
if (transport->packet == NULL) {
|
||||
talloc_free(transport);
|
||||
DEBUG_FN_FAIL("packet_init_failed, return NULL");
|
||||
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);
|
||||
|
||||
DEBUG_FN_EXIT;
|
||||
|
||||
return transport;
|
||||
}
|
||||
|
||||
/*
|
||||
mark the transport as dead
|
||||
*/
|
||||
void smbcli_transport_dead(struct smbcli_transport *transport, NTSTATUS status)
|
||||
{
|
||||
DEBUG_FN_ENTER;
|
||||
|
||||
smbcli_sock_dead(transport->socket);
|
||||
|
||||
if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
|
||||
status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Getting "using freed memory" in these callbacks when a connection
|
||||
* times out and the transport is being shutdown.
|
||||
*/
|
||||
#if 0
|
||||
void* transportRef = talloc_named_const(NULL,1,"TRANSPORT_REF");
|
||||
talloc_reference(transportRef, transport);
|
||||
|
||||
/* kill all pending receives */
|
||||
while (transport->pending_recv) {
|
||||
struct smbcli_request *req = transport->pending_recv;
|
||||
req->state = SMBCLI_REQUEST_ERROR;
|
||||
req->status = status;
|
||||
DLIST_REMOVE(transport->pending_recv, req);
|
||||
talloc_set_destructor(req, smbcli_request_deny_destructor);
|
||||
if (req->async.fn) {
|
||||
req->async.fn(req);
|
||||
talloc_set_destructor(req, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* all done with transport, free holder reference*/
|
||||
talloc_free(transportRef);
|
||||
#endif
|
||||
DEBUG_FN_EXIT;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
send a session request
|
||||
*/
|
||||
struct smbcli_request *smbcli_transport_connect_send(struct smbcli_transport *transport,
|
||||
struct nbt_name *calling,
|
||||
struct nbt_name *called)
|
||||
{
|
||||
DEBUG_FN_ENTER;
|
||||
|
||||
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, &calling_blob, calling);
|
||||
if (!NT_STATUS_IS_OK(status)) goto failed;
|
||||
|
||||
status = nbt_name_to_blob(tmp_ctx, &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);
|
||||
DEBUG_FN_EXIT;
|
||||
|
||||
return req;
|
||||
|
||||
failed:
|
||||
talloc_free(tmp_ctx);
|
||||
DEBUG_FN_FAIL("failed, return NULL");
|
||||
|
||||
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;
|
||||
|
||||
DEBUG_FN_ENTER;
|
||||
|
||||
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);
|
||||
DEBUG_FN_EXIT;
|
||||
|
||||
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;
|
||||
|
||||
DEBUG_FN_ENTER;
|
||||
|
||||
if (transport->socket->port == 445) {
|
||||
return True;
|
||||
}
|
||||
|
||||
req = smbcli_transport_connect_send(transport,
|
||||
calling, called);
|
||||
status = smbcli_transport_connect_recv(req);
|
||||
DEBUG_FN_EXIT;
|
||||
|
||||
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 */
|
||||
DEBUG_FN_ENTER;
|
||||
|
||||
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;
|
||||
DEBUG_FN_EXIT;
|
||||
|
||||
return mid;
|
||||
}
|
||||
|
||||
static void idle_handler(struct event_context *ev,
|
||||
struct timed_event *te, struct timeval t, void *private)
|
||||
{
|
||||
struct smbcli_transport *transport = talloc_get_type(private,
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
setup the idle handler for a transport
|
||||
the period is in microseconds
|
||||
*/
|
||||
void smbcli_transport_idle_handler(struct smbcli_transport *transport,
|
||||
void (*idle_func)(struct smbcli_transport *, void *),
|
||||
uint64_t period,
|
||||
void *private)
|
||||
{
|
||||
transport->idle.func = idle_func;
|
||||
transport->idle.private = private;
|
||||
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_BLOB blob)
|
||||
{
|
||||
DEBUG_FN_ENTER;
|
||||
|
||||
struct smbcli_transport *transport = talloc_get_type(private,
|
||||
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);
|
||||
|
||||
if (!(req->flags2 & FLAGS2_32_BIT_ERROR_CODES)) {
|
||||
int class = CVAL(req->in.hdr,HDR_RCLS);
|
||||
int code = SVAL(req->in.hdr,HDR_ERR);
|
||||
if (class == 0 && code == 0) {
|
||||
transport->error.e.nt_status = NT_STATUS_OK;
|
||||
} else {
|
||||
transport->error.e.nt_status = NT_STATUS_DOS(class, 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 */
|
||||
DLIST_REMOVE(transport->pending_recv, req);
|
||||
req->state = SMBCLI_REQUEST_DONE;
|
||||
if (req->async.fn) {
|
||||
req->async.fn(req);
|
||||
}
|
||||
DEBUG_FN_EXIT_MSG("async");
|
||||
|
||||
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);
|
||||
}
|
||||
DEBUG_FN_FAIL("failed, continue NT_STATUS_OK");
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
process some read/write requests that are pending
|
||||
return False if the socket is dead
|
||||
*/
|
||||
BOOL smbcli_transport_process(struct smbcli_transport *transport)
|
||||
{
|
||||
NTSTATUS status;
|
||||
size_t npending;
|
||||
|
||||
DEBUG_FN_ENTER;
|
||||
|
||||
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;
|
||||
}
|
||||
DEBUG_FN_EXIT;
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
/*
|
||||
handle timeouts of individual smb requests
|
||||
*/
|
||||
static void smbcli_timeout_handler(struct event_context *ev, struct timed_event *te,
|
||||
struct timeval t, void *private)
|
||||
{
|
||||
DEBUG_FN_ENTER;
|
||||
|
||||
struct smbcli_request *req = talloc_get_type(private, 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);
|
||||
}
|
||||
DEBUG_FN_EXIT;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
destroy a request
|
||||
*/
|
||||
static int smbcli_request_destructor(struct smbcli_request *req)
|
||||
{
|
||||
DEBUG_FN_ENTER;
|
||||
|
||||
if (req->state == SMBCLI_REQUEST_RECV) {
|
||||
DLIST_REMOVE(req->transport->pending_recv, req);
|
||||
}
|
||||
DEBUG_FN_EXIT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int smbcli_request_deny_destructor(struct smbcli_request *req)
|
||||
{
|
||||
DEBUG_FN_ENTER;
|
||||
|
||||
DEBUG_FN_EXIT;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
put a request into the send queue
|
||||
*/
|
||||
void smbcli_transport_send(struct smbcli_request *req)
|
||||
{
|
||||
DATA_BLOB blob;
|
||||
NTSTATUS status;
|
||||
|
||||
DEBUG_FN_ENTER;
|
||||
|
||||
/* check if the transport is dead */
|
||||
if (req->transport->socket->sock == NULL) {
|
||||
req->state = SMBCLI_REQUEST_ERROR;
|
||||
req->status = NT_STATUS_NET_WRITE_FAULT;
|
||||
DEBUG_FN_FAIL("req->transport->socket->sock is NULL");
|
||||
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;
|
||||
DEBUG_FN_FAIL("packet_send fail, !NTSTATUS_IS_OK");
|
||||
return;
|
||||
}
|
||||
|
||||
if (req->one_way_request) {
|
||||
req->state = SMBCLI_REQUEST_DONE;
|
||||
smbcli_request_destroy(req);
|
||||
DEBUG_FN_EXIT_MSG("one way request");
|
||||
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);
|
||||
DEBUG_FN_EXIT;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.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
|
||||
****************************************************************************/
|
||||
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, mem_ctx, &parms->tconx.out.dev_type,
|
||||
p, -1, STR_ASCII | STR_TERMINATE);
|
||||
p += smbcli_req_pull_string(req, 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)
|
||||
****************************************************************************/
|
||||
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.
|
||||
****************************************************************************/
|
||||
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, int port,
|
||||
const char *service, const char *service_type,
|
||||
struct cli_credentials *credentials,
|
||||
struct event_context *ev)
|
||||
{
|
||||
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.port = port;
|
||||
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.fallback_to_anonymous = False;
|
||||
io.in.workgroup = lp_workgroup();
|
||||
|
||||
status = smb_composite_connect(&io, parent_ctx, ev);
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
*ret_tree = io.out.tree;
|
||||
}
|
||||
talloc_free(tmp_ctx);
|
||||
return status;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
|
||||
/* 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)
|
||||
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#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 cli_credentials;
|
||||
|
||||
/* 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 event_context *ctx;
|
||||
struct fd_event *fde;
|
||||
struct timed_event *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;
|
||||
uint32_t max_xmit;
|
||||
uint16_t max_mux;
|
||||
int request_timeout;
|
||||
};
|
||||
|
||||
/* 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;
|
||||
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);
|
||||
/* private data passed to the oplock handler */
|
||||
void *private;
|
||||
} 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;
|
||||
};
|
||||
|
||||
/* 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;
|
||||
};
|
||||
|
||||
/*
|
||||
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;
|
||||
|
||||
/* 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 mid of this packet - used to match replies */
|
||||
uint16_t mid;
|
||||
|
||||
struct request_buffer in;
|
||||
struct 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;
|
||||
} 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"
|
||||
#include "libcli/raw/raw_proto.h"
|
||||
|
||||
#endif /* __LIBCLI_RAW__H__ */
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.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;
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
status = ndr_pull_security_descriptor(ndr, NDR_SCALARS|NDR_BUFFERS,
|
||||
io->query_secdesc.out.sd);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
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;
|
||||
NTSTATUS status;
|
||||
|
||||
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();
|
||||
if (!ndr) return NULL;
|
||||
|
||||
status = ndr_push_security_descriptor(ndr, NDR_SCALARS|NDR_BUFFERS, io->set_secdesc.in.sd);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
ndr_push_free(ndr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nt.in.data = ndr_push_blob(ndr);
|
||||
|
||||
req = smb_raw_nttrans_send(tree, &nt);
|
||||
|
||||
ndr_push_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);
|
||||
}
|
||||
@@ -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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.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);
|
||||
}
|
||||
@@ -0,0 +1,361 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "smb.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)
|
||||
{
|
||||
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 + 3) & ~3;
|
||||
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)
|
||||
{
|
||||
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 + 3) & ~3) - 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;
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,945 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "smb.h"
|
||||
#include "libcli/raw/libcliraw.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)
|
||||
|
||||
/****************************************************************************
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (!smbcli_request_send(req)) {
|
||||
smbcli_request_destroy(req);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Rename a file - sync interface
|
||||
****************************************************************************/
|
||||
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
|
||||
*/
|
||||
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
|
||||
****************************************************************************/
|
||||
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
|
||||
****************************************************************************/
|
||||
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;
|
||||
NTSTATUS status;
|
||||
|
||||
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) {
|
||||
status = ndr_push_struct_blob(&sd_blob, mem_ctx,
|
||||
parms->ntcreatex.in.sec_desc,
|
||||
(ndr_push_flags_fn_t)ndr_push_security_descriptor);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
****************************************************************************/
|
||||
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
|
||||
****************************************************************************/
|
||||
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, 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, 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
|
||||
****************************************************************************/
|
||||
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
|
||||
****************************************************************************/
|
||||
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
|
||||
****************************************************************************/
|
||||
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
|
||||
****************************************************************************/
|
||||
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
|
||||
****************************************************************************/
|
||||
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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,751 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
client trans2 operations
|
||||
Copyright (C) James Myers 2003
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.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;
|
||||
ssize_t size;
|
||||
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;
|
||||
}
|
||||
size = convert_string_talloc(io->streams, CH_UTF16, CH_UNIX,
|
||||
blob.data+ofs+24, nlen, &vstr);
|
||||
if (size == -1) {
|
||||
return NT_STATUS_ILLEGAL_CHARACTER;
|
||||
}
|
||||
io->streams[n].stream_name.s = 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 = BVAL(blob->data, 0x58);
|
||||
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: {
|
||||
NTSTATUS status;
|
||||
|
||||
parms->query_secdesc.out.sd = talloc(mem_ctx, struct security_descriptor);
|
||||
NT_STATUS_HAVE_NO_MEMORY(parms->query_secdesc.out.sd);
|
||||
|
||||
status = ndr_pull_struct_blob(blob, mem_ctx,
|
||||
parms->query_secdesc.out.sd,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
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_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)
|
||||
****************************************************************************/
|
||||
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)
|
||||
****************************************************************************/
|
||||
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)
|
||||
****************************************************************************/
|
||||
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)
|
||||
****************************************************************************/
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.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;
|
||||
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);
|
||||
status = ndr_pull_struct_blob(&blob, mem_ctx, &fsinfo->objectid_information.out.guid,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_GUID);
|
||||
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)
|
||||
****************************************************************************/
|
||||
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);
|
||||
}
|
||||
@@ -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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.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, 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 = (uint16_t *)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
|
||||
*/
|
||||
_PUBLIC_ 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);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "smb.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);
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.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"},
|
||||
};
|
||||
|
||||
/*
|
||||
Send a negprot command.
|
||||
*/
|
||||
struct smbcli_request *smb_raw_negotiate_send(struct smbcli_transport *transport,
|
||||
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 (lp_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, transport, req->in.data, 16);
|
||||
transport->negotiate.secblob = smbcli_req_pull_blob(req, 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, transport, req->in.data, transport->negotiate.key_len);
|
||||
smbcli_req_pull_string(req, 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, 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 (!lp_unicode()) {
|
||||
transport->negotiate.capabilities &= ~CAP_UNICODE;
|
||||
}
|
||||
|
||||
if (!lp_nt_status_support()) {
|
||||
transport->negotiate.capabilities &= ~CAP_STATUS32;
|
||||
}
|
||||
|
||||
failed:
|
||||
return smbcli_request_destroy(req);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Send a negprot command (sync interface)
|
||||
*/
|
||||
NTSTATUS smb_raw_negotiate(struct smbcli_transport *transport, int maxprotocol)
|
||||
{
|
||||
struct smbcli_request *req = smb_raw_negotiate_send(transport, maxprotocol);
|
||||
return smb_raw_negotiate_recv(req);
|
||||
}
|
||||
@@ -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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "lib/util/dlinklist.h"
|
||||
|
||||
/****************************************************************************
|
||||
change notify (async send)
|
||||
****************************************************************************/
|
||||
struct smbcli_request *smb_raw_changenotify_send(struct smbcli_tree *tree, union smb_notify *parms)
|
||||
{
|
||||
struct smb_nttrans nt;
|
||||
uint16_t setup[4];
|
||||
|
||||
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)
|
||||
****************************************************************************/
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.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)
|
||||
****************************************************************************/
|
||||
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)
|
||||
****************************************************************************/
|
||||
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, 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, 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);
|
||||
}
|
||||
}
|
||||
|
||||
if (parms->readx.out.nread > MAX(parms->readx.in.mincnt, parms->readx.in.maxcnt) ||
|
||||
!smbcli_raw_pull_data(req, 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)
|
||||
****************************************************************************/
|
||||
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)
|
||||
****************************************************************************/
|
||||
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)
|
||||
****************************************************************************/
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,973 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
this file implements functions for manipulating the 'struct smbcli_request' structure in libsmb
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "lib/util/dlinklist.h"
|
||||
#include "lib/events/events.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
|
||||
|
||||
/* destroy a request structure and return final status */
|
||||
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;
|
||||
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_size(req, 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;
|
||||
|
||||
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));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
receive another reply to a request - this is used for requests that
|
||||
have multi-part replies (such as SMBtrans2)
|
||||
*/
|
||||
BOOL smbcli_request_receive_more(struct smbcli_request *req)
|
||||
{
|
||||
req->state = SMBCLI_REQUEST_RECV;
|
||||
DLIST_ADD(req->transport->pending_recv, req);
|
||||
|
||||
return smbcli_request_receive(req);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
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);
|
||||
}
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
/*
|
||||
wait for a reply to be received for a packet that just returns an error
|
||||
code and nothing more
|
||||
*/
|
||||
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 smbcli_request *req, TALLOC_CTX *mem_ctx,
|
||||
char **dest, const uint8_t *src, int byte_len, uint_t flags)
|
||||
{
|
||||
int src_len, src_len2, alignment=0;
|
||||
ssize_t ret;
|
||||
|
||||
if (!(flags & STR_NOALIGN) && ucs2_align(req->in.buffer, src, flags)) {
|
||||
src++;
|
||||
alignment=1;
|
||||
if (byte_len != -1) {
|
||||
byte_len--;
|
||||
}
|
||||
}
|
||||
|
||||
src_len = req->in.data_size - PTR_DIFF(src, req->in.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);
|
||||
if (ret == -1) {
|
||||
*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 smbcli_request *req, TALLOC_CTX *mem_ctx,
|
||||
char **dest, const uint8_t *src, int byte_len, uint_t flags)
|
||||
{
|
||||
int src_len, src_len2;
|
||||
ssize_t ret;
|
||||
|
||||
src_len = req->in.data_size - PTR_DIFF(src, req->in.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);
|
||||
|
||||
if (ret == -1) {
|
||||
*dest = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
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 smbcli_request *req, TALLOC_CTX *mem_ctx,
|
||||
char **dest, const uint8_t *src, int byte_len, uint_t flags)
|
||||
{
|
||||
if (!(flags & STR_ASCII) &&
|
||||
(((flags & STR_UNICODE) || (req->flags2 & FLAGS2_UNICODE_STRINGS)))) {
|
||||
return smbcli_req_pull_ucs2(req, mem_ctx, dest, src, byte_len, flags);
|
||||
}
|
||||
|
||||
return smbcli_req_pull_ascii(req, 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 smbcli_request *req, TALLOC_CTX *mem_ctx, const uint8_t *src, int byte_len)
|
||||
{
|
||||
int src_len;
|
||||
|
||||
src_len = req->in.data_size - PTR_DIFF(src, req->in.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 smbcli_request *req, const uint8_t *ptr, uint32_t count)
|
||||
{
|
||||
/* be careful with wraparound! */
|
||||
if (ptr < req->in.data ||
|
||||
ptr >= req->in.data + req->in.data_size ||
|
||||
count > req->in.data_size ||
|
||||
ptr + count > req->in.data + req->in.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 smbcli_request *req, const uint8_t *src, int len, uint8_t *dest)
|
||||
{
|
||||
if (len == 0) return True;
|
||||
|
||||
if (smbcli_req_data_oob(req, 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
|
||||
*/
|
||||
static 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;
|
||||
ssize_t 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);
|
||||
if (ret == -1) {
|
||||
*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;
|
||||
ssize_t 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);
|
||||
|
||||
if (ret == -1) {
|
||||
*dest = NULL;
|
||||
return 0;
|
||||
}
|
||||
*dest = dest2;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,796 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
client directory search routines
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.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,
|
||||
BOOL (*callback)(void *private, union smb_search_data *file))
|
||||
|
||||
{
|
||||
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, mem_ctx, &name, p+30, 13, STR_ASCII);
|
||||
search_data.search.name = name;
|
||||
if (!callback(private, &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,
|
||||
BOOL (*callback)(void *private, union smb_search_data *file))
|
||||
|
||||
{
|
||||
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, 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,
|
||||
BOOL (*callback)(void *private, union smb_search_data *file))
|
||||
|
||||
{
|
||||
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, 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_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,
|
||||
BOOL (*callback)(void *private, union smb_search_data *file))
|
||||
|
||||
{
|
||||
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, &search_data)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (len == 0) break;
|
||||
|
||||
blob2.data += len;
|
||||
blob2.length -= len;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/* Implements trans2findfirst2 and old search
|
||||
*/
|
||||
NTSTATUS smb_raw_search_first(struct smbcli_tree *tree,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
union smb_search_first *io, void *private,
|
||||
BOOL (*callback)(void *private, union smb_search_data *file))
|
||||
{
|
||||
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, 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, 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,
|
||||
BOOL (*callback)(void *private, union smb_search_data *file))
|
||||
{
|
||||
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, 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, 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);
|
||||
}
|
||||
@@ -0,0 +1,431 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
RAW_SFILEINFO_* calls
|
||||
Copyright (C) James Myers 2003
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.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) 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_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: {
|
||||
NTSTATUS status;
|
||||
|
||||
status = ndr_push_struct_blob(blob, mem_ctx,
|
||||
parms->set_secdesc.in.sd,
|
||||
(ndr_push_flags_fn_t)ndr_push_security_descriptor);
|
||||
if (!NT_STATUS_IS_OK(status)) return False;
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
/* Unhandled levels */
|
||||
case RAW_SFILEINFO_1023:
|
||||
case RAW_SFILEINFO_1025:
|
||||
case RAW_SFILEINFO_1029:
|
||||
case RAW_SFILEINFO_1032:
|
||||
case RAW_SFILEINFO_1039:
|
||||
case RAW_SFILEINFO_1040:
|
||||
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_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_1023:
|
||||
case RAW_SFILEINFO_1025:
|
||||
case RAW_SFILEINFO_1029:
|
||||
case RAW_SFILEINFO_1032:
|
||||
case RAW_SFILEINFO_1039:
|
||||
case RAW_SFILEINFO_1040:
|
||||
return smb_raw_setfileinfo_passthru(mem_ctx, parms->generic.level,
|
||||
parms, blob);
|
||||
|
||||
/* Unhandled levels */
|
||||
|
||||
case RAW_SFILEINFO_UNIX_LINK:
|
||||
case RAW_SFILEINFO_UNIX_HLINK:
|
||||
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)
|
||||
****************************************************************************/
|
||||
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)
|
||||
****************************************************************************/
|
||||
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)
|
||||
****************************************************************************/
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,641 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "lib/util/dlinklist.h"
|
||||
#include "libcli/raw/libcliraw.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 (ptr < req->in.data ||
|
||||
ptr >= req->in.data + req->in.data_size ||
|
||||
count > req->in.data_size ||
|
||||
ptr + count > req->in.data + req->in.data_size) {
|
||||
return True;
|
||||
}
|
||||
return False;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
receive a SMB trans or trans2 response allocating the necessary memory
|
||||
****************************************************************************/
|
||||
NTSTATUS smb_raw_trans2_recv(struct smbcli_request *req,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct smb_trans2 *parms)
|
||||
{
|
||||
int total_data=0;
|
||||
int total_param=0;
|
||||
uint8_t *tdata;
|
||||
uint8_t *tparam;
|
||||
|
||||
parms->out.data.length = 0;
|
||||
parms->out.data.data = NULL;
|
||||
parms->out.params.length = 0;
|
||||
parms->out.params.data = NULL;
|
||||
|
||||
if (!smbcli_request_receive(req)) {
|
||||
return smbcli_request_destroy(req);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 (NT_STATUS_IS_ERR(req->status)) {
|
||||
return smbcli_request_destroy(req);
|
||||
}
|
||||
|
||||
SMBCLI_CHECK_MIN_WCT(req, 10);
|
||||
|
||||
/* parse out the lengths */
|
||||
total_data = SVAL(req->in.vwv, VWV(1));
|
||||
total_param = SVAL(req->in.vwv, VWV(0));
|
||||
|
||||
/* allocate it */
|
||||
if (total_data != 0) {
|
||||
tdata = talloc_size(mem_ctx, total_data);
|
||||
if (!tdata) {
|
||||
DEBUG(0,("smb_raw_receive_trans: failed to enlarge data buffer to %d bytes\n", total_data));
|
||||
req->status = NT_STATUS_NO_MEMORY;
|
||||
return smbcli_request_destroy(req);
|
||||
}
|
||||
parms->out.data.data = tdata;
|
||||
}
|
||||
|
||||
if (total_param != 0) {
|
||||
tparam = talloc_size(mem_ctx, total_param);
|
||||
if (!tparam) {
|
||||
DEBUG(0,("smb_raw_receive_trans: failed to enlarge param buffer to %d bytes\n", total_param));
|
||||
req->status = NT_STATUS_NO_MEMORY;
|
||||
return smbcli_request_destroy(req);
|
||||
}
|
||||
parms->out.params.data = tparam;
|
||||
}
|
||||
|
||||
parms->out.setup_count = SVAL(req->in.vwv, VWV(9));
|
||||
SMBCLI_CHECK_WCT(req, 10 + parms->out.setup_count);
|
||||
|
||||
if (parms->out.setup_count > 0) {
|
||||
int i;
|
||||
parms->out.setup = talloc_array(mem_ctx, uint16_t, parms->out.setup_count);
|
||||
if (!parms->out.setup) {
|
||||
req->status = NT_STATUS_NO_MEMORY;
|
||||
return smbcli_request_destroy(req);
|
||||
}
|
||||
for (i=0;i<parms->out.setup_count;i++) {
|
||||
parms->out.setup[i] = SVAL(req->in.vwv, VWV(10+i));
|
||||
}
|
||||
}
|
||||
|
||||
while (1) {
|
||||
uint16_t param_count, param_ofs, param_disp;
|
||||
uint16_t data_count, data_ofs, data_disp;
|
||||
uint16_t total_data2, total_param2;
|
||||
|
||||
/* parse out the total lengths again - they can shrink! */
|
||||
total_data2 = SVAL(req->in.vwv, VWV(1));
|
||||
total_param2 = SVAL(req->in.vwv, VWV(0));
|
||||
|
||||
if (total_data2 > total_data ||
|
||||
total_param2 > total_param) {
|
||||
/* they must *only* shrink */
|
||||
DEBUG(1,("smb_raw_receive_trans: data/params expanded!\n"));
|
||||
req->status = NT_STATUS_BUFFER_TOO_SMALL;
|
||||
return smbcli_request_destroy(req);
|
||||
}
|
||||
|
||||
total_data = total_data2;
|
||||
total_param = total_param2;
|
||||
|
||||
/* parse params for this lump */
|
||||
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 (data_count + data_disp > total_data ||
|
||||
param_count + param_disp > total_param) {
|
||||
DEBUG(1,("smb_raw_receive_trans: Buffer overflow\n"));
|
||||
req->status = NT_STATUS_BUFFER_TOO_SMALL;
|
||||
return smbcli_request_destroy(req);
|
||||
}
|
||||
|
||||
/* 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_receive_trans: out of bounds parameters!\n"));
|
||||
req->status = NT_STATUS_BUFFER_TOO_SMALL;
|
||||
return smbcli_request_destroy(req);
|
||||
}
|
||||
|
||||
if (data_count) {
|
||||
memcpy(parms->out.data.data + data_disp,
|
||||
req->in.hdr + data_ofs,
|
||||
data_count);
|
||||
}
|
||||
|
||||
if (param_count) {
|
||||
memcpy(parms->out.params.data + param_disp,
|
||||
req->in.hdr + param_ofs,
|
||||
param_count);
|
||||
}
|
||||
|
||||
parms->out.data.length += data_count;
|
||||
parms->out.params.length += param_count;
|
||||
|
||||
if (total_data <= parms->out.data.length && total_param <= parms->out.params.length)
|
||||
break;
|
||||
|
||||
if (!smbcli_request_receive_more(req)) {
|
||||
req->status = NT_STATUS_UNSUCCESSFUL;
|
||||
return smbcli_request_destroy(req);
|
||||
}
|
||||
}
|
||||
|
||||
failed:
|
||||
return smbcli_request_destroy(req);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
int wct = 14 + parms->in.setup_count;
|
||||
struct smbcli_request *req, *req2;
|
||||
uint8_t *outdata,*outparam;
|
||||
int i;
|
||||
int padding;
|
||||
size_t namelen = 0;
|
||||
uint16_t data_disp, data_length, max_data;
|
||||
|
||||
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, wct, padding);
|
||||
if (!req) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* fill in SMB parameters */
|
||||
outparam = req->out.data + padding;
|
||||
outdata = outparam + parms->in.params.length;
|
||||
|
||||
/* make sure we don't leak data via the padding */
|
||||
memset(req->out.data, 0, padding);
|
||||
|
||||
data_length = parms->in.data.length;
|
||||
|
||||
max_data = smb_raw_max_trans_data(tree, parms->in.params.length);
|
||||
if (max_data < data_length) {
|
||||
data_length = max_data;
|
||||
}
|
||||
|
||||
#if TORTURE_TRANS_DATA
|
||||
if (data_length > 1) {
|
||||
data_length /= 2;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* 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);
|
||||
SSVAL(req->out.vwv,VWV(4),parms->in.max_setup);
|
||||
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),parms->in.params.length);
|
||||
SSVAL(req->out.vwv,VWV(10),PTR_DIFF(outparam,req->out.hdr)+namelen);
|
||||
SSVAL(req->out.vwv,VWV(11),data_length);
|
||||
SSVAL(req->out.vwv,VWV(12),PTR_DIFF(outdata,req->out.hdr)+namelen);
|
||||
SSVAL(req->out.vwv,VWV(13),parms->in.setup_count);
|
||||
for (i=0;i<parms->in.setup_count;i++) {
|
||||
SSVAL(req->out.vwv,VWV(14)+i*2,parms->in.setup[i]);
|
||||
}
|
||||
if (parms->in.params.data) {
|
||||
smbcli_req_append_blob(req, &parms->in.params);
|
||||
}
|
||||
if (parms->in.data.data) {
|
||||
DATA_BLOB data;
|
||||
data.data = parms->in.data.data;
|
||||
data.length = data_length;
|
||||
smbcli_req_append_blob(req, &data);
|
||||
}
|
||||
|
||||
if (!smbcli_request_send(req)) {
|
||||
smbcli_request_destroy(req);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data_disp = data_length;
|
||||
|
||||
|
||||
if (data_disp != parms->in.data.length) {
|
||||
/* TODO: this should be done asynchronously .... */
|
||||
if (!smbcli_request_receive(req) ||
|
||||
!NT_STATUS_IS_OK(req->status)) {
|
||||
return req;
|
||||
}
|
||||
|
||||
req->state = SMBCLI_REQUEST_RECV;
|
||||
DLIST_ADD(req->transport->pending_recv, req);
|
||||
}
|
||||
|
||||
|
||||
while (data_disp != parms->in.data.length) {
|
||||
data_length = parms->in.data.length - data_disp;
|
||||
|
||||
max_data = smb_raw_max_trans_data(tree, 0);
|
||||
if (max_data < data_length) {
|
||||
data_length = max_data;
|
||||
}
|
||||
|
||||
#if TORTURE_TRANS_DATA
|
||||
if (data_length > 1) {
|
||||
data_length /= 2;
|
||||
}
|
||||
#endif
|
||||
|
||||
req2 = smbcli_request_setup(tree, command+1, 9, data_length);
|
||||
if (!req2) {
|
||||
return NULL;
|
||||
}
|
||||
req2->mid = req->mid;
|
||||
SSVAL(req2->out.hdr, HDR_MID, req2->mid);
|
||||
|
||||
outdata = req2->out.data;
|
||||
|
||||
SSVAL(req2->out.vwv,VWV(0), parms->in.params.length);
|
||||
SSVAL(req2->out.vwv,VWV(1), parms->in.data.length);
|
||||
SSVAL(req2->out.vwv,VWV(2), 0);
|
||||
SSVAL(req2->out.vwv,VWV(3), 0);
|
||||
SSVAL(req2->out.vwv,VWV(4), 0);
|
||||
SSVAL(req2->out.vwv,VWV(5), data_length);
|
||||
SSVAL(req2->out.vwv,VWV(6), PTR_DIFF(outdata,req2->out.hdr));
|
||||
SSVAL(req2->out.vwv,VWV(7), data_disp);
|
||||
SSVAL(req2->out.vwv,VWV(8), 0xFFFF);
|
||||
|
||||
if (data_length != 0) {
|
||||
memcpy(req2->out.data, parms->in.data.data + data_disp,
|
||||
data_length);
|
||||
}
|
||||
|
||||
data_disp += data_length;
|
||||
|
||||
req2->one_way_request = 1;
|
||||
|
||||
if (!smbcli_request_send(req2)) {
|
||||
smbcli_request_destroy(req2);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
req->seq_num = req2->seq_num;
|
||||
}
|
||||
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
trans/trans2 raw async interface - only BLOBs used in this interface.
|
||||
note that this doesn't yet support multi-part requests
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
receive a SMB nttrans response allocating the necessary memory
|
||||
****************************************************************************/
|
||||
NTSTATUS smb_raw_nttrans_recv(struct smbcli_request *req,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct smb_nttrans *parms)
|
||||
{
|
||||
uint32_t total_data, recvd_data=0;
|
||||
uint32_t total_param, recvd_param=0;
|
||||
|
||||
if (!smbcli_request_receive(req) ||
|
||||
smbcli_request_is_error(req)) {
|
||||
return smbcli_request_destroy(req);
|
||||
}
|
||||
|
||||
/* sanity check */
|
||||
if (CVAL(req->in.hdr, HDR_COM) != SMBnttrans) {
|
||||
DEBUG(0,("smb_raw_receive_nttrans: Expected %s response, got command 0x%02x\n",
|
||||
"SMBnttrans",
|
||||
CVAL(req->in.hdr,HDR_COM)));
|
||||
req->status = NT_STATUS_UNSUCCESSFUL;
|
||||
return smbcli_request_destroy(req);
|
||||
}
|
||||
|
||||
SMBCLI_CHECK_MIN_WCT(req, 18);
|
||||
|
||||
/* parse out the lengths */
|
||||
total_param = IVAL(req->in.vwv, 3);
|
||||
total_data = IVAL(req->in.vwv, 7);
|
||||
|
||||
parms->out.data = data_blob_talloc(mem_ctx, NULL, total_data);
|
||||
parms->out.params = data_blob_talloc(mem_ctx, NULL, total_param);
|
||||
|
||||
if (parms->out.data.length != total_data ||
|
||||
parms->out.params.length != total_param) {
|
||||
req->status = NT_STATUS_NO_MEMORY;
|
||||
return smbcli_request_destroy(req);
|
||||
}
|
||||
|
||||
parms->out.setup_count = CVAL(req->in.vwv, 35);
|
||||
SMBCLI_CHECK_WCT(req, 18 + parms->out.setup_count);
|
||||
|
||||
if (parms->out.setup_count > 0) {
|
||||
int i;
|
||||
parms->out.setup = talloc_array(mem_ctx, uint16_t, parms->out.setup_count);
|
||||
if (!parms->out.setup) {
|
||||
req->status = NT_STATUS_NO_MEMORY;
|
||||
return smbcli_request_destroy(req);
|
||||
}
|
||||
for (i=0;i<parms->out.setup_count;i++) {
|
||||
parms->out.setup[i] = SVAL(req->in.vwv, VWV(18+i));
|
||||
}
|
||||
}
|
||||
|
||||
while (recvd_data < total_data ||
|
||||
recvd_param < total_param) {
|
||||
uint32_t param_count, param_ofs, param_disp;
|
||||
uint32_t data_count, data_ofs, data_disp;
|
||||
uint32_t total_data2, total_param2;
|
||||
|
||||
/* parse out the total lengths again - they can shrink! */
|
||||
total_param2 = IVAL(req->in.vwv, 3);
|
||||
total_data2 = IVAL(req->in.vwv, 7);
|
||||
|
||||
if (total_data2 > total_data ||
|
||||
total_param2 > total_param) {
|
||||
/* they must *only* shrink */
|
||||
DEBUG(1,("smb_raw_receive_nttrans: data/params expanded!\n"));
|
||||
req->status = NT_STATUS_BUFFER_TOO_SMALL;
|
||||
return smbcli_request_destroy(req);
|
||||
}
|
||||
|
||||
total_data = total_data2;
|
||||
total_param = total_param2;
|
||||
parms->out.data.length = total_data;
|
||||
parms->out.params.length = total_param;
|
||||
|
||||
/* parse params for this lump */
|
||||
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 (data_count + data_disp > total_data ||
|
||||
param_count + param_disp > total_param) {
|
||||
DEBUG(1,("smb_raw_receive_nttrans: Buffer overflow\n"));
|
||||
req->status = NT_STATUS_BUFFER_TOO_SMALL;
|
||||
return smbcli_request_destroy(req);
|
||||
}
|
||||
|
||||
/* 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_receive_nttrans: out of bounds parameters!\n"));
|
||||
req->status = NT_STATUS_BUFFER_TOO_SMALL;
|
||||
return smbcli_request_destroy(req);
|
||||
}
|
||||
|
||||
if (data_count) {
|
||||
memcpy(parms->out.data.data + data_disp,
|
||||
req->in.hdr + data_ofs,
|
||||
data_count);
|
||||
}
|
||||
|
||||
if (param_count) {
|
||||
memcpy(parms->out.params.data + param_disp,
|
||||
req->in.hdr + param_ofs,
|
||||
param_count);
|
||||
}
|
||||
|
||||
recvd_param += param_count;
|
||||
recvd_data += data_count;
|
||||
|
||||
if (recvd_data >= total_data &&
|
||||
recvd_param >= total_param) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!smbcli_request_receive(req) ||
|
||||
smbcli_request_is_error(req)) {
|
||||
return smbcli_request_destroy(req);
|
||||
}
|
||||
|
||||
/* sanity check */
|
||||
if (CVAL(req->in.hdr, HDR_COM) != SMBnttrans) {
|
||||
DEBUG(0,("smb_raw_receive_nttrans: Expected nttranss, got command 0x%02x\n",
|
||||
CVAL(req->in.hdr, HDR_COM)));
|
||||
req->status = NT_STATUS_UNSUCCESSFUL;
|
||||
return smbcli_request_destroy(req);
|
||||
}
|
||||
}
|
||||
|
||||
failed:
|
||||
return smbcli_request_destroy(req);
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
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;
|
||||
uint8_t *outdata, *outparam;
|
||||
int i;
|
||||
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 +
|
||||
parms->in.params.length +
|
||||
parms->in.data.length);
|
||||
if (!req) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* fill in SMB parameters */
|
||||
outparam = req->out.data + align;
|
||||
outdata = outparam + parms->in.params.length;
|
||||
|
||||
if (align != 0) {
|
||||
memset(req->out.data, 0, align);
|
||||
}
|
||||
|
||||
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, parms->in.params.length);
|
||||
SIVAL(req->out.vwv, 23, PTR_DIFF(outparam,req->out.hdr));
|
||||
SIVAL(req->out.vwv, 27, parms->in.data.length);
|
||||
SIVAL(req->out.vwv, 31, PTR_DIFF(outdata,req->out.hdr));
|
||||
SCVAL(req->out.vwv, 35, parms->in.setup_count);
|
||||
SSVAL(req->out.vwv, 36, parms->in.function);
|
||||
for (i=0;i<parms->in.setup_count;i++) {
|
||||
SSVAL(req->out.vwv,VWV(19+i),parms->in.setup[i]);
|
||||
}
|
||||
if (parms->in.params.length) {
|
||||
memcpy(outparam, parms->in.params.data, parms->in.params.length);
|
||||
}
|
||||
if (parms->in.data.length) {
|
||||
memcpy(outdata, parms->in.data.data, parms->in.data.length);
|
||||
}
|
||||
|
||||
if (!smbcli_request_send(req)) {
|
||||
smbcli_request_destroy(req);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
work out the maximum data size for a trans request while avoiding
|
||||
multi-part replies
|
||||
|
||||
TODO: we only need to avoid multi-part replies because the
|
||||
multi-part trans receive code is broken.
|
||||
*/
|
||||
size_t smb_raw_max_trans_data(struct smbcli_tree *tree, size_t param_size)
|
||||
{
|
||||
return tree->session->transport->negotiate.max_xmit - (70 + param_size);
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
#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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "libcli/raw/signing.h"
|
||||
|
||||
/*
|
||||
Shared state structure between client and server, representing the basic packet.
|
||||
*/
|
||||
|
||||
struct 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;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,44 @@
|
||||
#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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
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
|
||||
@@ -0,0 +1,593 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#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
|
||||
|
||||
/* 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_SYNC_ALERT 0x0010
|
||||
#define NTCREATEX_OPTIONS_ASYNC_ALERT 0x0020
|
||||
#define NTCREATEX_OPTIONS_NON_DIRECTORY_FILE 0x0040
|
||||
#define NTCREATEX_OPTIONS_NO_EA_KNOWLEDGE 0x0200
|
||||
#define NTCREATEX_OPTIONS_EIGHT_DOT_THREE_ONLY 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_UNKNOWN_400000 0x400000
|
||||
|
||||
/* create options these bits are for private use by backends, they are
|
||||
not valid on the wire */
|
||||
#define NTCREATEX_OPTIONS_PRIVATE_MASK 0xFF000000
|
||||
#define NTCREATEX_OPTIONS_PRIVATE_DENY_DOS 0x01000000
|
||||
#define NTCREATEX_OPTIONS_PRIVATE_DENY_FCB 0x02000000
|
||||
|
||||
|
||||
/* 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
|
||||
|
||||
/* 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 */
|
||||
@@ -0,0 +1,400 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "smb.h"
|
||||
#include "libcli/raw/libcliraw.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 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 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 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;
|
||||
sign_info->next_seq_num = 0;
|
||||
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;
|
||||
|
||||
switch (req->transport->negotiate.sign_info.signing_state)
|
||||
{
|
||||
case SMB_SIGNING_ENGINE_OFF:
|
||||
return True;
|
||||
case SMB_SIGNING_ENGINE_BSRSPYL:
|
||||
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);
|
||||
|
||||
/* Initialise the sequence number */
|
||||
sign_info->next_seq_num = 0;
|
||||
|
||||
sign_info->signing_state = SMB_SIGNING_ENGINE_ON;
|
||||
|
||||
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.mac_key = data_blob(NULL, 0);
|
||||
if (!smbcli_set_signing_off(&transport->negotiate.sign_info)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
switch (lp_client_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;
|
||||
}
|
||||
@@ -0,0 +1,435 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
SMB transaction2 handling
|
||||
Copyright (C) Jeremy Allison 1994-2002.
|
||||
Copyright (C) Andrew Tridgell 1995-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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#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_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_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_BASIC_INFORMATION 1004
|
||||
#define SMB_SFILEINFO_RENAME_INFORMATION 1010
|
||||
#define SMB_SFILEINFO_DISPOSITION_INFORMATION 1013
|
||||
#define SMB_SFILEINFO_POSITION_INFORMATION 1014
|
||||
#define SMB_SFILEINFO_MODE_INFORMATION 1016
|
||||
#define SMB_SFILEINFO_ALLOCATION_INFORMATION 1019
|
||||
#define SMB_SFILEINFO_END_OF_FILE_INFORMATION 1020
|
||||
|
||||
/* filemon shows FilePipeInformation */
|
||||
#define SMB_SFILEINFO_1023 1023
|
||||
|
||||
/* filemon shows FilePipeRemoteInformation */
|
||||
#define SMB_SFILEINFO_1025 1025
|
||||
|
||||
/* filemon shows CopyOnWriteInformation */
|
||||
#define SMB_SFILEINFO_1029 1029
|
||||
|
||||
/* filemon shows OleClassIdInformation */
|
||||
#define SMB_SFILEINFO_1032 1032
|
||||
|
||||
/* seems to be the file size - perhaps valid data size?
|
||||
filemon shows 'InheritContentIndexInfo'
|
||||
*/
|
||||
#define SMB_SFILEINFO_1039 1039
|
||||
|
||||
/* OLE_INFORMATION? */
|
||||
#define SMB_SFILEINFO_1040 1040
|
||||
|
||||
|
||||
/* 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
|
||||
|
||||
/* 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_QFILEINFO_UNIX_BASIC 0x200 /* UNIX File Info*/
|
||||
#define SMB_SFILEINFO_UNIX_BASIC 0x200
|
||||
|
||||
#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
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
/* 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
|
||||
|
||||
#define SMB_QFILEINFO_UNIX_LINK 0x201
|
||||
#define SMB_SFILEINFO_UNIX_LINK 0x201
|
||||
#define SMB_SFILEINFO_UNIX_HLINK 0x203
|
||||
|
||||
#define SMB_FIND_FILE_UNIX 0x202
|
||||
|
||||
/*
|
||||
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
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
broadcast 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/resolve/resolve.h"
|
||||
#include "system/network.h"
|
||||
#include "lib/socket/netif.h"
|
||||
|
||||
/*
|
||||
broadcast name resolution method - async send
|
||||
*/
|
||||
struct composite_context *resolve_name_bcast_send(TALLOC_CTX *mem_ctx,
|
||||
struct event_context *event_ctx,
|
||||
struct nbt_name *name)
|
||||
{
|
||||
int num_interfaces = iface_count();
|
||||
const char **address_list;
|
||||
struct composite_context *c;
|
||||
int i, count=0;
|
||||
|
||||
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(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, name, address_list, 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, const char **reply_addr)
|
||||
{
|
||||
return resolve_name_nbtlist_recv(c, mem_ctx, reply_addr);
|
||||
}
|
||||
|
||||
/*
|
||||
broadcast name resolution method - sync call
|
||||
*/
|
||||
NTSTATUS resolve_name_bcast(struct nbt_name *name,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const char **reply_addr)
|
||||
{
|
||||
struct composite_context *c = resolve_name_bcast_send(mem_ctx, NULL, name);
|
||||
return resolve_name_bcast_recv(c, mem_ctx, reply_addr);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
async gethostbyname() 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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
this module uses a fork() per gethostbyname() 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 library
|
||||
(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 "libcli/composite/composite.h"
|
||||
#include "librpc/gen_ndr/ndr_nbt.h"
|
||||
|
||||
struct host_state {
|
||||
struct nbt_name name;
|
||||
const char *reply_addr;
|
||||
pid_t child;
|
||||
int child_fd;
|
||||
struct fd_event *fde;
|
||||
struct event_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 host_destructor(struct host_state *state)
|
||||
{
|
||||
close(state->child_fd);
|
||||
if (state->child != (pid_t)-1) {
|
||||
kill(state->child, SIGTERM);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
the blocking child
|
||||
*/
|
||||
static void run_child(struct composite_context *c, int fd)
|
||||
{
|
||||
struct host_state *state = talloc_get_type(c->private_data, struct host_state);
|
||||
struct ipv4_addr ip;
|
||||
const char *address;
|
||||
|
||||
/* this is the blocking call we are going to lots of trouble
|
||||
to avoid in the parent */
|
||||
ip = interpret_addr2(state->name.name);
|
||||
|
||||
address = sys_inet_ntoa(ip);
|
||||
if (address != NULL) {
|
||||
write(fd, address, strlen(address)+1);
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/*
|
||||
handle a read event on the pipe
|
||||
*/
|
||||
static void pipe_handler(struct event_context *ev, struct fd_event *fde,
|
||||
uint16_t flags, void *private_data)
|
||||
{
|
||||
struct composite_context *c = talloc_get_type(private_data, struct composite_context);
|
||||
struct host_state *state = talloc_get_type(c->private_data, struct host_state);
|
||||
char address[128];
|
||||
int ret;
|
||||
|
||||
/* if we get any event from the child then we know that we
|
||||
won't need to kill it off */
|
||||
state->child = (pid_t)-1;
|
||||
|
||||
/* 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, sizeof(address)-1);
|
||||
if (ret <= 0) {
|
||||
composite_error(c, NT_STATUS_BAD_NETWORK_NAME);
|
||||
return;
|
||||
}
|
||||
|
||||
/* enusre the address looks good */
|
||||
address[ret] = 0;
|
||||
if (strcmp(address, "0.0.0.0") == 0 ||
|
||||
inet_addr(address) == INADDR_NONE) {
|
||||
composite_error(c, NT_STATUS_BAD_NETWORK_NAME);
|
||||
return;
|
||||
}
|
||||
|
||||
state->reply_addr = talloc_strdup(state, address);
|
||||
if (composite_nomem(state->reply_addr, c)) return;
|
||||
|
||||
composite_done(c);
|
||||
}
|
||||
|
||||
/*
|
||||
gethostbyname name resolution method - async send
|
||||
*/
|
||||
struct composite_context *resolve_name_host_send(TALLOC_CTX *mem_ctx,
|
||||
struct event_context *event_ctx,
|
||||
struct nbt_name *name)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct host_state *state;
|
||||
int fd[2] = { -1, -1 };
|
||||
int ret;
|
||||
|
||||
c = composite_create(mem_ctx, event_ctx);
|
||||
if (c == NULL) return NULL;
|
||||
|
||||
c->event_ctx = talloc_reference(c, event_ctx);
|
||||
if (composite_nomem(c->event_ctx, c)) return c;
|
||||
|
||||
state = talloc(c, struct host_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->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 gethostbyname() 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;
|
||||
}
|
||||
|
||||
/* signal handling in posix really sucks - doing this in a library
|
||||
affects the whole app, but what else to do?? */
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
|
||||
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]);
|
||||
run_child(c, fd[1]);
|
||||
_exit(0);
|
||||
}
|
||||
close(fd[1]);
|
||||
|
||||
/* cleanup wayward children */
|
||||
talloc_set_destructor(state, host_destructor);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
gethostbyname name resolution method - recv side
|
||||
*/
|
||||
NTSTATUS resolve_name_host_recv(struct composite_context *c,
|
||||
TALLOC_CTX *mem_ctx, const char **reply_addr)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
status = composite_wait(c);
|
||||
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
struct host_state *state = talloc_get_type(c->private_data, struct host_state);
|
||||
*reply_addr = talloc_steal(mem_ctx, state->reply_addr);
|
||||
}
|
||||
|
||||
talloc_free(c);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
gethostbyname name resolution method - sync call
|
||||
*/
|
||||
NTSTATUS resolve_name_host(struct nbt_name *name,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const char **reply_addr)
|
||||
{
|
||||
struct composite_context *c = resolve_name_host_send(mem_ctx, NULL, name);
|
||||
return resolve_name_host_recv(c, mem_ctx, reply_addr);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
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/netif.h"
|
||||
#include "librpc/gen_ndr/ndr_nbt.h"
|
||||
#include "libcli/nbt/libnbt.h"
|
||||
|
||||
struct nbtlist_state {
|
||||
struct nbt_name name;
|
||||
struct nbt_name_socket *nbtsock;
|
||||
int num_queries;
|
||||
struct nbt_name_request **queries;
|
||||
struct nbt_name_query *io_queries;
|
||||
const char *reply_addr;
|
||||
};
|
||||
|
||||
/*
|
||||
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,
|
||||
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 (state->io_queries[i].out.num_addrs < 1) {
|
||||
composite_error(c, NT_STATUS_UNEXPECTED_NETWORK_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
/* favor a local address if possible */
|
||||
state->reply_addr = NULL;
|
||||
for (i=0;i<q->out.num_addrs;i++) {
|
||||
if (iface_is_local(q->out.reply_addrs[i])) {
|
||||
state->reply_addr = talloc_steal(state,
|
||||
q->out.reply_addrs[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (state->reply_addr == NULL) {
|
||||
state->reply_addr = talloc_steal(state,
|
||||
q->out.reply_addrs[0]);
|
||||
}
|
||||
|
||||
composite_done(c);
|
||||
}
|
||||
|
||||
/*
|
||||
nbtlist name resolution method - async send
|
||||
*/
|
||||
struct composite_context *resolve_name_nbtlist_send(TALLOC_CTX *mem_ctx,
|
||||
struct event_context *event_ctx,
|
||||
struct nbt_name *name,
|
||||
const char **address_list,
|
||||
BOOL broadcast,
|
||||
BOOL wins_lookup)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct nbtlist_state *state;
|
||||
int i;
|
||||
|
||||
c = composite_create(event_ctx, event_ctx);
|
||||
if (c == NULL) return NULL;
|
||||
|
||||
c->event_ctx = talloc_reference(c, event_ctx);
|
||||
if (composite_nomem(c->event_ctx, c)) return c;
|
||||
|
||||
state = talloc(c, struct nbtlist_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;
|
||||
|
||||
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->nbtsock = nbt_name_socket_init(state, event_ctx);
|
||||
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]);
|
||||
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 = lp_parm_int(-1, "nbt", "timeout", 1);
|
||||
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 = 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, const char **reply_addr)
|
||||
{
|
||||
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);
|
||||
*reply_addr = talloc_steal(mem_ctx, state->reply_addr);
|
||||
}
|
||||
|
||||
talloc_free(c);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
nbt list of addresses name resolution method - sync call
|
||||
*/
|
||||
NTSTATUS resolve_name_nbtlist(struct nbt_name *name,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const char **address_list,
|
||||
BOOL broadcast, BOOL wins_lookup,
|
||||
const char **reply_addr)
|
||||
{
|
||||
struct composite_context *c = resolve_name_nbtlist_send(mem_ctx, NULL, name, address_list,
|
||||
broadcast, wins_lookup);
|
||||
return resolve_name_nbtlist_recv(c, mem_ctx, reply_addr);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "lib/events/events.h"
|
||||
#include "libcli/composite/composite.h"
|
||||
#include "libcli/resolve/resolve.h"
|
||||
#include "librpc/gen_ndr/ndr_nbt.h"
|
||||
|
||||
struct resolve_state {
|
||||
struct nbt_name name;
|
||||
const char **methods;
|
||||
struct composite_context *creq;
|
||||
const char *reply_addr;
|
||||
};
|
||||
|
||||
static struct composite_context *setup_next_method(struct composite_context *c);
|
||||
|
||||
/* pointers to the resolver backends */
|
||||
static const struct resolve_method {
|
||||
const char *name;
|
||||
struct composite_context *(*send_fn)(TALLOC_CTX *mem_ctx, struct event_context *, struct nbt_name *);
|
||||
NTSTATUS (*recv_fn)(struct composite_context *, TALLOC_CTX *, const char **);
|
||||
|
||||
} resolve_methods[] = {
|
||||
{ "bcast", resolve_name_bcast_send, resolve_name_bcast_recv },
|
||||
{ "wins", resolve_name_wins_send, resolve_name_wins_recv },
|
||||
{ "host", resolve_name_host_send, resolve_name_host_recv }
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
find a matching backend
|
||||
*/
|
||||
static const struct resolve_method *find_method(const char *name)
|
||||
{
|
||||
int i;
|
||||
if (name == NULL) return NULL;
|
||||
for (i=0;i<ARRAY_SIZE(resolve_methods);i++) {
|
||||
if (strcasecmp(name, resolve_methods[i].name) == 0) {
|
||||
return &resolve_methods[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
handle completion of one name resolve method
|
||||
*/
|
||||
static void resolve_handler(struct composite_context *creq)
|
||||
{
|
||||
struct composite_context *c = creq->async.private_data;
|
||||
struct resolve_state *state = talloc_get_type(c->private_data, struct resolve_state);
|
||||
const struct resolve_method *method = find_method(state->methods[0]);
|
||||
|
||||
c->status = method->recv_fn(creq, state, &state->reply_addr);
|
||||
|
||||
if (!NT_STATUS_IS_OK(c->status)) {
|
||||
state->methods++;
|
||||
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 {
|
||||
const struct resolve_method *method = find_method(state->methods[0]);
|
||||
if (method) {
|
||||
creq = method->send_fn(c, c->event_ctx, &state->name);
|
||||
}
|
||||
if (creq == NULL && state->methods[0]) state->methods++;
|
||||
|
||||
} while (!creq && state->methods[0]);
|
||||
|
||||
if (creq) {
|
||||
creq->async.fn = resolve_handler;
|
||||
creq->async.private_data = c;
|
||||
}
|
||||
|
||||
return creq;
|
||||
}
|
||||
|
||||
/*
|
||||
general name resolution - async send
|
||||
*/
|
||||
struct composite_context *resolve_name_send(struct nbt_name *name, struct event_context *event_ctx,
|
||||
const char **methods)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct resolve_state *state;
|
||||
|
||||
c = composite_create(event_ctx, event_ctx);
|
||||
if (c == NULL) return NULL;
|
||||
|
||||
if (methods == NULL) {
|
||||
composite_error(c, NT_STATUS_INVALID_PARAMETER);
|
||||
return c;
|
||||
}
|
||||
|
||||
if (event_ctx == NULL) {
|
||||
c->event_ctx = event_context_init(c);
|
||||
} else {
|
||||
c->event_ctx = talloc_reference(c, event_ctx);
|
||||
}
|
||||
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;
|
||||
|
||||
c->status = nbt_name_dup(state, name, &state->name);
|
||||
if (!composite_is_ok(c)) return c;
|
||||
|
||||
state->methods = str_list_copy(state, methods);
|
||||
if (composite_nomem(state->methods, c)) return c;
|
||||
|
||||
if (is_ipaddress(state->name.name) ||
|
||||
strcasecmp(state->name.name, "localhost") == 0) {
|
||||
struct ipv4_addr ip = interpret_addr2(state->name.name);
|
||||
state->reply_addr = talloc_strdup(state, sys_inet_ntoa(ip));
|
||||
if (composite_nomem(state->reply_addr, c)) return c;
|
||||
composite_done(c);
|
||||
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_recv(struct composite_context *c,
|
||||
TALLOC_CTX *mem_ctx, const char **reply_addr)
|
||||
{
|
||||
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);
|
||||
*reply_addr = talloc_steal(mem_ctx, state->reply_addr);
|
||||
}
|
||||
|
||||
talloc_free(c);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
general name resolution - sync call
|
||||
*/
|
||||
NTSTATUS resolve_name(struct nbt_name *name, TALLOC_CTX *mem_ctx, const char **reply_addr,
|
||||
struct event_context *ev)
|
||||
{
|
||||
struct composite_context *c = resolve_name_send(name, ev, lp_name_resolve_order());
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef __RESOLVE_H__
|
||||
#define __RESOLVE_H__
|
||||
|
||||
#include "libcli/nbt/libnbt.h"
|
||||
#include "libcli/resolve/proto.h"
|
||||
|
||||
#endif /* __RESOLVE_H__ */
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/nbt/libnbt.h"
|
||||
#include "libcli/resolve/resolve.h"
|
||||
|
||||
/*
|
||||
wins name resolution method - async send
|
||||
*/
|
||||
struct composite_context *resolve_name_wins_send(TALLOC_CTX *mem_ctx,
|
||||
struct event_context *event_ctx,
|
||||
struct nbt_name *name)
|
||||
{
|
||||
const char **address_list = lp_wins_server_list();
|
||||
if (address_list == NULL) return NULL;
|
||||
return resolve_name_nbtlist_send(mem_ctx, event_ctx, name, address_list, False, True);
|
||||
}
|
||||
|
||||
/*
|
||||
wins name resolution method - recv side
|
||||
*/
|
||||
NTSTATUS resolve_name_wins_recv(struct composite_context *c,
|
||||
TALLOC_CTX *mem_ctx, const char **reply_addr)
|
||||
{
|
||||
return resolve_name_nbtlist_recv(c, mem_ctx, reply_addr);
|
||||
}
|
||||
|
||||
/*
|
||||
wins name resolution method - sync call
|
||||
*/
|
||||
NTSTATUS resolve_name_wins(struct nbt_name *name,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const char **reply_addr)
|
||||
{
|
||||
struct composite_context *c = resolve_name_wins_send(mem_ctx, NULL, name);
|
||||
return resolve_name_wins_recv(c, mem_ctx, reply_addr);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* dacl not present allows access */
|
||||
if (!(sd->type & SEC_DESC_DACL_PRESENT)) {
|
||||
*access_granted = access_desired;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* empty dacl denies access */
|
||||
if (sd->dacl == NULL || sd->dacl->num_aces == 0) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
if (bits_remaining != 0) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#################################
|
||||
# Start SUBSYSTEM LIBSECURITY
|
||||
[SUBSYSTEM::LIBSECURITY]
|
||||
PRIVATE_PROTO_HEADER = proto.h
|
||||
OBJ_FILES = security_token.o \
|
||||
security_descriptor.o \
|
||||
dom_sid.o \
|
||||
access_check.o \
|
||||
privilege.o \
|
||||
sddl.o
|
||||
PUBLIC_DEPENDENCIES = NDR_MISC
|
||||
# End SUBSYSTEM LIBSECURITY
|
||||
#################################
|
||||
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Samba utility functions
|
||||
|
||||
Copyright (C) Stefan (metze) Metzmacher 2002-2004
|
||||
Copyright (C) Andrew Tridgell 1992-2004
|
||||
Copyright (C) Jeremy Allison 1999
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "librpc/gen_ndr/security.h"
|
||||
|
||||
/*****************************************************************
|
||||
Compare the auth portion of two sids.
|
||||
*****************************************************************/
|
||||
|
||||
static int dom_sid_compare_auth(const struct dom_sid *sid1, const struct dom_sid *sid2)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (sid1 == sid2)
|
||||
return 0;
|
||||
if (!sid1)
|
||||
return -1;
|
||||
if (!sid2)
|
||||
return 1;
|
||||
|
||||
if (sid1->sid_rev_num != sid2->sid_rev_num)
|
||||
return sid1->sid_rev_num - sid2->sid_rev_num;
|
||||
|
||||
for (i = 0; i < 6; i++)
|
||||
if (sid1->id_auth[i] != sid2->id_auth[i])
|
||||
return sid1->id_auth[i] - sid2->id_auth[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
Compare two sids.
|
||||
*****************************************************************/
|
||||
|
||||
static int dom_sid_compare(const struct dom_sid *sid1, const struct dom_sid *sid2)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (sid1 == sid2)
|
||||
return 0;
|
||||
if (!sid1)
|
||||
return -1;
|
||||
if (!sid2)
|
||||
return 1;
|
||||
|
||||
/* Compare most likely different rids, first: i.e start at end */
|
||||
if (sid1->num_auths != sid2->num_auths)
|
||||
return sid1->num_auths - sid2->num_auths;
|
||||
|
||||
for (i = sid1->num_auths-1; i >= 0; --i)
|
||||
if (sid1->sub_auths[i] != sid2->sub_auths[i])
|
||||
return sid1->sub_auths[i] - sid2->sub_auths[i];
|
||||
|
||||
return dom_sid_compare_auth(sid1, sid2);
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
Compare two sids.
|
||||
*****************************************************************/
|
||||
|
||||
BOOL dom_sid_equal(const struct dom_sid *sid1, const struct dom_sid *sid2)
|
||||
{
|
||||
return dom_sid_compare(sid1, sid2) == 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
convert a string to a dom_sid, returning a talloc'd dom_sid
|
||||
*/
|
||||
struct dom_sid *dom_sid_parse_talloc(TALLOC_CTX *mem_ctx, const char *sidstr)
|
||||
{
|
||||
struct dom_sid *ret;
|
||||
uint_t rev, ia, num_sub_auths, i;
|
||||
char *p;
|
||||
|
||||
if (strncasecmp(sidstr, "S-", 2)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sidstr += 2;
|
||||
|
||||
rev = strtol(sidstr, &p, 10);
|
||||
if (*p != '-') {
|
||||
return NULL;
|
||||
}
|
||||
sidstr = p+1;
|
||||
|
||||
ia = strtol(sidstr, &p, 10);
|
||||
if (p == sidstr) {
|
||||
return NULL;
|
||||
}
|
||||
sidstr = p;
|
||||
|
||||
num_sub_auths = 0;
|
||||
for (i=0;sidstr[i];i++) {
|
||||
if (sidstr[i] == '-') num_sub_auths++;
|
||||
}
|
||||
|
||||
ret = talloc(mem_ctx, struct dom_sid);
|
||||
if (!ret) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret->sub_auths = talloc_array(ret, uint32_t, num_sub_auths);
|
||||
if (!ret->sub_auths) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret->sid_rev_num = rev;
|
||||
ret->id_auth[0] = 0;
|
||||
ret->id_auth[1] = 0;
|
||||
ret->id_auth[2] = ia >> 24;
|
||||
ret->id_auth[3] = ia >> 16;
|
||||
ret->id_auth[4] = ia >> 8;
|
||||
ret->id_auth[5] = ia;
|
||||
ret->num_auths = num_sub_auths;
|
||||
|
||||
for (i=0;i<num_sub_auths;i++) {
|
||||
if (sidstr[0] != '-') {
|
||||
return NULL;
|
||||
}
|
||||
sidstr++;
|
||||
ret->sub_auths[i] = strtoul(sidstr, &p, 10);
|
||||
if (p == sidstr) {
|
||||
return NULL;
|
||||
}
|
||||
sidstr = p;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
copy a dom_sid structure
|
||||
*/
|
||||
struct dom_sid *dom_sid_dup(TALLOC_CTX *mem_ctx, const struct dom_sid *dom_sid)
|
||||
{
|
||||
struct dom_sid *ret;
|
||||
int i;
|
||||
|
||||
if (!dom_sid) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = talloc(mem_ctx, struct dom_sid);
|
||||
if (!ret) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret->sub_auths = talloc_array(ret, uint32_t, dom_sid->num_auths);
|
||||
if (!ret->sub_auths) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret->sid_rev_num = dom_sid->sid_rev_num;
|
||||
ret->id_auth[0] = dom_sid->id_auth[0];
|
||||
ret->id_auth[1] = dom_sid->id_auth[1];
|
||||
ret->id_auth[2] = dom_sid->id_auth[2];
|
||||
ret->id_auth[3] = dom_sid->id_auth[3];
|
||||
ret->id_auth[4] = dom_sid->id_auth[4];
|
||||
ret->id_auth[5] = dom_sid->id_auth[5];
|
||||
ret->num_auths = dom_sid->num_auths;
|
||||
|
||||
for (i=0;i<dom_sid->num_auths;i++) {
|
||||
ret->sub_auths[i] = dom_sid->sub_auths[i];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
add a rid to a domain dom_sid to make a full dom_sid. This function
|
||||
returns a new sid in the suppplied memory context
|
||||
*/
|
||||
struct dom_sid *dom_sid_add_rid(TALLOC_CTX *mem_ctx,
|
||||
const struct dom_sid *domain_sid,
|
||||
uint32_t rid)
|
||||
{
|
||||
struct dom_sid *sid;
|
||||
|
||||
sid = talloc(mem_ctx, struct dom_sid);
|
||||
if (!sid) return NULL;
|
||||
|
||||
*sid = *domain_sid;
|
||||
|
||||
sid->sub_auths = talloc_array(sid, uint32_t, sid->num_auths+1);
|
||||
if (!sid->sub_auths) {
|
||||
return NULL;
|
||||
}
|
||||
memcpy(sid->sub_auths, domain_sid->sub_auths, sid->num_auths*sizeof(uint32_t));
|
||||
sid->sub_auths[sid->num_auths] = rid;
|
||||
sid->num_auths++;
|
||||
|
||||
return sid;
|
||||
}
|
||||
|
||||
/*
|
||||
Split up a SID into its domain and RID part
|
||||
*/
|
||||
NTSTATUS dom_sid_split_rid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
|
||||
struct dom_sid **domain, uint32_t *rid)
|
||||
{
|
||||
if (sid->num_auths == 0) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!(*domain = dom_sid_dup(mem_ctx, sid))) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
(*domain)->num_auths -= 1;
|
||||
*rid = (*domain)->sub_auths[(*domain)->num_auths];
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
return True if the 2nd sid is in the domain given by the first sid
|
||||
*/
|
||||
BOOL dom_sid_in_domain(const struct dom_sid *domain_sid,
|
||||
const struct dom_sid *sid)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!domain_sid || !sid) {
|
||||
return False;
|
||||
}
|
||||
|
||||
if (domain_sid->num_auths > sid->num_auths) {
|
||||
return False;
|
||||
}
|
||||
|
||||
for (i = domain_sid->num_auths-1; i >= 0; --i) {
|
||||
if (domain_sid->sub_auths[i] != sid->sub_auths[i]) {
|
||||
return False;
|
||||
}
|
||||
}
|
||||
|
||||
return dom_sid_compare_auth(domain_sid, sid) == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
convert a dom_sid to a string
|
||||
*/
|
||||
char *dom_sid_string(TALLOC_CTX *mem_ctx, const struct dom_sid *sid)
|
||||
{
|
||||
int i, ofs, maxlen;
|
||||
uint32_t ia;
|
||||
char *ret;
|
||||
|
||||
if (!sid) {
|
||||
return talloc_strdup(mem_ctx, "(NULL SID)");
|
||||
}
|
||||
|
||||
maxlen = sid->num_auths * 11 + 25;
|
||||
ret = talloc_size(mem_ctx, maxlen);
|
||||
if (!ret) return talloc_strdup(mem_ctx, "(SID ERR)");
|
||||
|
||||
ia = (sid->id_auth[5]) +
|
||||
(sid->id_auth[4] << 8 ) +
|
||||
(sid->id_auth[3] << 16) +
|
||||
(sid->id_auth[2] << 24);
|
||||
|
||||
ofs = snprintf(ret, maxlen, "S-%u-%lu",
|
||||
(unsigned int)sid->sid_rev_num, (unsigned long)ia);
|
||||
|
||||
for (i = 0; i < sid->num_auths; i++) {
|
||||
ofs += snprintf(ret + ofs, maxlen - ofs, "-%lu", (unsigned long)sid->sub_auths[i]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "librpc/gen_ndr/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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,582 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "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) {
|
||||
size_t len = strspn(sddl+2, "-0123456789");
|
||||
(*sddlp) += len+2;
|
||||
return dom_sid_parse_talloc(mem_ctx, sddl);
|
||||
}
|
||||
|
||||
/* 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_steal(mem_ctx, sid->sub_auths);
|
||||
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;
|
||||
}
|
||||
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(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(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(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(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(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(sddl, "S:%s", acl);
|
||||
if (sddl == NULL) goto failed;
|
||||
}
|
||||
|
||||
talloc_free(tmp_ctx);
|
||||
return sddl;
|
||||
|
||||
failed:
|
||||
talloc_free(sddl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "librpc/gen_ndr/security.h"
|
||||
#include "libcli/security/proto.h"
|
||||
@@ -0,0 +1,366 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "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;
|
||||
int i;
|
||||
|
||||
nacl = talloc (mem_ctx, struct security_acl);
|
||||
if (nacl == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nacl->aces = talloc_memdup (nacl, oacl->aces, sizeof(struct security_ace) * oacl->num_aces);
|
||||
if ((nacl->aces == NULL) && (oacl->num_aces > 0)) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* remapping array in trustee dom_sid from old acl to new acl */
|
||||
|
||||
for (i = 0; i < oacl->num_aces; i++) {
|
||||
nacl->aces[i].trustee.sub_auths =
|
||||
talloc_memdup(nacl->aces, nacl->aces[i].trustee.sub_auths,
|
||||
sizeof(uint32_t) * nacl->aces[i].trustee.num_auths);
|
||||
|
||||
if ((nacl->aces[i].trustee.sub_auths == NULL) && (nacl->aces[i].trustee.num_auths > 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;
|
||||
}
|
||||
}
|
||||
|
||||
return nsd;
|
||||
|
||||
failed:
|
||||
talloc_free(nsd);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
add an ACE to the DACL of a security_descriptor
|
||||
*/
|
||||
NTSTATUS security_descriptor_dacl_add(struct security_descriptor *sd,
|
||||
const struct security_ace *ace)
|
||||
{
|
||||
if (sd->dacl == NULL) {
|
||||
sd->dacl = talloc(sd, struct security_acl);
|
||||
if (sd->dacl == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
sd->dacl->revision = NT4_ACL_REVISION;
|
||||
sd->dacl->size = 0;
|
||||
sd->dacl->num_aces = 0;
|
||||
sd->dacl->aces = NULL;
|
||||
}
|
||||
|
||||
sd->dacl->aces = talloc_realloc(sd->dacl, sd->dacl->aces,
|
||||
struct security_ace, sd->dacl->num_aces+1);
|
||||
if (sd->dacl->aces == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
sd->dacl->aces[sd->dacl->num_aces] = *ace;
|
||||
sd->dacl->aces[sd->dacl->num_aces].trustee.sub_auths =
|
||||
talloc_memdup(sd->dacl->aces,
|
||||
sd->dacl->aces[sd->dacl->num_aces].trustee.sub_auths,
|
||||
sizeof(uint32_t) *
|
||||
sd->dacl->aces[sd->dacl->num_aces].trustee.num_auths);
|
||||
if (sd->dacl->aces[sd->dacl->num_aces].trustee.sub_auths == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
sd->dacl->num_aces++;
|
||||
|
||||
sd->type |= SEC_DESC_DACL_PRESENT;
|
||||
|
||||
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,
|
||||
struct dom_sid *trustee)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (sd->dacl == NULL) {
|
||||
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
for (i=0;i<sd->dacl->num_aces;i++) {
|
||||
if (dom_sid_equal(trustee, &sd->dacl->aces[i].trustee)) {
|
||||
memmove(&sd->dacl->aces[i], &sd->dacl->aces[i+1],
|
||||
sizeof(sd->dacl->aces[i]) * (sd->dacl->num_aces - (i+1)));
|
||||
sd->dacl->num_aces--;
|
||||
if (sd->dacl->num_aces == 0) {
|
||||
sd->dacl->aces = NULL;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
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_create(mem_ctx,
|
||||
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_create(TALLOC_CTX *mem_ctx,
|
||||
const char *owner_sid,
|
||||
const char *group_sid,
|
||||
...)
|
||||
{
|
||||
va_list ap;
|
||||
struct security_descriptor *sd;
|
||||
const char *sidstr;
|
||||
|
||||
sd = security_descriptor_initialise(mem_ctx);
|
||||
if (sd == NULL) return NULL;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
va_start(ap, group_sid);
|
||||
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);
|
||||
va_end(ap);
|
||||
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) {
|
||||
va_end(ap);
|
||||
talloc_free(sd);
|
||||
return NULL;
|
||||
}
|
||||
ace->trustee = *sid;
|
||||
status = security_descriptor_dacl_add(sd, ace);
|
||||
/* TODO: check: would talloc_free(ace) here be correct? */
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
va_end(ap);
|
||||
talloc_free(sd);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return sd;
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "dsdb/samdb/samdb.h"
|
||||
#include "libcli/security/security.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);
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "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_UNKNOWN1, 0x0030);
|
||||
SIVAL(c->out.hdr, SMB2_HDR_PID, r->cancel.pending_id);
|
||||
SBVAL(c->out.hdr, SMB2_HDR_SEQNUM, c->seqnum);
|
||||
if (r->session) {
|
||||
SBVAL(c->out.hdr, SMB2_HDR_UID, r->session->uid);
|
||||
}
|
||||
|
||||
SSVAL(c->out.body, 0x02, 0);
|
||||
|
||||
old_timeout = c->transport->options.timeout;
|
||||
c->transport->options.timeout = 0;
|
||||
smb2_transport_send(c);
|
||||
c->transport->options.timeout = old_timeout;
|
||||
|
||||
if (c->state == SMB2_REQUEST_ERROR) {
|
||||
status = c->status;
|
||||
} else {
|
||||
status = NT_STATUS_OK;
|
||||
}
|
||||
|
||||
talloc_free(c);
|
||||
return status;
|
||||
}
|
||||
@@ -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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.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);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
[SUBSYSTEM::LIBCLI_SMB2]
|
||||
PRIVATE_PROTO_HEADER = smb2_proto.h
|
||||
OBJ_FILES = \
|
||||
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
|
||||
PUBLIC_DEPENDENCIES = LIBCLI_RAW LIBPACKET gensec
|
||||
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
#include "libcli/composite/composite.h"
|
||||
#include "libcli/resolve/resolve.h"
|
||||
|
||||
struct smb2_connect_state {
|
||||
struct cli_credentials *credentials;
|
||||
const char *host;
|
||||
const char *share;
|
||||
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,
|
||||
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.unknown1 = 0x09;
|
||||
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 = c;
|
||||
}
|
||||
|
||||
/*
|
||||
continue after negprot reply
|
||||
*/
|
||||
static void continue_negprot(struct smb2_request *req)
|
||||
{
|
||||
struct composite_context *c = talloc_get_type(req->async.private,
|
||||
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;
|
||||
|
||||
state->session = smb2_session_init(transport, 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;
|
||||
|
||||
c->status = smbcli_sock_connect_recv(creq, state, &sock);
|
||||
if (!composite_is_ok(c)) return;
|
||||
|
||||
transport = smb2_transport_init(sock, state);
|
||||
if (composite_nomem(transport, c)) return;
|
||||
|
||||
ZERO_STRUCT(state->negprot);
|
||||
state->negprot.in.unknown1 = 0x0001;
|
||||
|
||||
req = smb2_negprot_send(transport, &state->negprot);
|
||||
if (composite_nomem(req, c)) return;
|
||||
|
||||
req->async.fn = continue_negprot;
|
||||
req->async.private = 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;
|
||||
|
||||
c->status = resolve_name_recv(creq, state, &addr);
|
||||
if (!composite_is_ok(c)) return;
|
||||
|
||||
creq = smbcli_sock_connect_send(state, addr, 445, state->host, c->event_ctx);
|
||||
|
||||
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 *share,
|
||||
struct cli_credentials *credentials,
|
||||
struct event_context *ev)
|
||||
{
|
||||
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->host = talloc_strdup(c, host);
|
||||
if (composite_nomem(state->host, c)) return c;
|
||||
state->share = talloc_strdup(c, share);
|
||||
if (composite_nomem(state->share, c)) return c;
|
||||
|
||||
ZERO_STRUCT(name);
|
||||
name.name = host;
|
||||
|
||||
creq = resolve_name_send(&name, c->event_ctx, lp_name_resolve_order());
|
||||
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 *share,
|
||||
struct cli_credentials *credentials,
|
||||
struct smb2_tree **tree,
|
||||
struct event_context *ev)
|
||||
{
|
||||
struct composite_context *c = smb2_connect_send(mem_ctx, host, share,
|
||||
credentials, ev);
|
||||
return smb2_connect_recv(c, mem_ctx, tree);
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
|
||||
#define CREATE_TAG_EXTA 0x41747845 /* "ExtA" */
|
||||
#define CREATE_TAG_MXAC 0x6341784D /* "MxAc" */
|
||||
|
||||
/*
|
||||
add a blob to a smb2_create attribute blob
|
||||
*/
|
||||
static NTSTATUS smb2_create_blob_add(TALLOC_CTX *mem_ctx, DATA_BLOB *blob,
|
||||
uint32_t tag,
|
||||
DATA_BLOB add, BOOL last)
|
||||
{
|
||||
NTSTATUS status;
|
||||
uint32_t ofs = blob->length;
|
||||
uint8_t pad = smb2_padding_size(add.length, 8);
|
||||
status = data_blob_realloc(mem_ctx, blob, blob->length + 0x18 + add.length + pad);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
if (last) {
|
||||
SIVAL(blob->data, ofs+0x00, 0);
|
||||
} else {
|
||||
SIVAL(blob->data, ofs+0x00, 0x18 + add.length + pad);
|
||||
}
|
||||
SSVAL(blob->data, ofs+0x04, 0x10); /* offset of tag */
|
||||
SIVAL(blob->data, ofs+0x06, 0x04); /* tag length */
|
||||
SSVAL(blob->data, ofs+0x0A, 0x18); /* offset of data */
|
||||
SIVAL(blob->data, ofs+0x0C, add.length);
|
||||
SIVAL(blob->data, ofs+0x10, tag);
|
||||
SIVAL(blob->data, ofs+0x14, 0); /* pad? */
|
||||
memcpy(blob->data+ofs+0x18, add.data, add.length);
|
||||
memset(blob->data+ofs+0x18+add.length, 0, pad);
|
||||
|
||||
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 = data_blob(NULL, 0);
|
||||
|
||||
req = smb2_request_init_tree(tree, SMB2_OP_CREATE, 0x38, True, 0);
|
||||
if (req == NULL) return NULL;
|
||||
|
||||
SSVAL(req->out.body, 0x02, io->in.oplock_flags);
|
||||
SIVAL(req->out.body, 0x04, io->in.impersonation);
|
||||
SIVAL(req->out.body, 0x08, io->in.unknown3[0]);
|
||||
SIVAL(req->out.body, 0x0C, io->in.unknown3[1]);
|
||||
SIVAL(req->out.body, 0x10, io->in.unknown3[2]);
|
||||
SIVAL(req->out.body, 0x14, io->in.unknown3[3]);
|
||||
SIVAL(req->out.body, 0x18, io->in.access_mask);
|
||||
SIVAL(req->out.body, 0x1C, io->in.file_attr);
|
||||
SIVAL(req->out.body, 0x20, io->in.share_access);
|
||||
SIVAL(req->out.body, 0x24, io->in.open_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;
|
||||
}
|
||||
|
||||
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));
|
||||
ea_put_list_chained(b.data, io->in.eas.num_eas, io->in.eas.eas);
|
||||
status = smb2_create_blob_add(req, &blob, CREATE_TAG_EXTA, b, False);
|
||||
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 */
|
||||
status = smb2_create_blob_add(req, &blob, CREATE_TAG_MXAC, data_blob(NULL, 0), True);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (!smb2_request_receive(req) ||
|
||||
!smb2_request_is_ok(req)) {
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
SMB2_CHECK_PACKET_RECV(req, 0x58, True);
|
||||
|
||||
io->out.oplock_flags = SVAL(req->in.body, 0x02);
|
||||
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._pad = 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, &io->out.blob);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
smb2_request_destroy(req);
|
||||
return status;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.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.unknown);
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "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, 0); /* pad? */
|
||||
SIVAL(req->out.body, 0x04, io->in.unknown);
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.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;
|
||||
|
||||
req = smb2_request_init_tree(tree, SMB2_OP_GETINFO, 0x28, False, 0);
|
||||
if (req == NULL) return NULL;
|
||||
|
||||
/* this seems to be a bug, they use 0x29 but only send 0x28 bytes */
|
||||
SSVAL(req->out.body, 0x00, 0x29);
|
||||
|
||||
SSVAL(req->out.body, 0x02, io->in.level);
|
||||
SIVAL(req->out.body, 0x04, io->in.max_response_size);
|
||||
SIVAL(req->out.body, 0x08, io->in.unknown1);
|
||||
SIVAL(req->out.body, 0x0C, io->in.unknown2);
|
||||
SIVAL(req->out.body, 0x10, io->in.flags);
|
||||
SIVAL(req->out.body, 0x14, io->in.flags2);
|
||||
smb2_push_handle(req->out.body+0x18, &io->in.file.handle);
|
||||
|
||||
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 class)
|
||||
{
|
||||
if (class == SMB2_GETINFO_FILE &&
|
||||
level == RAW_FILEINFO_SEC_DESC) {
|
||||
return SMB2_GETINFO_SECURITY;
|
||||
}
|
||||
if ((level & 0xFF) == class) {
|
||||
return level;
|
||||
} else if (level > 1000) {
|
||||
return ((level-1000)<<8) | class;
|
||||
}
|
||||
DEBUG(0,("Unable to map SMB2 info level 0x%04x of class %d\n", level, 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.max_response_size = 0x10000;
|
||||
b.in.file.handle = io->generic.in.file.handle;
|
||||
b.in.level = smb2_level;
|
||||
|
||||
if (io->generic.level == RAW_FILEINFO_SEC_DESC) {
|
||||
b.in.flags = io->query_secdesc.in.secinfo_flags;
|
||||
}
|
||||
if (io->generic.level == RAW_FILEINFO_SMB2_ALL_EAS) {
|
||||
b.in.flags2 = 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.max_response_size = 0x10000;
|
||||
b.in.file.handle = io->generic.handle;
|
||||
b.in.level = smb2_level;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "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);
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "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);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "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;
|
||||
|
||||
req = smb2_request_init_tree(tree, SMB2_OP_LOCK, 0x30, False, 0);
|
||||
if (req == NULL) return NULL;
|
||||
|
||||
SSVAL(req->out.body, 0x02, io->in.unknown1);
|
||||
SIVAL(req->out.body, 0x04, io->in.unknown2);
|
||||
smb2_push_handle(req->out.body+0x08, &io->in.file.handle);
|
||||
SBVAL(req->out.body, 0x18, io->in.offset);
|
||||
SBVAL(req->out.body, 0x20, io->in.count);
|
||||
SIVAL(req->out.body, 0x28, io->in.unknown5);
|
||||
SIVAL(req->out.body, 0x28, io->in.flags);
|
||||
|
||||
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.unknown1 = 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);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user