1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-25 23:21:54 +03:00
samba-mirror/source3/libsmb/clifsinfo.c
Jeremy Allison 32106b2395 r22920: Add in the UNIX capability for 24-bit readX, as discussed
with the Apple guys and Linux kernel guys. Still looking
at how to do writeX as there's no recvfile().
Jeremy.
(This used to be commit a53268fb20)
2007-10-10 12:22:08 -05:00

629 lines
15 KiB
C

/*
Unix SMB/CIFS implementation.
FS info functions
Copyright (C) Stefan (metze) Metzmacher 2003
Copyright (C) Jeremy Allison 2007.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 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"
/****************************************************************************
Get UNIX extensions version info.
****************************************************************************/
BOOL cli_unix_extensions_version(struct cli_state *cli, uint16 *pmajor, uint16 *pminor,
uint32 *pcaplow, uint32 *pcaphigh)
{
BOOL ret = False;
uint16 setup;
char param[2];
char *rparam=NULL, *rdata=NULL;
unsigned int rparam_count=0, rdata_count=0;
setup = TRANSACT2_QFSINFO;
SSVAL(param,0,SMB_QUERY_CIFS_UNIX_INFO);
if (!cli_send_trans(cli, SMBtrans2,
NULL,
0, 0,
&setup, 1, 0,
param, 2, 0,
NULL, 0, 560)) {
goto cleanup;
}
if (!cli_receive_trans(cli, SMBtrans2,
&rparam, &rparam_count,
&rdata, &rdata_count)) {
goto cleanup;
}
if (cli_is_error(cli)) {
ret = False;
goto cleanup;
} else {
ret = True;
}
if (rdata_count < 12) {
goto cleanup;
}
*pmajor = SVAL(rdata,0);
*pminor = SVAL(rdata,2);
cli->posix_capabilities = *pcaplow = IVAL(rdata,4);
*pcaphigh = IVAL(rdata,8);
/* todo: but not yet needed
* return the other stuff
*/
cleanup:
SAFE_FREE(rparam);
SAFE_FREE(rdata);
return ret;
}
/****************************************************************************
Set UNIX extensions capabilities.
****************************************************************************/
BOOL cli_set_unix_extensions_capabilities(struct cli_state *cli, uint16 major, uint16 minor,
uint32 caplow, uint32 caphigh)
{
BOOL ret = False;
uint16 setup;
char param[4];
char data[12];
char *rparam=NULL, *rdata=NULL;
unsigned int rparam_count=0, rdata_count=0;
setup = TRANSACT2_SETFSINFO;
SSVAL(param,0,0);
SSVAL(param,2,SMB_SET_CIFS_UNIX_INFO);
SSVAL(data,0,major);
SSVAL(data,2,minor);
SIVAL(data,4,caplow);
SIVAL(data,8,caphigh);
if (!cli_send_trans(cli, SMBtrans2,
NULL,
0, 0,
&setup, 1, 0,
param, 4, 0,
data, 12, 560)) {
goto cleanup;
}
if (!cli_receive_trans(cli, SMBtrans2,
&rparam, &rparam_count,
&rdata, &rdata_count)) {
goto cleanup;
}
if (cli_is_error(cli)) {
ret = False;
goto cleanup;
} else {
ret = True;
}
cleanup:
SAFE_FREE(rparam);
SAFE_FREE(rdata);
return ret;
}
BOOL cli_get_fs_attr_info(struct cli_state *cli, uint32 *fs_attr)
{
BOOL ret = False;
uint16 setup;
char param[2];
char *rparam=NULL, *rdata=NULL;
unsigned int rparam_count=0, rdata_count=0;
if (!cli||!fs_attr)
smb_panic("cli_get_fs_attr_info() called with NULL Pionter!");
setup = TRANSACT2_QFSINFO;
SSVAL(param,0,SMB_QUERY_FS_ATTRIBUTE_INFO);
if (!cli_send_trans(cli, SMBtrans2,
NULL,
0, 0,
&setup, 1, 0,
param, 2, 0,
NULL, 0, 560)) {
goto cleanup;
}
if (!cli_receive_trans(cli, SMBtrans2,
&rparam, &rparam_count,
&rdata, &rdata_count)) {
goto cleanup;
}
if (cli_is_error(cli)) {
ret = False;
goto cleanup;
} else {
ret = True;
}
if (rdata_count < 12) {
goto cleanup;
}
*fs_attr = IVAL(rdata,0);
/* todo: but not yet needed
* return the other stuff
*/
cleanup:
SAFE_FREE(rparam);
SAFE_FREE(rdata);
return ret;
}
BOOL cli_get_fs_volume_info_old(struct cli_state *cli, fstring volume_name, uint32 *pserial_number)
{
BOOL ret = False;
uint16 setup;
char param[2];
char *rparam=NULL, *rdata=NULL;
unsigned int rparam_count=0, rdata_count=0;
unsigned char nlen;
setup = TRANSACT2_QFSINFO;
SSVAL(param,0,SMB_INFO_VOLUME);
if (!cli_send_trans(cli, SMBtrans2,
NULL,
0, 0,
&setup, 1, 0,
param, 2, 0,
NULL, 0, 560)) {
goto cleanup;
}
if (!cli_receive_trans(cli, SMBtrans2,
&rparam, &rparam_count,
&rdata, &rdata_count)) {
goto cleanup;
}
if (cli_is_error(cli)) {
ret = False;
goto cleanup;
} else {
ret = True;
}
if (rdata_count < 5) {
goto cleanup;
}
if (pserial_number) {
*pserial_number = IVAL(rdata,0);
}
nlen = CVAL(rdata,l2_vol_cch);
clistr_pull(cli, volume_name, rdata + l2_vol_szVolLabel, sizeof(fstring), nlen, STR_NOALIGN);
/* todo: but not yet needed
* return the other stuff
*/
cleanup:
SAFE_FREE(rparam);
SAFE_FREE(rdata);
return ret;
}
BOOL cli_get_fs_volume_info(struct cli_state *cli, fstring volume_name, uint32 *pserial_number, time_t *pdate)
{
BOOL ret = False;
uint16 setup;
char param[2];
char *rparam=NULL, *rdata=NULL;
unsigned int rparam_count=0, rdata_count=0;
unsigned int nlen;
setup = TRANSACT2_QFSINFO;
SSVAL(param,0,SMB_QUERY_FS_VOLUME_INFO);
if (!cli_send_trans(cli, SMBtrans2,
NULL,
0, 0,
&setup, 1, 0,
param, 2, 0,
NULL, 0, 560)) {
goto cleanup;
}
if (!cli_receive_trans(cli, SMBtrans2,
&rparam, &rparam_count,
&rdata, &rdata_count)) {
goto cleanup;
}
if (cli_is_error(cli)) {
ret = False;
goto cleanup;
} else {
ret = True;
}
if (rdata_count < 19) {
goto cleanup;
}
if (pdate) {
struct timespec ts;
ts = interpret_long_date(rdata);
*pdate = ts.tv_sec;
}
if (pserial_number) {
*pserial_number = IVAL(rdata,8);
}
nlen = IVAL(rdata,12);
clistr_pull(cli, volume_name, rdata + 18, sizeof(fstring), nlen, STR_UNICODE);
/* todo: but not yet needed
* return the other stuff
*/
cleanup:
SAFE_FREE(rparam);
SAFE_FREE(rdata);
return ret;
}
/******************************************************************************
Send/receive the request encryption blob.
******************************************************************************/
static NTSTATUS enc_blob_send_receive(struct cli_state *cli, DATA_BLOB *in, DATA_BLOB *out, DATA_BLOB *param_out)
{
uint16 setup;
char param[4];
char *rparam=NULL, *rdata=NULL;
unsigned int rparam_count=0, rdata_count=0;
NTSTATUS status = NT_STATUS_OK;
setup = TRANSACT2_SETFSINFO;
SSVAL(param,0,0);
SSVAL(param,2,SMB_REQUEST_TRANSPORT_ENCRYPTION);
if (!cli_send_trans(cli, SMBtrans2,
NULL,
0, 0,
&setup, 1, 0,
param, 4, 0,
(char *)in->data, in->length, CLI_BUFFER_SIZE)) {
status = cli_nt_error(cli);
goto out;
}
if (!cli_receive_trans(cli, SMBtrans2,
&rparam, &rparam_count,
&rdata, &rdata_count)) {
status = cli_nt_error(cli);
goto out;
}
if (cli_is_error(cli)) {
status = cli_nt_error(cli);
if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
goto out;
}
}
*out = data_blob(rdata, rdata_count);
*param_out = data_blob(rparam, rparam_count);
out:
SAFE_FREE(rparam);
SAFE_FREE(rdata);
return status;
}
/******************************************************************************
Make a client state struct.
******************************************************************************/
static struct smb_trans_enc_state *make_cli_enc_state(enum smb_trans_enc_type smb_enc_type)
{
struct smb_trans_enc_state *es = NULL;
es = SMB_MALLOC_P(struct smb_trans_enc_state);
if (!es) {
return NULL;
}
ZERO_STRUCTP(es);
es->smb_enc_type = smb_enc_type;
if (smb_enc_type == SMB_TRANS_ENC_GSS) {
#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
es->s.gss_state = SMB_MALLOC_P(struct smb_tran_enc_state_gss);
if (!es->s.gss_state) {
SAFE_FREE(es);
return NULL;
}
ZERO_STRUCTP(es->s.gss_state);
#else
DEBUG(0,("make_cli_enc_state: no krb5 compiled.\n"));
SAFE_FREE(es);
return NULL;
#endif
}
return es;
}
/******************************************************************************
Start a raw ntlmssp encryption.
******************************************************************************/
NTSTATUS cli_raw_ntlm_smb_encryption_start(struct cli_state *cli,
const char *user,
const char *pass,
const char *domain)
{
DATA_BLOB blob_in = data_blob_null;
DATA_BLOB blob_out = data_blob_null;
DATA_BLOB param_out = data_blob_null;
NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
struct smb_trans_enc_state *es = make_cli_enc_state(SMB_TRANS_ENC_NTLM);
if (!es) {
return NT_STATUS_NO_MEMORY;
}
status = ntlmssp_client_start(&es->s.ntlmssp_state);
if (!NT_STATUS_IS_OK(status)) {
goto fail;
}
ntlmssp_want_feature(es->s.ntlmssp_state, NTLMSSP_FEATURE_SESSION_KEY);
es->s.ntlmssp_state->neg_flags |= (NTLMSSP_NEGOTIATE_SIGN|NTLMSSP_NEGOTIATE_SEAL);
if (!NT_STATUS_IS_OK(status = ntlmssp_set_username(es->s.ntlmssp_state, user))) {
goto fail;
}
if (!NT_STATUS_IS_OK(status = ntlmssp_set_domain(es->s.ntlmssp_state, domain))) {
goto fail;
}
if (!NT_STATUS_IS_OK(status = ntlmssp_set_password(es->s.ntlmssp_state, pass))) {
goto fail;
}
do {
status = ntlmssp_update(es->s.ntlmssp_state, blob_in, &blob_out);
data_blob_free(&blob_in);
data_blob_free(&param_out);
if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) || NT_STATUS_IS_OK(status)) {
status = enc_blob_send_receive(cli, &blob_out, &blob_in, &param_out);
}
if (param_out.length == 2) {
es->enc_ctx_num = SVAL(param_out.data, 0);
}
data_blob_free(&blob_out);
} while (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED));
data_blob_free(&blob_in);
if (NT_STATUS_IS_OK(status)) {
/* Replace the old state, if any. */
if (cli->trans_enc_state) {
common_free_encryption_state(&cli->trans_enc_state);
}
cli->trans_enc_state = es;
cli->trans_enc_state->enc_on = True;
es = NULL;
}
fail:
common_free_encryption_state(&es);
return status;
}
#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
#ifndef SMB_GSS_REQUIRED_FLAGS
#define SMB_GSS_REQUIRED_FLAGS (GSS_C_CONF_FLAG|GSS_C_INTEG_FLAG|GSS_C_MUTUAL_FLAG|GSS_C_REPLAY_FLAG|GSS_C_SEQUENCE_FLAG)
#endif
/******************************************************************************
Get client gss blob to send to a server.
******************************************************************************/
static NTSTATUS make_cli_gss_blob(struct smb_trans_enc_state *es,
const char *service,
const char *host,
NTSTATUS status_in,
DATA_BLOB spnego_blob_in,
DATA_BLOB *p_blob_out)
{
const char *krb_mechs[] = {OID_KERBEROS5, NULL};
OM_uint32 ret;
OM_uint32 min;
gss_name_t srv_name;
gss_buffer_desc input_name;
gss_buffer_desc *p_tok_in;
gss_buffer_desc tok_out, tok_in;
DATA_BLOB blob_out = data_blob_null;
DATA_BLOB blob_in = data_blob_null;
char *host_princ_s = NULL;
OM_uint32 ret_flags = 0;
NTSTATUS status = NT_STATUS_OK;
gss_OID_desc nt_hostbased_service =
{10, CONST_DISCARD(char *,"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04")};
memset(&tok_out, '\0', sizeof(tok_out));
/* Get a ticket for the service@host */
asprintf(&host_princ_s, "%s@%s", service, host);
if (host_princ_s == NULL) {
return NT_STATUS_NO_MEMORY;
}
input_name.value = host_princ_s;
input_name.length = strlen(host_princ_s) + 1;
ret = gss_import_name(&min,
&input_name,
&nt_hostbased_service,
&srv_name);
if (ret != GSS_S_COMPLETE) {
SAFE_FREE(host_princ_s);
return map_nt_error_from_gss(ret, min);
}
if (spnego_blob_in.length == 0) {
p_tok_in = GSS_C_NO_BUFFER;
} else {
/* Remove the SPNEGO wrapper */
if (!spnego_parse_auth_response(spnego_blob_in, status_in, OID_KERBEROS5, &blob_in)) {
status = NT_STATUS_UNSUCCESSFUL;
goto fail;
}
tok_in.value = blob_in.data;
tok_in.length = blob_in.length;
p_tok_in = &tok_in;
}
ret = gss_init_sec_context(&min,
GSS_C_NO_CREDENTIAL, /* Use our default cred. */
&es->s.gss_state->gss_ctx,
srv_name,
GSS_C_NO_OID, /* default OID. */
GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG,
GSS_C_INDEFINITE, /* requested ticket lifetime. */
NULL, /* no channel bindings */
p_tok_in,
NULL, /* ignore mech type */
&tok_out,
&ret_flags,
NULL); /* ignore time_rec */
status = map_nt_error_from_gss(ret, min);
if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status,NT_STATUS_MORE_PROCESSING_REQUIRED)) {
ADS_STATUS adss = ADS_ERROR_GSS(ret, min);
DEBUG(10,("make_cli_gss_blob: gss_init_sec_context failed with %s\n",
ads_errstr(adss)));
goto fail;
}
if ((ret_flags & SMB_GSS_REQUIRED_FLAGS) != SMB_GSS_REQUIRED_FLAGS) {
status = NT_STATUS_ACCESS_DENIED;
}
blob_out = data_blob(tok_out.value, tok_out.length);
/* Wrap in an SPNEGO wrapper */
*p_blob_out = gen_negTokenTarg(krb_mechs, blob_out);
fail:
data_blob_free(&blob_out);
data_blob_free(&blob_in);
SAFE_FREE(host_princ_s);
gss_release_name(&min, &srv_name);
if (tok_out.value) {
gss_release_buffer(&min, &tok_out);
}
return status;
}
/******************************************************************************
Start a SPNEGO gssapi encryption context.
******************************************************************************/
NTSTATUS cli_gss_smb_encryption_start(struct cli_state *cli)
{
DATA_BLOB blob_recv = data_blob_null;
DATA_BLOB blob_send = data_blob_null;
DATA_BLOB param_out = data_blob_null;
NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
fstring fqdn;
const char *servicename;
struct smb_trans_enc_state *es = make_cli_enc_state(SMB_TRANS_ENC_GSS);
if (!es) {
return NT_STATUS_NO_MEMORY;
}
name_to_fqdn(fqdn, cli->desthost);
strlower_m(fqdn);
servicename = "cifs";
status = make_cli_gss_blob(es, servicename, fqdn, NT_STATUS_OK, blob_recv, &blob_send);
if (!NT_STATUS_EQUAL(status,NT_STATUS_MORE_PROCESSING_REQUIRED)) {
servicename = "host";
status = make_cli_gss_blob(es, servicename, fqdn, NT_STATUS_OK, blob_recv, &blob_send);
if (!NT_STATUS_EQUAL(status,NT_STATUS_MORE_PROCESSING_REQUIRED)) {
goto fail;
}
}
do {
data_blob_free(&blob_recv);
status = enc_blob_send_receive(cli, &blob_send, &blob_recv, &param_out);
if (param_out.length == 2) {
es->enc_ctx_num = SVAL(param_out.data, 0);
}
data_blob_free(&blob_send);
status = make_cli_gss_blob(es, servicename, fqdn, status, blob_recv, &blob_send);
} while (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED));
data_blob_free(&blob_recv);
if (NT_STATUS_IS_OK(status)) {
/* Replace the old state, if any. */
if (cli->trans_enc_state) {
common_free_encryption_state(&cli->trans_enc_state);
}
cli->trans_enc_state = es;
cli->trans_enc_state->enc_on = True;
es = NULL;
}
fail:
common_free_encryption_state(&es);
return status;
}
#else
NTSTATUS cli_gss_smb_encryption_start(struct cli_state *cli)
{
return NT_STATUS_NOT_SUPPORTED;
}
#endif