mirror of
https://github.com/samba-team/samba.git
synced 2024-12-25 23:21:54 +03:00
added NTLMSSP authentication to libsmb. It seems to work well so I have enabled it by default if the server supports it. Let me know if this breaks anything. Choose kerberos with the -k flag to smbclient, otherwise it will use SPNEGO/NTLMSSP/NTLM
This commit is contained in:
parent
4f12df9fc5
commit
076aa97bee
@ -116,7 +116,8 @@ UBIQX_OBJ = ubiqx/ubi_BinTree.o ubiqx/ubi_Cache.o ubiqx/ubi_SplayTree.o \
|
||||
|
||||
PARAM_OBJ = param/loadparm.o param/params.o
|
||||
|
||||
LIBSMB_OBJ = libsmb/clientgen.o libsmb/cliconnect.o libsmb/clifile.o libsmb/clikrb5.o libsmb/asn1.o \
|
||||
LIBSMB_OBJ = libsmb/clientgen.o libsmb/cliconnect.o libsmb/clifile.o \
|
||||
libsmb/clikrb5.o libsmb/clispnego.o libsmb/asn1.o \
|
||||
libsmb/clirap.o libsmb/clierror.o libsmb/climessage.o \
|
||||
libsmb/clireadwrite.o libsmb/clilist.o libsmb/cliprint.o \
|
||||
libsmb/clitrans.o libsmb/clisecdesc.o libsmb/clidgram.o \
|
||||
|
@ -2115,10 +2115,7 @@ struct cli_state *do_connect(const char *server, const char *share)
|
||||
}
|
||||
|
||||
c->protocol = max_protocol;
|
||||
|
||||
if (use_kerberos) {
|
||||
c->use_spnego = True;
|
||||
}
|
||||
c->use_kerberos = use_kerberos;
|
||||
|
||||
if (!cli_session_request(c, &calling, &called)) {
|
||||
char *p;
|
||||
|
@ -42,5 +42,6 @@ typedef struct {
|
||||
#define ASN1_OCTET_STRING 0x4
|
||||
#define ASN1_OID 0x6
|
||||
#define ASN1_BOOLEAN 0x1
|
||||
#define ASN1_ENUMERATED 0xa
|
||||
|
||||
#define ASN1_MAX_OIDS 20
|
||||
|
@ -128,7 +128,7 @@ struct cli_state {
|
||||
uint16 max_recv_frag;
|
||||
vuser_key key;
|
||||
uint32 ntlmssp_flags;
|
||||
BOOL use_spnego; /* until we do NTLMSSP we need to make this optional */
|
||||
BOOL use_kerberos;
|
||||
|
||||
BOOL use_oplocks; /* should we use oplocks? */
|
||||
BOOL use_level_II_oplocks; /* should we use level II oplocks? */
|
||||
|
@ -315,3 +315,30 @@ BOOL asn1_read_GeneralString(ASN1_DATA *data, char **s)
|
||||
asn1_end_tag(data);
|
||||
return !data->has_error;
|
||||
}
|
||||
|
||||
/* read a octet string blob */
|
||||
BOOL asn1_read_octet_string(ASN1_DATA *data, DATA_BLOB *blob)
|
||||
{
|
||||
int len;
|
||||
if (!asn1_start_tag(data, ASN1_OCTET_STRING)) return False;
|
||||
len = asn1_tag_remaining(data);
|
||||
blob->data = malloc(len);
|
||||
if (!blob->data) {
|
||||
data->has_error = True;
|
||||
return False;
|
||||
}
|
||||
asn1_read(data, blob->data, len);
|
||||
blob->length = len;
|
||||
asn1_end_tag(data);
|
||||
return !data->has_error;
|
||||
}
|
||||
|
||||
/* check a enumarted value is correct */
|
||||
BOOL asn1_check_enumerated(ASN1_DATA *data, int v)
|
||||
{
|
||||
uint8 b;
|
||||
if (!asn1_start_tag(data, ASN1_ENUMERATED)) return False;
|
||||
asn1_read_uint8(data, &b);
|
||||
asn1_end_tag(data);
|
||||
return !data->has_error && (v == b);
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ prots[] =
|
||||
{PROTOCOL_LANMAN1,"LANMAN1.0"},
|
||||
{PROTOCOL_LANMAN1,"Windows for Workgroups 3.1a"},
|
||||
{PROTOCOL_LANMAN2,"LM1.2X002"},
|
||||
{PROTOCOL_NT1,"Samba"},
|
||||
{PROTOCOL_NT1,"LANMAN2.1"},
|
||||
{PROTOCOL_NT1,"NT LM 0.12"},
|
||||
{-1,NULL}
|
||||
@ -315,16 +316,177 @@ static BOOL cli_session_setup_nt1(struct cli_state *cli, char *user,
|
||||
return True;
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
send a extended security session setup blob, returning a reply blob
|
||||
****************************************************************************/
|
||||
static DATA_BLOB cli_session_setup_blob(struct cli_state *cli, DATA_BLOB blob)
|
||||
{
|
||||
uint32 capabilities = cli_session_setup_capabilities(cli);
|
||||
char *p;
|
||||
DATA_BLOB blob2;
|
||||
|
||||
blob2 = data_blob(NULL, 0);
|
||||
|
||||
capabilities |= CAP_EXTENDED_SECURITY;
|
||||
|
||||
/* send a session setup command */
|
||||
memset(cli->outbuf,'\0',smb_size);
|
||||
|
||||
set_message(cli->outbuf,12,0,True);
|
||||
CVAL(cli->outbuf,smb_com) = SMBsesssetupX;
|
||||
cli_setup_packet(cli);
|
||||
|
||||
CVAL(cli->outbuf,smb_vwv0) = 0xFF;
|
||||
SSVAL(cli->outbuf,smb_vwv2,CLI_BUFFER_SIZE);
|
||||
SSVAL(cli->outbuf,smb_vwv3,2);
|
||||
SSVAL(cli->outbuf,smb_vwv4,0);
|
||||
SIVAL(cli->outbuf,smb_vwv5,0);
|
||||
SSVAL(cli->outbuf,smb_vwv7,blob.length);
|
||||
SIVAL(cli->outbuf,smb_vwv10,capabilities);
|
||||
p = smb_buf(cli->outbuf);
|
||||
memcpy(p, blob.data, blob.length);
|
||||
p += blob.length;
|
||||
p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE);
|
||||
p += clistr_push(cli, p, "Samba", -1, STR_TERMINATE);
|
||||
cli_setup_bcc(cli, p);
|
||||
|
||||
cli_send_smb(cli);
|
||||
if (!cli_receive_smb(cli))
|
||||
return blob2;
|
||||
|
||||
show_msg(cli->inbuf);
|
||||
|
||||
if (cli_is_error(cli) && !NT_STATUS_EQUAL(cli_nt_error(cli),
|
||||
NT_STATUS_MORE_PROCESSING_REQUIRED)) {
|
||||
return blob2;
|
||||
}
|
||||
|
||||
/* use the returned vuid from now on */
|
||||
cli->vuid = SVAL(cli->inbuf,smb_uid);
|
||||
|
||||
p = smb_buf(cli->inbuf);
|
||||
|
||||
blob2 = data_blob(p, SVAL(cli->inbuf, smb_vwv3));
|
||||
|
||||
p += blob2.length;
|
||||
p += clistr_pull(cli, cli->server_os, p, sizeof(fstring), -1, STR_TERMINATE);
|
||||
p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), -1, STR_TERMINATE);
|
||||
p += clistr_pull(cli, cli->server_domain, p, sizeof(fstring), -1, STR_TERMINATE);
|
||||
|
||||
return blob2;
|
||||
}
|
||||
|
||||
|
||||
#if HAVE_KRB5
|
||||
/****************************************************************************
|
||||
do a spnego/kerberos encrypted session setup
|
||||
****************************************************************************/
|
||||
static BOOL cli_session_setup_kerberos(struct cli_state *cli, char *principle, char *workgroup)
|
||||
{
|
||||
DATA_BLOB blob2, negTokenTarg;
|
||||
|
||||
/* generate the encapsulated kerberos5 ticket */
|
||||
negTokenTarg = spnego_gen_negTokenTarg(cli, principle);
|
||||
|
||||
if (!negTokenTarg.data) return False;
|
||||
|
||||
blob2 = cli_session_setup_blob(cli, negTokenTarg);
|
||||
|
||||
/* we don't need this blob for kerberos */
|
||||
data_blob_free(blob2);
|
||||
|
||||
return !cli_is_error(cli);
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
do a spnego/NTLMSSP encrypted session setup
|
||||
****************************************************************************/
|
||||
static BOOL cli_session_setup_ntlmssp(struct cli_state *cli, char *user,
|
||||
char *pass, char *workgroup)
|
||||
{
|
||||
const char *mechs[] = {"1 3 6 1 4 1 311 2 2 10", NULL};
|
||||
DATA_BLOB msg1;
|
||||
DATA_BLOB blob, chal1, chal2, auth;
|
||||
uint8 challenge[8];
|
||||
uint8 nthash[24], lmhash[24], sess_key[16];
|
||||
uint32 neg_flags;
|
||||
|
||||
neg_flags = NTLMSSP_NEGOTIATE_UNICODE |
|
||||
NTLMSSP_NEGOTIATE_LM_KEY |
|
||||
NTLMSSP_NEGOTIATE_NTLM;
|
||||
|
||||
memset(sess_key, 0, 16);
|
||||
|
||||
/* generate the ntlmssp negotiate packet */
|
||||
msrpc_gen(&blob, "CddB",
|
||||
"NTLMSSP",
|
||||
NTLMSSP_NEGOTIATE,
|
||||
neg_flags,
|
||||
sess_key, 16);
|
||||
|
||||
/* and wrap it in a SPNEGO wrapper */
|
||||
msg1 = gen_negTokenTarg(mechs, blob);
|
||||
data_blob_free(blob);
|
||||
|
||||
/* now send that blob on its way */
|
||||
blob = cli_session_setup_blob(cli, msg1);
|
||||
|
||||
data_blob_free(msg1);
|
||||
|
||||
if (!NT_STATUS_EQUAL(cli_nt_error(cli), NT_STATUS_MORE_PROCESSING_REQUIRED)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
/* the server gives us back two challenges */
|
||||
if (!spnego_parse_challenge(blob, &chal1, &chal2)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
data_blob_free(blob);
|
||||
|
||||
/* encrypt the password with the challenge */
|
||||
memcpy(challenge, chal1.data + 24, 8);
|
||||
SMBencrypt(pass, challenge,lmhash);
|
||||
SMBNTencrypt(pass, challenge,nthash);
|
||||
|
||||
data_blob_free(chal1);
|
||||
data_blob_free(chal2);
|
||||
|
||||
/* this generates the actual auth packet */
|
||||
msrpc_gen(&blob, "CdBBUUUBd",
|
||||
"NTLMSSP",
|
||||
NTLMSSP_AUTH,
|
||||
lmhash, 24,
|
||||
nthash, 24,
|
||||
workgroup,
|
||||
user,
|
||||
cli->calling.name,
|
||||
sess_key, 16,
|
||||
neg_flags);
|
||||
|
||||
/* wrap it in SPNEGO */
|
||||
auth = spnego_gen_auth(blob);
|
||||
|
||||
data_blob_free(blob);
|
||||
|
||||
/* now send the auth packet and we should be done */
|
||||
blob = cli_session_setup_blob(cli, auth);
|
||||
|
||||
data_blob_free(auth);
|
||||
data_blob_free(blob);
|
||||
|
||||
return !cli_is_error(cli);
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
do a spnego encrypted session setup
|
||||
****************************************************************************/
|
||||
static BOOL cli_session_setup_spnego(struct cli_state *cli, char *user,
|
||||
char *pass, char *workgroup)
|
||||
{
|
||||
uint32 capabilities = cli_session_setup_capabilities(cli);
|
||||
char *p;
|
||||
DATA_BLOB blob2, negTokenTarg;
|
||||
char *principle;
|
||||
char *OIDs[ASN1_MAX_OIDS];
|
||||
uint8 guid[16];
|
||||
@ -347,73 +509,18 @@ static BOOL cli_session_setup_spnego(struct cli_state *cli, char *user,
|
||||
}
|
||||
DEBUG(3,("got principle=%s\n", principle));
|
||||
|
||||
if (!got_kerberos_mechanism) {
|
||||
DEBUG(1,("Server didn't offer kerberos5 mechanism!?\n"));
|
||||
return False;
|
||||
}
|
||||
fstrcpy(cli->user_name, user);
|
||||
|
||||
/* generate the encapsulated kerberos5 ticket */
|
||||
negTokenTarg = spnego_gen_negTokenTarg(cli, principle);
|
||||
#if HAVE_KRB5
|
||||
if (got_kerberos_mechanism && cli->use_kerberos) {
|
||||
return cli_session_setup_kerberos(cli, principle, workgroup);
|
||||
}
|
||||
#endif
|
||||
|
||||
free(principle);
|
||||
|
||||
if (!negTokenTarg.data) return False;
|
||||
|
||||
capabilities |= CAP_EXTENDED_SECURITY;
|
||||
|
||||
/* send a session setup command */
|
||||
memset(cli->outbuf,'\0',smb_size);
|
||||
|
||||
set_message(cli->outbuf,12,0,True);
|
||||
CVAL(cli->outbuf,smb_com) = SMBsesssetupX;
|
||||
cli_setup_packet(cli);
|
||||
|
||||
CVAL(cli->outbuf,smb_vwv0) = 0xFF;
|
||||
SSVAL(cli->outbuf,smb_vwv2,CLI_BUFFER_SIZE);
|
||||
SSVAL(cli->outbuf,smb_vwv3,2);
|
||||
SSVAL(cli->outbuf,smb_vwv4,0);
|
||||
SIVAL(cli->outbuf,smb_vwv5,0);
|
||||
SSVAL(cli->outbuf,smb_vwv7,negTokenTarg.length);
|
||||
SIVAL(cli->outbuf,smb_vwv10,capabilities);
|
||||
p = smb_buf(cli->outbuf);
|
||||
memcpy(p, negTokenTarg.data, negTokenTarg.length);
|
||||
p += negTokenTarg.length;
|
||||
p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE);
|
||||
p += clistr_push(cli, p, "Samba", -1, STR_TERMINATE);
|
||||
cli_setup_bcc(cli, p);
|
||||
|
||||
cli_send_smb(cli);
|
||||
if (!cli_receive_smb(cli))
|
||||
return False;
|
||||
|
||||
show_msg(cli->inbuf);
|
||||
|
||||
if (cli_is_error(cli)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
/* use the returned vuid from now on */
|
||||
cli->vuid = SVAL(cli->inbuf,smb_uid);
|
||||
|
||||
p = smb_buf(cli->inbuf);
|
||||
|
||||
blob2 = data_blob(p, SVAL(cli->inbuf, smb_vwv3));
|
||||
|
||||
p += blob2.length;
|
||||
p += clistr_pull(cli, cli->server_os, p, sizeof(fstring), -1, STR_TERMINATE);
|
||||
p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), -1, STR_TERMINATE);
|
||||
p += clistr_pull(cli, cli->server_domain, p, sizeof(fstring), -1, STR_TERMINATE);
|
||||
|
||||
fstrcpy(cli->user_name, user);
|
||||
|
||||
data_blob_free(negTokenTarg);
|
||||
|
||||
/* we don't need this blob until we do NTLMSSP */
|
||||
data_blob_free(blob2);
|
||||
|
||||
return True;
|
||||
return cli_session_setup_ntlmssp(cli, user, pass, workgroup);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
@ -674,11 +781,6 @@ BOOL cli_negprot(struct cli_state *cli)
|
||||
|
||||
CVAL(smb_buf(cli->outbuf),0) = 2;
|
||||
|
||||
if (cli->use_spnego) {
|
||||
SSVAL(cli->outbuf, smb_flg2,
|
||||
SVAL(cli->outbuf, smb_flg2) | FLAGS2_EXTENDED_SECURITY);
|
||||
}
|
||||
|
||||
cli_send_smb(cli);
|
||||
if (!cli_receive_smb(cli))
|
||||
return False;
|
||||
|
@ -112,10 +112,7 @@ void cli_setup_packet(struct cli_state *cli)
|
||||
if (cli->capabilities & CAP_STATUS32) {
|
||||
flags2 |= FLAGS2_32_BIT_ERROR_CODES;
|
||||
}
|
||||
if (cli->use_spnego) {
|
||||
/* once we have NTLMSSP we can enable this unconditionally */
|
||||
flags2 |= FLAGS2_EXTENDED_SECURITY;
|
||||
}
|
||||
flags2 |= FLAGS2_EXTENDED_SECURITY;
|
||||
SSVAL(cli->outbuf,smb_flg2, flags2);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
Unix SMB/Netbios implementation.
|
||||
Version 3.0
|
||||
simple kerberos5/SPNEGO routines
|
||||
simple kerberos5 routines for active directory
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@ -24,9 +24,6 @@
|
||||
#if HAVE_KRB5
|
||||
#include <krb5.h>
|
||||
|
||||
#define OID_SPNEGO "1 3 6 1 5 5 2"
|
||||
#define OID_KERBEROS5 "1 2 840 113554 1 2 2"
|
||||
|
||||
/*
|
||||
we can't use krb5_mk_req because w2k wants the service to be in a particular format
|
||||
*/
|
||||
@ -86,7 +83,7 @@ cleanup_princ:
|
||||
/*
|
||||
get a kerberos5 ticket for the given service
|
||||
*/
|
||||
static DATA_BLOB krb5_get_ticket(char *service, char *realm)
|
||||
DATA_BLOB krb5_get_ticket(char *service, char *realm)
|
||||
{
|
||||
krb5_error_code retval;
|
||||
krb5_data packet;
|
||||
@ -126,213 +123,11 @@ failed:
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
generate a negTokenInit packet given a GUID, a list of supported
|
||||
OIDs (the mechanisms) and a principle name string
|
||||
*/
|
||||
ASN1_DATA spnego_gen_negTokenInit(uint8 guid[16],
|
||||
const char *OIDs[],
|
||||
const char *principle)
|
||||
{
|
||||
int i;
|
||||
ASN1_DATA data;
|
||||
|
||||
memset(&data, 0, sizeof(data));
|
||||
|
||||
asn1_write(&data, guid, 16);
|
||||
asn1_push_tag(&data,ASN1_APPLICATION(0));
|
||||
asn1_write_OID(&data,OID_SPNEGO);
|
||||
asn1_push_tag(&data,ASN1_CONTEXT(0));
|
||||
asn1_push_tag(&data,ASN1_SEQUENCE(0));
|
||||
|
||||
asn1_push_tag(&data,ASN1_CONTEXT(0));
|
||||
asn1_push_tag(&data,ASN1_SEQUENCE(0));
|
||||
for (i=0; OIDs[i]; i++) {
|
||||
asn1_write_OID(&data,OIDs[i]);
|
||||
}
|
||||
asn1_pop_tag(&data);
|
||||
asn1_pop_tag(&data);
|
||||
|
||||
asn1_push_tag(&data, ASN1_CONTEXT(3));
|
||||
asn1_push_tag(&data, ASN1_SEQUENCE(0));
|
||||
asn1_push_tag(&data, ASN1_CONTEXT(0));
|
||||
asn1_write_GeneralString(&data,principle);
|
||||
asn1_pop_tag(&data);
|
||||
asn1_pop_tag(&data);
|
||||
asn1_pop_tag(&data);
|
||||
|
||||
asn1_pop_tag(&data);
|
||||
asn1_pop_tag(&data);
|
||||
|
||||
asn1_pop_tag(&data);
|
||||
|
||||
if (data.has_error) {
|
||||
DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data.ofs));
|
||||
asn1_free(&data);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
parse a negTokenInit packet giving a GUID, a list of supported
|
||||
OIDs (the mechanisms) and a principle name string
|
||||
*/
|
||||
BOOL spnego_parse_negTokenInit(DATA_BLOB blob,
|
||||
uint8 guid[16],
|
||||
char *OIDs[ASN1_MAX_OIDS],
|
||||
char **principle)
|
||||
{
|
||||
int i;
|
||||
BOOL ret;
|
||||
ASN1_DATA data;
|
||||
|
||||
asn1_load(&data, blob);
|
||||
|
||||
asn1_read(&data, guid, 16);
|
||||
asn1_start_tag(&data,ASN1_APPLICATION(0));
|
||||
asn1_check_OID(&data,OID_SPNEGO);
|
||||
asn1_start_tag(&data,ASN1_CONTEXT(0));
|
||||
asn1_start_tag(&data,ASN1_SEQUENCE(0));
|
||||
|
||||
asn1_start_tag(&data,ASN1_CONTEXT(0));
|
||||
asn1_start_tag(&data,ASN1_SEQUENCE(0));
|
||||
for (i=0; asn1_tag_remaining(&data) > 0 && i < ASN1_MAX_OIDS; i++) {
|
||||
char *oid = NULL;
|
||||
asn1_read_OID(&data,&oid);
|
||||
OIDs[i] = oid;
|
||||
}
|
||||
OIDs[i] = NULL;
|
||||
asn1_end_tag(&data);
|
||||
asn1_end_tag(&data);
|
||||
|
||||
asn1_start_tag(&data, ASN1_CONTEXT(3));
|
||||
asn1_start_tag(&data, ASN1_SEQUENCE(0));
|
||||
asn1_start_tag(&data, ASN1_CONTEXT(0));
|
||||
asn1_read_GeneralString(&data,principle);
|
||||
asn1_end_tag(&data);
|
||||
asn1_end_tag(&data);
|
||||
asn1_end_tag(&data);
|
||||
|
||||
asn1_end_tag(&data);
|
||||
asn1_end_tag(&data);
|
||||
|
||||
asn1_end_tag(&data);
|
||||
|
||||
ret = !data.has_error;
|
||||
asn1_free(&data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
generate a negTokenTarg packet given a list of OIDs and a security blob
|
||||
*/
|
||||
static ASN1_DATA gen_negTokenTarg(const char *OIDs[], ASN1_DATA blob)
|
||||
{
|
||||
int i;
|
||||
ASN1_DATA data;
|
||||
|
||||
memset(&data, 0, sizeof(data));
|
||||
|
||||
asn1_push_tag(&data, ASN1_APPLICATION(0));
|
||||
asn1_write_OID(&data,OID_SPNEGO);
|
||||
asn1_push_tag(&data, ASN1_CONTEXT(0));
|
||||
asn1_push_tag(&data, ASN1_SEQUENCE(0));
|
||||
|
||||
asn1_push_tag(&data, ASN1_CONTEXT(0));
|
||||
asn1_push_tag(&data, ASN1_SEQUENCE(0));
|
||||
for (i=0; OIDs[i]; i++) {
|
||||
asn1_write_OID(&data,OIDs[i]);
|
||||
}
|
||||
asn1_pop_tag(&data);
|
||||
asn1_pop_tag(&data);
|
||||
|
||||
asn1_push_tag(&data, ASN1_CONTEXT(2));
|
||||
asn1_write_OctetString(&data,blob.data,blob.length);
|
||||
asn1_pop_tag(&data);
|
||||
|
||||
asn1_pop_tag(&data);
|
||||
asn1_pop_tag(&data);
|
||||
|
||||
asn1_pop_tag(&data);
|
||||
|
||||
if (data.has_error) {
|
||||
DEBUG(1,("Failed to build negTokenTarg at offset %d\n", (int)data.ofs));
|
||||
asn1_free(&data);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
generate a krb5 GSS-API wrapper packet given a ticket
|
||||
*/
|
||||
static ASN1_DATA spnego_gen_krb5_wrap(DATA_BLOB ticket)
|
||||
{
|
||||
ASN1_DATA data;
|
||||
|
||||
memset(&data, 0, sizeof(data));
|
||||
|
||||
asn1_push_tag(&data, ASN1_APPLICATION(0));
|
||||
asn1_write_OID(&data, OID_KERBEROS5);
|
||||
asn1_write_BOOLEAN(&data, 0);
|
||||
asn1_write(&data, ticket.data, ticket.length);
|
||||
asn1_pop_tag(&data);
|
||||
|
||||
if (data.has_error) {
|
||||
DEBUG(1,("Failed to build krb5 wrapper at offset %d\n", (int)data.ofs));
|
||||
asn1_free(&data);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY
|
||||
kerberos session setup
|
||||
*/
|
||||
DATA_BLOB spnego_gen_negTokenTarg(struct cli_state *cli, char *principle)
|
||||
{
|
||||
char *p;
|
||||
fstring service;
|
||||
char *realm;
|
||||
DATA_BLOB tkt, ret;
|
||||
ASN1_DATA tkt_wrapped, targ;
|
||||
const char *krb_mechs[] =
|
||||
{"1 2 840 48018 1 2 2", "1 3 6 1 4 1 311 2 2 10", NULL};
|
||||
|
||||
fstrcpy(service, principle);
|
||||
p = strchr_m(service, '@');
|
||||
if (!p) {
|
||||
DEBUG(1,("Malformed principle [%s] in spnego_gen_negTokenTarg\n",
|
||||
principle));
|
||||
return data_blob(NULL, 0);
|
||||
}
|
||||
*p = 0;
|
||||
realm = p+1;
|
||||
|
||||
/* get a kerberos ticket for the service */
|
||||
tkt = krb5_get_ticket(service, realm);
|
||||
|
||||
/* wrap that up in a nice GSS-API wrapping */
|
||||
tkt_wrapped = spnego_gen_krb5_wrap(tkt);
|
||||
|
||||
/* and wrap that in a shiny SPNEGO wrapper */
|
||||
targ = gen_negTokenTarg(krb_mechs, tkt_wrapped);
|
||||
|
||||
ret = data_blob(targ.data, targ.length);
|
||||
|
||||
asn1_free(&tkt_wrapped);
|
||||
asn1_free(&targ);
|
||||
data_blob_free(tkt);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else /* HAVE_KRB5 */
|
||||
void clikrb5_dummy(void) {}
|
||||
/* this saves a few linking headaches */
|
||||
DATA_BLOB krb5_get_ticket(char *service, char *realm)
|
||||
{
|
||||
DEBUG(0,("NO KERBEROS SUPPORT\n"));
|
||||
return data_blob(NULL, 0);
|
||||
}
|
||||
#endif
|
||||
|
395
source/libsmb/clispnego.c
Normal file
395
source/libsmb/clispnego.c
Normal file
@ -0,0 +1,395 @@
|
||||
/*
|
||||
Unix SMB/Netbios implementation.
|
||||
Version 3.0
|
||||
simple kerberos5/SPNEGO routines
|
||||
Copyright (C) Andrew Tridgell 2001
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
#define OID_SPNEGO "1 3 6 1 5 5 2"
|
||||
#define OID_KERBEROS5 "1 2 840 113554 1 2 2"
|
||||
|
||||
/*
|
||||
generate a negTokenInit packet given a GUID, a list of supported
|
||||
OIDs (the mechanisms) and a principle name string
|
||||
*/
|
||||
ASN1_DATA spnego_gen_negTokenInit(uint8 guid[16],
|
||||
const char *OIDs[],
|
||||
const char *principle)
|
||||
{
|
||||
int i;
|
||||
ASN1_DATA data;
|
||||
|
||||
memset(&data, 0, sizeof(data));
|
||||
|
||||
asn1_write(&data, guid, 16);
|
||||
asn1_push_tag(&data,ASN1_APPLICATION(0));
|
||||
asn1_write_OID(&data,OID_SPNEGO);
|
||||
asn1_push_tag(&data,ASN1_CONTEXT(0));
|
||||
asn1_push_tag(&data,ASN1_SEQUENCE(0));
|
||||
|
||||
asn1_push_tag(&data,ASN1_CONTEXT(0));
|
||||
asn1_push_tag(&data,ASN1_SEQUENCE(0));
|
||||
for (i=0; OIDs[i]; i++) {
|
||||
asn1_write_OID(&data,OIDs[i]);
|
||||
}
|
||||
asn1_pop_tag(&data);
|
||||
asn1_pop_tag(&data);
|
||||
|
||||
asn1_push_tag(&data, ASN1_CONTEXT(3));
|
||||
asn1_push_tag(&data, ASN1_SEQUENCE(0));
|
||||
asn1_push_tag(&data, ASN1_CONTEXT(0));
|
||||
asn1_write_GeneralString(&data,principle);
|
||||
asn1_pop_tag(&data);
|
||||
asn1_pop_tag(&data);
|
||||
asn1_pop_tag(&data);
|
||||
|
||||
asn1_pop_tag(&data);
|
||||
asn1_pop_tag(&data);
|
||||
|
||||
asn1_pop_tag(&data);
|
||||
|
||||
if (data.has_error) {
|
||||
DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data.ofs));
|
||||
asn1_free(&data);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
parse a negTokenInit packet giving a GUID, a list of supported
|
||||
OIDs (the mechanisms) and a principle name string
|
||||
*/
|
||||
BOOL spnego_parse_negTokenInit(DATA_BLOB blob,
|
||||
uint8 guid[16],
|
||||
char *OIDs[ASN1_MAX_OIDS],
|
||||
char **principle)
|
||||
{
|
||||
int i;
|
||||
BOOL ret;
|
||||
ASN1_DATA data;
|
||||
|
||||
asn1_load(&data, blob);
|
||||
|
||||
asn1_read(&data, guid, 16);
|
||||
asn1_start_tag(&data,ASN1_APPLICATION(0));
|
||||
asn1_check_OID(&data,OID_SPNEGO);
|
||||
asn1_start_tag(&data,ASN1_CONTEXT(0));
|
||||
asn1_start_tag(&data,ASN1_SEQUENCE(0));
|
||||
|
||||
asn1_start_tag(&data,ASN1_CONTEXT(0));
|
||||
asn1_start_tag(&data,ASN1_SEQUENCE(0));
|
||||
for (i=0; asn1_tag_remaining(&data) > 0 && i < ASN1_MAX_OIDS; i++) {
|
||||
char *oid = NULL;
|
||||
asn1_read_OID(&data,&oid);
|
||||
OIDs[i] = oid;
|
||||
}
|
||||
OIDs[i] = NULL;
|
||||
asn1_end_tag(&data);
|
||||
asn1_end_tag(&data);
|
||||
|
||||
asn1_start_tag(&data, ASN1_CONTEXT(3));
|
||||
asn1_start_tag(&data, ASN1_SEQUENCE(0));
|
||||
asn1_start_tag(&data, ASN1_CONTEXT(0));
|
||||
asn1_read_GeneralString(&data,principle);
|
||||
asn1_end_tag(&data);
|
||||
asn1_end_tag(&data);
|
||||
asn1_end_tag(&data);
|
||||
|
||||
asn1_end_tag(&data);
|
||||
asn1_end_tag(&data);
|
||||
|
||||
asn1_end_tag(&data);
|
||||
|
||||
ret = !data.has_error;
|
||||
asn1_free(&data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
generate a negTokenTarg packet given a list of OIDs and a security blob
|
||||
*/
|
||||
DATA_BLOB gen_negTokenTarg(const char *OIDs[], DATA_BLOB blob)
|
||||
{
|
||||
int i;
|
||||
ASN1_DATA data;
|
||||
DATA_BLOB ret;
|
||||
|
||||
memset(&data, 0, sizeof(data));
|
||||
|
||||
asn1_push_tag(&data, ASN1_APPLICATION(0));
|
||||
asn1_write_OID(&data,OID_SPNEGO);
|
||||
asn1_push_tag(&data, ASN1_CONTEXT(0));
|
||||
asn1_push_tag(&data, ASN1_SEQUENCE(0));
|
||||
|
||||
asn1_push_tag(&data, ASN1_CONTEXT(0));
|
||||
asn1_push_tag(&data, ASN1_SEQUENCE(0));
|
||||
for (i=0; OIDs[i]; i++) {
|
||||
asn1_write_OID(&data,OIDs[i]);
|
||||
}
|
||||
asn1_pop_tag(&data);
|
||||
asn1_pop_tag(&data);
|
||||
|
||||
asn1_push_tag(&data, ASN1_CONTEXT(2));
|
||||
asn1_write_OctetString(&data,blob.data,blob.length);
|
||||
asn1_pop_tag(&data);
|
||||
|
||||
asn1_pop_tag(&data);
|
||||
asn1_pop_tag(&data);
|
||||
|
||||
asn1_pop_tag(&data);
|
||||
|
||||
if (data.has_error) {
|
||||
DEBUG(1,("Failed to build negTokenTarg at offset %d\n", (int)data.ofs));
|
||||
asn1_free(&data);
|
||||
}
|
||||
|
||||
ret = data_blob(data.data, data.length);
|
||||
asn1_free(&data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
generate a krb5 GSS-API wrapper packet given a ticket
|
||||
*/
|
||||
static DATA_BLOB spnego_gen_krb5_wrap(DATA_BLOB ticket)
|
||||
{
|
||||
ASN1_DATA data;
|
||||
DATA_BLOB ret;
|
||||
|
||||
memset(&data, 0, sizeof(data));
|
||||
|
||||
asn1_push_tag(&data, ASN1_APPLICATION(0));
|
||||
asn1_write_OID(&data, OID_KERBEROS5);
|
||||
asn1_write_BOOLEAN(&data, 0);
|
||||
asn1_write(&data, ticket.data, ticket.length);
|
||||
asn1_pop_tag(&data);
|
||||
|
||||
if (data.has_error) {
|
||||
DEBUG(1,("Failed to build krb5 wrapper at offset %d\n", (int)data.ofs));
|
||||
asn1_free(&data);
|
||||
}
|
||||
|
||||
ret = data_blob(data.data, data.length);
|
||||
asn1_free(&data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY
|
||||
kerberos session setup
|
||||
*/
|
||||
DATA_BLOB spnego_gen_negTokenTarg(struct cli_state *cli, char *principle)
|
||||
{
|
||||
char *p;
|
||||
fstring service;
|
||||
char *realm;
|
||||
DATA_BLOB tkt, tkt_wrapped, targ;
|
||||
const char *krb_mechs[] =
|
||||
{"1 2 840 48018 1 2 2", "1 3 6 1 4 1 311 2 2 10", NULL};
|
||||
|
||||
fstrcpy(service, principle);
|
||||
p = strchr_m(service, '@');
|
||||
if (!p) {
|
||||
DEBUG(1,("Malformed principle [%s] in spnego_gen_negTokenTarg\n",
|
||||
principle));
|
||||
return data_blob(NULL, 0);
|
||||
}
|
||||
*p = 0;
|
||||
realm = p+1;
|
||||
|
||||
/* get a kerberos ticket for the service */
|
||||
tkt = krb5_get_ticket(service, realm);
|
||||
|
||||
/* wrap that up in a nice GSS-API wrapping */
|
||||
tkt_wrapped = spnego_gen_krb5_wrap(tkt);
|
||||
|
||||
/* and wrap that in a shiny SPNEGO wrapper */
|
||||
targ = gen_negTokenTarg(krb_mechs, tkt_wrapped);
|
||||
|
||||
data_blob_free(tkt_wrapped);
|
||||
data_blob_free(tkt);
|
||||
|
||||
return targ;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
parse a spnego NTLMSSP challenge packet giving two security blobs
|
||||
*/
|
||||
BOOL spnego_parse_challenge(DATA_BLOB blob,
|
||||
DATA_BLOB *chal1, DATA_BLOB *chal2)
|
||||
{
|
||||
BOOL ret;
|
||||
ASN1_DATA data;
|
||||
|
||||
asn1_load(&data, blob);
|
||||
asn1_start_tag(&data,ASN1_CONTEXT(1));
|
||||
asn1_start_tag(&data,ASN1_SEQUENCE(0));
|
||||
|
||||
asn1_start_tag(&data,ASN1_CONTEXT(0));
|
||||
asn1_check_enumerated(&data,1);
|
||||
asn1_end_tag(&data);
|
||||
|
||||
asn1_start_tag(&data,ASN1_CONTEXT(1));
|
||||
asn1_check_OID(&data, "1 3 6 1 4 1 311 2 2 10");
|
||||
asn1_end_tag(&data);
|
||||
|
||||
asn1_start_tag(&data,ASN1_CONTEXT(2));
|
||||
asn1_read_octet_string(&data, chal1);
|
||||
asn1_end_tag(&data);
|
||||
|
||||
asn1_start_tag(&data,ASN1_CONTEXT(3));
|
||||
asn1_read_octet_string(&data, chal2);
|
||||
asn1_end_tag(&data);
|
||||
|
||||
asn1_end_tag(&data);
|
||||
asn1_end_tag(&data);
|
||||
|
||||
ret = !data.has_error;
|
||||
asn1_free(&data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
generate a SPNEGO NTLMSSP auth packet. This will contain the encrypted passwords
|
||||
*/
|
||||
DATA_BLOB spnego_gen_auth(DATA_BLOB blob)
|
||||
{
|
||||
ASN1_DATA data;
|
||||
DATA_BLOB ret;
|
||||
|
||||
memset(&data, 0, sizeof(data));
|
||||
|
||||
asn1_push_tag(&data, ASN1_CONTEXT(1));
|
||||
asn1_push_tag(&data, ASN1_SEQUENCE(0));
|
||||
asn1_push_tag(&data, ASN1_CONTEXT(2));
|
||||
asn1_write_OctetString(&data,blob.data,blob.length);
|
||||
asn1_pop_tag(&data);
|
||||
asn1_pop_tag(&data);
|
||||
asn1_pop_tag(&data);
|
||||
|
||||
ret = data_blob(data.data, data.length);
|
||||
|
||||
asn1_free(&data);
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
this is a tiny msrpc packet generator. I am only using this to
|
||||
avoid tying this code to a particular varient of our rpc code. This
|
||||
generator is not general enough for all our rpc needs, its just
|
||||
enough for the spnego/ntlmssp code
|
||||
|
||||
format specifiers are:
|
||||
|
||||
U = unicode string (input is unix string)
|
||||
B = data blob (pointer + length)
|
||||
d = word (4 bytes)
|
||||
C = constant ascii string
|
||||
*/
|
||||
BOOL msrpc_gen(DATA_BLOB *blob,
|
||||
const char *format, ...)
|
||||
{
|
||||
int i, n;
|
||||
va_list ap;
|
||||
char *s;
|
||||
uint8 *b;
|
||||
int head_size=0, data_size=0;
|
||||
int head_ofs, data_ofs;
|
||||
|
||||
/* first scan the format to work out the header and body size */
|
||||
va_start(ap, format);
|
||||
for (i=0; format[i]; i++) {
|
||||
switch (format[i]) {
|
||||
case 'U':
|
||||
s = va_arg(ap, char *);
|
||||
head_size += 8;
|
||||
data_size += str_charnum(s) * 2;
|
||||
break;
|
||||
case 'B':
|
||||
b = va_arg(ap, uint8 *);
|
||||
head_size += 8;
|
||||
data_size += va_arg(ap, int);
|
||||
break;
|
||||
case 'd':
|
||||
n = va_arg(ap, int);
|
||||
head_size += 4;
|
||||
break;
|
||||
case 'C':
|
||||
s = va_arg(ap, char *);
|
||||
head_size += str_charnum(s) + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
/* allocate the space, then scan the format again to fill in the values */
|
||||
blob->data = malloc(head_size + data_size);
|
||||
blob->length = head_size + data_size;
|
||||
if (!blob->data) return False;
|
||||
|
||||
head_ofs = 0;
|
||||
data_ofs = head_size;
|
||||
|
||||
va_start(ap, format);
|
||||
for (i=0; format[i]; i++) {
|
||||
switch (format[i]) {
|
||||
case 'U':
|
||||
s = va_arg(ap, char *);
|
||||
n = str_charnum(s);
|
||||
SSVAL(blob->data, head_ofs, n*2); head_ofs += 2;
|
||||
SSVAL(blob->data, head_ofs, n*2); head_ofs += 2;
|
||||
SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
|
||||
push_string(NULL, blob->data+data_ofs, s, n*2, STR_UNICODE|STR_NOALIGN);
|
||||
data_ofs += n*2;
|
||||
break;
|
||||
case 'B':
|
||||
b = va_arg(ap, uint8 *);
|
||||
n = va_arg(ap, int);
|
||||
SSVAL(blob->data, head_ofs, n); head_ofs += 2;
|
||||
SSVAL(blob->data, head_ofs, n); head_ofs += 2;
|
||||
SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
|
||||
memcpy(blob->data+data_ofs, b, n);
|
||||
data_ofs += n;
|
||||
break;
|
||||
case 'd':
|
||||
n = va_arg(ap, int);
|
||||
SIVAL(blob->data, head_ofs, n); head_ofs += 4;
|
||||
break;
|
||||
case 'C':
|
||||
s = va_arg(ap, char *);
|
||||
head_ofs += push_string(NULL, blob->data+head_ofs, s, -1,
|
||||
STR_ASCII|STR_TERMINATE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return True;
|
||||
}
|
Loading…
Reference in New Issue
Block a user