summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/include/asn1.h4
-rw-r--r--source3/libsmb/asn1.c186
-rw-r--r--source3/libsmb/cliconnect.c33
-rw-r--r--source3/libsmb/clikrb5.c179
4 files changed, 324 insertions, 78 deletions
diff --git a/source3/include/asn1.h b/source3/include/asn1.h
index f09c7ebbc1..183a9ced6c 100644
--- a/source3/include/asn1.h
+++ b/source3/include/asn1.h
@@ -22,6 +22,7 @@
struct nesting {
off_t start;
+ size_t taglen; /* for parsing */
struct nesting *next;
};
@@ -30,6 +31,7 @@ typedef struct {
size_t length;
off_t ofs;
struct nesting *nesting;
+ BOOL has_error;
} ASN1_DATA;
@@ -40,3 +42,5 @@ typedef struct {
#define ASN1_OCTET_STRING 0x4
#define ASN1_OID 0x6
#define ASN1_BOOLEAN 0x1
+
+#define ASN1_MAX_OIDS 20
diff --git a/source3/libsmb/asn1.c b/source3/libsmb/asn1.c
index e4c2d3af80..17b1ee1089 100644
--- a/source3/libsmb/asn1.c
+++ b/source3/libsmb/asn1.c
@@ -30,9 +30,13 @@ void asn1_free(ASN1_DATA *data)
/* write to the ASN1 buffer, advancing the buffer pointer */
BOOL asn1_write(ASN1_DATA *data, const void *p, int len)
{
+ if (data->has_error) return False;
if (data->length < data->ofs+len) {
data->data = Realloc(data->data, data->ofs+len);
- if (!data->data) return False;
+ if (!data->data) {
+ data->has_error = True;
+ return False;
+ }
data->length = data->ofs+len;
}
memcpy(data->data + data->ofs, p, len);
@@ -53,13 +57,15 @@ BOOL asn1_push_tag(ASN1_DATA *data, uint8 tag)
asn1_write_uint8(data, tag);
nesting = (struct nesting *)malloc(sizeof(struct nesting));
- if (!nesting) return False;
+ if (!nesting) {
+ data->has_error = True;
+ return False;
+ }
nesting->start = data->ofs;
nesting->next = data->nesting;
data->nesting = nesting;
- asn1_write_uint8(data, 0xff);
- return True;
+ return asn1_write_uint8(data, 0xff);
}
/* pop a tag */
@@ -71,6 +77,7 @@ BOOL asn1_pop_tag(ASN1_DATA *data)
nesting = data->nesting;
if (!nesting) {
+ data->has_error = True;
return False;
}
len = data->ofs - (nesting->start+1);
@@ -79,8 +86,8 @@ BOOL asn1_pop_tag(ASN1_DATA *data)
need to correct our mistake */
if (len > 127) {
data->data[nesting->start] = 0x82;
- asn1_write_uint8(data, 0);
- asn1_write_uint8(data, 0);
+ if (!asn1_write_uint8(data, 0)) return False;
+ if (!asn1_write_uint8(data, 0)) return False;
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;
@@ -99,10 +106,10 @@ BOOL asn1_write_OID(ASN1_DATA *data, const char *OID)
unsigned v, v2;
char *p = (char *)OID;
- asn1_push_tag(data, ASN1_OID);
+ if (!asn1_push_tag(data, ASN1_OID)) return False;
v = strtol(p, &p, 10);
v2 = strtol(p, &p, 10);
- asn1_write_uint8(data, 40*v + v2);
+ if (!asn1_write_uint8(data, 40*v + v2)) return False;
while (*p) {
v = strtol(p, &p, 10);
@@ -110,10 +117,9 @@ BOOL asn1_write_OID(ASN1_DATA *data, const char *OID)
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);
+ if (!asn1_write_uint8(data, v&0x7f)) return False;
}
- asn1_pop_tag(data);
- return True;
+ return asn1_pop_tag(data);
}
/* write an octet string */
@@ -122,7 +128,7 @@ 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;
+ return !data->has_error;
}
/* write a general string */
@@ -131,7 +137,7 @@ 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;
+ return !data->has_error;
}
/* write a BOOLEAN */
@@ -139,17 +145,20 @@ BOOL asn1_write_BOOLEAN(ASN1_DATA *data, BOOL v)
{
asn1_write_uint8(data, ASN1_BOOLEAN);
asn1_write_uint8(data, v);
- return True;
+ return !data->has_error;
}
/* load a ASN1_DATA structure with a lump of data, ready to be parsed */
-BOOL asn1_load(ASN1_DATA *data, void *p, size_t length)
+BOOL asn1_load(ASN1_DATA *data, DATA_BLOB blob)
{
ZERO_STRUCTP(data);
- data->data = memdup(p, length);
- if (!data->data) return False;
- data->length = length;
+ data->data = memdup(blob.data, blob.length);
+ if (!data->data) {
+ data->has_error = True;
+ return False;
+ }
+ data->length = blob.length;
return True;
}
@@ -157,6 +166,7 @@ BOOL asn1_load(ASN1_DATA *data, void *p, size_t length)
BOOL asn1_read(ASN1_DATA *data, void *p, int len)
{
if (data->ofs + len > data->length) {
+ data->has_error = True;
return False;
}
memcpy(p, data->data + data->ofs, len);
@@ -164,4 +174,144 @@ BOOL asn1_read(ASN1_DATA *data, void *p, int len)
return True;
}
+/* read a uint8 from a ASN1 buffer */
+BOOL asn1_read_uint8(ASN1_DATA *data, uint8 *v)
+{
+ return asn1_read(data, v, 1);
+}
+/* start reading a nested asn1 structure */
+BOOL asn1_start_tag(ASN1_DATA *data, uint8 tag)
+{
+ uint8 b;
+ struct nesting *nesting;
+
+ asn1_read_uint8(data, &b);
+ if (b != tag) {
+ data->has_error = True;
+ return False;
+ }
+ nesting = (struct nesting *)malloc(sizeof(struct nesting));
+ if (!nesting) {
+ data->has_error = True;
+ return False;
+ }
+
+ asn1_read_uint8(data, &b);
+ if (b & 0x80) {
+ int n = b & 0x7f;
+ if (n != 2) {
+ data->has_error = True;
+ return False;
+ }
+ asn1_read_uint8(data, &b);
+ nesting->taglen = b<<8;
+ asn1_read_uint8(data, &b);
+ nesting->taglen |= b;
+ } else {
+ nesting->taglen = b;
+ }
+ nesting->start = data->ofs;
+ nesting->next = data->nesting;
+ data->nesting = nesting;
+ return !data->has_error;
+}
+
+
+/* stop reading a tag */
+BOOL asn1_end_tag(ASN1_DATA *data)
+{
+ struct nesting *nesting;
+
+ /* make sure we read it all */
+ if (asn1_tag_remaining(data) != 0) {
+ data->has_error = True;
+ return False;
+ }
+
+ nesting = data->nesting;
+
+ if (!nesting) {
+ data->has_error = True;
+ return False;
+ }
+
+ data->nesting = nesting->next;
+ free(nesting);
+ return True;
+}
+
+/* work out how many bytes are left in this nested tag */
+int asn1_tag_remaining(ASN1_DATA *data)
+{
+ if (!data->nesting) {
+ data->has_error = True;
+ return -1;
+ }
+ return data->nesting->taglen - (data->ofs - data->nesting->start);
+}
+
+/* read an object ID from a ASN1 buffer */
+BOOL asn1_read_OID(ASN1_DATA *data, char **OID)
+{
+ uint8 b;
+ pstring oid;
+ fstring el;
+
+ if (!asn1_start_tag(data, ASN1_OID)) return False;
+ asn1_read_uint8(data, &b);
+
+ oid[0] = 0;
+ snprintf(el, sizeof(el), "%u", b/40);
+ pstrcat(oid, el);
+ snprintf(el, sizeof(el), " %u", b%40);
+ pstrcat(oid, el);
+
+ while (asn1_tag_remaining(data) > 0) {
+ unsigned v = 0;
+ do {
+ asn1_read_uint8(data, &b);
+ v = (v<<7) | (b&0x7f);
+ } while (!data->has_error && b & 0x80);
+ snprintf(el, sizeof(el), " %u", v);
+ pstrcat(oid, el);
+ }
+
+ asn1_end_tag(data);
+
+ *OID = strdup(oid);
+
+ return !data->has_error;
+}
+
+/* check that the next object ID is correct */
+BOOL asn1_check_OID(ASN1_DATA *data, char *OID)
+{
+ char *id;
+
+ if (!asn1_read_OID(data, &id)) return False;
+
+ if (strcmp(id, OID) != 0) {
+ data->has_error = True;
+ return False;
+ }
+ free(id);
+ return True;
+}
+
+/* read a GeneralString from a ASN1 buffer */
+BOOL asn1_read_GeneralString(ASN1_DATA *data, char **s)
+{
+ int len;
+ if (!asn1_start_tag(data, ASN1_GENERAL_STRING)) return False;
+ len = asn1_tag_remaining(data);
+ *s = malloc(len+1);
+ if (! *s) {
+ data->has_error = True;
+ return False;
+ }
+ asn1_read(data, *s, len);
+ (*s)[len] = 0;
+ asn1_end_tag(data);
+ return !data->has_error;
+}
diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c
index 77a8232ed5..36aedf2d59 100644
--- a/source3/libsmb/cliconnect.c
+++ b/source3/libsmb/cliconnect.c
@@ -325,8 +325,39 @@ static BOOL cli_session_setup_spnego(struct cli_state *cli, char *user,
uint32 capabilities = cli_session_setup_capabilities(cli);
char *p;
DATA_BLOB blob2, negTokenTarg;
+ char *principle;
+ char *OIDs[ASN1_MAX_OIDS];
+ uint8 guid[16];
+ int i;
+ BOOL got_kerberos_mechanism = False;
+
+ /* the server sent us the first part of the SPNEGO exchange in the negprot
+ reply */
+ if (!spnego_parse_negTokenInit(cli->secblob, guid, OIDs, &principle)) {
+ return False;
+ }
+
+ /* make sure the server understands kerberos */
+ for (i=0;OIDs[i];i++) {
+ DEBUG(3,("got OID=%s\n", OIDs[i]));
+ if (strcmp(OIDs[i], "1 2 840 48018 1 2 2") == 0) {
+ got_kerberos_mechanism = True;
+ }
+ free(OIDs[i]);
+ }
+ DEBUG(3,("got principle=%s\n", principle));
+
+ if (!got_kerberos_mechanism) {
+ DEBUG(1,("Server didn't offer kerberos5 mechanism!?\n"));
+ return False;
+ }
+
+ /* generate the encapsulated kerberos5 ticket */
+ negTokenTarg = spnego_gen_negTokenTarg(cli, principle);
+
+ free(principle);
- negTokenTarg = spnego_gen_negTokenTarg(cli);
+ if (!negTokenTarg.data) return False;
capabilities |= CAP_EXTENDED_SECURITY;
diff --git a/source3/libsmb/clikrb5.c b/source3/libsmb/clikrb5.c
index 0e049c14a1..fb442f7f09 100644
--- a/source3/libsmb/clikrb5.c
+++ b/source3/libsmb/clikrb5.c
@@ -27,6 +27,8 @@
#define OID_SPNEGO "1 3 6 1 5 5 2"
#define OID_KERBEROS5 "1 2 840 113554 1 2 2"
+#define CHECK_CALL(x) if (! x) goto failed
+
/*
we can't use krb5_mk_req because w2k wants the service to be in a particular format
*/
@@ -34,60 +36,62 @@ 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,
+ const char *realm,
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_error_code retval;
+ krb5_principal server;
+ krb5_creds * credsp;
+ krb5_creds creds;
+ krb5_data in_data;
+
+ retval = krb5_build_principal(context, &server, strlen(realm),
+ realm, service, NULL);
+ if (retval) {
+ DEBUG(1,("Failed to build principle for %s@%s\n", service, realm));
+ 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))) {
+ DEBUG(1,("krb5_get_credentials failed (%d)\n", retval));
+ goto cleanup_creds;
+ }
- krb5_free_creds(context, credsp);
+ in_data.length = 0;
+ retval = krb5_mk_req_extended(context, auth_context, ap_req_options,
+ &in_data, credsp, outbuf);
+ if (retval) {
+ DEBUG(1,("krb5_mk_req_extended failed (%d)\n", retval));
+ }
+
+ krb5_free_creds(context, credsp);
cleanup_creds:
- krb5_free_cred_contents(context, &creds);
+ krb5_free_cred_contents(context, &creds);
cleanup_princ:
- krb5_free_principal(context, server);
+ krb5_free_principal(context, server);
- return retval;
+ return retval;
}
/*
get a kerberos5 ticket for the given service
*/
-static DATA_BLOB krb5_get_ticket(char *service)
+static DATA_BLOB krb5_get_ticket(char *service, char *realm)
{
krb5_error_code retval;
- krb5_data packet, inbuf;
+ krb5_data packet;
krb5_ccache ccdef;
krb5_context context;
krb5_auth_context auth_context = NULL;
@@ -99,8 +103,6 @@ static DATA_BLOB krb5_get_ticket(char *service)
goto failed;
}
- inbuf.length = 0;
-
if ((retval = krb5_cc_default(context, &ccdef))) {
DEBUG(1,("krb5_cc_default failed\n"));
goto failed;
@@ -109,8 +111,8 @@ static DATA_BLOB krb5_get_ticket(char *service)
if ((retval = krb5_mk_req2(context,
&auth_context,
AP_OPTS_MUTUAL_REQUIRED,
- service,
- &inbuf, ccdef, &packet))) {
+ service, realm,
+ ccdef, &packet))) {
DEBUG(1,("krb5_mk_req2 failed\n"));
goto failed;
}
@@ -139,16 +141,16 @@ ASN1_DATA spnego_gen_negTokenInit(uint8 guid[16],
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));
+ CHECK_CALL(asn1_write(&data, guid, 16));
+ CHECK_CALL(asn1_push_tag(&data,ASN1_APPLICATION(0)));
+ CHECK_CALL(asn1_write_OID(&data,OID_SPNEGO));
+ CHECK_CALL(asn1_push_tag(&data,ASN1_CONTEXT(0)));
+ CHECK_CALL(asn1_push_tag(&data,ASN1_SEQUENCE(0)));
- asn1_push_tag(&data,ASN1_CONTEXT(0));
- asn1_push_tag(&data,ASN1_SEQUENCE(0));
+ CHECK_CALL(asn1_push_tag(&data,ASN1_CONTEXT(0)));
+ CHECK_CALL(asn1_push_tag(&data,ASN1_SEQUENCE(0)));
for (i=0; OIDs[i]; i++) {
- asn1_write_OID(&data,OIDs[i]);
+ CHECK_CALL(asn1_write_OID(&data,OIDs[i]));
}
asn1_pop_tag(&data);
asn1_pop_tag(&data);
@@ -167,6 +169,62 @@ ASN1_DATA spnego_gen_negTokenInit(uint8 guid[16],
asn1_pop_tag(&data);
return data;
+
+failed:
+ 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;
}
@@ -229,25 +287,28 @@ static ASN1_DATA spnego_gen_krb5_wrap(DATA_BLOB ticket)
generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY
kerberos session setup
*/
-DATA_BLOB spnego_gen_negTokenTarg(struct cli_state *cli)
+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};
- /* 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);
+ 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);
+ tkt = krb5_get_ticket(service, realm);
/* wrap that up in a nice GSS-API wrapping */
tkt_wrapped = spnego_gen_krb5_wrap(tkt);