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 
26 /**
27  * @file
28  *
29  * Hyper Text Transfer Protocol (HTTP) Digest authentication
30  *
31  */
32 
33 #include <stdio.h>
34 #include <errno.h>
35 #include <strings.h>
36 #include <ipxe/uri.h>
37 #include <ipxe/md5.h>
38 #include <ipxe/base16.h>
39 #include <ipxe/vsprintf.h>
40 #include <ipxe/http.h>
41 
42 /* Disambiguate the various error causes */
43 #define EACCES_USERNAME __einfo_error ( EINFO_EACCES_USERNAME )
44 #define EINFO_EACCES_USERNAME \
45  __einfo_uniqify ( EINFO_EACCES, 0x01, \
46  "No username available for Digest authentication" )
47 
48 /** An HTTP Digest "WWW-Authenticate" response field */
50  /** Name */
51  const char *name;
52  /** Offset */
53  size_t offset;
54 };
55 
56 /** Define an HTTP Digest "WWW-Authenticate" response field */
57 #define HTTP_DIGEST_FIELD( _name ) { \
58  .name = #_name, \
59  .offset = offsetof ( struct http_transaction, \
60  response.auth.digest._name ), \
61  }
62 
63 /**
64  * Set HTTP Digest "WWW-Authenticate" response field value
65  *
66  * @v http HTTP transaction
67  * @v field Response field
68  * @v value Field value
69  */
70 static inline void
72  struct http_digest_field *field, char *value ) {
73  char **ptr;
74 
75  ptr = ( ( ( void * ) http ) + field->offset );
76  *ptr = value;
77 }
78 
79 /** HTTP Digest "WWW-Authenticate" fields */
81  HTTP_DIGEST_FIELD ( realm ),
82  HTTP_DIGEST_FIELD ( qop ),
85  HTTP_DIGEST_FIELD ( opaque ),
86 };
87 
88 /**
89  * Parse HTTP "WWW-Authenticate" header for Digest authentication
90  *
91  * @v http HTTP transaction
92  * @v line Remaining header line
93  * @ret rc Return status code
94  */
95 static int http_parse_digest_auth ( struct http_transaction *http,
96  char *line ) {
97  struct http_digest_field *field;
98  char *key;
99  char *value;
100  unsigned int i;
101 
102  /* Process fields */
103  while ( ( key = http_token ( &line, &value ) ) ) {
104  for ( i = 0 ; i < ( sizeof ( http_digest_fields ) /
105  sizeof ( http_digest_fields[0] ) ) ; i++){
106  field = &http_digest_fields[i];
107  if ( strcasecmp ( key, field->name ) == 0 )
108  http_digest_field ( http, field, value );
109  }
110  }
111 
112  /* Allow HTTP request to be retried if the request had not
113  * already tried authentication.
114  */
115  if ( ! http->request.auth.auth )
117 
118  return 0;
119 }
120 
121 /**
122  * Initialise HTTP Digest
123  *
124  * @v ctx Digest context
125  * @v string Initial string
126  */
127 static void http_digest_init ( struct md5_context *ctx ) {
128 
129  /* Initialise MD5 digest */
130  digest_init ( &md5_algorithm, ctx );
131 }
132 
133 /**
134  * Update HTTP Digest with new data
135  *
136  * @v ctx Digest context
137  * @v string String to append
138  */
139 static void http_digest_update ( struct md5_context *ctx, const char *string ) {
140  static const char colon = ':';
141 
142  /* Add (possibly colon-separated) field to MD5 digest */
143  if ( ctx->len )
144  digest_update ( &md5_algorithm, ctx, &colon, sizeof ( colon ) );
145  digest_update ( &md5_algorithm, ctx, string, strlen ( string ) );
146 }
147 
148 /**
149  * Finalise HTTP Digest
150  *
151  * @v ctx Digest context
152  * @v out Buffer for digest output
153  * @v len Buffer length
154  */
155 static void http_digest_final ( struct md5_context *ctx, char *out,
156  size_t len ) {
158 
159  /* Finalise and base16-encode MD5 digest */
160  digest_final ( &md5_algorithm, ctx, digest );
161  base16_encode ( digest, sizeof ( digest ), out, len );
162 }
163 
164 /**
165  * Perform HTTP Digest authentication
166  *
167  * @v http HTTP transaction
168  * @ret rc Return status code
169  */
170 static int http_digest_authenticate ( struct http_transaction *http ) {
171  struct http_request_auth_digest *req = &http->request.auth.digest;
173  char ha1[ base16_encoded_len ( MD5_DIGEST_SIZE ) + 1 /* NUL */ ];
174  char ha2[ base16_encoded_len ( MD5_DIGEST_SIZE ) + 1 /* NUL */ ];
175  static const char md5sess[] = "MD5-sess";
176  static const char md5[] = "MD5";
177  struct md5_context ctx;
178  const char *password;
179 
180  /* Check for required response parameters */
181  if ( ! rsp->realm ) {
182  DBGC ( http, "HTTP %p has no realm for Digest authentication\n",
183  http );
184  return -EINVAL;
185  }
186  if ( ! rsp->nonce ) {
187  DBGC ( http, "HTTP %p has no nonce for Digest authentication\n",
188  http );
189  return -EINVAL;
190  }
191 
192  /* Record username and password */
193  if ( ! http->uri->user ) {
194  DBGC ( http, "HTTP %p has no username for Digest "
195  "authentication\n", http );
196  return -EACCES_USERNAME;
197  }
198  req->username = http->uri->user;
199  password = ( http->uri->password ? http->uri->password : "" );
200 
201  /* Handle quality of protection */
202  if ( rsp->qop ) {
203 
204  /* Use "auth" in subsequent request */
205  req->qop = "auth";
206 
207  /* Generate a client nonce */
208  snprintf ( req->cnonce, sizeof ( req->cnonce ),
209  "%08lx", random() );
210 
211  /* Determine algorithm */
212  req->algorithm = md5;
213  if ( rsp->algorithm &&
214  ( strcasecmp ( rsp->algorithm, md5sess ) == 0 ) ) {
215  req->algorithm = md5sess;
216  }
217  }
218 
219  /* Generate HA1 */
220  http_digest_init ( &ctx );
221  http_digest_update ( &ctx, req->username );
222  http_digest_update ( &ctx, rsp->realm );
223  http_digest_update ( &ctx, password );
224  http_digest_final ( &ctx, ha1, sizeof ( ha1 ) );
225  if ( req->algorithm == md5sess ) {
226  http_digest_init ( &ctx );
227  http_digest_update ( &ctx, ha1 );
228  http_digest_update ( &ctx, rsp->nonce );
229  http_digest_update ( &ctx, req->cnonce );
230  http_digest_final ( &ctx, ha1, sizeof ( ha1 ) );
231  }
232 
233  /* Generate HA2 */
234  http_digest_init ( &ctx );
236  http_digest_update ( &ctx, http->request.uri );
237  http_digest_final ( &ctx, ha2, sizeof ( ha2 ) );
238 
239  /* Generate response */
240  http_digest_init ( &ctx );
241  http_digest_update ( &ctx, ha1 );
242  http_digest_update ( &ctx, rsp->nonce );
243  if ( req->qop ) {
245  http_digest_update ( &ctx, req->cnonce );
246  http_digest_update ( &ctx, req->qop );
247  }
248  http_digest_update ( &ctx, ha2 );
249  http_digest_final ( &ctx, req->response, sizeof ( req->response ) );
250 
251  return 0;
252 }
253 
254 /**
255  * Construct HTTP "Authorization" header for Digest authentication
256  *
257  * @v http HTTP transaction
258  * @v buf Buffer
259  * @v len Length of buffer
260  * @ret len Length of header value, or negative error
261  */
262 static int http_format_digest_auth ( struct http_transaction *http,
263  char *buf, size_t len ) {
264  struct http_request_auth_digest *req = &http->request.auth.digest;
266  size_t used = 0;
267 
268  /* Sanity checks */
269  assert ( rsp->realm != NULL );
270  assert ( rsp->nonce != NULL );
271  assert ( req->username != NULL );
272  if ( req->qop ) {
273  assert ( req->algorithm != NULL );
274  assert ( req->cnonce[0] != '\0' );
275  }
276  assert ( req->response[0] != '\0' );
277 
278  /* Construct response */
279  used += ssnprintf ( ( buf + used ), ( len - used ),
280  "realm=\"%s\", nonce=\"%s\", uri=\"%s\", "
281  "username=\"%s\"", rsp->realm, rsp->nonce,
282  http->request.uri, req->username );
283  if ( rsp->opaque ) {
284  used += ssnprintf ( ( buf + used ), ( len - used ),
285  ", opaque=\"%s\"", rsp->opaque );
286  }
287  if ( req->qop ) {
288  used += ssnprintf ( ( buf + used ), ( len - used ),
289  ", qop=%s, algorithm=%s, cnonce=\"%s\", "
290  "nc=" HTTP_DIGEST_NC, req->qop,
291  req->algorithm, req->cnonce );
292  }
293  used += ssnprintf ( ( buf + used ), ( len - used ),
294  ", response=\"%s\"", req->response );
295 
296  return used;
297 }
298 
299 /** HTTP Digest authentication scheme */
300 struct http_authentication http_digest_auth __http_authentication = {
301  .name = "Digest",
302  .parse = http_parse_digest_auth,
303  .authenticate = http_digest_authenticate,
304  .format = http_format_digest_auth,
305 };
306 
307 /* Drag in HTTP authentication support */
308 REQUIRING_SYMBOL ( http_digest_auth );
309 REQUIRE_OBJECT ( httpauth );
const char * qop
Quality of protection.
Definition: http.h:165
#define EINVAL
Invalid argument.
Definition: errno.h:428
const char * username
Username.
Definition: http.h:163
struct http_response_auth_digest digest
Digest authorization descriptor.
Definition: http.h:303
const char * name
Name.
Definition: httpdigest.c:51
static void http_digest_final(struct md5_context *ctx, char *out, size_t len)
Finalise HTTP Digest.
Definition: httpdigest.c:155
An HTTP Digest "WWW-Authenticate" response field.
Definition: httpdigest.c:49
unsigned int flags
Flags.
Definition: http.h:350
static struct http_digest_field http_digest_fields[]
HTTP Digest "WWW-Authenticate" fields.
Definition: httpdigest.c:80
static int http_digest_authenticate(struct http_transaction *http)
Perform HTTP Digest authentication.
Definition: httpdigest.c:170
Error codes.
printf() and friends
#define DBGC(...)
Definition: compiler.h:505
struct uri * uri
Request URI.
Definition: http.h:432
int strcasecmp(const char *first, const char *second)
Compare case-insensitive strings.
Definition: string.c:208
An HTTP authentication scheme.
Definition: http.h:516
Uniform Resource Identifiers.
struct http_response_auth auth
Authorization descriptor.
Definition: http.h:346
__be32 out[4]
Definition: CIB_PRM.h:36
struct http_request request
Request.
Definition: http.h:434
static size_t base16_encoded_len(size_t raw_len)
Calculate length of base16-encoded data.
Definition: base16.h:24
int ssnprintf(char *buf, ssize_t ssize, const char *fmt,...)
Version of vsnprintf() that accepts a signed buffer size.
Definition: vsprintf.c:420
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
size_t offset
Offset.
Definition: httpdigest.c:53
struct http_response response
Response.
Definition: http.h:436
An HTTP transaction.
Definition: http.h:415
Hyper Text Transport Protocol.
assert((readw(&hdr->flags) &(GTF_reading|GTF_writing))==0)
const char * uri
Request URI string.
Definition: http.h:214
const char * algorithm
Algorithm.
Definition: http.h:167
static void struct digest_algorithm * digest
HMAC-MD5 digest.
Definition: crypto.h:308
struct http_request_auth auth
Authentication descriptor.
Definition: http.h:222
HTTP request Digest authentication descriptor.
Definition: http.h:161
REQUIRE_OBJECT(httpauth)
const char * name
Method name (e.g.
Definition: http.h:101
pseudo_bit_t value[0x00020]
Definition: arbel.h:13
uint64_t rsp
Definition: librm.h:267
struct http_authentication * auth
Authentication scheme (if any)
Definition: http.h:189
struct golan_eq_context ctx
Definition: CIB_PRM.h:28
long int random(void)
Generate a pseudo-random number between 0 and 2147483647L or 2147483562?
Definition: random.c:31
size_t strlen(const char *src)
Get length of string.
Definition: string.c:243
An MD5 context.
Definition: md5.h:58
unsigned char uint8_t
Definition: stdint.h:10
char * http_token(char **line, char **value)
Get HTTP response token.
Definition: httpcore.c:191
REQUIRING_SYMBOL(http_digest_auth)
u16 algorithm
Authentication algorithm (Open System or Shared Key)
Definition: ieee80211.h:1030
uint32_t len
Length.
Definition: ena.h:14
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:262
struct http_method * method
Method.
Definition: http.h:212
HTTP response Digest authorization descriptor.
Definition: http.h:273
const char * name
Name (e.g.
Definition: http.h:518
const char * password
Password.
Definition: uri.h:74
char cnonce[HTTP_DIGEST_CNONCE_LEN+1]
Client nonce.
Definition: http.h:169
#define MD5_DIGEST_SIZE
MD5 digest size.
Definition: md5.h:72
const char * user
User name.
Definition: uri.h:72
int snprintf(char *buf, size_t size, const char *fmt,...)
Write a formatted string to a buffer.
Definition: vsprintf.c:382
static void http_digest_update(struct md5_context *ctx, const char *string)
Update HTTP Digest with new data.
Definition: httpdigest.c:139
Transaction may be retried on failure.
Definition: http.h:360
static void http_digest_init(struct md5_context *ctx)
Initialise HTTP Digest.
Definition: httpdigest.c:127
struct http_authentication http_digest_auth __http_authentication
HTTP Digest authentication scheme.
Definition: httpdigest.c:300
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:71
static int http_parse_digest_auth(struct http_transaction *http, char *line)
Parse HTTP "WWW-Authenticate" header for Digest authentication.
Definition: httpdigest.c:95
#define NULL
NULL pointer (VOID *)
Definition: Base.h:321
struct ntlm_nonce nonce
Server nonce.
Definition: ntlm.h:18
#define EACCES_USERNAME
Definition: httpdigest.c:43
union @382 key
Sense key.
Definition: crypto.h:284
char response[HTTP_DIGEST_RESPONSE_LEN+1]
Response.
Definition: http.h:171
#define HTTP_DIGEST_FIELD(_name)
Define an HTTP Digest "WWW-Authenticate" response field.
Definition: httpdigest.c:57
#define HTTP_DIGEST_NC
HTTP Digest authentication client nonce count.
Definition: http.h:119
struct digest_algorithm md5_algorithm
MD5 algorithm.
Definition: md5.c:286
String functions.
struct http_request_auth_digest digest
Digest authentication descriptor.
Definition: http.h:195
Base16 encoding.