1 /** 2 * @file digest.c HTTP Digest authentication (RFC 2617) 3 * 4 * Copyright (C) 2010 Creytiv.com 5 */ 6 #include <string.h> 7 #include <re_types.h> 8 #include <re_fmt.h> 9 #include <re_mbuf.h> 10 #include <re_md5.h> 11 #include <re_sys.h> 12 #include <re_httpauth.h> 13 14 15 typedef void (digest_decode_h)(const struct pl *name, const struct pl *val, 16 void *arg); 17 18 19 static const struct pl param_algorithm = PL("algorithm"); 20 static const struct pl param_cnonce = PL("cnonce"); 21 static const struct pl param_nc = PL("nc"); 22 static const struct pl param_nonce = PL("nonce"); 23 static const struct pl param_opaque = PL("opaque"); 24 static const struct pl param_qop = PL("qop"); 25 static const struct pl param_realm = PL("realm"); 26 static const struct pl param_response = PL("response"); 27 static const struct pl param_uri = PL("uri"); 28 static const struct pl param_username = PL("username"); 29 static const struct pl param_stale = PL("stale"); 30 31 32 static void challenge_decode(const struct pl *name, const struct pl *val, 33 void *arg) 34 { 35 struct httpauth_digest_chall *chall = arg; 36 37 if (!pl_casecmp(name, ¶m_realm)) 38 chall->realm = *val; 39 else if (!pl_casecmp(name, ¶m_nonce)) 40 chall->nonce = *val; 41 else if (!pl_casecmp(name, ¶m_opaque)) 42 chall->opaque= *val; 43 else if (!pl_casecmp(name, ¶m_stale)) 44 chall->stale = *val; 45 else if (!pl_casecmp(name, ¶m_algorithm)) 46 chall->algorithm = *val; 47 else if (!pl_casecmp(name, ¶m_qop)) 48 chall->qop = *val; 49 } 50 51 52 static void response_decode(const struct pl *name, const struct pl *val, 53 void *arg) 54 { 55 struct httpauth_digest_resp *resp = arg; 56 57 if (!pl_casecmp(name, ¶m_realm)) 58 resp->realm = *val; 59 else if (!pl_casecmp(name, ¶m_nonce)) 60 resp->nonce = *val; 61 else if (!pl_casecmp(name, ¶m_response)) 62 resp->response = *val; 63 else if (!pl_casecmp(name, ¶m_username)) 64 resp->username = *val; 65 else if (!pl_casecmp(name, ¶m_uri)) 66 resp->uri = *val; 67 else if (!pl_casecmp(name, ¶m_nc)) 68 resp->nc = *val; 69 else if (!pl_casecmp(name, ¶m_cnonce)) 70 resp->cnonce = *val; 71 else if (!pl_casecmp(name, ¶m_qop)) 72 resp->qop = *val; 73 } 74 75 76 static int digest_decode(const struct pl *hval, digest_decode_h *dech, 77 void *arg) 78 { 79 struct pl r = *hval, start, end, name, val; 80 81 if (re_regex(r.p, r.l, "[ \t\r\n]*Digest[ \t\r\n]+", &start, &end) || 82 start.p != r.p) 83 return EBADMSG; 84 85 pl_advance(&r, end.p - r.p); 86 87 while (!re_regex(r.p, r.l, 88 "[ \t\r\n,]+[a-z]+[ \t\r\n]*=[ \t\r\n]*[~ \t\r\n,]*", 89 NULL, &name, NULL, NULL, &val)) { 90 91 pl_advance(&r, val.p + val.l - r.p); 92 93 dech(&name, &val, arg); 94 } 95 96 return 0; 97 } 98 99 100 /** 101 * Decode a Digest challenge 102 * 103 * @param chall Digest challenge object to decode into 104 * @param hval Header value to decode from 105 * 106 * @return 0 if successfully decoded, otherwise errorcode 107 */ 108 int httpauth_digest_challenge_decode(struct httpauth_digest_chall *chall, 109 const struct pl *hval) 110 { 111 int err; 112 113 if (!chall || !hval) 114 return EINVAL; 115 116 memset(chall, 0, sizeof(*chall)); 117 118 err = digest_decode(hval, challenge_decode, chall); 119 if (err) 120 return err; 121 122 if (!chall->realm.p || !chall->nonce.p) 123 return EBADMSG; 124 125 return 0; 126 } 127 128 129 /** 130 * Decode a Digest response 131 * 132 * @param resp Digest response object to decode into 133 * @param hval Header value to decode from 134 * 135 * @return 0 if successfully decoded, otherwise errorcode 136 */ 137 int httpauth_digest_response_decode(struct httpauth_digest_resp *resp, 138 const struct pl *hval) 139 { 140 int err; 141 142 if (!resp || !hval) 143 return EINVAL; 144 145 memset(resp, 0, sizeof(*resp)); 146 147 err = digest_decode(hval, response_decode, resp); 148 if (err) 149 return err; 150 151 if (!resp->realm.p || 152 !resp->nonce.p || 153 !resp->response.p || 154 !resp->username.p || 155 !resp->uri.p) 156 return EBADMSG; 157 158 return 0; 159 } 160 161 162 /** 163 * Authenticate a digest response 164 * 165 * @param resp Digest response 166 * @param method Request method 167 * @param ha1 HA1 value from MD5(username:realm:password) 168 * 169 * @return 0 if successfully authenticated, otherwise errorcode 170 */ 171 int httpauth_digest_response_auth(const struct httpauth_digest_resp *resp, 172 const struct pl *method, const uint8_t *ha1) 173 { 174 uint8_t ha2[MD5_SIZE], digest[MD5_SIZE], response[MD5_SIZE]; 175 const char *p; 176 uint32_t i; 177 int err; 178 179 if (!resp || !method || !ha1) 180 return EINVAL; 181 182 if (resp->response.l != 32) 183 return EAUTH; 184 185 err = md5_printf(ha2, "%r:%r", method, &resp->uri); 186 if (err) 187 return err; 188 189 if (pl_isset(&resp->qop)) 190 err = md5_printf(digest, "%w:%r:%r:%r:%r:%w", 191 ha1, (size_t)MD5_SIZE, 192 &resp->nonce, 193 &resp->nc, 194 &resp->cnonce, 195 &resp->qop, 196 ha2, sizeof(ha2)); 197 else 198 err = md5_printf(digest, "%w:%r:%w", 199 ha1, (size_t)MD5_SIZE, 200 &resp->nonce, 201 ha2, sizeof(ha2)); 202 if (err) 203 return err; 204 205 for (i=0, p=resp->response.p; i<sizeof(response); i++) { 206 response[i] = ch_hex(*p++) << 4; 207 response[i] += ch_hex(*p++); 208 } 209 210 if (memcmp(digest, response, MD5_SIZE)) 211 return EAUTH; 212 213 return 0; 214 } 215