1<?php 2 3require('config.php'); 4 5$params = explode("/", $_SERVER["PATH_INFO"], 3); 6$realm = $params[1]; 7$cmd = $params[2]; 8$method = $_SERVER["REQUEST_METHOD"]; 9 10unset($user); 11unset($rowid); 12 13$db = new PDO($osu_db); 14if (!$db) { 15 error_log("EST: Could not access database"); 16 die("Could not access database"); 17} 18 19if (!empty($_SERVER['PHP_AUTH_DIGEST'])) { 20 $needed = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 21 'uri'=>1, 'response'=>1); 22 $data = array(); 23 $keys = implode('|', array_keys($needed)); 24 preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', 25 $_SERVER['PHP_AUTH_DIGEST'], $matches, PREG_SET_ORDER); 26 foreach ($matches as $m) { 27 $data[$m[1]] = $m[3] ? $m[3] : $m[4]; 28 unset($needed[$m[1]]); 29 } 30 if ($needed) { 31 error_log("EST: Missing auth parameter"); 32 die('Authentication failed'); 33 } 34 $user = $data['username']; 35 if (strlen($user) < 1) { 36 error_log("EST: Empty username"); 37 die('Authentication failed'); 38 } 39 40 $sql = "SELECT rowid,password,operation FROM sessions " . 41 "WHERE user='$user' AND realm='$realm'"; 42 $q = $db->query($sql); 43 if (!$q) { 44 error_log("EST: Session not found for user=$user realm=$realm"); 45 die("Session not found"); 46 } 47 $row = $q->fetch(); 48 if (!$row) { 49 error_log("EST: Session fetch failed for user=$user realm=$realm"); 50 die('Session not found'); 51 } 52 $rowid = $row['rowid']; 53 54 $oper = $row['operation']; 55 if ($oper != '5') { 56 error_log("EST: Unexpected operation $oper for user=$user realm=$realm"); 57 die("Session not found"); 58 } 59 $pw = $row['password']; 60 if (strlen($pw) < 1) { 61 error_log("EST: Empty password for user=$user realm=$realm"); 62 die('Authentication failed'); 63 } 64 65 $A1 = md5($user . ':' . $realm . ':' . $pw); 66 $A2 = md5($method . ':' . $data['uri']); 67 $resp = md5($A1 . ':' . $data['nonce'] . ':' . $data['nc'] . ':' . 68 $data['cnonce'] . ':' . $data['qop'] . ':' . $A2); 69 if ($data['response'] != $resp) { 70 error_log("EST: Incorrect authentication response for user=$user realm=$realm"); 71 die('Authentication failed'); 72 } 73} else if (isset($_SERVER["SSL_CLIENT_VERIFY"]) && 74 $_SERVER["SSL_CLIENT_VERIFY"] == "SUCCESS" && 75 isset($_SERVER["SSL_CLIENT_M_SERIAL"])) { 76 $user = "cert-" . $_SERVER["SSL_CLIENT_M_SERIAL"]; 77 $sql = "SELECT rowid,password,operation FROM sessions " . 78 "WHERE user='$user' AND realm='$realm'"; 79 $q = $db->query($sql); 80 if (!$q) { 81 error_log("EST: Session not found for user=$user realm=$realm"); 82 die("Session not found"); 83 } 84 $row = $q->fetch(); 85 if (!$row) { 86 error_log("EST: Session fetch failed for user=$user realm=$realm"); 87 die('Session not found'); 88 } 89 $rowid = $row['rowid']; 90 91 $oper = $row['operation']; 92 if ($oper != '10') { 93 error_log("EST: Unexpected operation $oper for user=$user realm=$realm"); 94 die("Session not found"); 95 } 96} 97 98 99if ($method == "GET" && $cmd == "cacerts") { 100 $fname = "$osu_root/est/$realm-cacerts.pkcs7"; 101 if (!file_exists($fname)) { 102 error_log("EST: cacerts - unknown realm $realm"); 103 die("Unknown realm"); 104 } 105 106 header("Content-Transfer-Encoding: base64"); 107 header("Content-Type: application/pkcs7-mime"); 108 109 $data = file_get_contents($fname); 110 echo wordwrap(base64_encode($data), 72, "\n", true); 111 echo "\n"; 112 error_log("EST: cacerts"); 113} else if ($method == "GET" && $cmd == "csrattrs") { 114 header("Content-Transfer-Encoding: base64"); 115 header("Content-Type: application/csrattrs"); 116 readfile("$osu_root/est/est-attrs.b64"); 117 error_log("EST: csrattrs"); 118} else if ($method == "POST" && 119 ($cmd == "simpleenroll" || $cmd == "simplereenroll")) { 120 $reenroll = $cmd == "simplereenroll"; 121 if (!$reenroll && (!isset($user) || strlen($user) == 0)) { 122 header('HTTP/1.1 401 Unauthorized'); 123 header('WWW-Authenticate: Digest realm="'.$realm. 124 '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"'); 125 error_log("EST: simpleenroll - require authentication"); 126 die('Authentication required'); 127 } 128 if ($reenroll && 129 (!isset($user) || 130 !isset($_SERVER["SSL_CLIENT_VERIFY"]) || 131 $_SERVER["SSL_CLIENT_VERIFY"] != "SUCCESS")) { 132 header('HTTP/1.1 403 Forbidden'); 133 error_log("EST: simplereenroll - require certificate authentication"); 134 die('Authentication required'); 135 } 136 if (!isset($_SERVER["CONTENT_TYPE"])) { 137 error_log("EST: simpleenroll without Content-Type"); 138 die("Missing Content-Type"); 139 } 140 if (!stristr($_SERVER["CONTENT_TYPE"], "application/pkcs10")) { 141 error_log("EST: simpleenroll - unexpected Content-Type: " . 142 $_SERVER["CONTENT_TYPE"]); 143 die("Unexpected Content-Type"); 144 } 145 146 $data = file_get_contents("php://input"); 147 error_log("EST: simpleenroll - POST data from php://input: " . $data); 148 $req = base64_decode($data); 149 if ($req == FALSE) { 150 error_log("EST: simpleenroll - Invalid base64-encoded PKCS#10 data"); 151 die("Invalid base64-encoded PKCS#10 data"); 152 } 153 $cadir = "$osu_root/est"; 154 $reqfile = "$cadir/tmp/cert-req.pkcs10"; 155 $f = fopen($reqfile, "wb"); 156 fwrite($f, $req); 157 fclose($f); 158 159 $req_pem = "$reqfile.pem"; 160 if (file_exists($req_pem)) 161 unlink($req_pem); 162 exec("openssl req -in $reqfile -inform DER -out $req_pem -outform PEM"); 163 if (!file_exists($req_pem)) { 164 error_log("EST: simpleenroll - Failed to parse certificate request"); 165 die("Failed to parse certificate request"); 166 } 167 168 /* FIX: validate request and add HS 2.0 extensions to cert */ 169 $cert_pem = "$cadir/tmp/req-signed.pem"; 170 if (file_exists($cert_pem)) 171 unlink($cert_pem); 172 exec("openssl x509 -req -in $req_pem -CAkey $cadir/cakey.pem -out $cert_pem -CA $cadir/cacert.pem -CAserial $cadir/serial -days 365 -text"); 173 if (!file_exists($cert_pem)) { 174 error_log("EST: simpleenroll - Failed to sign certificate"); 175 die("Failed to sign certificate"); 176 } 177 178 $cert = file_get_contents($cert_pem); 179 $handle = popen("openssl x509 -in $cert_pem -serial -noout", "r"); 180 $serial = fread($handle, 200); 181 pclose($handle); 182 $pattern = "/serial=(?P<snhex>[0-9a-fA-F:]*)/m"; 183 preg_match($pattern, $serial, $matches); 184 if (!isset($matches['snhex']) || strlen($matches['snhex']) < 1) { 185 error_log("EST: simpleenroll - Could not get serial number"); 186 die("Could not get serial number"); 187 } 188 $sn = str_replace(":", "", strtoupper($matches['snhex'])); 189 190 $user = "cert-$sn"; 191 error_log("EST: user = $user"); 192 193 $cert_der = "$cadir/tmp/req-signed.der"; 194 if (file_exists($cert_der)) 195 unlink($cert_der); 196 exec("openssl x509 -in $cert_pem -inform PEM -out $cert_der -outform DER"); 197 if (!file_exists($cert_der)) { 198 error_log("EST: simpleenroll - Failed to convert certificate"); 199 die("Failed to convert certificate"); 200 } 201 $der = file_get_contents($cert_der); 202 $fingerprint = hash("sha256", $der); 203 error_log("EST: sha256(DER cert): $fingerprint"); 204 205 $pkcs7 = "$cadir/tmp/est-client.pkcs7"; 206 if (file_exists($pkcs7)) 207 unlink($pkcs7); 208 exec("openssl crl2pkcs7 -nocrl -certfile $cert_pem -out $pkcs7 -outform DER"); 209 if (!file_exists($pkcs7)) { 210 error_log("EST: simpleenroll - Failed to prepare PKCS#7 file"); 211 die("Failed to prepare PKCS#7 file"); 212 } 213 214 if (!$db->exec("UPDATE sessions SET user='$user', cert='$fingerprint', cert_pem='$cert' WHERE rowid=$rowid")) { 215 error_log("EST: simpleenroll - Failed to update session database"); 216 die("Failed to update session database"); 217 } 218 219 header("Content-Transfer-Encoding: base64"); 220 header("Content-Type: application/pkcs7-mime"); 221 222 $data = file_get_contents($pkcs7); 223 $resp = wordwrap(base64_encode($data), 72, "\n", true); 224 echo $resp . "\n"; 225 error_log("EST: simpleenroll - PKCS#7 response: " . $resp); 226} else { 227 header("HTTP/1.0 404 Not Found"); 228 error_log("EST: Unexpected method or path"); 229 die("Unexpected method or path"); 230} 231 232?> 233