Browse code

Neue Zertifikatsverwaltung

git-svn-id: https://svn.schokokeks.org/repos/tools/webinterface/trunk@1421 87cf0b9e-d624-0410-a070-f6ee81989793

bernd authored on19/07/2009 09:45:51
Showing10 changed files
... ...
@@ -169,7 +169,6 @@ function encode_querystring($querystring)
169 169
     $querystring = 'debug&'.$querystring;
170 170
   DEBUG($querystring);
171 171
   $query = explode('&', $querystring);
172
-  DEBUG($query);
173 172
   $new_query = array();
174 173
   foreach ($query AS $item)
175 174
     if ($item != '')
... ...
@@ -180,7 +179,6 @@ function encode_querystring($querystring)
180 179
       else
181 180
         $new_query[] = $key.'='.urlencode($val);
182 181
     }
183
-  DEBUG($new_query);
184 182
   $querystring = implode('&', $new_query);
185 183
   if ($querystring)
186 184
     $querystring = '?'.$querystring;
187 185
new file mode 100644
... ...
@@ -0,0 +1,39 @@
1
+<?php
2
+
3
+require_once("certs.php");
4
+require_role(ROLE_SYSTEMUSER);
5
+
6
+$title = "SSL-Zertifikate";
7
+
8
+output('<h3>SSL-Zertifikate</h3>
9
+<p>Bei schokokeks.org können Sie Ihre eigenen SSL-Zertifikate nutzen. Wir verwenden dafür (wenn nicht anders vereinbart) die <a href="https://wiki.schokokeks.org/SNI">SNI-Technik</a>.</p>
10
+<p>Das Verfahren ist bei uns folgendermaßen implementiert: Sie können hier eines oder mehrere SSL-Zertifikate hochladen, die Sie vorher extern erzeugt haben. Beim Anlegen von Webserver-Konfigurationen können Sie dann eines Ihrer Zertifikate für jede Konfiguration auswählen.</p>
11
+
12
+<h4>Ihre bisher vorhandenen Zertifikate</h4>
13
+');
14
+
15
+$certs = user_certs();
16
+
17
+if (count($certs) > 0)
18
+{
19
+  output("<table><tr><th>Name/Details</th><th>CommonName</th><th>Gültig ab</th><th>Gültig bis</th><th>&#160;</th></tr>");
20
+  foreach ($certs as $c)
21
+  {
22
+    output("<tr><td>{$c['subject']}</td><td>{$c['cn']}</td><td>{$c['valid_from']}</td><td>{$c['valid_until']}</td><td>".internal_link('savecert', '<img src="'.$prefix.'images/delete.png" />', 'action=delete&id='.$c['id'])."</td></tr>");
23
+  } 
24
+  output("</table>");
25
+}
26
+else
27
+{
28
+  output('<p><em>Bisher haben Sie keine Zertifikate eingetragen</em></p>');
29
+}
30
+
31
+output('<p>'.internal_link('newcert', 'Neues Zertifikat hinzufügen').'</p>');
32
+
33
+
34
+
35
+
36
+
37
+
38
+
39
+
... ...
@@ -4,6 +4,7 @@ require_once('inc/debug.php');
4 4
 require_once('inc/security.php');
5 5
 
6 6
 require_once('vhosts.php');
7
+require_once('certs.php');
7 8
 
8 9
 $title = "Subdomain bearbeiten";
9 10
 $section = 'vhosts_vhosts';
... ...
@@ -192,7 +193,7 @@ $form .= "
192 193
 <br />
193 194
 </div>
194 195
 
195
-<h4 style=\"margin-top: 3em;\">Allgemeine Optionen</h4>
196
+<h4 style=\"clear: right; margin-top: 3em;\">Allgemeine Optionen</h4>
196 197
 <div style=\"margin-left: 2em;\">
197 198
     <h5>SSL-Verschlüsselung</h5>
198 199
     <div style=\"margin-left: 2em;\">
... ...
@@ -215,6 +216,46 @@ $form .= "
215 216
 </div>
216 217
     ";
217 218
 
219
+$ipaddrs = user_ipaddrs();
220
+$certs = user_certs();
221
+
222
+if (count($ipaddrs) || count($certs))
223
+{
224
+  $form .= "
225
+<h4 style=\"margin-top: 3em;\">Erweiterte Optionen</h4>
226
+<div style=\"margin-left: 2em;\">
227
+";
228
+  if (count($certs))
229
+  {
230
+    $certselect = array(0 => 'kein Zertifikat / System-Standard benutzen');
231
+    foreach ($certs as $c)
232
+    {
233
+      $certselect[$c['id']] = $c['subject'];
234
+    }
235
+    $form .= "
236
+      <h5>verwendetes SSL-Zertifikat</h5>
237
+      <div style=\"margin-left: 2em;\">
238
+      ".html_select('cert', $certselect, $vhost['certid'])."
239
+      </div>";
240
+  }
241
+ /* if (count($ipaddrs))
242
+  {
243
+    $ipselect = array(0 => 'System-Standard');
244
+    foreach ($ipaddrs AS $i)
245
+    {
246
+      $ipselect[$i] = $i;
247
+    }
248
+    $form .= "
249
+      <h5>IP-Adresse</h5>
250
+      <div style=\"margin-left: 2em;\">
251
+      ".html_select('ipaddr', $ipselect, $vhost['ipaddr'])."
252
+      </div>";
253
+  } */
254
+  $form .= "</div>";
255
+}
256
+
257
+
258
+
218 259
 $form .= '
219 260
   <p><input type="submit" value="Speichern" />&#160;&#160;&#160;&#160;'.internal_link('vhosts', 'Abbrechen').'</p>
220 261
   <p class="warning"><span class="warning">*</span>Es ist im Moment fraglich, ob die Speicherung von Logfiles mit IP-Adressen auf Webservern
221 262
new file mode 100644
... ...
@@ -0,0 +1,100 @@
1
+<?php
2
+
3
+require_once('inc/base.php');
4
+
5
+define("CERT_OK", 0);
6
+define("CERT_INVALID", 1);
7
+define("CERT_NOCHAIN", 2);
8
+
9
+function user_certs()
10
+{
11
+  $uid = (int) $_SESSION['userinfo']['uid'];
12
+  $result = db_query("SELECT id, valid_from, valid_until, subject, cn FROM vhosts.certs WHERE uid=${uid}");
13
+  $ret = array();
14
+  while ($i = mysql_fetch_assoc($result))
15
+    $ret[] = $i;
16
+  DEBUG($ret);
17
+  return $ret;
18
+}
19
+
20
+
21
+function cert_details($id)
22
+{
23
+  $id = (int) $id;
24
+  $uid = (int) $_SESSION['userinfo']['uid'];
25
+  
26
+  $result = db_query("SELECT id, lastchange, valid_from, valid_until, subject, cn, cert, `key`, cabundle FROM vhosts.certs WHERE uid={$uid} AND id={$id}");
27
+  if (mysql_num_rows($result) != 1)
28
+    system_failure("Ungültiges Zertifikat");
29
+  return mysql_fetch_assoc($result);
30
+}
31
+
32
+
33
+function get_available_CAs()
34
+{
35
+  $path = '/etc/apache2/certs/cabundle/';
36
+  $ret = glob($path.'*.pem');
37
+  if (! $ret)
38
+    system_failure("Konnte die CA-Zertifikate nicht laden");
39
+  DEBUG($ret);
40
+  return $ret;
41
+}
42
+
43
+
44
+function validate_certificate($cert, $key)
45
+{  
46
+  if (openssl_x509_check_private_key($cert, $key) !== true)
47
+  {
48
+    DEBUG("Zertifikat und Key passen nicht zusammen");
49
+    return CERT_INVALID;
50
+  }
51
+
52
+  $cacerts = get_available_CAs();
53
+
54
+  if (openssl_x509_checkpurpose($cert, X509_PURPOSE_SSL_SERVER, $cacerts) !== true)
55
+  { 
56
+    DEBUG('certificate was not validated as a server certificate with the available chain');
57
+    return CERT_NOCHAIN;
58
+  }
59
+
60
+  return CERT_OK;
61
+}
62
+
63
+
64
+function parse_cert_details($cert)
65
+{
66
+  $certdata = openssl_x509_parse($cert, true);
67
+  /* 
68
+name => /CN=*.bwurst.org
69
+validFrom_time_t => 1204118790
70
+validTo_time_t => 1267190790
71
+
72
+
73
+  */
74
+ 
75
+  return array('subject' => $certdata['name'], 'cn' => $certdata['subject']['CN'], 'valid_from' => date('Y-m-d', $certdata['validFrom_time_t']), 'valid_until' => date('Y-m-d', $certdata['validTo_time_t']));
76
+}
77
+
78
+
79
+function save_cert($info, $cert, $key)
80
+{
81
+  $subject = mysql_real_escape_string(filter_input_general($info['subject']));
82
+  $cn = mysql_real_escape_string(filter_input_general($info['cn']));
83
+  $valid_from = mysql_real_escape_string($info['valid_from']);
84
+  $valid_until = mysql_real_escape_string($info['valid_until']);
85
+  $cert = mysql_real_escape_string($cert);
86
+  $key = mysql_real_escape_string($key);
87
+  $uid = (int) $_SESSION['userinfo']['uid'];
88
+
89
+  db_query("INSERT INTO vhosts.certs (uid, subject, cn, valid_from, valid_until, cert, `key`) VALUES ({$uid}, '{$subject}', '{$cn}', '{$valid_from}', '{$valid_until}', '{$cert}', '{$key}')");
90
+}
91
+
92
+
93
+function delete_cert($id)
94
+{
95
+  $uid = (int) $_SESSION['userinfo']['uid'];
96
+  $id = (int) $id;
97
+  
98
+  db_query("DELETE FROM vhosts.certs WHERE uid={$uid} AND id={$id} LIMIT 1");
99
+}
100
+
... ...
@@ -6,18 +6,19 @@ require_once("inc/security.php");
6 6
 
7 7
 require_once('class/domain.php');
8 8
 
9
+require_once("certs.php");
10
+
9 11
 
10 12
 function list_vhosts()
11 13
 {
12 14
   $uid = (int) $_SESSION['userinfo']['uid'];
13
-  $result = db_query("SELECT vh.id,fqdn,docroot,docroot_is_default,php,vh.options,logtype,errorlog,IF(dav.id IS NULL OR dav.type='svn', 0, 1) AS is_dav,IF(dav.id IS NULL OR dav.type='dav', 0, 1) AS is_svn, IF(webapps.id IS NULL, 0, 1) AS is_webapp FROM vhosts.v_vhost AS vh LEFT JOIN vhosts.dav ON (dav.vhost=vh.id) LEFT JOIN vhosts.webapps ON (webapps.vhost = vh.id) WHERE uid={$uid} ORDER BY domain,hostname");
15
+  $result = db_query("SELECT vh.id,fqdn,docroot,docroot_is_default,php,vh.certid AS cert, vh.ssl, vh.options,logtype,errorlog,IF(dav.id IS NULL OR dav.type='svn', 0, 1) AS is_dav,IF(dav.id IS NULL OR dav.type='dav', 0, 1) AS is_svn, IF(webapps.id IS NULL, 0, 1) AS is_webapp FROM vhosts.v_vhost AS vh LEFT JOIN vhosts.dav ON (dav.vhost=vh.id) LEFT JOIN vhosts.webapps ON (webapps.vhost = vh.id) WHERE uid={$uid} ORDER BY domain,hostname");
14 16
   $ret = array();
15 17
   while ($item = mysql_fetch_assoc($result))
16 18
     array_push($ret, $item);
17 19
   return $ret;
18 20
 }
19 21
 
20
-
21 22
 function empty_vhost()
22 23
 {
23 24
   $vhost['hostname'] = '';
... ...
@@ -33,8 +34,10 @@ function empty_vhost()
33 34
   $vhost['is_dav'] = 0;
34 35
   $vhost['is_svn'] = 0;
35 36
   $vhost['is_webapp'] = 0;
36
-  $vhsot['webapp_id'] = NULL;
37
-    
37
+  $vhost['webapp_id'] = NULL;
38
+  
39
+  $vhost['cert'] = NULL;
40
+
38 41
   $vhost['options'] = '';
39 42
   return $vhost;
40 43
 }
... ...
@@ -82,7 +85,9 @@ function get_vhost_details($id)
82 85
   if (mysql_num_rows($result) != 1)
83 86
     system_failure('Interner Fehler beim Auslesen der Daten');
84 87
 
85
-  return mysql_fetch_assoc($result);
88
+  $ret = mysql_fetch_assoc($result);
89
+  DEBUG($ret);
90
+  return $ret;
86 91
 }
