summaryrefslogtreecommitdiff
path: root/source3/libsmb
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2001-10-11 07:42:52 +0000
committerAndrew Tridgell <tridge@samba.org>2001-10-11 07:42:52 +0000
commit81f56139b6964ddbe2c03232475f87f474136490 (patch)
tree1213ad9ba9f34506f2b4bbc38d925a3bcda2f5de /source3/libsmb
parent76745313b16c07092b0198da4d4fc05b38e600f7 (diff)
downloadsamba-81f56139b6964ddbe2c03232475f87f474136490.tar.gz
samba-81f56139b6964ddbe2c03232475f87f474136490.tar.bz2
samba-81f56139b6964ddbe2c03232475f87f474136490.zip
initial kerberos/ADS/SPNEGO support in libsmb and smbclient. To
activate you need to: - install krb5 libraries - run configure - build smbclient - run kinit to get a TGT - run smbclient with the -k option to choose kerberos auth (This used to be commit d33057585644e1337bac743e25ed7653bfb39eef)
Diffstat (limited to 'source3/libsmb')
-rw-r--r--source3/libsmb/asn1.c139
-rw-r--r--source3/libsmb/cliconnect.c531
-rw-r--r--source3/libsmb/clientgen.c12
-rw-r--r--source3/libsmb/clikrb5.c267
4 files changed, 802 insertions, 147 deletions
diff --git a/source3/libsmb/asn1.c b/source3/libsmb/asn1.c
new file mode 100644
index 0000000000..5735f372ca
--- /dev/null
+++ b/source3/libsmb/asn1.c
@@ -0,0 +1,139 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 3.0
+ simple SPNEGO routines
+ Copyright (C) Andrew Tridgell 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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"
+
+void asn1_free(ASN1_DATA *data)
+{
+ free(data->data);
+}
+
+BOOL asn1_check_empty(ASN1_DATA *data)
+{
+ if (data->nesting) return False;
+ return True;
+}
+
+BOOL asn1_write(ASN1_DATA *data, const void *p, int len)
+{
+ if (data->length < data->ofs+len) {
+ data->data = Realloc(data->data, data->ofs+len);
+ if (!data->data) return False;
+ data->length = data->ofs+len;
+ }
+ memcpy(data->data + data->ofs, p, len);
+ data->ofs += len;
+ return True;
+}
+
+BOOL asn1_write_uint8(ASN1_DATA *data, uint8 v)
+{
+ return asn1_write(data, &v, 1);
+}
+
+BOOL asn1_push_tag(ASN1_DATA *data, uint8 tag)
+{
+ struct nesting *nesting;
+
+ asn1_write_uint8(data, tag);
+ nesting = (struct nesting *)malloc(sizeof(struct nesting));
+ if (!nesting) return False;
+
+ nesting->start = data->ofs;
+ nesting->next = data->nesting;
+ data->nesting = nesting;
+ asn1_write_uint8(data, 0xff);
+ return True;
+}
+
+BOOL asn1_pop_tag(ASN1_DATA *data)
+{
+ struct nesting *nesting;
+ size_t len;
+
+ nesting = data->nesting;
+
+ if (!nesting) {
+ return False;
+ }
+ len = data->ofs - (nesting->start+1);
+ if (len > 127) {
+ data->data[nesting->start] = 0x82;
+ asn1_write_uint8(data, 0);
+ asn1_write_uint8(data, 0);
+ memmove(data->data+nesting->start+3, data->data+nesting->start+1, len);
+ data->data[nesting->start+1] = len>>8;
+ data->data[nesting->start+2] = len&0xff;
+ } else {
+ data->data[nesting->start] = len;
+ }
+
+ data->nesting = nesting->next;
+ free(nesting);
+ return True;
+}
+
+
+BOOL asn1_write_OID(ASN1_DATA *data, const char *OID)
+{
+ unsigned v, v2;
+ char *p = (char *)OID;
+
+ asn1_push_tag(data, ASN1_OID);
+ v = strtol(p, &p, 10);
+ v2 = strtol(p, &p, 10);
+ asn1_write_uint8(data, 40*v + v2);
+
+ while (*p) {
+ v = strtol(p, &p, 10);
+ if (v >= (1<<28)) asn1_write_uint8(data, 0x80 | ((v>>28)&0xff));
+ if (v >= (1<<21)) asn1_write_uint8(data, 0x80 | ((v>>21)&0xff));
+ if (v >= (1<<14)) asn1_write_uint8(data, 0x80 | ((v>>14)&0xff));
+ if (v >= (1<<7)) asn1_write_uint8(data, 0x80 | ((v>>7)&0xff));
+ asn1_write_uint8(data, v&0x7f);
+ }
+ asn1_pop_tag(data);
+ return True;
+}
+
+BOOL asn1_write_OctetString(ASN1_DATA *data, const void *p, size_t length)
+{
+ asn1_push_tag(data, ASN1_OCTET_STRING);
+ asn1_write(data, p, length);
+ asn1_pop_tag(data);
+ return True;
+}
+
+BOOL asn1_write_GeneralString(ASN1_DATA *data, const char *s)
+{
+ asn1_push_tag(data, ASN1_GENERAL_STRING);
+ asn1_write(data, s, strlen(s));
+ asn1_pop_tag(data);
+ return True;
+}
+
+BOOL asn1_write_BOOLEAN(ASN1_DATA *data, BOOL v)
+{
+ asn1_write_uint8(data, ASN1_BOOLEAN);
+ asn1_write_uint8(data, v);
+ return True;
+}
+
diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c
index 14faf6e8fe..77a8232ed5 100644
--- a/source3/libsmb/cliconnect.c
+++ b/source3/libsmb/cliconnect.c
@@ -31,23 +31,365 @@ static struct {
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,"Samba"},
- {PROTOCOL_NT1,"NT LANMAN 1.0"},
+ {PROTOCOL_NT1,"LANMAN2.1"},
{PROTOCOL_NT1,"NT LM 0.12"},
{-1,NULL}
};
/****************************************************************************
+do an old lanman2 style session setup
+****************************************************************************/
+static BOOL cli_session_setup_lanman2(struct cli_state *cli, char *user,
+ char *pass, int passlen)
+{
+ fstring pword;
+ char *p;
+
+ if (passlen > sizeof(pword)-1) {
+ return False;
+ }
+
+ /* if in share level security then don't send a password now */
+ if (!(cli->sec_mode & 1)) {
+ passlen = 0;
+ }
+
+ if (passlen > 0 && (cli->sec_mode & 2) && passlen != 24) {
+ /* Encrypted mode needed, and non encrypted password supplied. */
+ passlen = 24;
+ clistr_push(cli, pword, pass, -1, STR_TERMINATE);
+ SMBencrypt((uchar *)pword,cli->secblob.data,(uchar *)pword);
+ } else if ((cli->sec_mode & 2) && passlen == 24) {
+ /* Encrypted mode needed, and encrypted password supplied. */
+ memcpy(pword, pass, passlen);
+ } else if (passlen > 0) {
+ /* Plaintext mode needed, assume plaintext supplied. */
+ passlen = clistr_push(cli, pword, pass, -1, STR_TERMINATE);
+ }
+
+ /* send a session setup command */
+ memset(cli->outbuf,'\0',smb_size);
+ set_message(cli->outbuf,10, 0, True);
+ CVAL(cli->outbuf,smb_com) = SMBsesssetupX;
+ cli_setup_packet(cli);
+
+ CVAL(cli->outbuf,smb_vwv0) = 0xFF;
+ SSVAL(cli->outbuf,smb_vwv2,cli->max_xmit);
+ SSVAL(cli->outbuf,smb_vwv3,2);
+ SSVAL(cli->outbuf,smb_vwv4,1);
+ SIVAL(cli->outbuf,smb_vwv5,cli->sesskey);
+ SSVAL(cli->outbuf,smb_vwv7,passlen);
+
+ p = smb_buf(cli->outbuf);
+ memcpy(p,pword,passlen);
+ p += passlen;
+ p += clistr_push(cli, p, user, -1, STR_UPPER|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);
+ fstrcpy(cli->user_name, user);
+
+ return True;
+}
+
+
+/****************************************************************************
+work out suitable capabilities to offer the server
+****************************************************************************/
+static uint32 cli_session_setup_capabilities(struct cli_state *cli)
+{
+ uint32 capabilities = CAP_NT_SMBS;
+
+ /* Set the CLI_FORCE_DOSERR environment variable to test
+ client routines using DOS errors instead of STATUS32
+ ones. This intended only as a temporary hack. */
+ if (!getenv("CLI_FORCE_DOSERR")) {
+ capabilities |= CAP_STATUS32;
+ }
+
+ if (cli->use_level_II_oplocks) {
+ capabilities |= CAP_LEVEL_II_OPLOCKS;
+ }
+
+ if (cli->capabilities & CAP_UNICODE) {
+ capabilities |= CAP_UNICODE;
+ }
+
+ return capabilities;
+}
+
+
+/****************************************************************************
+do a NT1 guest session setup
+****************************************************************************/
+static BOOL cli_session_setup_guest(struct cli_state *cli)
+{
+ char *p;
+ uint32 capabilities = cli_session_setup_capabilities(cli);
+
+ set_message(cli->outbuf,13,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,cli->pid);
+ SIVAL(cli->outbuf,smb_vwv5,cli->sesskey);
+ SSVAL(cli->outbuf,smb_vwv7,0);
+ SSVAL(cli->outbuf,smb_vwv8,0);
+ SIVAL(cli->outbuf,smb_vwv11,capabilities);
+ p = smb_buf(cli->outbuf);
+ p += clistr_push(cli, p, "", -1, STR_TERMINATE); /* username */
+ p += clistr_push(cli, p, "", -1, STR_TERMINATE); /* workgroup */
+ p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE);
+ p += clistr_push(cli, p, "Samba", -1, STR_TERMINATE);
+ cli_setup_bcc(cli, p);
+
+ cli_send_smb(cli);
+ if (!cli_receive_smb(cli))
+ return False;
+
+ show_msg(cli->inbuf);
+
+ if (cli_is_error(cli)) {
+ return False;
+ }
+
+ cli->vuid = SVAL(cli->inbuf,smb_uid);
+
+ p = smb_buf(cli->inbuf);
+ p += clistr_pull(cli, cli->server_os, p, sizeof(fstring), -1, STR_TERMINATE);
+ p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), -1, STR_TERMINATE);
+ p += clistr_pull(cli, cli->server_domain, p, sizeof(fstring), -1, STR_TERMINATE);
+
+ fstrcpy(cli->user_name, "");
+
+ return True;
+}
+
+
+/****************************************************************************
+do a NT1 plaintext session setup
+****************************************************************************/
+static BOOL cli_session_setup_plaintext(struct cli_state *cli, char *user,
+ char *pass, char *workgroup)
+{
+ uint32 capabilities = cli_session_setup_capabilities(cli);
+ fstring pword;
+ int passlen;
+ char *p;
+
+ passlen = clistr_push(cli, pword, pass, sizeof(pword), STR_TERMINATE);
+
+ set_message(cli->outbuf,13,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,cli->pid);
+ SIVAL(cli->outbuf,smb_vwv5,cli->sesskey);
+ SSVAL(cli->outbuf,smb_vwv7,passlen);
+ SSVAL(cli->outbuf,smb_vwv8,0);
+ SIVAL(cli->outbuf,smb_vwv11,capabilities);
+ p = smb_buf(cli->outbuf);
+ memcpy(p, pword, passlen);
+ p += passlen;
+ p += clistr_push(cli, p, user, -1, STR_TERMINATE); /* username */
+ p += clistr_push(cli, p, workgroup, -1, STR_TERMINATE); /* workgroup */
+ p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE);
+ p += clistr_push(cli, p, "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;
+ }
+
+ cli->vuid = SVAL(cli->inbuf,smb_uid);
+ p = smb_buf(cli->inbuf);
+ p += clistr_pull(cli, cli->server_os, p, sizeof(fstring), -1, STR_TERMINATE);
+ p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), -1, STR_TERMINATE);
+ p += clistr_pull(cli, cli->server_domain, p, sizeof(fstring), -1, STR_TERMINATE);
+ fstrcpy(cli->user_name, user);
+
+ return True;
+}
+
+
+/****************************************************************************
+do a NT1 NTLM/LM encrypted session setup
+****************************************************************************/
+static BOOL cli_session_setup_nt1(struct cli_state *cli, char *user,
+ char *pass, int passlen,
+ char *ntpass, int ntpasslen,
+ char *workgroup)
+{
+ uint32 capabilities = cli_session_setup_capabilities(cli);
+ fstring pword, ntpword;
+ char *p;
+
+ if (passlen > sizeof(pword)-1 || ntpasslen > sizeof(ntpword)-1) {
+ return False;
+ }
+
+ if (passlen != 24) {
+ /* non encrypted password supplied. */
+ passlen = 24;
+ ntpasslen = 24;
+ clistr_push(cli, pword, pass, sizeof(pword), STR_TERMINATE);
+ clistr_push(cli, ntpword, ntpass, sizeof(ntpword), STR_TERMINATE);
+ SMBencrypt((uchar *)pword,cli->secblob.data,(uchar *)pword);
+ SMBNTencrypt((uchar *)ntpword,cli->secblob.data,(uchar *)ntpword);
+ } else {
+ memcpy(pword, pass, passlen);
+ memcpy(ntpword, ntpass, ntpasslen);
+ }
+
+ /* send a session setup command */
+ memset(cli->outbuf,'\0',smb_size);
+
+ set_message(cli->outbuf,13,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,cli->pid);
+ SIVAL(cli->outbuf,smb_vwv5,cli->sesskey);
+ SSVAL(cli->outbuf,smb_vwv7,passlen);
+ SSVAL(cli->outbuf,smb_vwv8,ntpasslen);
+ SIVAL(cli->outbuf,smb_vwv11,capabilities);
+ p = smb_buf(cli->outbuf);
+ memcpy(p,pword,passlen); p += passlen;
+ memcpy(p,ntpword,ntpasslen); p += ntpasslen;
+ p += clistr_push(cli, p, user, -1, STR_TERMINATE|STR_UPPER);
+ p += clistr_push(cli, p, workgroup, -1, STR_TERMINATE|STR_UPPER);
+ p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE);
+ p += clistr_push(cli, p, "Samba", -1, STR_TERMINATE);
+ cli_setup_bcc(cli, p);
+
+ 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);
+ 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);
+
+ return True;
+}
+
+#if HAVE_KRB5
+/****************************************************************************
+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;
+
+ negTokenTarg = spnego_gen_negTokenTarg(cli);
+
+ 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;
+}
+#endif
+
+
+/****************************************************************************
Send a session setup. The username and workgroup is in UNIX character
format and must be converted to DOS codepage format before sending. If the
password is in plaintext, the same should be done.
****************************************************************************/
-
BOOL cli_session_setup(struct cli_state *cli,
char *user,
char *pass, int passlen,
@@ -55,7 +397,6 @@ BOOL cli_session_setup(struct cli_state *cli,
char *workgroup)
{
char *p;
- fstring pword, ntpword;
fstring user2;
/* allow for workgroups as part of the username */
@@ -69,146 +410,45 @@ BOOL cli_session_setup(struct cli_state *cli,
if (cli->protocol < PROTOCOL_LANMAN1)
return True;
- if (passlen > sizeof(pword)-1 || ntpasslen > sizeof(ntpword)-1) {
- return False;
- }
+ /* now work out what sort of session setup we are going to
+ do. I have split this into separate functions to make the
+ flow a bit easier to understand (tridge) */
- if (((passlen == 0) || (passlen == 1)) && (pass[0] == '\0')) {
- /* Null session connect. */
- pword[0] = '\0';
- ntpword[0] = '\0';
- } else {
- if ((cli->sec_mode & 2) && passlen != 24) {
- /*
- * Encrypted mode needed, and non encrypted password supplied.
- */
- passlen = 24;
- ntpasslen = 24;
- clistr_push(cli, pword, pass, -1, STR_TERMINATE);
- fstrcpy(ntpword, ntpass);;
- SMBencrypt((uchar *)pword,(uchar *)cli->cryptkey,(uchar *)pword);
- SMBNTencrypt((uchar *)ntpword,(uchar *)cli->cryptkey,(uchar *)ntpword);
- } else if ((cli->sec_mode & 2) && passlen == 24) {
- /*
- * Encrypted mode needed, and encrypted password supplied.
- */
- memcpy(pword, pass, passlen);
- if(ntpasslen == 24) {
- memcpy(ntpword, ntpass, ntpasslen);
- } else {
- fstrcpy(ntpword, "");
- ntpasslen = 0;
- }
- } else {
- /*
- * Plaintext mode needed, assume plaintext supplied.
- */
- passlen = clistr_push(cli, pword, pass, -1, STR_TERMINATE);
- fstrcpy(ntpword, "");
- ntpasslen = 0;
- }
+ /* if its an older server then we have to use the older request format */
+ if (cli->protocol < PROTOCOL_NT1) {
+ return cli_session_setup_lanman2(cli, user, pass, passlen);
}
- /* if in share level security then don't send a password now */
- if (!(cli->sec_mode & 1)) {
- fstrcpy(pword, "");
- passlen=1;
- fstrcpy(ntpword, "");
- ntpasslen=1;
- }
-
- /* send a session setup command */
- memset(cli->outbuf,'\0',smb_size);
-
- if (cli->protocol < PROTOCOL_NT1)
- {
- set_message(cli->outbuf,10, 0, True);
- CVAL(cli->outbuf,smb_com) = SMBsesssetupX;
- cli_setup_packet(cli);
-
- CVAL(cli->outbuf,smb_vwv0) = 0xFF;
- SSVAL(cli->outbuf,smb_vwv2,cli->max_xmit);
- SSVAL(cli->outbuf,smb_vwv3,2);
- SSVAL(cli->outbuf,smb_vwv4,1);
- SIVAL(cli->outbuf,smb_vwv5,cli->sesskey);
- SSVAL(cli->outbuf,smb_vwv7,passlen);
- p = smb_buf(cli->outbuf);
- memcpy(p,pword,passlen);
- p += passlen;
- p += clistr_push(cli, p, user, -1, STR_UPPER|STR_TERMINATE);
- cli_setup_bcc(cli, p);
+ /* if no user is supplied then we have to do an anonymous connection.
+ passwords are ignored */
+ if (!user || !*user) {
+ return cli_session_setup_guest(cli);
}
- else
- {
- uint32 capabilities;
-
- capabilities = CAP_NT_SMBS;
- /* Set the CLI_FORCE_DOSERR environment variable to test
- client routines using DOS errors instead of STATUS32
- ones. This intended only as a temporary hack. */
-
- if (!getenv("CLI_FORCE_DOSERR")) {
- capabilities |= CAP_STATUS32;
- }
-
- if (cli->use_level_II_oplocks) {
- capabilities |= CAP_LEVEL_II_OPLOCKS;
- }
- if (cli->capabilities & CAP_UNICODE) {
- capabilities |= CAP_UNICODE;
- }
- set_message(cli->outbuf,13,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,cli->pid);
- SIVAL(cli->outbuf,smb_vwv5,cli->sesskey);
- SSVAL(cli->outbuf,smb_vwv7,passlen);
- SSVAL(cli->outbuf,smb_vwv8,ntpasslen);
- SIVAL(cli->outbuf,smb_vwv11,capabilities);
- p = smb_buf(cli->outbuf);
- memcpy(p,pword,passlen);
- p += SVAL(cli->outbuf,smb_vwv7);
- memcpy(p,ntpword,ntpasslen);
- p += SVAL(cli->outbuf,smb_vwv8);
- p += clistr_push(cli, p, user, -1, STR_TERMINATE|STR_UPPER);
- p += clistr_push(cli, p, workgroup, -1, STR_TERMINATE|STR_UPPER);
- p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE);
- p += clistr_push(cli, p, "Samba", -1, STR_TERMINATE);
- cli_setup_bcc(cli, p);
- }
-
- 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);
+ /* if the server is share level then send a plaintext null
+ password at this point. The password is sent in the tree
+ connect */
+ if ((cli->sec_mode & 1) == 0) {
+ return cli_session_setup_plaintext(cli, user, "", workgroup);
+ }
- if (cli->protocol >= PROTOCOL_NT1) {
- /*
- * Save off some of the connected server
- * info.
- */
- char *q = smb_buf(cli->inbuf);
- q += clistr_pull(cli, cli->server_os, q, sizeof(fstring), -1, STR_TERMINATE);
- q += clistr_pull(cli, cli->server_type, q, sizeof(fstring), -1, STR_TERMINATE);
- q += clistr_pull(cli, cli->server_domain, q, sizeof(fstring), -1, STR_TERMINATE);
- }
+ /* if the server doesn't support encryption then we have to use plaintext. The
+ second password is ignored */
+ if ((cli->sec_mode & 2) == 0) {
+ return cli_session_setup_plaintext(cli, user, pass, workgroup);
+ }
- fstrcpy(cli->user_name, user);
+#if HAVE_KRB5
+ /* if the server supports extended security then use SPNEGO */
+ if (cli->capabilities & CAP_EXTENDED_SECURITY) {
+ return cli_session_setup_spnego(cli, user, pass, workgroup);
+ }
+#endif
- return True;
+ /* otherwise do a NT1 style session setup */
+ return cli_session_setup_nt1(cli, user,
+ pass, passlen, ntpass, ntpasslen,
+ workgroup);
}
/****************************************************************************
@@ -256,8 +496,7 @@ BOOL cli_send_tconX(struct cli_state *cli,
*/
passlen = 24;
clistr_push(cli, dos_pword, pass, -1, STR_TERMINATE);
-
- SMBencrypt((uchar *)dos_pword,(uchar *)cli->cryptkey,(uchar *)pword);
+ SMBencrypt((uchar *)dos_pword,cli->secblob.data,(uchar *)pword);
} else {
if((cli->sec_mode & 3) == 0) {
/*
@@ -404,6 +643,11 @@ 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;
@@ -427,7 +671,7 @@ BOOL cli_negprot(struct cli_state *cli)
cli->serverzone *= 60;
/* this time arrives in real GMT */
cli->servertime = interpret_long_date(cli->inbuf+smb_vwv11+1);
- memcpy(cli->cryptkey,smb_buf(cli->inbuf),8);
+ cli->secblob = data_blob(smb_buf(cli->inbuf),smb_buflen(cli->inbuf));
cli->capabilities = IVAL(cli->inbuf,smb_vwv9+1);
if (cli->capabilities & CAP_RAW_MODE) {
cli->readbraw_supported = True;
@@ -449,7 +693,7 @@ BOOL cli_negprot(struct cli_state *cli)
cli->servertime = make_unix_date(cli->inbuf+smb_vwv8);
cli->readbraw_supported = ((SVAL(cli->inbuf,smb_vwv5) & 0x1) != 0);
cli->writebraw_supported = ((SVAL(cli->inbuf,smb_vwv5) & 0x2) != 0);
- memcpy(cli->cryptkey,smb_buf(cli->inbuf),8);
+ cli->secblob = data_blob(smb_buf(cli->inbuf),smb_buflen(cli->inbuf));
} else {
/* the old core protocol */
cli->sec_mode = 0;
@@ -481,7 +725,6 @@ BOOL cli_session_request(struct cli_state *cli,
if (cli->port == 445) return True;
/* send a session request (RFC 1002) */
-
memcpy(&(cli->calling), calling, sizeof(*calling));
memcpy(&(cli->called ), called , sizeof(*called ));
@@ -758,7 +1001,7 @@ BOOL cli_establish_connection(struct cli_state *cli,
unsigned char lm_sess_pwd[24];
/* creates (storing a copy of) and then obtains a 24 byte password OWF */
- pwd_make_lm_nt_owf(&(cli->pwd), cli->cryptkey);
+ pwd_make_lm_nt_owf(&(cli->pwd), cli->secblob.data);
pwd_get_lm_nt_owf(&(cli->pwd), lm_sess_pwd, nt_sess_pwd);
/* attempt encrypted session */
diff --git a/source3/libsmb/clientgen.c b/source3/libsmb/clientgen.c
index 62b46a7904..b5eddd5644 100644
--- a/source3/libsmb/clientgen.c
+++ b/source3/libsmb/clientgen.c
@@ -112,6 +112,10 @@ 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;
+ }
SSVAL(cli->outbuf,smb_flg2, flags2);
}
}
@@ -215,15 +219,17 @@ void cli_shutdown(struct cli_state *cli)
SAFE_FREE(cli->outbuf);
SAFE_FREE(cli->inbuf);
+ data_blob_free(cli->secblob);
+
if (cli->mem_ctx)
talloc_destroy(cli->mem_ctx);
#ifdef WITH_SSL
- if (cli->fd != -1)
- sslutil_disconnect(cli->fd);
+ if (cli->fd != -1)
+ sslutil_disconnect(cli->fd);
#endif /* WITH_SSL */
if (cli->fd != -1)
- close(cli->fd);
+ close(cli->fd);
memset(cli, 0, sizeof(*cli));
}
diff --git a/source3/libsmb/clikrb5.c b/source3/libsmb/clikrb5.c
new file mode 100644
index 0000000000..cd64dc8444
--- /dev/null
+++ b/source3/libsmb/clikrb5.c
@@ -0,0 +1,267 @@
+/*
+ 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"
+
+#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"
+
+static krb5_error_code krb5_mk_req2(krb5_context context,
+ krb5_auth_context *auth_context,
+ const krb5_flags ap_req_options,
+ const char *service,
+ krb5_data *in_data,
+ krb5_ccache ccache,
+ krb5_data *outbuf)
+{
+ krb5_error_code retval;
+ krb5_principal server;
+ krb5_creds * credsp;
+ krb5_creds creds;
+ char *realm;
+
+ /* we should really get the realm from the negTargInit packet,
+ but this will do until I've done the asn1 decoder for that */
+ if ((retval = krb5_get_default_realm(context, &realm))) {
+ return retval;
+ }
+
+ retval = krb5_build_principal(context, &server, strlen(realm),
+ realm, service, NULL);
+ if (retval)
+ return retval;
+
+ /* obtain ticket & session key */
+ memset((char *)&creds, 0, sizeof(creds));
+ if ((retval = krb5_copy_principal(context, server, &creds.server)))
+ goto cleanup_princ;
+
+ if ((retval = krb5_cc_get_principal(context, ccache, &creds.client)))
+ goto cleanup_creds;
+
+ if ((retval = krb5_get_credentials(context, 0,
+ ccache, &creds, &credsp)))
+ goto cleanup_creds;
+
+ retval = krb5_mk_req_extended(context, auth_context, ap_req_options,
+ in_data, credsp, outbuf);
+
+ krb5_free_creds(context, credsp);
+
+cleanup_creds:
+ krb5_free_cred_contents(context, &creds);
+
+cleanup_princ:
+ krb5_free_principal(context, server);
+
+ return retval;
+}
+
+/*
+ get a kerberos5 ticket for the given service
+*/
+static DATA_BLOB krb5_get_ticket(char *service)
+{
+ krb5_error_code retval;
+ krb5_data packet, inbuf;
+ krb5_ccache ccdef;
+ krb5_context context;
+ krb5_auth_context auth_context = NULL;
+ DATA_BLOB ret;
+
+ retval = krb5_init_context(&context);
+ if (retval) {
+ DEBUG(1,("krb5_init_context failed\n"));
+ goto failed;
+ }
+
+ inbuf.length = 0;
+
+ if ((retval = krb5_cc_default(context, &ccdef))) {
+ DEBUG(1,("krb5_cc_default failed\n"));
+ goto failed;
+ }
+
+ if ((retval = krb5_mk_req2(context,
+ &auth_context,
+ AP_OPTS_MUTUAL_REQUIRED,
+ service,
+ &inbuf, ccdef, &packet))) {
+ DEBUG(1,("krb5_mk_req2 failed\n"));
+ goto failed;
+ }
+
+ ret = data_blob(packet.data, packet.length);
+ /* XXX need to free up a bunch of krb5 stuff here */
+
+ return ret;
+
+failed:
+ return data_blob(NULL, 0);
+}
+
+
+/*
+ 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);
+
+ asn1_check_empty(&data);
+ return data;
+}
+
+
+/*
+ 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);
+
+ asn1_check_empty(&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);
+
+ 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 *p;
+ fstring service;
+ 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};
+
+ /* the service name is the WINS name of the server in lowercase with
+ a $ on the end */
+ fstrcpy(service, cli->desthost);
+ p = strchr_m(service, '.');
+ if (p) *p = 0;
+ fstrcat(service, "$");
+ strlower(service);
+
+ /* get a kerberos ticket for the service */
+ tkt = krb5_get_ticket(service);
+
+ /* 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) {}
+#endif