| 1 | #include <stdlib.h> |
| 2 | #include <stdio.h> |
| 3 | #include <stdint.h> |
| 4 | #include <string.h> |
| 5 | |
| 6 | #include "compat.h" |
| 7 | #include "utils.h" |
| 8 | #include "crypto/crypto.h" |
| 9 | |
| 10 | void |
| 11 | digest_md5_to_hex(const unsigned char *md5buf, char *md5hex) |
| 12 | { |
| 13 | int i; |
| 14 | for (i=0; i<MD5_SIZE*2; i++) { |
| 15 | int val = (i%2) ? md5buf[i/2]&0x0f : (md5buf[i/2]&0xf0)>>4; |
| 16 | md5hex[i] = (val<10) ? '0'+val : 'a'+(val-10); |
| 17 | } |
| 18 | } |
| 19 | |
| 20 | void |
| 21 | digest_get_response(const char *username, const char *realm, |
| 22 | const char *password, const char *nonce, |
| 23 | const char *method, const char *uri, |
| 24 | char *response) |
| 25 | { |
| 26 | MD5_CTX md5ctx; |
| 27 | unsigned char md5buf[MD5_SIZE]; |
| 28 | char md5hex[MD5_SIZE*2]; |
| 29 | |
| 30 | /* Calculate first inner MD5 hash */ |
| 31 | MD5_Init(&md5ctx); |
| 32 | MD5_Update(&md5ctx, (const unsigned char *)username, strlen(username)); |
| 33 | MD5_Update(&md5ctx, (const unsigned char *)":", 1); |
| 34 | MD5_Update(&md5ctx, (const unsigned char *)realm, strlen(realm)); |
| 35 | MD5_Update(&md5ctx, (const unsigned char *)":", 1); |
| 36 | MD5_Update(&md5ctx, (const unsigned char *)password, strlen(password)); |
| 37 | MD5_Final(md5buf, &md5ctx); |
| 38 | digest_md5_to_hex(md5buf, md5hex); |
| 39 | |
| 40 | /* Calculate second inner MD5 hash */ |
| 41 | MD5_Init(&md5ctx); |
| 42 | MD5_Update(&md5ctx, (const unsigned char *)method, strlen(method)); |
| 43 | MD5_Update(&md5ctx, (const unsigned char *)":", 1); |
| 44 | MD5_Update(&md5ctx, (const unsigned char *)uri, strlen(uri)); |
| 45 | MD5_Final(md5buf, &md5ctx); |
| 46 | |
| 47 | /* Calculate outer MD5 hash */ |
| 48 | MD5_Init(&md5ctx); |
| 49 | MD5_Update(&md5ctx, (const unsigned char *)md5hex, sizeof(md5hex)); |
| 50 | MD5_Update(&md5ctx, (const unsigned char *)":", 1); |
| 51 | MD5_Update(&md5ctx, (const unsigned char *)nonce, strlen(nonce)); |
| 52 | MD5_Update(&md5ctx, (const unsigned char *)":", 1); |
| 53 | digest_md5_to_hex(md5buf, md5hex); |
| 54 | MD5_Update(&md5ctx, (const unsigned char *)md5hex, sizeof(md5hex)); |
| 55 | MD5_Final(md5buf, &md5ctx); |
| 56 | |
| 57 | /* Store the final result to response */ |
| 58 | digest_md5_to_hex(md5buf, response); |
| 59 | } |
| 60 | |
| 61 | void |
| 62 | digest_generate_nonce(char *result, int resultlen) |
| 63 | { |
| 64 | MD5_CTX md5ctx; |
| 65 | unsigned char md5buf[MD5_SIZE]; |
| 66 | char md5hex[MD5_SIZE*2]; |
| 67 | unsigned int time; |
| 68 | |
| 69 | SYSTEM_GET_TIME(time); |
| 70 | |
| 71 | MD5_Init(&md5ctx); |
| 72 | MD5_Update(&md5ctx, (unsigned char *)&time, sizeof(time)); |
| 73 | MD5_Final(md5buf, &md5ctx); |
| 74 | digest_md5_to_hex(md5buf, md5hex); |
| 75 | |
| 76 | memset(result, 0, resultlen); |
| 77 | strncpy(result, md5hex, resultlen-1); |
| 78 | } |
| 79 | |
| 80 | int |
| 81 | digest_is_valid(const char *our_realm, const char *password, |
| 82 | const char *our_nonce, const char *method, |
| 83 | const char *our_uri, const char *authorization) |
| 84 | { |
| 85 | char *auth; |
| 86 | char *current; |
| 87 | char *value; |
| 88 | int success; |
| 89 | |
| 90 | /* Get values from authorization */ |
| 91 | char *username = NULL; |
| 92 | char *realm = NULL; |
| 93 | char *nonce = NULL; |
| 94 | char *uri = NULL; |
| 95 | char *response = NULL; |
| 96 | |
| 97 | /* Buffer for our response */ |
| 98 | char our_response[MD5_SIZE*2+1]; |
| 99 | |
| 100 | if (!authorization) { |
| 101 | return 0; |
| 102 | } |
| 103 | current = auth = strdup(authorization); |
| 104 | if (!auth) { |
| 105 | return 0; |
| 106 | } |
| 107 | |
| 108 | /* Check that the type is digest */ |
| 109 | if (strncmp("Digest", current, 6)) { |
| 110 | free(auth); |
| 111 | return 0; |
| 112 | } |
| 113 | current += 6; |
| 114 | |
| 115 | while ((value = utils_strsep(¤t, ",")) != NULL) { |
| 116 | char *first, *last; |
| 117 | |
| 118 | /* Find first and last characters */ |
| 119 | first = value; |
| 120 | last = value+strlen(value)-1; |
| 121 | |
| 122 | /* Trim spaces from the value */ |
| 123 | while (*first == ' ' && first < last) first++; |
| 124 | while (*last == ' ' && last > first) last--; |
| 125 | |
| 126 | /* Validate the last character */ |
| 127 | if (*last != '"') continue; |
| 128 | else *last = '\0'; |
| 129 | |
| 130 | /* Store value if it is relevant */ |
| 131 | if (!strncmp("username=\"", first, 10)) |
| 132 | username = first+10; |
| 133 | if (!strncmp("realm=\"", first, 7)) |
| 134 | realm = first+7; |
| 135 | if (!strncmp("nonce=\"", first, 7)) |
| 136 | nonce = first+7; |
| 137 | if (!strncmp("uri=\"", first, 5)) |
| 138 | uri = first+5; |
| 139 | if (!strncmp("response=\"", first, 10)) |
| 140 | response = first+10; |
| 141 | } |
| 142 | |
| 143 | if (!username || !realm || !nonce || !uri || !response) { |
| 144 | return 0; |
| 145 | } |
| 146 | if (strcmp(realm, our_realm) || strcmp(nonce, our_nonce) || strcmp(uri, our_uri)) { |
| 147 | return 0; |
| 148 | } |
| 149 | |
| 150 | /* Calculate our response */ |
| 151 | memset(our_response, 0, sizeof(our_response)); |
| 152 | digest_get_response(username, realm, password, nonce, |
| 153 | method, uri, our_response); |
| 154 | success = !strcmp(response, our_response); |
| 155 | free(auth); |
| 156 | |
| 157 | return success; |
| 158 | } |
| 159 | |
| 160 | |