87 92
 
88 93
 
... ...
@@ -207,13 +212,22 @@ function save_vhost($vhost)
207 212
   }
208 213
   $options = mysql_real_escape_string( $vhost['options'] );
209 214
 
215
+  $cert = 0;
216
+  $certs = user_certs();
217
+  foreach ($certs as $c)
218
+    if ($c['id'] == $vhost['cert'])
219
+      $cert = $c['id'];
220
+
221
+  if ($cert == 0)
222
+    $cert = 'NULL';
223
+
210 224
   if ($id != 0) {
211 225
     logger('modules/vhosts/include/vhosts', 'vhosts', 'Updating vhost #'.$id.' ('.$vhost['hostname'].'.'.$vhost['domain'].')');
212
-    db_query("UPDATE vhosts.vhost SET hostname={$hostname}, domain={$domain}, docroot={$docroot}, php={$php}, `ssl`={$ssl}, logtype={$logtype}, errorlog={$errorlog}, options='{$options}' WHERE id={$id} LIMIT 1");
226
+    db_query("UPDATE vhosts.vhost SET hostname={$hostname}, domain={$domain}, docroot={$docroot}, php={$php}, `ssl`={$ssl}, logtype={$logtype}, errorlog={$errorlog}, certid={$cert}, options='{$options}' WHERE id={$id} LIMIT 1");
213 227
   }
