00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #if HAVE_CONFIG_H
00024 # include "config.h"
00025 #endif
00026
00027
00028 #include "parser.h"
00029
00030
00031 #include <stdlib.h>
00032
00033
00034 #include <string.h>
00035
00036
00037 #include "validate.h"
00038
00039 #define DEFAULT_CHARSET "utf-8"
00040 #define DEFAULT_ALGORITHM "md5-sess"
00041
00042 enum
00043 {
00044
00045 CHALLENGE_REALM = 0,
00046 CHALLENGE_NONCE,
00047 CHALLENGE_QOP,
00048 CHALLENGE_STALE,
00049 CHALLENGE_MAXBUF,
00050 CHALLENGE_CHARSET,
00051 CHALLENGE_ALGORITHM,
00052 CHALLENGE_CIPHER
00053 };
00054
00055 static const char *const digest_challenge_opts[] = {
00056
00057 "realm",
00058 "nonce",
00059 "qop",
00060 "stale",
00061 "maxbuf",
00062 "charset",
00063 "algorithm",
00064 "cipher",
00065 NULL
00066 };
00067
00068
00069 enum
00070 {
00071
00072 QOP_AUTH = 0,
00073 QOP_AUTH_INT,
00074 QOP_AUTH_CONF
00075 };
00076
00077 static const char *const qop_opts[] = {
00078
00079 "auth",
00080 "auth-int",
00081 "auth-conf",
00082 NULL
00083 };
00084
00085
00086
00087
00088
00089 enum
00090 {
00091
00092 CIPHER_DES = 0,
00093 CIPHER_3DES,
00094 CIPHER_RC4,
00095 CIPHER_RC4_40,
00096 CIPHER_RC4_56,
00097 CIPHER_AES_CBC
00098 };
00099
00100 static const char *const cipher_opts[] = {
00101
00102 "des",
00103 "3des",
00104 "rc4",
00105 "rc4-40",
00106 "rc4-56",
00107 "aes-cbc",
00108 NULL
00109 };
00110
00111 static int
00112 parse_challenge (char *challenge, digest_md5_challenge * out)
00113 {
00114 int done_algorithm = 0;
00115 int disable_qop_auth_conf = 0;
00116 char *value;
00117
00118 memset (out, 0, sizeof (*out));
00119
00120
00121 if (strlen (challenge) >= 2048)
00122 return -1;
00123
00124 while (*challenge != '\0')
00125 switch (digest_md5_getsubopt (&challenge, digest_challenge_opts, &value))
00126 {
00127 case CHALLENGE_REALM:
00128 {
00129 char **tmp;
00130 out->nrealms++;
00131 tmp = realloc (out->realms, out->nrealms * sizeof (*out->realms));
00132 if (!tmp)
00133 return -1;
00134 out->realms = tmp;
00135 out->realms[out->nrealms - 1] = strdup (value);
00136 if (!out->realms[out->nrealms - 1])
00137 return -1;
00138 }
00139 break;
00140
00141 case CHALLENGE_NONCE:
00142
00143
00144
00145 if (out->nonce)
00146 return -1;
00147 out->nonce = strdup (value);
00148 if (!out->nonce)
00149 return -1;
00150 break;
00151
00152 case CHALLENGE_QOP:
00153
00154
00155 if (out->qops)
00156 return -1;
00157 {
00158 char *subsubopts;
00159 char *val;
00160
00161 subsubopts = value;
00162 while (*subsubopts != '\0')
00163 switch (digest_md5_getsubopt (&subsubopts, qop_opts, &val))
00164 {
00165 case QOP_AUTH:
00166 out->qops |= DIGEST_MD5_QOP_AUTH;
00167 break;
00168
00169 case QOP_AUTH_INT:
00170 out->qops |= DIGEST_MD5_QOP_AUTH_INT;
00171 break;
00172
00173 case QOP_AUTH_CONF:
00174 out->qops |= DIGEST_MD5_QOP_AUTH_CONF;
00175 break;
00176
00177 default:
00178
00179 break;
00180 }
00181 }
00182
00183
00184 if (disable_qop_auth_conf)
00185 out->qops &= ~DIGEST_MD5_QOP_AUTH_CONF;
00186
00187
00188 if (!out->qops)
00189 return -1;
00190 break;
00191
00192 case CHALLENGE_STALE:
00193
00194
00195
00196 if (out->stale)
00197 return -1;
00198 out->stale = 1;
00199 break;
00200
00201 case CHALLENGE_MAXBUF:
00202
00203
00204
00205 if (out->servermaxbuf)
00206 return -1;
00207 out->servermaxbuf = strtoul (value, NULL, 10);
00208
00209
00210
00211
00212 if (out->servermaxbuf <= 16 || out->servermaxbuf > 16777215)
00213 return -1;
00214 break;
00215
00216 case CHALLENGE_CHARSET:
00217
00218
00219
00220 if (out->utf8)
00221 return -1;
00222 if (strcmp (DEFAULT_CHARSET, value) != 0)
00223 return -1;
00224 out->utf8 = 1;
00225 break;
00226
00227 case CHALLENGE_ALGORITHM:
00228
00229
00230
00231 if (done_algorithm)
00232 return -1;
00233 if (strcmp (DEFAULT_ALGORITHM, value) != 0)
00234 return -1;
00235 done_algorithm = 1;
00236 break;
00237
00238
00239 case CHALLENGE_CIPHER:
00240
00241
00242 if (out->ciphers)
00243 return -1;
00244 {
00245 char *subsubopts;
00246 char *val;
00247
00248 subsubopts = value;
00249 while (*subsubopts != '\0')
00250 switch (digest_md5_getsubopt (&subsubopts, cipher_opts, &val))
00251 {
00252 case CIPHER_DES:
00253 out->ciphers |= DIGEST_MD5_CIPHER_DES;
00254 break;
00255
00256 case CIPHER_3DES:
00257 out->ciphers |= DIGEST_MD5_CIPHER_3DES;
00258 break;
00259
00260 case CIPHER_RC4:
00261 out->ciphers |= DIGEST_MD5_CIPHER_RC4;
00262 break;
00263
00264 case CIPHER_RC4_40:
00265 out->ciphers |= DIGEST_MD5_CIPHER_RC4_40;
00266 break;
00267
00268 case CIPHER_RC4_56:
00269 out->ciphers |= DIGEST_MD5_CIPHER_RC4_56;
00270 break;
00271
00272 case CIPHER_AES_CBC:
00273 out->ciphers |= DIGEST_MD5_CIPHER_AES_CBC;
00274 break;
00275
00276 default:
00277
00278 break;
00279 }
00280 }
00281
00282
00283 if (!out->ciphers)
00284 {
00285 disable_qop_auth_conf = 1;
00286 if (out->qops)
00287 {
00288
00289
00290 out->qops &= ~DIGEST_MD5_QOP_AUTH_CONF;
00291 if (!out->qops)
00292 return -1;
00293 }
00294 }
00295 break;
00296
00297 default:
00298
00299 break;
00300 }
00301
00302
00303
00304
00305 if (!done_algorithm)
00306 return -1;
00307
00308
00309 if (digest_md5_validate_challenge (out) != 0)
00310 return -1;
00311
00312 return 0;
00313 }
00314
00315 enum
00316 {
00317
00318 RESPONSE_USERNAME = 0,
00319 RESPONSE_REALM,
00320 RESPONSE_NONCE,
00321 RESPONSE_CNONCE,
00322 RESPONSE_NC,
00323 RESPONSE_QOP,
00324 RESPONSE_DIGEST_URI,
00325 RESPONSE_RESPONSE,
00326 RESPONSE_MAXBUF,
00327 RESPONSE_CHARSET,
00328 RESPONSE_CIPHER,
00329 RESPONSE_AUTHZID
00330 };
00331
00332 static const char *const digest_response_opts[] = {
00333
00334 "username",
00335 "realm",
00336 "nonce",
00337 "cnonce",
00338 "nc",
00339 "qop",
00340 "digest-uri",
00341 "response",
00342 "maxbuf",
00343 "charset",
00344 "cipher",
00345 "authzid",
00346 NULL
00347 };
00348
00349 static int
00350 parse_response (char *response, digest_md5_response * out)
00351 {
00352 char *value;
00353
00354 memset (out, 0, sizeof (*out));
00355
00356
00357 if (strlen (response) >= 4096)
00358 return -1;
00359
00360 while (*response != '\0')
00361 switch (digest_md5_getsubopt (&response, digest_response_opts, &value))
00362 {
00363 case RESPONSE_USERNAME:
00364
00365
00366 if (out->username)
00367 return -1;
00368 out->username = strdup (value);
00369 if (!out->username)
00370 return -1;
00371 break;
00372
00373 case RESPONSE_REALM:
00374
00375
00376
00377
00378 if (out->realm)
00379 return -1;
00380 out->realm = strdup (value);
00381 if (!out->realm)
00382 return -1;
00383 break;
00384
00385 case RESPONSE_NONCE:
00386
00387
00388 if (out->nonce)
00389 return -1;
00390 out->nonce = strdup (value);
00391 if (!out->nonce)
00392 return -1;
00393 break;
00394
00395 case RESPONSE_CNONCE:
00396
00397
00398 if (out->cnonce)
00399 return -1;
00400 out->cnonce = strdup (value);
00401 if (!out->cnonce)
00402 return -1;
00403 break;
00404
00405 case RESPONSE_NC:
00406
00407
00408 if (out->nc)
00409 return -1;
00410
00411 if (strlen (value) != 8)
00412 return -1;
00413 out->nc = strtoul (value, NULL, 16);
00414
00415 break;
00416
00417 case RESPONSE_QOP:
00418
00419
00420 if (out->qop)
00421 return -1;
00422 if (strcmp (value, "auth") == 0)
00423 out->qop = DIGEST_MD5_QOP_AUTH;
00424 else if (strcmp (value, "auth-int") == 0)
00425 out->qop = DIGEST_MD5_QOP_AUTH_INT;
00426 else if (strcmp (value, "auth-conf") == 0)
00427 out->qop = DIGEST_MD5_QOP_AUTH_CONF;
00428 else
00429 return -1;
00430 break;
00431
00432 case RESPONSE_DIGEST_URI:
00433
00434
00435
00436 if (out->digesturi)
00437 return -1;
00438
00439 out->digesturi = strdup (value);
00440 if (!out->digesturi)
00441 return -1;
00442 break;
00443
00444 case RESPONSE_RESPONSE:
00445
00446
00447 if (*out->response)
00448 return -1;
00449
00450 if (strlen (value) != DIGEST_MD5_RESPONSE_LENGTH)
00451 return -1;
00452 strcpy (out->response, value);
00453 break;
00454
00455 case RESPONSE_MAXBUF:
00456
00457
00458
00459 if (out->clientmaxbuf)
00460 return -1;
00461 out->clientmaxbuf = strtoul (value, NULL, 10);
00462
00463
00464
00465
00466 if (out->clientmaxbuf <= 16 || out->clientmaxbuf > 16777215)
00467 return -1;
00468 break;
00469
00470 case RESPONSE_CHARSET:
00471 if (strcmp (DEFAULT_CHARSET, value) != 0)
00472 return -1;
00473 out->utf8 = 1;
00474 break;
00475
00476 case RESPONSE_CIPHER:
00477 if (out->cipher)
00478 return -1;
00479 if (strcmp (value, "3des") == 0)
00480 out->cipher = DIGEST_MD5_CIPHER_3DES;
00481 else if (strcmp (value, "des") == 0)
00482 out->cipher = DIGEST_MD5_CIPHER_DES;
00483 else if (strcmp (value, "rc4-40") == 0)
00484 out->cipher = DIGEST_MD5_CIPHER_RC4_40;
00485 else if (strcmp (value, "rc4") == 0)
00486 out->cipher = DIGEST_MD5_CIPHER_RC4;
00487 else if (strcmp (value, "rc4-56") == 0)
00488 out->cipher = DIGEST_MD5_CIPHER_RC4_56;
00489 else if (strcmp (value, "aes-cbc") == 0)
00490 out->cipher = DIGEST_MD5_CIPHER_AES_CBC;
00491 else
00492 return -1;
00493 break;
00494
00495 case RESPONSE_AUTHZID:
00496
00497
00498
00499 if (out->authzid)
00500 return -1;
00501
00502 if (*value == '\0')
00503 return -1;
00504 out->authzid = strdup (value);
00505 if (!out->authzid)
00506 return -1;
00507 break;
00508
00509 default:
00510
00511 break;
00512 }
00513
00514
00515 if (digest_md5_validate_response (out) != 0)
00516 return -1;
00517
00518 return 0;
00519 }
00520
00521 enum
00522 {
00523
00524 RESPONSEAUTH_RSPAUTH = 0
00525 };
00526
00527 static const char *const digest_responseauth_opts[] = {
00528
00529 "rspauth",
00530 NULL
00531 };
00532
00533 static int
00534 parse_finish (char *finish, digest_md5_finish * out)
00535 {
00536 char *value;
00537
00538 memset (out, 0, sizeof (*out));
00539
00540
00541 if (strlen (finish) >= 2048)
00542 return -1;
00543
00544 while (*finish != '\0')
00545 switch (digest_md5_getsubopt (&finish, digest_responseauth_opts, &value))
00546 {
00547 case RESPONSEAUTH_RSPAUTH:
00548 if (*out->rspauth)
00549 return -1;
00550
00551 if (strlen (value) != DIGEST_MD5_RESPONSE_LENGTH)
00552 return -1;
00553 strcpy (out->rspauth, value);
00554 break;
00555
00556 default:
00557
00558 break;
00559 }
00560
00561
00562 if (digest_md5_validate_finish (out) != 0)
00563 return -1;
00564
00565 return 0;
00566 }
00567
00568 int
00569 digest_md5_parse_challenge (const char *challenge, size_t len,
00570 digest_md5_challenge * out)
00571 {
00572 size_t inlen = len ? len : strlen (challenge);
00573 char *subopts = malloc (inlen + 1);
00574 int rc;
00575
00576 if (!subopts)
00577 return -1;
00578
00579 memcpy (subopts, challenge, inlen);
00580 subopts[inlen] = '\0';
00581
00582 rc = parse_challenge (subopts, out);
00583
00584 free (subopts);
00585
00586 return rc;
00587 }
00588
00589 int
00590 digest_md5_parse_response (const char *response, size_t len,
00591 digest_md5_response * out)
00592 {
00593 size_t inlen = len ? len : strlen (response);
00594 char *subopts = malloc (inlen + 1);
00595 int rc;
00596
00597 if (!subopts)
00598 return -1;
00599
00600 memcpy (subopts, response, inlen);
00601 subopts[inlen] = '\0';
00602
00603 rc = parse_response (subopts, out);
00604
00605 free (subopts);
00606
00607 return rc;
00608 }
00609
00610 int
00611 digest_md5_parse_finish (const char *finish, size_t len,
00612 digest_md5_finish * out)
00613 {
00614 size_t inlen = len ? len : strlen (finish);
00615 char *subopts = malloc (inlen + 1);
00616 int rc;
00617
00618 if (!subopts)
00619 return -1;
00620
00621 memcpy (subopts, finish, inlen);
00622 subopts[inlen] = '\0';
00623
00624 rc = parse_finish (subopts, out);
00625
00626 free (subopts);
00627
00628 return rc;
00629 }