iPXE
httpntlm.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2017 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 /**
00027  * @file
00028  *
00029  * Hyper Text Transfer Protocol (HTTP) NTLM authentication
00030  *
00031  */
00032 
00033 #include <string.h>
00034 #include <errno.h>
00035 #include <ipxe/uri.h>
00036 #include <ipxe/base64.h>
00037 #include <ipxe/ntlm.h>
00038 #include <ipxe/netbios.h>
00039 #include <ipxe/http.h>
00040 
00041 struct http_authentication http_ntlm_auth __http_authentication;
00042 
00043 /** Workstation name used for NTLM authentication */
00044 static const char http_ntlm_workstation[] = "iPXE";
00045 
00046 /**
00047  * Parse HTTP "WWW-Authenticate" header for NTLM authentication
00048  *
00049  * @v http              HTTP transaction
00050  * @v line              Remaining header line
00051  * @ret rc              Return status code
00052  */
00053 static int http_parse_ntlm_auth ( struct http_transaction *http, char *line ) {
00054         struct http_response_auth_ntlm *rsp = &http->response.auth.ntlm;
00055         char *copy;
00056         int len;
00057         int rc;
00058 
00059         /* Create temporary copy of Base64-encoded challenge message */
00060         copy = strdup ( line );
00061         if ( ! copy ) {
00062                 rc = -ENOMEM;
00063                 goto err_alloc;
00064         }
00065 
00066         /* Decode challenge message, overwriting the original */
00067         len = base64_decode ( copy, line, strlen ( line ) );
00068         if ( len < 0 ) {
00069                 rc = len;
00070                 DBGC ( http, "HTTP %p could not decode NTLM challenge "
00071                        "\"%s\": %s\n", http, copy, strerror ( rc ) );
00072                 goto err_decode;
00073         }
00074 
00075         /* Parse challenge, if present */
00076         if ( len ) {
00077                 rsp->challenge = ( ( void * ) line );
00078                 if ( ( rc = ntlm_challenge ( rsp->challenge, len,
00079                                              &rsp->info ) ) != 0 ) {
00080                         DBGC ( http, "HTTP %p could not parse NTLM challenge: "
00081                                "%s\n", http, strerror ( rc ) );
00082                         goto err_challenge;
00083                 }
00084         }
00085 
00086         /* Allow HTTP request to be retried if the request had not
00087          * already tried authentication.  Note that NTLM requires an
00088          * additional round trip to obtain the challenge message,
00089          * which is not present in the initial WWW-Authenticate.
00090          */
00091         if ( ( http->request.auth.auth == NULL ) ||
00092              ( ( http->request.auth.auth == &http_ntlm_auth ) &&
00093                ( http->request.auth.ntlm.len == 0 ) && len ) ) {
00094                 http->response.flags |= HTTP_RESPONSE_RETRY;
00095         }
00096 
00097         /* Success */
00098         rc = 0;
00099 
00100  err_challenge:
00101  err_decode:
00102         free ( copy );
00103  err_alloc:
00104         return rc;
00105 }
00106 
00107 /**
00108  * Perform HTTP NTLM authentication
00109  *
00110  * @v http              HTTP transaction
00111  * @ret rc              Return status code
00112  */
00113 static int http_ntlm_authenticate ( struct http_transaction *http ) {
00114         struct http_request_auth_ntlm *req = &http->request.auth.ntlm;
00115         struct http_response_auth_ntlm *rsp = &http->response.auth.ntlm;
00116         struct ntlm_key key;
00117         const char *domain;
00118         char *username;
00119         const char *password;
00120 
00121         /* If we have no challenge yet, then just send a Negotiate message */
00122         if ( ! rsp->challenge ) {
00123                 DBGC ( http, "HTTP %p sending NTLM Negotiate\n", http );
00124                 return 0;
00125         }
00126 
00127         /* Record username */
00128         if ( ! http->uri->user ) {
00129                 DBGC ( http, "HTTP %p has no username for NTLM "
00130                        "authentication\n", http );
00131                 return -EACCES;
00132         }
00133         req->username = http->uri->user;
00134         password = ( http->uri->password ? http->uri->password : "" );
00135 
00136         /* Split NetBIOS [domain\]username */
00137         username = ( ( char * ) req->username );
00138         domain = netbios_domain ( &username );
00139 
00140         /* Generate key */
00141         ntlm_key ( domain, username, password, &key );
00142 
00143         /* Generate responses */
00144         ntlm_response ( &rsp->info, &key, NULL, &req->lm, &req->nt );
00145 
00146         /* Calculate Authenticate message length */
00147         req->len = ntlm_authenticate_len ( &rsp->info, domain, username,
00148                                            http_ntlm_workstation );
00149 
00150         /* Restore NetBIOS [domain\]username */
00151         netbios_domain_undo ( domain, username );
00152 
00153         return 0;
00154 }
00155 
00156 /**
00157  * Construct HTTP "Authorization" header for NTLM authentication
00158  *
00159  * @v http              HTTP transaction
00160  * @v buf               Buffer
00161  * @v len               Length of buffer
00162  * @ret len             Length of header value, or negative error
00163  */
00164 static int http_format_ntlm_auth ( struct http_transaction *http,
00165                                    char *buf, size_t len ) {
00166         struct http_request_auth_ntlm *req = &http->request.auth.ntlm;
00167         struct http_response_auth_ntlm *rsp = &http->response.auth.ntlm;
00168         struct ntlm_authenticate *auth;
00169         const char *domain;
00170         char *username;
00171         size_t check;
00172 
00173         /* If we have no challenge yet, then just send a Negotiate message */
00174         if ( ! rsp->challenge ) {
00175                 return base64_encode ( &ntlm_negotiate,
00176                                        sizeof ( ntlm_negotiate ), buf, len );
00177         }
00178 
00179         /* Skip allocation if just calculating length */
00180         if ( ! len )
00181                 return base64_encoded_len ( req->len );
00182 
00183         /* Allocate temporary buffer for Authenticate message */
00184         auth = malloc ( req->len );
00185         if ( ! auth )
00186                 return -ENOMEM;
00187 
00188         /* Split NetBIOS [domain\]username */
00189         username = ( ( char * ) req->username );
00190         domain = netbios_domain ( &username );
00191 
00192         /* Construct raw Authenticate message */
00193         check = ntlm_authenticate ( &rsp->info, domain, username,
00194                                     http_ntlm_workstation, &req->lm,
00195                                     &req->nt, auth );
00196         assert ( check == req->len );
00197 
00198         /* Restore NetBIOS [domain\]username */
00199         netbios_domain_undo ( domain, username );
00200 
00201         /* Base64-encode Authenticate message */
00202         len = base64_encode ( auth, req->len, buf, len );
00203 
00204         /* Free raw Authenticate message */
00205         free ( auth );
00206 
00207         return len;
00208 }
00209 
00210 /** HTTP NTLM authentication scheme */
00211 struct http_authentication http_ntlm_auth __http_authentication = {
00212         .name = "NTLM",
00213         .parse = http_parse_ntlm_auth,
00214         .authenticate = http_ntlm_authenticate,
00215         .format = http_format_ntlm_auth,
00216 };
00217 
00218 /* Drag in HTTP authentication support */
00219 REQUIRING_SYMBOL ( http_ntlm_auth );
00220 REQUIRE_OBJECT ( httpauth );