|  | <?php | 
|  |  | 
|  | require('config.php'); | 
|  |  | 
|  | $params = split("/", $_SERVER["PATH_INFO"], 3); | 
|  | $realm = $params[1]; | 
|  | $cmd = $params[2]; | 
|  | $method = $_SERVER["REQUEST_METHOD"]; | 
|  |  | 
|  | unset($user); | 
|  | unset($rowid); | 
|  |  | 
|  | if (!empty($_SERVER['PHP_AUTH_DIGEST'])) { | 
|  | $needed = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, | 
|  | 'uri'=>1, 'response'=>1); | 
|  | $data = array(); | 
|  | $keys = implode('|', array_keys($needed)); | 
|  | preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', | 
|  | $_SERVER['PHP_AUTH_DIGEST'], $matches, PREG_SET_ORDER); | 
|  | foreach ($matches as $m) { | 
|  | $data[$m[1]] = $m[3] ? $m[3] : $m[4]; | 
|  | unset($needed[$m[1]]); | 
|  | } | 
|  | if ($needed) { | 
|  | error_log("EST: Missing auth parameter"); | 
|  | die('Authentication failed'); | 
|  | } | 
|  | $user = $data['username']; | 
|  | if (strlen($user) < 1) { | 
|  | error_log("EST: Empty username"); | 
|  | die('Authentication failed'); | 
|  | } | 
|  |  | 
|  | $db = new PDO($osu_db); | 
|  | if (!$db) { | 
|  | error_log("EST: Could not access database"); | 
|  | die("Could not access database"); | 
|  | } | 
|  |  | 
|  | $sql = "SELECT rowid,password,operation FROM sessions " . | 
|  | "WHERE user='$user' AND realm='$realm'"; | 
|  | $q = $db->query($sql); | 
|  | if (!$q) { | 
|  | error_log("EST: Session not found for user=$user realm=$realm"); | 
|  | die("Session not found"); | 
|  | } | 
|  | $row = $q->fetch(); | 
|  | if (!$row) { | 
|  | error_log("EST: Session fetch failed for user=$user realm=$realm"); | 
|  | die('Session not found'); | 
|  | } | 
|  | $rowid = $row['rowid']; | 
|  |  | 
|  | $oper = $row['operation']; | 
|  | if ($oper != '5') { | 
|  | error_log("EST: Unexpected operation $oper for user=$user realm=$realm"); | 
|  | die("Session not found"); | 
|  | } | 
|  | $pw = $row['password']; | 
|  | if (strlen($pw) < 1) { | 
|  | error_log("EST: Empty password for user=$user realm=$realm"); | 
|  | die('Authentication failed'); | 
|  | } | 
|  |  | 
|  | $A1 = md5($user . ':' . $realm . ':' . $pw); | 
|  | $A2 = md5($method . ':' . $data['uri']); | 
|  | $resp = md5($A1 . ':' . $data['nonce'] . ':' . $data['nc'] . ':' . | 
|  | $data['cnonce'] . ':' . $data['qop'] . ':' . $A2); | 
|  | if ($data['response'] != $resp) { | 
|  | error_log("EST: Incorrect authentication response for user=$user realm=$realm"); | 
|  | die('Authentication failed'); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | if ($method == "GET" && $cmd == "cacerts") { | 
|  | $fname = "$osu_root/est/$realm-cacerts.pkcs7"; | 
|  | if (!file_exists($fname)) { | 
|  | error_log("EST: cacerts - unknown realm $realm"); | 
|  | die("Unknown realm"); | 
|  | } | 
|  |  | 
|  | header("Content-Transfer-Encoding: base64"); | 
|  | header("Content-Type: application/pkcs7-mime"); | 
|  |  | 
|  | $data = file_get_contents($fname); | 
|  | echo wordwrap(base64_encode($data), 72, "\n", true); | 
|  | echo "\n"; | 
|  | error_log("EST: cacerts"); | 
|  | } else if ($method == "GET" && $cmd == "csrattrs") { | 
|  | header("Content-Transfer-Encoding: base64"); | 
|  | header("Content-Type: application/csrattrs"); | 
|  | readfile("$osu_root/est/est-attrs.b64"); | 
|  | error_log("EST: csrattrs"); | 
|  | } else if ($method == "POST" && $cmd == "simpleenroll") { | 
|  | if (!isset($user) || strlen($user) == 0) { | 
|  | header('HTTP/1.1 401 Unauthorized'); | 
|  | header('WWW-Authenticate: Digest realm="'.$realm. | 
|  | '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"'); | 
|  | error_log("EST: simpleenroll - require authentication"); | 
|  | die('Authentication required'); | 
|  | } | 
|  | if (!isset($_SERVER["CONTENT_TYPE"])) { | 
|  | error_log("EST: simpleenroll without Content-Type"); | 
|  | die("Missing Content-Type"); | 
|  | } | 
|  | if (!stristr($_SERVER["CONTENT_TYPE"], "application/pkcs10")) { | 
|  | error_log("EST: simpleenroll - unexpected Content-Type: " . | 
|  | $_SERVER["CONTENT_TYPE"]); | 
|  | die("Unexpected Content-Type"); | 
|  | } | 
|  |  | 
|  | $data = file_get_contents("php://input"); | 
|  | error_log("EST: simpleenroll - POST data from php://input: " . $data); | 
|  | $req = base64_decode($data); | 
|  | if ($req == FALSE) { | 
|  | error_log("EST: simpleenroll - Invalid base64-encoded PKCS#10 data"); | 
|  | die("Invalid base64-encoded PKCS#10 data"); | 
|  | } | 
|  | $cadir = "$osu_root/est"; | 
|  | $reqfile = "$cadir/tmp/cert-req.pkcs10"; | 
|  | $f = fopen($reqfile, "wb"); | 
|  | fwrite($f, $req); | 
|  | fclose($f); | 
|  |  | 
|  | $req_pem = "$reqfile.pem"; | 
|  | if (file_exists($req_pem)) | 
|  | unlink($req_pem); | 
|  | exec("openssl req -in $reqfile -inform DER -out $req_pem -outform PEM"); | 
|  | if (!file_exists($req_pem)) { | 
|  | error_log("EST: simpleenroll - Failed to parse certificate request"); | 
|  | die("Failed to parse certificate request"); | 
|  | } | 
|  |  | 
|  | /* FIX: validate request and add HS 2.0 extensions to cert */ | 
|  | $cert_pem = "$cadir/tmp/req-signed.pem"; | 
|  | if (file_exists($cert_pem)) | 
|  | unlink($cert_pem); | 
|  | exec("openssl x509 -req -in $req_pem -CAkey $cadir/cakey.pem -out $cert_pem -CA $cadir/cacert.pem -CAserial $cadir/serial -days 365 -text"); | 
|  | if (!file_exists($cert_pem)) { | 
|  | error_log("EST: simpleenroll - Failed to sign certificate"); | 
|  | die("Failed to sign certificate"); | 
|  | } | 
|  |  | 
|  | $cert = file_get_contents($cert_pem); | 
|  | $handle = popen("openssl x509 -in $cert_pem -serial -noout", "r"); | 
|  | $serial = fread($handle, 200); | 
|  | pclose($handle); | 
|  | $pattern = "/serial=(?P<snhex>[0-9a-fA-F:]*)/m"; | 
|  | preg_match($pattern, $serial, $matches); | 
|  | if (!isset($matches['snhex']) || strlen($matches['snhex']) < 1) { | 
|  | error_log("EST: simpleenroll - Could not get serial number"); | 
|  | die("Could not get serial number"); | 
|  | } | 
|  | $sn = str_replace(":", "", strtoupper($matches['snhex'])); | 
|  |  | 
|  | $user = "cert-$sn"; | 
|  | error_log("EST: user = $user"); | 
|  |  | 
|  | $cert_der = "$cadir/tmp/req-signed.der"; | 
|  | if (file_exists($cert_der)) | 
|  | unlink($cert_der); | 
|  | exec("openssl x509 -in $cert_pem -inform PEM -out $cert_der -outform DER"); | 
|  | if (!file_exists($cert_der)) { | 
|  | error_log("EST: simpleenroll - Failed to convert certificate"); | 
|  | die("Failed to convert certificate"); | 
|  | } | 
|  | $der = file_get_contents($cert_der); | 
|  | $fingerprint = hash("sha256", $der); | 
|  |  | 
|  | $pkcs7 = "$cadir/tmp/est-client.pkcs7"; | 
|  | if (file_exists($pkcs7)) | 
|  | unlink($pkcs7); | 
|  | exec("openssl crl2pkcs7 -nocrl -certfile $cert_pem -out $pkcs7 -outform DER"); | 
|  | if (!file_exists($pkcs7)) { | 
|  | error_log("EST: simpleenroll - Failed to prepare PKCS#7 file"); | 
|  | die("Failed to prepare PKCS#7 file"); | 
|  | } | 
|  |  | 
|  | if (!$db->exec("UPDATE sessions SET user='$user', cert='$fingerprint', cert_pem='$cert' WHERE rowid=$rowid")) { | 
|  | error_log("EST: simpleenroll - Failed to update session database"); | 
|  | die("Failed to update session database"); | 
|  | } | 
|  |  | 
|  | header("Content-Transfer-Encoding: base64"); | 
|  | header("Content-Type: application/pkcs7-mime"); | 
|  |  | 
|  | $data = file_get_contents($pkcs7); | 
|  | $resp = wordwrap(base64_encode($data), 72, "\n", true); | 
|  | echo $resp . "\n"; | 
|  | error_log("EST: simpleenroll - PKCS#7 response: " . $resp); | 
|  | } else { | 
|  | header("HTTP/1.0 404 Not Found"); | 
|  | error_log("EST: Unexpected method or path"); | 
|  | die("Unexpected method or path"); | 
|  | } | 
|  |  | 
|  | ?> |