static bool parseDigestDetails(Webs *wp) { WebsTime when; char *value, *tok, *key, *dp, *sp, *secret, *realm; int seenComma; assure(wp); key = sclone(wp->authDetails); while (*key) { while (*key && isspace((uchar) *key)) { key++; } tok = key; while (*tok && !isspace((uchar) *tok) && *tok != ',' && *tok != '=') { tok++; } *tok++ = '\0'; while (isspace((uchar) *tok)) { tok++; } seenComma = 0; if (*tok == '\"') { value = ++tok; while (*tok != '\"' && *tok != '\0') { tok++; } } else { value = tok; while (*tok != ',' && *tok != '\0') { tok++; } seenComma++; } *tok++ = '\0'; /* Handle back-quoting */ if (strchr(value, '\\')) { for (dp = sp = value; *sp; sp++) { if (*sp == '\\') { sp++; } *dp++ = *sp++; } *dp = '\0'; } /* user, response, oqaque, uri, realm, nonce, nc, cnonce, qop */ switch (tolower((uchar) *key)) { case 'a': if (scaselesscmp(key, "algorithm") == 0) { break; } else if (scaselesscmp(key, "auth-param") == 0) { break; } break; case 'c': if (scaselesscmp(key, "cnonce") == 0) { wp->cnonce = sclone(value); } break; case 'd': if (scaselesscmp(key, "domain") == 0) { break; } break; case 'n': if (scaselesscmp(key, "nc") == 0) { wp->nc = sclone(value); } else if (scaselesscmp(key, "nonce") == 0) { wp->nonce = sclone(value); } break; case 'o': if (scaselesscmp(key, "opaque") == 0) { wp->opaque = sclone(value); } break; case 'q': if (scaselesscmp(key, "qop") == 0) { wp->qop = sclone(value); } break; case 'r': if (scaselesscmp(key, "realm") == 0) { wp->realm = sclone(value); } else if (scaselesscmp(key, "response") == 0) { /* Store the response digest in the password field. This is MD5(user:realm:password) */ wp->password = sclone(value); wp->encoded = 1; } break; case 's': if (scaselesscmp(key, "stale") == 0) { break; } case 'u': if (scaselesscmp(key, "uri") == 0) { wp->digestUri = sclone(value); } else if (scaselesscmp(key, "username") == 0 || scaselesscmp(key, "user") == 0) { wp->username = sclone(value); } break; default: /* Just ignore keywords we don't understand */ ; } key = tok; if (!seenComma) { while (*key && *key != ',') { key++; } if (*key) { key++; } } } if (wp->username == 0 || wp->realm == 0 || wp->nonce == 0 || wp->route == 0 || wp->password == 0) { return 0; } if (wp->qop && (wp->cnonce == 0 || wp->nc == 0)) { return 0; } if (wp->qop == 0) { wp->qop = sclone(""); } /* Validate the nonce value - prevents replay attacks */ when = 0; secret = 0; realm = 0; parseDigestNonce(wp->nonce, &secret, &realm, &when); if (!smatch(secret, secret)) { trace(2, "Access denied: Nonce mismatch\n"); return 0; } else if (!smatch(realm, BIT_REALM)) { trace(2, "Access denied: Realm mismatch\n"); return 0; } else if (!smatch(wp->qop, "auth")) { trace(2, "Access denied: Bad qop\n"); return 0; } else if ((when + (5 * 60)) < time(0)) { trace(2, "Access denied: Nonce is stale\n"); return 0; } if (!wp->user) { if ((wp->user = websLookupUser(wp->username)) == 0) { trace(2, "Access denied: user is unknown\n"); return 0; } } wp->digest = calcDigest(wp, 0, wp->user->password); return 1; }
/* Parse the client 'Authorization' header and the server 'Www-Authenticate' header */ PUBLIC int httpDigestParse(HttpConn *conn, cchar **username, cchar **password) { HttpRx *rx; DigestData *dp; MprTime when; char *value, *tok, *key, *cp, *sp; cchar *secret, *realm; int seenComma; rx = conn->rx; if (password) { *password = NULL; } if (username) { *username = NULL; } if (!rx->authDetails) { return 0; } dp = conn->authData = mprAllocObj(DigestData, manageDigestData); key = sclone(rx->authDetails); while (*key) { while (*key && isspace((uchar) *key)) { key++; } tok = key; while (*tok && !isspace((uchar) *tok) && *tok != ',' && *tok != '=') { tok++; } *tok++ = '\0'; while (isspace((uchar) *tok)) { tok++; } seenComma = 0; if (*tok == '\"') { value = ++tok; while (*tok != '\"' && *tok != '\0') { tok++; } } else { value = tok; while (*tok != ',' && *tok != '\0') { tok++; } seenComma++; } *tok++ = '\0'; /* Handle back-quoting */ if (strchr(value, '\\')) { for (cp = sp = value; *sp; sp++) { if (*sp == '\\') { sp++; } *cp++ = *sp++; } *cp = '\0'; } /* user, response, oqaque, uri, realm, nonce, nc, cnonce, qop */ switch (tolower((uchar) *key)) { case 'a': if (scaselesscmp(key, "algorithm") == 0) { dp->algorithm = sclone(value); break; } else if (scaselesscmp(key, "auth-param") == 0) { break; } break; case 'c': if (scaselesscmp(key, "cnonce") == 0) { dp->cnonce = sclone(value); } break; case 'd': if (scaselesscmp(key, "domain") == 0) { dp->domain = sclone(value); break; } break; case 'n': if (scaselesscmp(key, "nc") == 0) { dp->nc = sclone(value); } else if (scaselesscmp(key, "nonce") == 0) { dp->nonce = sclone(value); } break; case 'o': if (scaselesscmp(key, "opaque") == 0) { dp->opaque = sclone(value); } break; case 'q': if (scaselesscmp(key, "qop") == 0) { dp->qop = sclone(value); } break; case 'r': if (scaselesscmp(key, "realm") == 0) { dp->realm = sclone(value); } else if (scaselesscmp(key, "response") == 0) { /* Store the response digest in the password field. This is MD5(user:realm:password) */ if (password) { *password = sclone(value); } conn->encoded = 1; } break; case 's': if (scaselesscmp(key, "stale") == 0) { break; } case 'u': if (scaselesscmp(key, "uri") == 0) { dp->uri = sclone(value); } else if (scaselesscmp(key, "username") == 0 || scaselesscmp(key, "user") == 0) { if (username) { *username = sclone(value); } } break; default: /* Just ignore keywords we don't understand */ ; } key = tok; if (!seenComma) { while (*key && *key != ',') { key++; } if (*key) { key++; } } } if (username && *username == 0) { return MPR_ERR_BAD_FORMAT; } if (password && *password == 0) { return MPR_ERR_BAD_FORMAT; } if (dp->realm == 0 || dp->nonce == 0 || dp->uri == 0) { return MPR_ERR_BAD_FORMAT; } if (dp->qop && (dp->cnonce == 0 || dp->nc == 0)) { return MPR_ERR_BAD_FORMAT; } if (conn->endpoint) { realm = secret = 0; when = 0; parseDigestNonce(dp->nonce, &secret, &realm, &when); if (!smatch(secret, secret)) { mprTrace(2, "Access denied: Nonce mismatch\n"); return MPR_ERR_BAD_STATE; } else if (!smatch(realm, rx->route->auth->realm)) { mprTrace(2, "Access denied: Realm mismatch\n"); return MPR_ERR_BAD_STATE; } else if (dp->qop && !smatch(dp->qop, "auth")) { mprTrace(2, "Access denied: Bad qop\n"); return MPR_ERR_BAD_STATE; } else if ((when + (5 * 60)) < time(0)) { mprTrace(2, "Access denied: Nonce is stale\n"); return MPR_ERR_BAD_STATE; } rx->passwordDigest = calcDigest(conn, dp, *username); } else { if (dp->domain == 0 || dp->opaque == 0 || dp->algorithm == 0 || dp->stale == 0) { return MPR_ERR_BAD_FORMAT; } } return 0; }