From b5e0b5072317f11b27d2d813bd1c93788584a9f2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 11 Nov 2017 22:43:03 +0000 Subject: [http] Add support for NTLM authentication Signed-off-by: Michael Brown --- src/config/config_http.c | 3 + src/config/general.h | 1 + src/include/ipxe/errfile.h | 1 + src/include/ipxe/http.h | 25 ++++++ src/net/tcp/httpntlm.c | 201 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 231 insertions(+) create mode 100644 src/net/tcp/httpntlm.c diff --git a/src/config/config_http.c b/src/config/config_http.c index 3c0e7802..4373ea2c 100644 --- a/src/config/config_http.c +++ b/src/config/config_http.c @@ -40,6 +40,9 @@ REQUIRE_OBJECT ( httpbasic ); #ifdef HTTP_AUTH_DIGEST REQUIRE_OBJECT ( httpdigest ); #endif +#ifdef HTTP_AUTH_NTLM +REQUIRE_OBJECT ( httpntlm ); +#endif #ifdef HTTP_ENC_PEERDIST REQUIRE_OBJECT ( peerdist ); #endif diff --git a/src/config/general.h b/src/config/general.h index e06db525..3c14a2cd 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -77,6 +77,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #define HTTP_AUTH_BASIC /* Basic authentication */ #define HTTP_AUTH_DIGEST /* Digest authentication */ +//#define HTTP_AUTH_NTLM /* NTLM authentication */ //#define HTTP_ENC_PEERDIST /* PeerDist content encoding */ //#define HTTP_HACK_GCE /* Google Compute Engine hacks */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index e0d0dbc8..1a98599e 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -277,6 +277,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_peermux ( ERRFILE_NET | 0x00470000 ) #define ERRFILE_xsigo ( ERRFILE_NET | 0x00480000 ) #define ERRFILE_ntp ( ERRFILE_NET | 0x00490000 ) +#define ERRFILE_httpntlm ( ERRFILE_NET | 0x004a0000 ) #define ERRFILE_image ( ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_elf ( ERRFILE_IMAGE | 0x00010000 ) diff --git a/src/include/ipxe/http.h b/src/include/ipxe/http.h index 0f42a22e..0893c953 100644 --- a/src/include/ipxe/http.h +++ b/src/include/ipxe/http.h @@ -18,6 +18,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include struct http_transaction; @@ -172,6 +173,18 @@ struct http_request_auth_digest { char response[ HTTP_DIGEST_RESPONSE_LEN + 1 /* NUL */ ]; }; +/** HTTP request NTLM authentication descriptor */ +struct http_request_auth_ntlm { + /** Username */ + const char *username; + /** LAN Manager response */ + struct ntlm_lm_response lm; + /** NT response */ + struct ntlm_nt_response nt; + /** Authenticate message length */ + size_t len; +}; + /** HTTP request authentication descriptor */ struct http_request_auth { /** Authentication scheme (if any) */ @@ -182,6 +195,8 @@ struct http_request_auth { struct http_request_auth_basic basic; /** Digest authentication descriptor */ struct http_request_auth_digest digest; + /** NTLM authentication descriptor */ + struct http_request_auth_ntlm ntlm; }; }; @@ -270,6 +285,14 @@ struct http_response_auth_digest { const char *opaque; }; +/** HTTP response NTLM authorization descriptor */ +struct http_response_auth_ntlm { + /** Challenge message */ + struct ntlm_challenge *challenge; + /** Challenge information */ + struct ntlm_challenge_info info; +}; + /** HTTP response authorization descriptor */ struct http_response_auth { /** Authentication scheme (if any) */ @@ -280,6 +303,8 @@ struct http_response_auth { struct http_response_auth_basic basic; /** Digest authorization descriptor */ struct http_response_auth_digest digest; + /** NTLM authorization descriptor */ + struct http_response_auth_ntlm ntlm; }; }; diff --git a/src/net/tcp/httpntlm.c b/src/net/tcp/httpntlm.c new file mode 100644 index 00000000..00238e96 --- /dev/null +++ b/src/net/tcp/httpntlm.c @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2017 Michael Brown . + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * @file + * + * Hyper Text Transfer Protocol (HTTP) NTLM authentication + * + */ + +#include +#include +#include +#include +#include +#include + +struct http_authentication http_ntlm_auth __http_authentication; + +/** Workstation name used for NTLM authentication */ +static const char http_ntlm_workstation[] = "iPXE"; + +/** + * Parse HTTP "WWW-Authenticate" header for NTLM authentication + * + * @v http HTTP transaction + * @v line Remaining header line + * @ret rc Return status code + */ +static int http_parse_ntlm_auth ( struct http_transaction *http, char *line ) { + struct http_response_auth_ntlm *rsp = &http->response.auth.ntlm; + char *copy; + int len; + int rc; + + /* Create temporary copy of Base64-encoded challenge message */ + copy = strdup ( line ); + if ( ! copy ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Decode challenge message, overwriting the original */ + len = base64_decode ( copy, line, strlen ( line ) ); + if ( len < 0 ) { + rc = len; + DBGC ( http, "HTTP %p could not decode NTLM challenge " + "\"%s\": %s\n", http, copy, strerror ( rc ) ); + goto err_decode; + } + + /* Parse challenge, if present */ + if ( len ) { + rsp->challenge = ( ( void * ) line ); + if ( ( rc = ntlm_challenge ( rsp->challenge, len, + &rsp->info ) ) != 0 ) { + DBGC ( http, "HTTP %p could not parse NTLM challenge: " + "%s\n", http, strerror ( rc ) ); + goto err_challenge; + } + } + + /* Allow HTTP request to be retried if the request had not + * already tried authentication. Note that NTLM requires an + * additional round trip to obtain the challenge message, + * which is not present in the initial WWW-Authenticate. + */ + if ( ( http->request.auth.auth == NULL ) || + ( ( http->request.auth.auth == &http_ntlm_auth ) && + ( http->request.auth.ntlm.len == 0 ) && len ) ) { + http->response.flags |= HTTP_RESPONSE_RETRY; + } + + /* Success */ + rc = 0; + + err_challenge: + err_decode: + free ( copy ); + err_alloc: + return rc; +} + +/** + * Perform HTTP NTLM authentication + * + * @v http HTTP transaction + * @ret rc Return status code + */ +static int http_ntlm_authenticate ( struct http_transaction *http ) { + struct http_request_auth_ntlm *req = &http->request.auth.ntlm; + struct http_response_auth_ntlm *rsp = &http->response.auth.ntlm; + struct ntlm_key key; + const char *password; + + /* If we have no challenge yet, then just send a Negotiate message */ + if ( ! rsp->challenge ) { + DBGC ( http, "HTTP %p sending NTLM Negotiate\n", http ); + return 0; + } + + /* Record username */ + if ( ! http->uri->user ) { + DBGC ( http, "HTTP %p has no username for NTLM " + "authentication\n", http ); + return -EACCES; + } + req->username = http->uri->user; + password = ( http->uri->password ? http->uri->password : "" ); + + /* Generate key */ + ntlm_key ( NULL, req->username, password, &key ); + + /* Generate responses */ + ntlm_response ( &rsp->info, &key, NULL, &req->lm, &req->nt ); + + /* Calculate Authenticate message length */ + req->len = ntlm_authenticate_len ( &rsp->info, NULL, req->username, + http_ntlm_workstation ); + + return 0; +} + +/** + * Construct HTTP "Authorization" header for NTLM authentication + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length of header value, or negative error + */ +static int http_format_ntlm_auth ( struct http_transaction *http, + char *buf, size_t len ) { + struct http_request_auth_ntlm *req = &http->request.auth.ntlm; + struct http_response_auth_ntlm *rsp = &http->response.auth.ntlm; + struct ntlm_authenticate *auth; + size_t check; + + /* If we have no challenge yet, then just send a Negotiate message */ + if ( ! rsp->challenge ) { + return base64_encode ( &ntlm_negotiate, + sizeof ( ntlm_negotiate ), buf, len ); + } + + /* Skip allocation if just calculating length */ + if ( ! len ) + return base64_encoded_len ( req->len ); + + /* Allocate temporary buffer for Authenticate message */ + auth = malloc ( req->len ); + if ( ! auth ) + return -ENOMEM; + + /* Construct raw Authenticate message */ + check = ntlm_authenticate ( &rsp->info, NULL, req->username, + http_ntlm_workstation, &req->lm, + &req->nt, auth ); + assert ( check == req->len ); + + /* Base64-encode Authenticate message */ + len = base64_encode ( auth, req->len, buf, len ); + + /* Free raw Authenticate message */ + free ( auth ); + + return len; +} + +/** HTTP NTLM authentication scheme */ +struct http_authentication http_ntlm_auth __http_authentication = { + .name = "NTLM", + .parse = http_parse_ntlm_auth, + .authenticate = http_ntlm_authenticate, + .format = http_format_ntlm_auth, +}; + +/* Drag in HTTP authentication support */ +REQUIRING_SYMBOL ( http_ntlm_auth ); +REQUIRE_OBJECT ( httpauth ); -- cgit v1.2.3-55-g7522