214 228
   else {
215 229
     logger('modules/vhosts/include/vhosts', 'vhosts', 'Creating vhost '.$vhost['hostname'].'.'.$vhost['domain'].'');
216
-    $result = db_query("INSERT INTO vhosts.vhost (user, hostname, domain, docroot, php, `ssl`, logtype, errorlog, options) VALUES ({$_SESSION['userinfo']['uid']}, {$hostname}, {$domain}, {$docroot}, {$php}, {$ssl}, {$logtype}, {$errorlog}, '{$options}')");
230
+    $result = db_query("INSERT INTO vhosts.vhost (user, hostname, domain, docroot, php, `ssl`, logtype, errorlog, certid, options) VALUES ({$_SESSION['userinfo']['uid']}, {$hostname}, {$domain}, {$docroot}, {$php}, {$ssl}, {$logtype}, {$errorlog}, {$cert}, '{$options}')");
217 231
     $id = mysql_insert_id();
218 232
   }
219 233
   $oldvhost = get_vhost_details($id);
... ...
@@ -288,4 +302,19 @@ function save_alias($alias)
288 302
 
289 303
 
290 304
 
305
+
306
+function user_ipaddrs()
307
+{
308
+  $uid = (int) $_SESSION['userinfo']['uid'];
309
+  $result = db_query("SELECT ipaddr FROM vhosts.ipaddr_available WHERE uid={$uid}");
310
+  $ret = array();
311
+  while ($i = mysql_fetch_assoc($result))
312
+  {
313
+    $ret[] = $i['ipaddr'];
314
+  }
315
+  DEBUG($ret);
316
+  return $ret;
317
+}
318
+
319
+
291 320
 ?>
