iPXE
httpdigest.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23 
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25 FILE_SECBOOT ( PERMITTED );
26 
27 /**
28  * @file
29  *
30  * Hyper Text Transfer Protocol (HTTP) Digest authentication
31  *
32  */
33 
34 #include <stdio.h>
35 #include <errno.h>
36 #include <strings.h>
37 #include <ipxe/uri.h>
38 #include <ipxe/md5.h>
39 #include <ipxe/base16.h>
40 #include <ipxe/vsprintf.h>
41 #include <ipxe/http.h>
42 
43 /* Disambiguate the various error causes */
44 #define EACCES_USERNAME __einfo_error ( EINFO_EACCES_USERNAME )
45 #define EINFO_EACCES_USERNAME \
46  __einfo_uniqify ( EINFO_EACCES, 0x01, \
47  "No username available for Digest authentication" )
48 
49 /** An HTTP Digest "WWW-Authenticate" response field */
51  /** Name */
52  const char *name;
53  /** Offset */
54  size_t offset;
55 };
56 
57 /** Define an HTTP Digest "WWW-Authenticate" response field */
58 #define HTTP_DIGEST_FIELD( _name ) { \
59  .name = #_name, \
60  .offset = offsetof ( struct http_transaction, \
61  response.auth.digest._name ), \
62  }
63 
64 /**
65  * Set HTTP Digest "WWW-Authenticate" response field value
66  *
67  * @v http HTTP transaction
68  * @v field Response field
69  * @v value Field value
70  */
71 static inline void
73  struct http_digest_field *field, char *value ) {
74  char **ptr;
75 
76  ptr = ( ( ( void * ) http ) + field->offset );
77  *ptr = value;
78 }
79 
80 /** HTTP Digest "WWW-Authenticate" fields */
82  HTTP_DIGEST_FIELD ( realm ),
83  HTTP_DIGEST_FIELD ( qop ),
86  HTTP_DIGEST_FIELD ( opaque ),
87 };
88 
89 /**
90  * Parse HTTP "WWW-Authenticate" header for Digest authentication
91  *
92  * @v http HTTP transaction
93  * @v line Remaining header line
94  * @ret rc Return status code
95  */
96 static int http_parse_digest_auth ( struct http_transaction *http,
97  char *line ) {
98  struct http_digest_field *field;
99  char *key;
100  char *value;
101  unsigned int i;
102 
103  /* Process fields */
104  while ( ( key = http_token ( &line, &value ) ) ) {
105  for ( i = 0 ; i < ( sizeof ( http_digest_fields ) /
106  sizeof ( http_digest_fields[0] ) ) ; i++){
107  field = &http_digest_fields[i];
108  if ( strcasecmp ( key, field->name ) == 0 )
109  http_digest_field ( http, field, value );
110  }
111  }
112 
113  /* Allow HTTP request to be retried if the request had not
114  * already tried authentication.
115  */
116  if ( ! http->request.auth.auth )
118 
119  return 0;
120 }
121 
122 /**
123  * Initialise HTTP Digest
124  *
125  * @v ctx Digest context
126  * @v string Initial string
127  */
128 static void http_digest_init ( struct md5_context *ctx ) {
129 
130  /* Initialise MD5 digest */
132 }
133 
134 /**
135  * Update HTTP Digest with new data
136  *
137  * @v ctx Digest context
138  * @v string String to append
139  */
140 static void http_digest_update ( struct md5_context *ctx, const char *string ) {
141  static const char colon = ':';
142 
143  /* Add (possibly colon-separated) field to MD5 digest */
144  if ( ctx->len )
145  digest_update ( &md5_algorithm, ctx, &colon, sizeof ( colon ) );
146  digest_update ( &md5_algorithm, ctx, string, strlen ( string ) );
147 }
148 
149 /**
150  * Finalise HTTP Digest
151  *
152  * @v ctx Digest context
153  * @v out Buffer for digest output
154  * @v len Buffer length
155  */
156 static void http_digest_final ( struct md5_context *ctx, char *out,
157  size_t len ) {
158  uint8_t digest[MD5_DIGEST_SIZE];
159 
160  /* Finalise and base16-encode MD5 digest */
161  digest_final ( &md5_algorithm, ctx, digest );
162  base16_encode ( digest, sizeof ( digest ), out, len );
163 }
164 
165 /**
166  * Perform HTTP Digest authentication
167  *
168  * @v http HTTP transaction
169  * @ret rc Return status code
170  */
171 static int http_digest_authenticate ( struct http_transaction *http ) {
172  struct http_request_auth_digest *req = &http->request.auth.digest;
174  char ha1[ base16_encoded_len ( MD5_DIGEST_SIZE ) + 1 /* NUL */ ];
175  char ha2[ base16_encoded_len ( MD5_DIGEST_SIZE ) + 1 /* NUL */ ];
176  static const char md5sess[] = "MD5-sess";
177  static const char md5[] = "MD5";
178  struct md5_context ctx;
179  const char *password;
180 
181  /* Check for required response parameters */
182  if ( ! rsp->realm ) {
183  DBGC ( http, "HTTP %p has no realm for Digest authentication\n",
184  http );
185  return -EINVAL;
186  }
187  if ( ! rsp->nonce ) {
188  DBGC ( http, "HTTP %p has no nonce for Digest authentication\n",
189  http );
190  return -EINVAL;
191  }
192 
193  /* Record username and password */
194  if ( ! http->uri->user ) {
195  DBGC ( http, "HTTP %p has no username for Digest "
196  "authentication\n", http );
197  return -EACCES_USERNAME;
198  }
199  req->username = http->uri->user;
200  password = ( http->uri->password ? http->uri->password : "" );
201 
202  /* Handle quality of protection */
203  if ( rsp->qop ) {
204 
205  /* Use "auth" in subsequent request */
206  req->qop = "auth";
207 
208  /* Generate a client nonce */
209  snprintf ( req->cnonce, sizeof ( req->cnonce ),
210  "%08lx", random() );
211 
212  /* Determine algorithm */
213  req->algorithm = md5;
214  if ( rsp->algorithm &&
215  ( strcasecmp ( rsp->algorithm, md5sess ) == 0 ) ) {
216  req->algorithm = md5sess;
217  }
218  }
219 
220  /* Generate HA1 */
221  http_digest_init ( &ctx );
222  http_digest_update ( &ctx, req->username );
223  http_digest_update ( &ctx, rsp->realm );
225  http_digest_final ( &ctx, ha1, sizeof ( ha1 ) );
226  if ( req->algorithm == md5sess ) {
227  http_digest_init ( &ctx );
228  http_digest_update ( &ctx, ha1 );
229  http_digest_update ( &ctx, rsp->nonce );
230  http_digest_update ( &ctx, req->cnonce );
231  http_digest_final ( &ctx, ha1, sizeof ( ha1 ) );
232  }
233 
234  /* Generate HA2 */
235  http_digest_init ( &ctx );
237  http_digest_update ( &ctx, http->request.uri );
238  http_digest_final ( &ctx, ha2, sizeof ( ha2 ) );
239 
240  /* Generate response */
241  http_digest_init ( &ctx );
242  http_digest_update ( &ctx, ha1 );
243  http_digest_update ( &ctx, rsp->nonce );
244  if ( req->qop ) {
246  http_digest_update ( &ctx, req->cnonce );
247  http_digest_update ( &ctx, req->qop );
248  }
249  http_digest_update ( &ctx, ha2 );
250  http_digest_final ( &ctx, req->response, sizeof ( req->response ) );
251 
252  return 0;
253 }
254 
255 /**
256  * Construct HTTP "Authorization" header for Digest authentication
257  *
258  * @v http HTTP transaction
259  * @v buf Buffer
260  * @v len Length of buffer
261  * @ret len Length of header value, or negative error
262  */
263 static int http_format_digest_auth ( struct http_transaction *http,
264  char *buf, size_t len ) {
265  struct http_request_auth_digest *req = &http->request.auth.digest;
267  size_t used = 0;
268 
269  /* Sanity checks */
270  assert ( rsp->realm != NULL );
271  assert ( rsp->nonce != NULL );
272  assert ( req->username != NULL );
273  if ( req->qop ) {
274  assert ( req->algorithm != NULL );
275  assert ( req->cnonce[0] != '\0' );
276  }
277  assert ( req->response[0] != '\0' );
278 
279  /* Construct response */
280  used += ssnprintf ( ( buf + used ), ( len - used ),
281  "realm=\"%s\", nonce=\"%s\", uri=\"%s\", "
282  "username=\"%s\"", rsp->realm, rsp->nonce,
283  http->request.uri, req->username );
284  if ( rsp->opaque ) {
285  used += ssnprintf ( ( buf + used ), ( len - used ),
286  ", opaque=\"%s\"", rsp->opaque );
287  }
288  if ( req->qop ) {
289  used += ssnprintf ( ( buf + used ), ( len - used ),
290  ", qop=%s, algorithm=%s, cnonce=\"%s\", "
291  "nc=" HTTP_DIGEST_NC, req->qop,
292  req->algorithm, req->cnonce );
293  }
294  used += ssnprintf ( ( buf + used ), ( len - used ),
295  ", response=\"%s\"", req->response );
296 
297  return used;
298 }
299 
300 /** HTTP Digest authentication scheme */
301 struct http_authentication http_digest_auth __http_authentication = {
302  .name = "Digest",
303  .parse = http_parse_digest_auth,
304  .authenticate = http_digest_authenticate,
305  .format = http_format_digest_auth,
306 };
307 
308 /* Drag in HTTP authentication support */
309 REQUIRING_SYMBOL ( http_digest_auth );
310 REQUIRE_OBJECT ( httpauth );
const char * qop
Quality of protection.
Definition: http.h:166
#define EINVAL
Invalid argument.
Definition: errno.h:429
static void digest_update(struct digest_algorithm *digest, void *ctx, const void *data, size_t len)
Definition: crypto.h:224
const char * username
Username.
Definition: http.h:164
struct http_response_auth_digest digest
Digest authorization descriptor.
Definition: http.h:304
const char * name
Name.
Definition: httpdigest.c:52
static void http_digest_final(struct md5_context *ctx, char *out, size_t len)
Finalise HTTP Digest.
Definition: httpdigest.c:156
An HTTP Digest "WWW-Authenticate" response field.
Definition: httpdigest.c:50
unsigned int flags
Flags.
Definition: http.h:351
static struct http_digest_field http_digest_fields[]
HTTP Digest "WWW-Authenticate" fields.
Definition: httpdigest.c:81
static int http_digest_authenticate(struct http_transaction *http)
Perform HTTP Digest authentication.
Definition: httpdigest.c:171
Error codes.
static void digest_final(struct digest_algorithm *digest, void *ctx, void *out)
Definition: crypto.h:230
printf() and friends
#define DBGC(...)
Definition: compiler.h:505
struct uri * uri
Request URI.
Definition: http.h:435
int strcasecmp(const char *first, const char *second)
Compare case-insensitive strings.
Definition: string.c:209
An HTTP authentication scheme.
Definition: http.h:519
struct golan_eq_context ctx
Definition: CIB_PRM.h:28
Uniform Resource Identifiers.
struct http_response_auth auth
Authorization descriptor.
Definition: http.h:347
__be32 out[4]
Definition: CIB_PRM.h:36
struct http_request request
Request.
Definition: http.h:437
static size_t base16_encoded_len(size_t raw_len)
Calculate length of base16-encoded data.
Definition: base16.h:25
int ssnprintf(char *buf, ssize_t ssize, const char *fmt,...)
Version of vsnprintf() that accepts a signed buffer size.
Definition: vsprintf.c:421
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
size_t offset
Offset.
Definition: httpdigest.c:54
struct http_response response
Response.
Definition: http.h:439
An HTTP transaction.
Definition: http.h:416
Hyper Text Transport Protocol.
assert((readw(&hdr->flags) &(GTF_reading|GTF_writing))==0)
const char * uri
Request URI string.
Definition: http.h:215
const char * algorithm
Algorithm.
Definition: http.h:168
pseudo_bit_t value[0x00020]
Definition: arbel.h:13
struct http_request_auth auth
Authentication descriptor.
Definition: http.h:223
ring len
Length.
Definition: dwmac.h:231
HTTP request Digest authentication descriptor.
Definition: http.h:162
REQUIRE_OBJECT(httpauth)
const char * name
Method name (e.g.
Definition: http.h:102
static void digest_init(struct digest_algorithm *digest, void *ctx)
Definition: crypto.h:219
uint64_t rsp
Definition: librm.h:153
struct http_authentication * auth
Authentication scheme (if any)
Definition: http.h:190
long int random(void)
Generate a pseudo-random number between 0 and 2147483647L or 2147483562?
Definition: random.c:32
size_t strlen(const char *src)
Get length of string.
Definition: string.c:244
An MD5 context.
Definition: md5.h:59
unsigned char uint8_t
Definition: stdint.h:10
char * http_token(char **line, char **value)
Get HTTP response token.
Definition: httpcore.c:196
static struct dynamic_item password
Definition: login_ui.c:37
u8 nonce[32]
Nonce value.
Definition: wpa.h:52
REQUIRING_SYMBOL(http_digest_auth)
u16 algorithm
Authentication algorithm (Open System or Shared Key)
Definition: ieee80211.h:1030
static int http_format_digest_auth(struct http_transaction *http, char *buf, size_t len)
Construct HTTP "Authorization" header for Digest authentication.
Definition: httpdigest.c:263
struct http_method * method
Method.
Definition: http.h:213
HTTP response Digest authorization descriptor.
Definition: http.h:274
const char * name
Name (e.g.
Definition: http.h:521
const char * password
Password.
Definition: uri.h:75
char cnonce[HTTP_DIGEST_CNONCE_LEN+1]
Client nonce.
Definition: http.h:170
#define MD5_DIGEST_SIZE
MD5 digest size.
Definition: md5.h:73
const char * user
User name.
Definition: uri.h:73
int snprintf(char *buf, size_t size, const char *fmt,...)
Write a formatted string to a buffer.
Definition: vsprintf.c:383
static void http_digest_update(struct md5_context *ctx, const char *string)
Update HTTP Digest with new data.
Definition: httpdigest.c:140
Transaction may be retried on failure.
Definition: http.h:361
FILE_SECBOOT(PERMITTED)
static void http_digest_init(struct md5_context *ctx)
Initialise HTTP Digest.
Definition: httpdigest.c:128
struct http_authentication http_digest_auth __http_authentication
HTTP Digest authentication scheme.
Definition: httpdigest.c:301
MD5 algorithm.
static void http_digest_field(struct http_transaction *http, struct http_digest_field *field, char *value)
Set HTTP Digest "WWW-Authenticate" response field value.
Definition: httpdigest.c:72
static int http_parse_digest_auth(struct http_transaction *http, char *line)
Parse HTTP "WWW-Authenticate" header for Digest authentication.
Definition: httpdigest.c:96
#define NULL
NULL pointer (VOID *)
Definition: Base.h:322
#define EACCES_USERNAME
Definition: httpdigest.c:44
union @391 key
Sense key.
Definition: scsi.h:18
char response[HTTP_DIGEST_RESPONSE_LEN+1]
Response.
Definition: http.h:172
#define HTTP_DIGEST_FIELD(_name)
Define an HTTP Digest "WWW-Authenticate" response field.
Definition: httpdigest.c:58
#define HTTP_DIGEST_NC
HTTP Digest authentication client nonce count.
Definition: http.h:120
struct digest_algorithm md5_algorithm
MD5 algorithm.
Definition: md5.c:287
String functions.
struct http_request_auth_digest digest
Digest authentication descriptor.
Definition: http.h:196
Base16 encoding.