Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

httpdigest.c 6.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  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. FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
  24. /**
  25. * @file
  26. *
  27. * Hyper Text Transfer Protocol (HTTP) Digest authentication
  28. *
  29. */
  30. #include <stdio.h>
  31. #include <errno.h>
  32. #include <strings.h>
  33. #include <ipxe/uri.h>
  34. #include <ipxe/md5.h>
  35. #include <ipxe/base16.h>
  36. #include <ipxe/vsprintf.h>
  37. #include <ipxe/http.h>
  38. /* Disambiguate the various error causes */
  39. #define EACCES_USERNAME __einfo_error ( EINFO_EACCES_USERNAME )
  40. #define EINFO_EACCES_USERNAME \
  41. __einfo_uniqify ( EINFO_EACCES, 0x01, \
  42. "No username available for Digest authentication" )
  43. /**
  44. * Initialise HTTP Digest
  45. *
  46. * @v ctx Digest context
  47. * @v string Initial string
  48. */
  49. static void http_digest_init ( struct md5_context *ctx ) {
  50. /* Initialise MD5 digest */
  51. digest_init ( &md5_algorithm, ctx );
  52. }
  53. /**
  54. * Update HTTP Digest with new data
  55. *
  56. * @v ctx Digest context
  57. * @v string String to append
  58. */
  59. static void http_digest_update ( struct md5_context *ctx, const char *string ) {
  60. static const char colon = ':';
  61. /* Add (possibly colon-separated) field to MD5 digest */
  62. if ( ctx->len )
  63. digest_update ( &md5_algorithm, ctx, &colon, sizeof ( colon ) );
  64. digest_update ( &md5_algorithm, ctx, string, strlen ( string ) );
  65. }
  66. /**
  67. * Finalise HTTP Digest
  68. *
  69. * @v ctx Digest context
  70. * @v out Buffer for digest output
  71. * @v len Buffer length
  72. */
  73. static void http_digest_final ( struct md5_context *ctx, char *out,
  74. size_t len ) {
  75. uint8_t digest[MD5_DIGEST_SIZE];
  76. /* Finalise and base16-encode MD5 digest */
  77. digest_final ( &md5_algorithm, ctx, digest );
  78. base16_encode ( digest, sizeof ( digest ), out, len );
  79. }
  80. /**
  81. * Perform HTTP Digest authentication
  82. *
  83. * @v http HTTP transaction
  84. * @ret rc Return status code
  85. */
  86. static int http_digest_authenticate ( struct http_transaction *http ) {
  87. struct http_request_auth *req = &http->request.auth;
  88. struct http_response_auth *rsp = &http->response.auth;
  89. char ha1[ base16_encoded_len ( MD5_DIGEST_SIZE ) + 1 /* NUL */ ];
  90. char ha2[ base16_encoded_len ( MD5_DIGEST_SIZE ) + 1 /* NUL */ ];
  91. static const char md5sess[] = "MD5-sess";
  92. static const char md5[] = "MD5";
  93. struct md5_context ctx;
  94. /* Check for required response parameters */
  95. if ( ! rsp->realm ) {
  96. DBGC ( http, "HTTP %p has no realm for Digest authentication\n",
  97. http );
  98. return -EINVAL;
  99. }
  100. if ( ! rsp->nonce ) {
  101. DBGC ( http, "HTTP %p has no nonce for Digest authentication\n",
  102. http );
  103. return -EINVAL;
  104. }
  105. /* Record username and password */
  106. if ( ! http->uri->user ) {
  107. DBGC ( http, "HTTP %p has no username for Digest "
  108. "authentication\n", http );
  109. return -EACCES_USERNAME;
  110. }
  111. req->username = http->uri->user;
  112. req->password = ( http->uri->password ? http->uri->password : "" );
  113. /* Handle quality of protection */
  114. if ( rsp->qop ) {
  115. /* Use "auth" in subsequent request */
  116. req->qop = "auth";
  117. /* Generate a client nonce */
  118. snprintf ( req->cnonce, sizeof ( req->cnonce ),
  119. "%08lx", random() );
  120. /* Determine algorithm */
  121. req->algorithm = md5;
  122. if ( rsp->algorithm &&
  123. ( strcasecmp ( rsp->algorithm, md5sess ) == 0 ) ) {
  124. req->algorithm = md5sess;
  125. }
  126. }
  127. /* Generate HA1 */
  128. http_digest_init ( &ctx );
  129. http_digest_update ( &ctx, req->username );
  130. http_digest_update ( &ctx, rsp->realm );
  131. http_digest_update ( &ctx, req->password );
  132. http_digest_final ( &ctx, ha1, sizeof ( ha1 ) );
  133. if ( req->algorithm == md5sess ) {
  134. http_digest_init ( &ctx );
  135. http_digest_update ( &ctx, ha1 );
  136. http_digest_update ( &ctx, rsp->nonce );
  137. http_digest_update ( &ctx, req->cnonce );
  138. http_digest_final ( &ctx, ha1, sizeof ( ha1 ) );
  139. }
  140. /* Generate HA2 */
  141. http_digest_init ( &ctx );
  142. http_digest_update ( &ctx, http->request.method->name );
  143. http_digest_update ( &ctx, http->request.uri );
  144. http_digest_final ( &ctx, ha2, sizeof ( ha2 ) );
  145. /* Generate response */
  146. http_digest_init ( &ctx );
  147. http_digest_update ( &ctx, ha1 );
  148. http_digest_update ( &ctx, rsp->nonce );
  149. if ( req->qop ) {
  150. http_digest_update ( &ctx, HTTP_DIGEST_NC );
  151. http_digest_update ( &ctx, req->cnonce );
  152. http_digest_update ( &ctx, req->qop );
  153. }
  154. http_digest_update ( &ctx, ha2 );
  155. http_digest_final ( &ctx, req->response, sizeof ( req->response ) );
  156. return 0;
  157. }
  158. /**
  159. * Construct HTTP "Authorization" header for Digest authentication
  160. *
  161. * @v http HTTP transaction
  162. * @v buf Buffer
  163. * @v len Length of buffer
  164. * @ret len Length of header value, or negative error
  165. */
  166. static int http_format_digest_auth ( struct http_transaction *http,
  167. char *buf, size_t len ) {
  168. struct http_request_auth *req = &http->request.auth;
  169. struct http_response_auth *rsp = &http->response.auth;
  170. size_t used = 0;
  171. /* Sanity checks */
  172. assert ( rsp->realm != NULL );
  173. assert ( rsp->nonce != NULL );
  174. assert ( req->username != NULL );
  175. if ( req->qop ) {
  176. assert ( req->algorithm != NULL );
  177. assert ( req->cnonce[0] != '\0' );
  178. }
  179. assert ( req->response[0] != '\0' );
  180. /* Construct response */
  181. used += ssnprintf ( ( buf + used ), ( len - used ),
  182. "realm=\"%s\", nonce=\"%s\", uri=\"%s\", "
  183. "username=\"%s\"", rsp->realm, rsp->nonce,
  184. http->request.uri, req->username );
  185. if ( rsp->opaque ) {
  186. used += ssnprintf ( ( buf + used ), ( len - used ),
  187. ", opaque=\"%s\"", rsp->opaque );
  188. }
  189. if ( req->qop ) {
  190. used += ssnprintf ( ( buf + used ), ( len - used ),
  191. ", qop=%s, algorithm=%s, cnonce=\"%s\", "
  192. "nc=" HTTP_DIGEST_NC, req->qop,
  193. req->algorithm, req->cnonce );
  194. }
  195. used += ssnprintf ( ( buf + used ), ( len - used ),
  196. ", response=\"%s\"", req->response );
  197. return used;
  198. }
  199. /** HTTP Digest authentication scheme */
  200. struct http_authentication http_digest_auth __http_authentication = {
  201. .name = "Digest",
  202. .authenticate = http_digest_authenticate,
  203. .format = http_format_digest_auth,
  204. };
  205. /* Drag in HTTP authentication support */
  206. REQUIRING_SYMBOL ( http_digest_auth );
  207. REQUIRE_OBJECT ( httpauth );