... ...
@@ -5,6 +5,7 @@ $role = $_SESSION['role'];
5 5
 if ($role & ROLE_SYSTEMUSER)
6 6
 {
7 7
     $menu["vhosts_vhosts"] = array("label" => "Webserver", "file" => "vhosts", "weight" => 1);
8
+    $menu["vhosts_certs"] = array("label" => "SSL-Zertifikate", "file" => "certs", "weight" => 10, "submenu" => "vhosts_vhosts");
8 9
 }
9 10
 
10 11
 ?>
11 12
new file mode 100644
... ...
@@ -0,0 +1,31 @@
1
+<?php
2
+
3
+include_once('certs.php');
4
+require_role(ROLE_SYSTEMUSER);
5
+
6
+$section = 'vhosts_certs';
7
+
8
+$title = 'Neues Server-Zertifikat hinzufügen';
9
+
10
+
11
+output('<h3>Neues Server-Zertifikat hinzufügen</h3>
12
+<p>Sie können Ihr eigenes SSL-Zertifikat hinterlegen, das Sie dann für eine oder mehrere Webserver-Konfigurationen verwenden können.</p>
13
+<p>Sie benötigen dazu mindestens ein <strong>Zertifikat</strong> und einen <strong>privaten Schlüssel</strong> (ohne Passwort!). Alle Daten müssen im <strong>PEM-Format</strong> vorliegen, also in etwa die Form</p>
14
+<pre>-----BEGIN CERTIFICATE-----
15
+...
16
+-----END CERTIFICATE-----</pre>
17
+<p>aufweisen. Sind die genannten Vorausetzungen erfüllt, können Sie Ihre Zertifikats-Daten einfach in untenstehendes Formular eingeben.</p>');
18
+
19
+
20
+$form = '
21
+<h4>Server-Zertifikat:</h4>
22
+<p><textarea name="cert" rows="10" cols="70"></textarea></p>
23
+
24
+<h4>privater Schlüssel:</h4>
25
+<p><textarea name="key" rows="10" cols="70"></textarea></p>
26
+
27
+<p><input type="submit" value="Speichern" /></p>
28
+
29
+';
30
+
31
+output(html_form('vhosts_certs_new', 'savecert', 'action=new', $form));
... ...
@@ -101,6 +101,8 @@ if ($_GET['action'] == 'edit')
101 101
     /* Wenn etwas anderes kommt, ist das "beides". So einfach ist das. */
102 102
   }
