summaryrefslogtreecommitdiff
path: root/source3/utils/ntlm_auth.c
diff options
context:
space:
mode:
authorSimo Sorce <idra@samba.org>2003-08-02 20:06:57 +0000
committerSimo Sorce <idra@samba.org>2003-08-02 20:06:57 +0000
commit04bf12b176d5abe06b7f1401810369bcafe0b611 (patch)
tree8bb6627c3ffa4cab902787b874206f8012a33e3a /source3/utils/ntlm_auth.c
parent7efce478976e2ac71bcaf4e4d1049bb263634711 (diff)
downloadsamba-04bf12b176d5abe06b7f1401810369bcafe0b611.tar.gz
samba-04bf12b176d5abe06b7f1401810369bcafe0b611.tar.bz2
samba-04bf12b176d5abe06b7f1401810369bcafe0b611.zip
port latest changes from SAMBA_3_0 tree
(This used to be commit 3101c236b8241dc0183995ffceed551876427de4)
Diffstat (limited to 'source3/utils/ntlm_auth.c')
-rw-r--r--source3/utils/ntlm_auth.c509
1 files changed, 507 insertions, 2 deletions
diff --git a/source3/utils/ntlm_auth.c b/source3/utils/ntlm_auth.c
index 3dfa157bda..1d36a7ce52 100644
--- a/source3/utils/ntlm_auth.c
+++ b/source3/utils/ntlm_auth.c
@@ -32,7 +32,9 @@
enum squid_mode {
SQUID_2_4_BASIC,
SQUID_2_5_BASIC,
- SQUID_2_5_NTLMSSP
+ SQUID_2_5_NTLMSSP,
+ GSS_SPNEGO,
+ GSS_SPNEGO_CLIENT
};
@@ -342,6 +344,501 @@ static void manage_squid_basic_request(enum squid_mode squid_mode,
}
}
+static void offer_gss_spnego_mechs(void) {
+
+ DATA_BLOB token;
+ ASN1_DATA asn1;
+ SPNEGO_DATA spnego;
+ ssize_t len;
+ char *reply_base64;
+
+ ZERO_STRUCT(spnego);
+
+ /* Server negTokenInit (mech offerings) */
+ spnego.type = SPNEGO_NEG_TOKEN_INIT;
+ spnego.negTokenInit.mechTypes = smb_xmalloc(sizeof(char *) * 2);
+ spnego.negTokenInit.mechTypes[0] = smb_xstrdup(OID_NTLMSSP);
+ spnego.negTokenInit.mechTypes[1] = NULL;
+
+ ZERO_STRUCT(asn1);
+ asn1_push_tag(&asn1, ASN1_SEQUENCE(0));
+ asn1_push_tag(&asn1, ASN1_CONTEXT(0));
+ asn1_write_GeneralString(&asn1, "NONE");
+ asn1_pop_tag(&asn1);
+ asn1_pop_tag(&asn1);
+ spnego.negTokenInit.mechListMIC = data_blob(asn1.data, asn1.length);
+ asn1_free(&asn1);
+
+ len = write_spnego_data(&token, &spnego);
+ free_spnego_data(&spnego);
+
+ if (len == -1) {
+ DEBUG(1, ("Could not write SPNEGO data blob\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ reply_base64 = base64_encode_data_blob(token);
+ x_fprintf(x_stdout, "TT %s *\n", reply_base64);
+
+ SAFE_FREE(reply_base64);
+ data_blob_free(&token);
+ DEBUG(10, ("sent SPNEGO negTokenInit\n"));
+ return;
+}
+
+static void manage_gss_spnego_request(enum squid_mode squid_mode,
+ char *buf, int length)
+{
+ static NTLMSSP_STATE *ntlmssp_state = NULL;
+ SPNEGO_DATA spnego;
+ DATA_BLOB request, token;
+ NTSTATUS status;
+ ssize_t len;
+
+ const char *reply_code;
+ char *reply_base64;
+ pstring reply_argument;
+
+ if (strlen(buf) < 2) {
+
+ if (ntlmssp_state != NULL) {
+ DEBUG(1, ("Request for initial SPNEGO request where "
+ "we already have a state\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if ( (strlen(buf) == 2) && (strcmp(buf, "YR") == 0) ) {
+
+ /* Initial request, get the negTokenInit offering
+ mechanisms */
+
+ offer_gss_spnego_mechs();
+ return;
+ }
+
+ /* All subsequent requests are "KK" (Knock, Knock ;)) and have
+ a blob. This might be negTokenInit or negTokenTarg */
+
+ if ( (strlen(buf) <= 3) || (strncmp(buf, "KK", 2) != 0) ) {
+ DEBUG(1, ("GSS-SPNEGO query [%s] invalid\n", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ request = base64_decode_data_blob(buf + 3);
+ len = read_spnego_data(request, &spnego);
+ data_blob_free(&request);
+
+ if (len == -1) {
+ DEBUG(1, ("GSS-SPNEGO query [%s] invalid", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if (spnego.type == SPNEGO_NEG_TOKEN_INIT) {
+
+ /* Second request from Client. This is where the
+ client offers its mechanism to use. We currently
+ only support NTLMSSP, the decision for Kerberos
+ would be taken here. */
+
+ if ( (spnego.negTokenInit.mechTypes == NULL) ||
+ (spnego.negTokenInit.mechTypes[0] == NULL) ) {
+ DEBUG(1, ("Client did not offer any mechanism"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if ( strcmp(spnego.negTokenInit.mechTypes[0], OID_NTLMSSP) != 0 ) {
+ DEBUG(1, ("Client did not choose NTLMSSP but %s\n",
+ spnego.negTokenInit.mechTypes[0]));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if ( spnego.negTokenInit.mechToken.data == NULL ) {
+ DEBUG(1, ("Client did not provide NTLMSSP data\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if ( ntlmssp_state != NULL ) {
+ DEBUG(1, ("Client wants a new NTLMSSP challenge, but "
+ "already got one\n"));
+ x_fprintf(x_stdout, "BH\n");
+ ntlmssp_server_end(&ntlmssp_state);
+ return;
+ }
+
+ ntlmssp_server_start(&ntlmssp_state);
+ ntlmssp_state->check_password = winbind_pw_check;
+ ntlmssp_state->get_domain = get_winbind_domain;
+ ntlmssp_state->get_global_myname = get_winbind_netbios_name;
+
+ DEBUG(10, ("got NTLMSSP packet:\n"));
+ dump_data(10, spnego.negTokenInit.mechToken.data,
+ spnego.negTokenInit.mechToken.length);
+
+ free_spnego_data(&spnego);
+
+ spnego.type = SPNEGO_NEG_TOKEN_TARG;
+ spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
+ spnego.negTokenTarg.supportedMech = strdup(OID_NTLMSSP);
+
+ status = ntlmssp_server_update(ntlmssp_state,
+ spnego.negTokenInit.mechToken,
+ &spnego.negTokenTarg.responseToken);
+
+ } else {
+
+ /* spnego.type == SPNEGO_NEG_TOKEN_TARG */
+
+ DATA_BLOB response;
+
+ if (spnego.negTokenTarg.responseToken.data == NULL) {
+ DEBUG(1, ("Got a negTokenArg without a responseToken!\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ status = ntlmssp_server_update(ntlmssp_state,
+ spnego.negTokenTarg.responseToken,
+ &response);
+
+ data_blob_free(&spnego.negTokenTarg.responseToken);
+
+ spnego.negTokenTarg.responseToken = response;
+
+ }
+
+ if (NT_STATUS_IS_OK(status)) {
+ spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
+ reply_code = "AF";
+ pstr_sprintf(reply_argument, "%s\\%s",
+ ntlmssp_state->domain, ntlmssp_state->user);
+ } else if (NT_STATUS_EQUAL(status,
+ NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
+ reply_code = "TT";
+ pstr_sprintf(reply_argument, "*");
+ } else {
+ spnego.negTokenTarg.negResult = SPNEGO_REJECT;
+ reply_code = "NA";
+ pstrcpy(reply_argument, nt_errstr(status));
+ }
+
+ len = write_spnego_data(&token, &spnego);
+ free_spnego_data(&spnego);
+
+ if (len == -1) {
+ DEBUG(1, ("Could not write SPNEGO data blob\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ reply_base64 = base64_encode_data_blob(token);
+
+ x_fprintf(x_stdout, "%s %s %s\n",
+ reply_code, reply_base64, reply_argument);
+
+ SAFE_FREE(reply_base64);
+ data_blob_free(&token);
+
+ if (NT_STATUS_IS_OK(status)) {
+ ntlmssp_server_end(&ntlmssp_state);
+ }
+
+ return;
+}
+
+static NTLMSSP_CLIENT_STATE *client_ntlmssp_state = NULL;
+
+static void manage_client_ntlmssp_init(SPNEGO_DATA spnego)
+{
+ NTSTATUS status;
+ DATA_BLOB null_blob = data_blob(NULL, 0);
+ DATA_BLOB to_server;
+ char *to_server_base64;
+ const char *my_mechs[] = {OID_NTLMSSP, NULL};
+
+ DEBUG(10, ("Got spnego negTokenInit with NTLMSSP\n"));
+
+ if (client_ntlmssp_state != NULL) {
+ DEBUG(1, ("Request for initial SPNEGO request where "
+ "we already have a state\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if ( (opt_username == NULL) || (opt_domain == NULL) ) {
+ DEBUG(1, ("Need username and domain for NTLMSSP\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if (opt_password == NULL) {
+
+ /* Request a password from the calling process. After
+ sending it, the calling process should retry with
+ the negTokenInit. */
+
+ DEBUG(10, ("Requesting password\n"));
+ x_fprintf(x_stdout, "PW\n");
+ return;
+ }
+
+ status = ntlmssp_client_start(&client_ntlmssp_state);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not start NTLMSSP client: %s\n",
+ nt_errstr(status)));
+ x_fprintf(x_stdout, "BH\n");
+ ntlmssp_client_end(&client_ntlmssp_state);
+ return;
+ }
+
+ status = ntlmssp_set_username(client_ntlmssp_state, opt_username);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not set username: %s\n",
+ nt_errstr(status)));
+ x_fprintf(x_stdout, "BH\n");
+ ntlmssp_client_end(&client_ntlmssp_state);
+ return;
+ }
+
+ status = ntlmssp_set_domain(client_ntlmssp_state, opt_domain);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not set domain: %s\n",
+ nt_errstr(status)));
+ x_fprintf(x_stdout, "BH\n");
+ ntlmssp_client_end(&client_ntlmssp_state);
+ return;
+ }
+
+ status = ntlmssp_set_password(client_ntlmssp_state, opt_password);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not set password: %s\n",
+ nt_errstr(status)));
+ x_fprintf(x_stdout, "BH\n");
+ ntlmssp_client_end(&client_ntlmssp_state);
+ return;
+ }
+
+ spnego.type = SPNEGO_NEG_TOKEN_INIT;
+ spnego.negTokenInit.mechTypes = my_mechs;
+ spnego.negTokenInit.reqFlags = 0;
+ spnego.negTokenInit.mechListMIC = null_blob;
+
+ status = ntlmssp_client_update(client_ntlmssp_state, null_blob,
+ &spnego.negTokenInit.mechToken);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED, got: %s\n",
+ nt_errstr(status)));
+ x_fprintf(x_stdout, "BH\n");
+ ntlmssp_client_end(&client_ntlmssp_state);
+ return;
+ }
+
+ write_spnego_data(&to_server, &spnego);
+ data_blob_free(&spnego.negTokenInit.mechToken);
+
+ to_server_base64 = base64_encode_data_blob(to_server);
+ data_blob_free(&to_server);
+ x_fprintf(x_stdout, "KK %s\n", to_server_base64);
+ SAFE_FREE(to_server_base64);
+ return;
+}
+
+static void manage_client_ntlmssp_targ(SPNEGO_DATA spnego)
+{
+ NTSTATUS status;
+ DATA_BLOB null_blob = data_blob(NULL, 0);
+ DATA_BLOB request;
+ DATA_BLOB to_server;
+ char *to_server_base64;
+
+ DEBUG(10, ("Got spnego negTokenTarg with NTLMSSP\n"));
+
+ if (client_ntlmssp_state == NULL) {
+ DEBUG(1, ("Got NTLMSSP tArg without a client state\n"));
+ x_fprintf(x_stdout, "BH\n");
+ ntlmssp_client_end(&client_ntlmssp_state);
+ return;
+ }
+
+ if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
+ x_fprintf(x_stdout, "NA\n");
+ ntlmssp_client_end(&client_ntlmssp_state);
+ return;
+ }
+
+ if (spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
+ x_fprintf(x_stdout, "AF\n");
+ ntlmssp_client_end(&client_ntlmssp_state);
+ return;
+ }
+
+ status = ntlmssp_client_update(client_ntlmssp_state,
+ spnego.negTokenTarg.responseToken,
+ &request);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED from "
+ "ntlmssp_client_update, got: %s\n",
+ nt_errstr(status)));
+ x_fprintf(x_stdout, "BH\n");
+ data_blob_free(&request);
+ ntlmssp_client_end(&client_ntlmssp_state);
+ return;
+ }
+
+ spnego.type = SPNEGO_NEG_TOKEN_TARG;
+ spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
+ spnego.negTokenTarg.supportedMech = OID_NTLMSSP;
+ spnego.negTokenTarg.responseToken = request;
+ spnego.negTokenTarg.mechListMIC = null_blob;
+
+ write_spnego_data(&to_server, &spnego);
+ data_blob_free(&request);
+
+ to_server_base64 = base64_encode_data_blob(to_server);
+ data_blob_free(&to_server);
+ x_fprintf(x_stdout, "KK %s\n", to_server_base64);
+ SAFE_FREE(to_server_base64);
+ return;
+}
+
+static void manage_client_krb5_init(SPNEGO_DATA spnego)
+{
+ DEBUG(1, ("to be done ... \n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+}
+
+static void manage_client_krb5_targ(SPNEGO_DATA spnego)
+{
+ DEBUG(1, ("Got a negTokenTarg with a Kerberos token. This should not "
+ "happen!\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+}
+
+static void manage_gss_spnego_client_request(enum squid_mode squid_mode,
+ char *buf, int length)
+{
+ DATA_BLOB request;
+ SPNEGO_DATA spnego;
+ ssize_t len;
+
+ if (strlen(buf) <= 3) {
+ DEBUG(1, ("SPNEGO query [%s] too short\n", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ request = base64_decode_data_blob(buf+3);
+
+ if (strncmp(buf, "PW ", 3) == 0) {
+
+ /* We asked for a password and obviously got it :-) */
+
+ opt_password = strndup(request.data, request.length);
+
+ if (opt_password == NULL) {
+ DEBUG(1, ("Out of memory\n"));
+ x_fprintf(x_stdout, "BH\n");
+ data_blob_free(&request);
+ return;
+ }
+
+ x_fprintf(x_stdout, "OK\n");
+ data_blob_free(&request);
+ return;
+ }
+
+ if ( (strncmp(buf, "TT ", 3) != 0) &&
+ (strncmp(buf, "AF ", 3) != 0) &&
+ (strncmp(buf, "NA ", 3) != 0) ) {
+ DEBUG(1, ("SPNEGO request [%s] invalid\n", buf));
+ x_fprintf(x_stdout, "BH\n");
+ data_blob_free(&request);
+ return;
+ }
+
+ /* So we got a server challenge to generate a SPNEGO
+ client-to-server request... */
+
+ len = read_spnego_data(request, &spnego);
+ data_blob_free(&request);
+
+ if (len == -1) {
+ DEBUG(1, ("Could not read SPNEGO data for [%s]\n", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if (spnego.type == SPNEGO_NEG_TOKEN_INIT) {
+
+ /* The server offers a list of mechanisms */
+
+ const char **mechType = spnego.negTokenInit.mechTypes;
+
+ while (*mechType != NULL) {
+
+ if (strcmp(*mechType, OID_NTLMSSP) == 0) {
+ manage_client_ntlmssp_init(spnego);
+ goto out;
+ }
+
+ if (strcmp(*mechType, OID_KERBEROS5_OLD) == 0) {
+ manage_client_krb5_init(spnego);
+ goto out;
+ }
+
+ mechType++;
+ }
+
+ DEBUG(1, ("Server offered no compatible mechanism\n"));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+ }
+
+ if (spnego.type == SPNEGO_NEG_TOKEN_TARG) {
+
+ if (strcmp(spnego.negTokenTarg.supportedMech,
+ OID_NTLMSSP) == 0) {
+ manage_client_ntlmssp_targ(spnego);
+ goto out;
+ }
+
+ if (strcmp(spnego.negTokenTarg.supportedMech,
+ OID_KERBEROS5_OLD) == 0) {
+ manage_client_krb5_targ(spnego);
+ goto out;
+ }
+
+ }
+
+ DEBUG(1, ("Got an SPNEGO token I could not handle [%s]!\n", buf));
+ x_fprintf(x_stdout, "BH\n");
+ return;
+
+ out:
+ free_spnego_data(&spnego);
+ return;
+}
+
static void manage_squid_request(enum squid_mode squid_mode)
{
char buf[SQUID_BUFFER_SIZE+1];
@@ -383,6 +880,10 @@ static void manage_squid_request(enum squid_mode squid_mode)
manage_squid_basic_request(squid_mode, buf, length);
} else if (squid_mode == SQUID_2_5_NTLMSSP) {
manage_squid_ntlmssp_request(squid_mode, buf, length);
+ } else if (squid_mode == GSS_SPNEGO) {
+ manage_gss_spnego_request(squid_mode, buf, length);
+ } else if (squid_mode == GSS_SPNEGO_CLIENT) {
+ manage_gss_spnego_client_request(squid_mode, buf, length);
}
}
@@ -1334,6 +1835,10 @@ enum {
squid_stream(SQUID_2_5_BASIC);
} else if (strcmp(helper_protocol, "squid-2.4-basic")== 0) {
squid_stream(SQUID_2_4_BASIC);
+ } else if (strcmp(helper_protocol, "gss-spnego")== 0) {
+ squid_stream(GSS_SPNEGO);
+ } else if (strcmp(helper_protocol, "gss-spnego-client") == 0) {
+ squid_stream(GSS_SPNEGO_CLIENT);
} else {
x_fprintf(x_stderr, "unknown helper protocol [%s]\n", helper_protocol);
exit(1);
@@ -1372,7 +1877,7 @@ enum {
} else {
fstring user;
- snprintf(user, sizeof(user)-1, "%s%c%s", opt_domain, winbind_separator(), opt_username);
+ fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
if (!check_plaintext_auth(user, opt_password, True)) {
exit(1);
}