diff options
author | Samuel Cabrero <samuelcabrero@kernevil.me> | 2014-09-16 18:05:53 +0200 |
---|---|---|
committer | Stefan Metzmacher <metze@samba.org> | 2014-09-22 23:09:08 +0200 |
commit | cc55bc2d45df3406130a5fe127f5eb35e466a7cd (patch) | |
tree | 16f6213c3dcb17e52b32521ebd2b384d64a9ce67 | |
parent | 8260ae6dbe5e65033d406ba26bbe97a6e4712c4c (diff) | |
download | samba-cc55bc2d45df3406130a5fe127f5eb35e466a7cd.tar.gz |
ncacn_http: Authentication modules for http library
Signed-off-by: Samuel Cabrero <samuelcabrero@kernevil.me>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
-rw-r--r-- | source4/lib/http/gensec/basic.c | 137 | ||||
-rw-r--r-- | source4/lib/http/gensec/ntlm.c | 120 | ||||
-rw-r--r-- | source4/lib/http/http.h | 15 | ||||
-rw-r--r-- | source4/lib/http/http_auth.c | 361 | ||||
-rw-r--r-- | source4/lib/http/wscript_build | 16 |
5 files changed, 645 insertions, 4 deletions
diff --git a/source4/lib/http/gensec/basic.c b/source4/lib/http/gensec/basic.c new file mode 100644 index 00000000000..86a2d51d67a --- /dev/null +++ b/source4/lib/http/gensec/basic.c @@ -0,0 +1,137 @@ +/* + Unix SMB/CIFS implementation. + + HTTP library - Basic authentication mechanism gensec module + + Copyright (C) 2014 Samuel Cabrero <samuelcabrero@kernevil.me> + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "auth/auth.h" +#include "auth/gensec/gensec.h" +#include "auth/gensec/gensec_internal.h" +#include "auth/credentials/credentials.h" + +_PUBLIC_ NTSTATUS gensec_http_basic_init(void); + +struct gensec_http_basic_state { + enum { + GENSEC_HTTP_BASIC_START, + GENSEC_HTTP_BASIC_DONE, + GENSEC_HTTP_BASIC_ERROR, + } step; +}; + +static NTSTATUS gensec_http_basic_client_start(struct gensec_security *gensec) +{ + struct gensec_http_basic_state *state; + + state = talloc_zero(gensec, struct gensec_http_basic_state); + if (state == NULL) { + return NT_STATUS_NO_MEMORY; + } + gensec->private_data = state; + + state->step = GENSEC_HTTP_BASIC_START; + + return NT_STATUS_OK; +} + +static NTSTATUS gensec_http_basic_update(struct gensec_security *gensec_ctx, + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const DATA_BLOB in, + DATA_BLOB *out) +{ + struct gensec_http_basic_state *state; + struct cli_credentials *creds; + char *tmp, *b64; + + state = talloc_get_type_abort(gensec_ctx->private_data, + struct gensec_http_basic_state); + creds = gensec_get_credentials(gensec_ctx); + + switch (gensec_ctx->gensec_role) { + case GENSEC_CLIENT: + switch (state->step) { + case GENSEC_HTTP_BASIC_START: + tmp = talloc_asprintf(mem_ctx, "%s\\%s:%s", + cli_credentials_get_domain(creds), + cli_credentials_get_username(creds), + cli_credentials_get_password(creds)); + if (tmp == NULL) { + state->step = GENSEC_HTTP_BASIC_ERROR; + return NT_STATUS_NO_MEMORY; + } + *out = data_blob_string_const(tmp); + + b64 = base64_encode_data_blob(mem_ctx, *out); + if (b64 == NULL) { + state->step = GENSEC_HTTP_BASIC_ERROR; + return NT_STATUS_NO_MEMORY; + } + TALLOC_FREE(tmp); + + tmp = talloc_asprintf(mem_ctx, "Basic %s", b64); + if (tmp == NULL) { + state->step = GENSEC_HTTP_BASIC_ERROR; + return NT_STATUS_NO_MEMORY; + } + TALLOC_FREE(b64); + + *out = data_blob_string_const(tmp); + state->step = GENSEC_HTTP_BASIC_DONE; + return NT_STATUS_OK; + + case GENSEC_HTTP_BASIC_DONE: + case GENSEC_HTTP_BASIC_ERROR: + default: + break; + } + state->step = GENSEC_HTTP_BASIC_ERROR; + return NT_STATUS_INTERNAL_ERROR; + + case GENSEC_SERVER: + state->step = GENSEC_HTTP_BASIC_ERROR; + return NT_STATUS_NOT_IMPLEMENTED; + } + + state->step = GENSEC_HTTP_BASIC_ERROR; + return NT_STATUS_INTERNAL_ERROR; +} + +static const struct gensec_security_ops gensec_http_basic_security_ops = { + .name = "http_basic", + .auth_type = 0, + .client_start = gensec_http_basic_client_start, + .update = gensec_http_basic_update, + .enabled = true, + .priority = GENSEC_EXTERNAL, +}; + +_PUBLIC_ NTSTATUS gensec_http_basic_init(void) +{ + NTSTATUS status; + + status = gensec_register(&gensec_http_basic_security_ops); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("Failed to register '%s' gensec backend!\n", + gensec_http_basic_security_ops.name)); + return status; + } + + return status; +} diff --git a/source4/lib/http/gensec/ntlm.c b/source4/lib/http/gensec/ntlm.c new file mode 100644 index 00000000000..07470fa71c2 --- /dev/null +++ b/source4/lib/http/gensec/ntlm.c @@ -0,0 +1,120 @@ +/* + Unix SMB/CIFS implementation. + + HTTP library - NTLM authentication mechanism gensec module + + Copyright (C) 2014 Samuel Cabrero <samuelcabrero@kernevil.me> + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "auth/auth.h" +#include "auth/gensec/gensec.h" +#include "auth/gensec/gensec_internal.h" + +_PUBLIC_ NTSTATUS gensec_http_ntlm_init(void); + +struct gensec_http_ntlm_state { + struct gensec_security *sub; +}; + +static NTSTATUS gensec_http_ntlm_client_start(struct gensec_security *gensec) +{ + NTSTATUS status; + struct gensec_http_ntlm_state *state; + + state = talloc_zero(gensec, struct gensec_http_ntlm_state); + if (state == NULL) { + return NT_STATUS_NO_MEMORY; + } + gensec->private_data = state; + + status = gensec_subcontext_start(state, gensec, &state->sub); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return gensec_start_mech_by_oid(state->sub, GENSEC_OID_NTLMSSP); +} + +static NTSTATUS gensec_http_ntlm_update(struct gensec_security *gensec_ctx, + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const DATA_BLOB in, + DATA_BLOB *out) +{ + NTSTATUS status; + struct gensec_http_ntlm_state *state; + DATA_BLOB ntlm_in; + + state = talloc_get_type_abort(gensec_ctx->private_data, + struct gensec_http_ntlm_state); + + if (in.length) { + if (strncasecmp((char *)in.data, "NTLM ", 5) != 0) { + return NT_STATUS_INVALID_PARAMETER; + } + ntlm_in = base64_decode_data_blob_talloc(mem_ctx, + (char *)&in.data[5]); + } else { + ntlm_in = data_blob_null; + } + + status = gensec_update_ev(state->sub, mem_ctx, ev, ntlm_in, out); + if (NT_STATUS_IS_OK(status) || + NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + char *tmp, *b64; + b64 = base64_encode_data_blob(mem_ctx, *out); + if (b64 == NULL) { + return NT_STATUS_NO_MEMORY; + } + + tmp = talloc_asprintf(mem_ctx, "NTLM %s", b64); + TALLOC_FREE(b64); + if (tmp == NULL) { + return NT_STATUS_NO_MEMORY; + } + *out = data_blob_string_const(tmp); + } + + if (ntlm_in.data) { + data_blob_free(&ntlm_in); + } + + return status; +} + +static const struct gensec_security_ops gensec_http_ntlm_security_ops = { + .name = "http_ntlm", + .auth_type = 0, + .client_start = gensec_http_ntlm_client_start, + .update = gensec_http_ntlm_update, + .enabled = true, + .priority = GENSEC_EXTERNAL, +}; + +_PUBLIC_ NTSTATUS gensec_http_ntlm_init(void) +{ + NTSTATUS status; + + status = gensec_register(&gensec_http_ntlm_security_ops); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("Failed to register '%s' gensec backend!\n", + gensec_http_ntlm_security_ops.name)); + return status; + } + + return status; +} diff --git a/source4/lib/http/http.h b/source4/lib/http/http.h index 671f3836e5a..75a04b1179b 100644 --- a/source4/lib/http/http.h +++ b/source4/lib/http/http.h @@ -63,10 +63,8 @@ enum http_cmd_type { }; enum http_auth_method { - HTTP_AUTH_AUTO, - HTTP_AUTH_BASIC, + HTTP_AUTH_BASIC=1, HTTP_AUTH_NTLM, - HTTP_AUTH_NEGOTIATE, }; struct http_header { @@ -108,4 +106,15 @@ NTSTATUS http_read_response_recv(struct tevent_req *, TALLOC_CTX *, struct http_request **); +/* HTTP authenticated request */ +struct tevent_req *http_send_auth_request_send(TALLOC_CTX *, + struct tevent_context *, + struct tstream_context *, + struct tevent_queue *, + struct http_request *, + struct cli_credentials *, + struct loadparm_context *, + enum http_auth_method); +NTSTATUS http_send_auth_request_recv(struct tevent_req *); + #endif /* _HTTP_H_ */ diff --git a/source4/lib/http/http_auth.c b/source4/lib/http/http_auth.c new file mode 100644 index 00000000000..2bf4392e8f2 --- /dev/null +++ b/source4/lib/http/http_auth.c @@ -0,0 +1,361 @@ +/* + Unix SMB/CIFS implementation. + + HTTP library + + Copyright (C) 2014 Samuel Cabrero <samuelcabrero@kernevil.me> + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "http.h" +#include "http_internal.h" +#include "lib/util/tevent_ntstatus.h" +#include "lib/param/param.h" +#include "tevent.h" +#include "auth/gensec/gensec.h" +#include "auth/credentials/credentials.h" +#include "lib/util/data_blob.h" + +/** + * Copy the request headers from src to dst + */ +static NTSTATUS http_copy_header(struct http_request *src, + struct http_request *dst) +{ + struct http_header *h; + + dst->type = src->type; + dst->major = src->major; + dst->minor = src->minor; + dst->uri = talloc_strdup(dst, src->uri); + + for (h = src->headers; h != NULL; h = h->next) { + http_add_header(dst, &dst->headers, h->key, h->value); + } + dst->headers_size = src->headers_size; + + return NT_STATUS_OK; +} + +/* + * Retrieve the WWW-Authenticate header from server response based on the + * authentication scheme being used. + */ +static NTSTATUS http_parse_auth_response(enum http_auth_method auth, + struct http_request *auth_response, + DATA_BLOB *in) +{ + struct http_header *h; + + for (h = auth_response->headers; h != NULL; h = h->next) { + if (strncasecmp(h->key, "WWW-Authenticate", 16) == 0) { + switch (auth) { + case HTTP_AUTH_NTLM: + if (strncasecmp(h->value, "NTLM ", 5) == 0) { + *in = data_blob_string_const(h->value); + return NT_STATUS_OK; + } + break; + default: + break; + } + } + } + + return NT_STATUS_NOT_SUPPORTED; +} + +/* + * Create the next authentication request to send to server if authentication + * is not completed. If it is completed, attachs the 'Authorization' header + * to the original request. + */ +static NTSTATUS http_create_auth_request(TALLOC_CTX *mem_ctx, + struct gensec_security *gensec_ctx, + struct tevent_context *ev, + enum http_auth_method auth, + struct http_request *original_request, + struct http_request *auth_response, + struct http_request **auth_request) +{ + NTSTATUS status; + DATA_BLOB in, out; + + if (auth_response) { + status = http_parse_auth_response(auth, auth_response, &in); + } else { + in = data_blob_null; + } + + status = gensec_update_ev(gensec_ctx, mem_ctx, ev, in, &out); + if (NT_STATUS_IS_OK(status)) { + if (out.length) { + http_add_header(original_request, + &original_request->headers, + "Authorization", (char*)out.data); + } + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + NTSTATUS status2; + + *auth_request = talloc_zero(mem_ctx, struct http_request); + if (*auth_request == NULL) { + return NT_STATUS_NO_MEMORY; + } + + status2 = http_copy_header(original_request, *auth_request); + if (!NT_STATUS_IS_OK(status2)) { + talloc_free(*auth_request); + return status2; + } + + http_replace_header(*auth_request, &((*auth_request)->headers), + "Content-Length", "0"); + if (out.length) { + http_add_header(*auth_request, + &((*auth_request)->headers), + "Authorization", (char*)out.data); + } + } + + return status; +} + +struct http_auth_state +{ + struct loadparm_context *lp_ctx; + struct tevent_context *ev; + struct tstream_context *stream; + struct tevent_queue *send_queue; + struct cli_credentials *credentials; + struct http_request *original_request; + struct gensec_security *gensec_ctx; + NTSTATUS gensec_status; + enum http_auth_method auth; + + int sys_errno; + int nwritten; +}; + + +static void http_send_auth_request_done(struct tevent_req *); +struct tevent_req *http_send_auth_request_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tstream_context *stream, + struct tevent_queue *send_queue, + struct http_request *original_request, + struct cli_credentials *credentials, + struct loadparm_context *lp_ctx, + enum http_auth_method auth) +{ + struct tevent_req *req; + struct tevent_req *subreq; + struct http_auth_state *state; + NTSTATUS status; + struct http_request *auth_request; + struct http_request *request_to_send; + + req = tevent_req_create(mem_ctx, &state, struct http_auth_state); + if (req == NULL) { + return NULL; + } + + state->ev = ev; + state->stream = stream; + state->send_queue = send_queue; + state->original_request = original_request; + state->credentials = credentials; + state->lp_ctx = lp_ctx; + state->auth = auth; + + status = gensec_init(); + if (!NT_STATUS_IS_OK(status)) { + goto post_status; + } + status = gensec_client_start(state, &state->gensec_ctx, + lpcfg_gensec_settings(state, lp_ctx)); + if (!NT_STATUS_IS_OK(status)) { + goto post_status; + } + status = gensec_set_credentials(state->gensec_ctx, credentials); + if (!NT_STATUS_IS_OK(status)) { + goto post_status; + } + + switch (state->auth) { + case HTTP_AUTH_BASIC: + status = gensec_start_mech_by_name(state->gensec_ctx, + "http_basic"); + if (!NT_STATUS_IS_OK(status)) { + goto post_status; + } + break; + case HTTP_AUTH_NTLM: + status = gensec_start_mech_by_name(state->gensec_ctx, + "http_ntlm"); + if (!NT_STATUS_IS_OK(status)) { + goto post_status; + } + break; + default: + tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED); + return tevent_req_post(req, ev); + } + + /* + * Store the gensec status to read the server response on callback + * if more processing is required + */ + state->gensec_status = http_create_auth_request(state, + state->gensec_ctx, + state->ev, + state->auth, + state->original_request, + NULL, + &auth_request); + if (!NT_STATUS_IS_OK(state->gensec_status) && + !NT_STATUS_EQUAL(state->gensec_status, + NT_STATUS_MORE_PROCESSING_REQUIRED)) { + goto post_status; + } + + /* + * If no more processing is necessary, the http_create_auth_request + * function will attach the authentication header to the original + * request + */ + request_to_send = NT_STATUS_IS_OK(state->gensec_status) ? + state->original_request : auth_request; + + subreq = http_send_request_send(state, ev, stream, send_queue, + request_to_send); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, http_send_auth_request_done, req); + return req; +post_status: + tevent_req_nterror(req, status); + return tevent_req_post(req, ev); +} + +static void http_send_auth_request_done2(struct tevent_req *subreq); +static void http_send_auth_request_done(struct tevent_req *subreq) +{ + NTSTATUS status; + struct tevent_req *req; + struct http_auth_state *state; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct http_auth_state); + + status = http_send_request_recv(subreq); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + /* If no more processing required, it is done */ + if (NT_STATUS_IS_OK(state->gensec_status)) { + tevent_req_done(req); + return; + } + + /* If more processing required, read the response from server */ + if (NT_STATUS_EQUAL(state->gensec_status, + NT_STATUS_MORE_PROCESSING_REQUIRED)) { + subreq = http_read_response_send(state, state->ev, + state->stream); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, http_send_auth_request_done2, + req); + return; + } + + /* + * If gensec status is not NT_STATUS_OK neither + * NT_STATUS_MORE_PROCESSING_REQUIRED , it is an error + */ + tevent_req_nterror(req, state->gensec_status); +} + +static void http_send_auth_request_done2(struct tevent_req *subreq) +{ + NTSTATUS status; + struct tevent_req *req; + struct http_auth_state *state; + struct http_request *auth_response; + struct http_request *auth_request; + struct http_request *request_to_send; + + req = tevent_req_callback_data(subreq, struct tevent_req); + state = tevent_req_data(req, struct http_auth_state); + + status = http_read_response_recv(subreq, state, &auth_response); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + state->gensec_status = http_create_auth_request(state, + state->gensec_ctx, + state->ev, + state->auth, + state->original_request, + auth_response, + &auth_request); + if (!NT_STATUS_IS_OK(state->gensec_status) && + !NT_STATUS_EQUAL(state->gensec_status, + NT_STATUS_MORE_PROCESSING_REQUIRED)) { + tevent_req_nterror(req, status); + return; + } + + /* + * If no more processing is necessary, the http_create_auth_request + * function will attach the authentication header to the original + * request + */ + request_to_send = NT_STATUS_IS_OK(state->gensec_status) ? + state->original_request : auth_request; + + subreq = http_send_request_send(state, + state->ev, + state->stream, + state->send_queue, + request_to_send); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, http_send_auth_request_done, req); +} + + +NTSTATUS http_send_auth_request_recv(struct tevent_req *req) +{ + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + tevent_req_received(req); + return status; + } + tevent_req_received(req); + + return NT_STATUS_OK; +} diff --git a/source4/lib/http/wscript_build b/source4/lib/http/wscript_build index 61f40704ecc..e18d85319e8 100644 --- a/source4/lib/http/wscript_build +++ b/source4/lib/http/wscript_build @@ -1,7 +1,21 @@ #!/usr/bin/env python bld.SAMBA_LIBRARY('http', - source='http.c', + source='http.c http_auth.c', deps='talloc tevent samba3core', private_library=True, ) + +bld.SAMBA_MODULE('gensec_http_basic', + source='gensec/basic.c', + subsystem='gensec', + init_function='gensec_http_basic_init', + deps='samba-util auth_session' +) + +bld.SAMBA_MODULE('gensec_http_ntlm', + source='gensec/ntlm.c', + subsystem='gensec', + init_function='gensec_http_ntlm_init', + deps='samba-util auth_session' +) |