function _digest_md5_response in Secure Site 8
Same name and namespace in other branches
- 6.2 digest_md5/digest_md5.php \_digest_md5_response()
- 7.2 digest_md5/digest_md5.php \_digest_md5_response()
Process an authentication string.
Parameters
$edit:
- data*
- method*
- uri
- realm (defaults to machine name if not in data)
- entity-body
Return value
Authentication info string or new challenge if authentication failed.
1 call to _digest_md5_response()
- digest_md5.php in digest_md5/
digest_md5.php - This script implements the DIGEST-MD5 mechanism for all protocols. Only the root user should have access to this script and the database used to store passwords and nonce values.
File
- digest_md5/
digest_md5.php, line 186 - This script implements the DIGEST-MD5 mechanism for all protocols. Only the root user should have access to this script and the database used to store passwords and nonce values.
Code
function _digest_md5_response($edit) {
global $time, $max_nc;
// Get status.
$fields = array();
foreach (explode(',', trim($edit['data'])) as $part) {
if (!empty($part)) {
list($key, $value) = explode('=', trim($part), 2);
$fields[$key] = trim($value, '"');
}
}
$required = array(
'username',
'realm',
'nonce',
'uri',
'response',
);
if (isset($fields['qop'])) {
$required[] = 'cnonce';
// if ($edit['method'] != 'AUTHENTICATE') {
$required[] = 'opaque';
// }
$required[] = 'nc';
}
$uri = isset($edit['uri']) ? parse_url($edit['uri']) : NULL;
$field_uri = isset($fields['uri']) ? parse_url($fields['uri']) : NULL;
if (array_diff($required, array_keys($fields)) == array() && (!isset($edit['uri']) || $uri['path'] == $field_uri['path'])) {
// Required fields are present and URI matches.
$edit['realm'] = isset($edit['realm']) ? $edit['realm'] : $fields['realm'];
$sn = db_query("SELECT qop, nc, opaque, hash FROM `securesite_nonce` WHERE nonce = :nonce AND realm = :realm", array(
':nonce' => $fields['nonce'],
':realm' => $edit['realm'],
))
->fetchAssoc();
$pass = db_query("SELECT pass FROM `securesite_passwords` WHERE name = :name AND realm = :realm", array(
':name' => $fields['username'],
':realm' => $edit['realm'],
))
->fetchField();
if ($pass !== FALSE) {
// Password exists for this user.
$ha1 = md5("{$fields['username']}:{$fields['realm']}:{$pass}");
if (isset($fields['qop'])) {
// Generate digest with quality of protection.
switch ($fields['qop']) {
case 'auth-int':
$ha2 = md5("{$edit['method']}:{$fields['uri']}:{$sn['hash']}");
break;
case 'auth':
$ha2 = md5("{$edit['method']}:{$fields['uri']}");
break;
}
$digest = md5("{$ha1}:{$fields['nonce']}:{$fields['nc']}:{$fields['cnonce']}:{$fields['qop']}:{$ha2}");
}
else {
// Generate digest without quality of protection.
$ha2 = md5("{$edit['method']}:{$fields['uri']}");
$digest = md5("{$ha1}:{$fields['nonce']}:{$ha2}");
}
if ($digest == $fields['response']) {
// Response is valid.
if ($sn === FALSE) {
// Stale nonce; send new challenge with stale notice.
$status = STALE_NONCE;
$fields['nonce'] = uniqid();
}
else {
if (isset($fields['qop']) && in_array($fields['qop'], explode(',', $sn['qop'])) && $fields['opaque'] == $sn['opaque'] || !isset($fields['qop']) && !isset($fields['nc'])) {
$dec_nc = isset($fields['qop']) ? hexdec($fields['nc']) : $sn['nc'] + 1;
$max_nc = isset($max_nc) ? $max_nc : $dec_nc + 1;
if ($dec_nc <= $sn['nc']) {
// Replay attack; re-send challenge.
$status = REPLAY_ATTACK;
}
elseif ($dec_nc > $max_nc) {
// Stale nonce; send new challenge with stale notice.
$status = STALE_NONCE;
db_query("DELETE FROM `securesite_nonce` WHERE nonce = :nonce AND realm = :realm", array(
':nonce' => $fields['nonce'],
':realm' => $edit['realm'],
));
$fields['nonce'] = uniqid();
}
else {
// User authenticated; send response.
$status = AUTHENTICATED;
}
}
else {
// Bad request; re-send challenge.
$status = BAD_REQUEST;
}
}
}
else {
// Response is invalid; re-send challenge.
$status = WRONG_PASSWORD;
}
}
else {
// Unknown user; re-send challenge.
$status = UNKNOWN_USER;
}
}
else {
// Bad request; re-send challenge.
$status = BAD_REQUEST;
if (!isset($edit['realm']) && !isset($fields['realm'])) {
$uname = posix_uname();
$edit['realm'] = $fields['realm'] = $uname['nodename'];
}
elseif (!isset($edit['realm']) && isset($fields['realm'])) {
$edit['realm'] = $fields['realm'];
}
elseif (isset($edit['realm']) && !isset($fields['realm'])) {
$fields['realm'] = $edit['realm'];
}
if (isset($fields['nonce'])) {
$sn = db_query("SELECT qop, nc, opaque, hash FROM `securesite_nonce` WHERE nonce = :nonce AND realm = :realm", array(
':nonce' => $fields['nonce'],
':realm' => $edit['realm'],
))
->fetchAssoc();
}
else {
$fields['nonce'] = uniqid();
}
}
// Create output.
switch ($status) {
case BAD_REQUEST:
case UNKNOWN_USER:
case WRONG_PASSWORD:
case REPLAY_ATTACK:
case STALE_NONCE:
if (isset($sn) && $sn !== FALSE) {
$fields['opaque'] = $sn['opaque'];
$fields['qop'] = $sn['qop'];
}
else {
$fields['opaque'] = isset($fields['opaque']) ? $fields['opaque'] : base64_encode($fields['nonce']);
$qop = isset($edit['entity-body']) ? 'auth,auth-int' : 'auth';
$fields['qop'] = isset($fields['qop']) ? $fields['qop'] : $qop;
}
$challenge = array(
'realm="' . $fields['realm'] . '"',
'nonce="' . $fields['nonce'] . '"',
'qop="' . $fields['qop'] . '"',
'opaque="' . $fields['opaque'] . '"',
);
if ($status == 'stale') {
$challenge[] = 'stale=true';
}
if (!isset($sn) || $sn === FALSE) {
$values = array(
'nonce' => $fields['nonce'],
'opaque' => $fields['opaque'],
'time' => $time,
'realm' => $edit['realm'],
'qop' => $fields['qop'],
);
$values += isset($edit['entity-body']) ? array(
'hash' => md5($edit['entity-body']),
) : array();
$output = _digest_md5_challenge(array(
'values' => $values,
'challenge' => $challenge,
'new' => TRUE,
));
}
else {
$output = _digest_md5_challenge(array(
'challenge' => $challenge,
'new' => FALSE,
));
}
break;
case AUTHENTICATED:
$response = array();
if ($dec_nc < $max_nc) {
$values = array(
'nonce' => $fields['nonce'],
'time' => $time,
'realm' => $edit['realm'],
);
$values += isset($edit['entity-body']) ? array(
'hash' => md5($edit['entity-body']),
) : array();
$values += isset($fields['qop']) ? array(
'nc' => $dec_nc,
) : array();
_digest_md5_challenge(array(
'values' => $values,
'new' => FALSE,
));
}
else {
db_query("DELETE FROM `securesite_nonce` WHERE nonce = :nonce AND realm = :realm", array(
':nonce' => $fields['nonce'],
':realm' => $edit['realm'],
));
$nextnonce = uniqid();
$values = array(
'nonce' => $nextnonce,
'opaque' => $fields['opaque'],
'time' => $time,
'realm' => $edit['realm'],
'qop' => $fields['qop'],
);
$values += isset($edit['entity-body']) ? array(
'hash' => md5($edit['entity-body']),
) : array();
_digest_md5_challenge(array(
'values' => $values,
'new' => TRUE,
));
$response[] = 'nextnonce="' . $nextnonce . '"';
}
if (isset($fields['qop'])) {
$response[] = 'qop=' . $fields['qop'];
switch ($fields['qop']) {
case 'auth-int':
$response[] = 'cnonce="' . $fields['cnonce'] . '"';
$response[] = 'nc="' . $fields['nc'] . '"';
//TODO check next line
$ha2 = md5(":{$fields['uri']}:{$sn['hash']}");
break;
case 'auth':
$response[] = 'cnonce="' . $fields['cnonce'] . '"';
$response[] = 'nc=' . $fields['nc'];
$ha2 = md5(":{$fields['uri']}");
break;
default:
$ha2 = md5(":{$fields['uri']}");
break;
}
$digest = md5("{$ha1}:{$fields['nonce']}:{$fields['nc']}:{$fields['cnonce']}:{$fields['qop']}:{$ha2}");
}
else {
$digest = md5("{$ha1}:{$fields['nonce']}:" . md5(":{$fields['uri']}"));
}
$response[] = 'rspauth="' . $digest . '"';
$output = implode(', ', $response);
break;
}
return array(
$output,
$status,
);
}