103 103
 
104
+  $cert = (int) $_POST['cert'];
105
+
104 106
   $logtype = '';
105 107
   switch ($_POST['logtype']) {
106 108
     case 'anonymous':
... ...
@@ -138,6 +140,7 @@ if ($_GET['action'] == 'edit')
138 140
   $vhost['docroot'] = $docroot;
139 141
   $vhost['php'] = $php;
140 142
   $vhost['ssl'] = $ssl;
143
+  $vhost['cert'] = $cert;
141 144
   $vhost['logtype'] = $logtype;
142 145
   $vhost['errorlog'] = $errorlog; 
143 146
   $vhost['options'] = $options;
144 147
new file mode 100644
... ...
@@ -0,0 +1,63 @@
1
+<?php
2
+
3
+require_once("certs.php");
4
+require_role(ROLE_SYSTEMUSER);
5
+
6
+$section = 'vhosts_certs';
7
+
8
+if ($_GET['action'] == 'new')
9
+{
10
+  check_form_token('vhosts_certs_new');
11
+  $cert = $_POST['cert'];
12
+  $key = $_POST['key'];
13
+  if (! $cert or ! $key)
14
+    system_failure('Es muss ein Zertifikat und der dazu passende private Schlüssel eingetragen werden');
15
+
16
+  $result = validate_certificate($cert, $key);
17
+  switch ($result)
18
+  {
19
+    case CERT_OK:
20
+      $certinfo = parse_cert_details($cert);
21
+      save_cert($certinfo, $cert, $key, $cabundle);
22
+      header('Location: certs');
23
+      die();
24
+      break;
25
+    case CERT_INVALID:
26
+      system_failure("Das Zertifikat konnte nicht gelesen werden. Eventuell ist der private Schlüssel mit einem Paswort versehen?");
27
+      break;
28
+    case CERT_NOCHAIN:
29
+      warning('Ihr Zertifikat konnte nicht mit einer Zertifikats-Kette validiert werden. Dies wird zu Problemen beim Betrachten der damit betriebenen Websites führen. Meist liegt dies an einem nicht hinterlegten CA-Bundle. Die Admins können Ihr Zertifikats-Bundle auf dem System eintragen. Das Zertifikat wurde dennoch gespeichert.');
30
+      $certinfo = parse_cert_details($cert);
31
+      save_cert($certinfo, $cert, $key, $cabundle);
32
+      output('<p>'.internal_link('certs', 'Zurück zur Übersicht').'</p>');
33
+      break;
34
+  }
35
+
36
+}
37
+elseif ($_GET['action'] == 'delete')
38
+{
39
+  $cert = cert_details($_GET['id']);
40
+  $sure = user_is_sure();
41
+  if ($sure === NULL)
42
+  {
43
+    are_you_sure("action=delete&id={$cert['id']}", "Soll das Zertifikat für »{$cert['subject']}« (gültig von {$cert['valid_from']} bis {$cert['valid_until']}) wirklich entfernt werden?");
44
+  }
45
+  elseif ($sure === false)
46
+  {
47
+    header('Location: certs');
48
+    die();
49
+  }
50
+  elseif ($sure === true)
51
+  { 
52
+    delete_cert($cert['id']);
53
+    header('Location: certs');
54
+    die();
55
+  }
56
+}
57
+else
58
+{
59
+  system_failure('not implemented');
60
+}
61
+
62
+
63
+
... ...
@@ -21,7 +21,7 @@ $vhosts = list_vhosts();
21 21
 
22 22
 if (count($vhosts) > 0)
23 23
 {
24
-  output("<table><tr><th>(Sub-)Domain</th><th></th><th>Zusätzliche Alias-Namen</th><th>Protokoll</th><th>PHP</th><th>Lokaler Pfad<sup>*</sup></th></tr>\n");
24
+  output("<table><tr><th>(Sub-)Domain</th><th></th><th>Zusätzliche Alias-Namen</th><th>Protokoll</th><th>SSL</th><th>PHP</th><th>Lokaler Pfad<sup>*</sup></th></tr>\n");
25 25
 
26 26
   $even = True;
27 27
 
... ...
@@ -58,6 +58,20 @@ if (count($vhosts) > 0)
58 58
         $logfiles .= ' und Fehler';
59 59
     }
60 60
     output("<td>{$logfiles}</td>");
61
+  
62
+    if ($vhost['ssl'] == 'http')
63
+    {
64
+      output("<td><img src=\"{$prefix}images/error.png\" style=\"height: 18px; width: 18px;\" alt=\"aus\" title=\"SSL ausgeschaltet\" /></td>");
65
+    }
66
+    elseif ($vhost['cert'])
67
+    {
68
+      output("<td><img src=\"{$prefix}images/secure.png\" style=\"height: 16px; width: 16px;\" alt=\"cert\" title=\"SSL mit eigenem Zertifikat\" /></td>");
69
+    }
70
+    else
71
+    {
72
+      output("<td><img src=\"{$prefix}images/ok.png\" style=\"height: 17px; width: 17px;\" alt=\"ein\" title=\"SSL eingeschaltet\" /></td>");
73
+    }
74
+
61 75
     if ($vhost['is_webapp'] == 1) {
62 76
       output('<td colspan="2"><em><strong>Sonderanwendung:</strong> Vorinstallierte Webanwendung</em></td>');
63 77
     }