bug 1200: Use runCommand() to execute gpg to avoid using an unneeded tempfile
[cacert-devel.git] / www / gpg.php
1 <? /*
2 LibreSSL - CAcert web application
3 Copyright (C) 2004-2008 CAcert Inc.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; version 2 of the License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */ ?>
18 <?
19 require_once("../includes/loggedin.php");
20 require_once("../includes/lib/general.php");
21
22 $id = 0; if(array_key_exists('id',$_REQUEST)) $id=intval($_REQUEST['id']);
23 $oldid = $_REQUEST['oldid'] = array_key_exists('oldid',$_REQUEST) ? intval($_REQUEST['oldid']) : 0;
24
25 if($_SESSION['profile']['points'] < 50)
26 {
27 header("location: /account.php");
28 exit;
29 }
30
31 loadem("account");
32
33
34
35 $CSR=""; if(array_key_exists('CSR',$_REQUEST)) $CSR=stripslashes($_REQUEST['CSR']);
36
37
38 if($oldid == "0")
39 {
40 if(array_key_exists('process',$_REQUEST) && $_REQUEST['process'] != "" && $CSR == "")
41 {
42 $_SESSION['_config']['errmsg'] = _("You failed to paste a valid GPG/PGP key.");
43 $id = $oldid;
44 $oldid=0;
45 }
46 }
47
48 $keyid="";
49
50 if(0)
51 {
52 if($_SESSION["profile"]["id"] != 5897)
53 {
54 showheader(_("Welcome to CAcert.org"));
55 echo "The OpenPGP signing system is currently shutdown due to a maintenance. We hope to get it fixed within the next few hours. We are very sorry for the inconvenience.";
56
57 exit(0);
58 }
59 }
60
61
62 function verifyName($name)
63 {
64 if($name == "") return 0;
65 if($name == $_SESSION['profile']['fname']." ".$_SESSION['profile']['lname']) return 1;
66 if($name == $_SESSION['profile']['fname']." ".$_SESSION['profile']['mname']." ".$_SESSION['profile']['lname']) return 1;
67 if($name == $_SESSION['profile']['fname']." ".$_SESSION['profile']['lname']." ".$_SESSION['profile']['suffix']) return 1;
68 if($name == $_SESSION['profile']['fname']." ".$_SESSION['profile']['mname']." ".$_SESSION['profile']['lname']." ".$_SESSION['profile']['suffix']) return 1;
69 return 0;
70
71 }
72
73 function verifyEmail($email)
74 {
75 if($email == "") return 0;
76 if(mysql_num_rows(mysql_query("select * from `email` where `memid`='".$_SESSION['profile']['id']."' and `email`='".mysql_real_escape_string($email)."' and `deleted`=0 and `hash`=''")) > 0) return 1;
77 return 0;
78 }
79
80
81
82 $ToBeDeleted=array();
83 $state=0;
84 if($oldid == "0" && $CSR != "")
85 {
86 if (runCommand('gpg --with-colons --homedir /tmp 2>&1',
87 clean_gpgcsr($CSR),
88 $gpg))
89 {
90 showheader(_("Welcome to CAcert.org"));
91
92 echo "<p style='color:#ff0000'>"._("There was an error parsing your key.")."</p>";
93 unset($_REQUEST['process']);
94 $id = $oldid;
95 unset($oldid);
96 exit();
97 }
98
99 $lines = "";
100 $gpgarr = explode("\n", trim($gpg));
101 foreach($gpgarr as $line)
102 {
103 #echo "Line[]: $line <br/>\n";
104 if(substr($line, 0, 3) == "pub" || substr($line, 0, 3) == "uid")
105 {
106 if($lines != "")
107 $lines .= "\n";
108 $lines .= $line;
109 }
110 }
111 $gpg = $lines;
112 $expires = 0;
113 $nerr=0; $nok=0;
114 $multiple = 0;
115
116 $resulttable=_("The following UIDs were found in your key:")."<br/><table border='1'><tr><td>#</td><td>"._("Name")."</td><td>"._("Email")."</td><td>Result</td>";
117 $i=0;
118 $lastvalidemail="";
119 $npubs=0;
120 foreach(explode("\n", $gpg) as $line)
121 {
122 $bits = explode(":", $line);
123 $resulttable.="<tr><td>".++$i."</td>";
124 $name = $comment = "";
125 if($bits[0] == "pub")
126 {
127 $npubs++;
128 }
129 if($npubs>1)
130 {
131 showheader(_("Welcome to CAcert.org"));
132 echo "<font color='#ff0000'>"._("Please upload only one key at a time.")."</font>";
133 unset($_REQUEST['process']);
134 $id = $oldid;
135 unset($oldid);
136 exit();
137 }
138 if($bits[0] == "pub" && (!$keyid || !$when))
139 {
140 $keyid = $bits[4];
141 $when = $bits[5];
142 if($bits[6] != "")
143 $expires = 1;
144 }
145 $name="";
146 $comm="";
147 $mail="";
148 $uidformatwrong=0;
149
150 if(sizeof($bits)<10) $uidformatwrong=1;
151
152 if(preg_match("/\@.*\@/",$bits[9]))
153 {
154 showheader(_("Welcome to CAcert.org"));
155
156 echo "<font color='#ff0000'>"._("Multiple Email Adresses per UID are not allowed.")."</font>";
157 unset($_REQUEST['process']);
158 $id = $oldid;
159 unset($oldid);
160 exit();
161 }
162
163 // Name (Comment) <Email>
164 if(preg_match("/^([^\(\)\[@<>]+) \(([^\(\)@<>]*)\) <([\w=\/%.-]*\@[\w.-]*|[\w.-]*\![\w=\/%.-]*)>/",$bits[9],$matches))
165 {
166 $name=trim(hex2bin($matches[1]));
167 $nocomment=0;
168 $comm=trim(hex2bin($matches[2]));
169 $mail=trim(hex2bin($matches[3]));
170 }
171 // Name <EMail>
172 elseif(preg_match("/^([^\(\)\[@<>]+) <([\w=\/%.-]*\@[\w.-]*|[\w.-]*\![\w=\/%.-]*)>/",$bits[9],$matches))
173 {
174 $name=trim(hex2bin($matches[1]));
175 $nocomment=1;
176 $comm="";
177 $mail=trim(hex2bin($matches[2]));
178 }
179 // Unrecognized format
180 else
181 {
182 $nocomment=1;
183 $uidformatwrong=1;
184 }
185 $nameok=verifyName($name);
186 $emailok=verifyEmail($mail);
187
188
189 if($comm != "")
190 $comment[] = $comm;
191
192 $resulttable.="<td bgcolor='#".($nameok?"c0ffc0":"ffc0c0")."'>".sanitizeHTML($name)."</td>";
193 $resulttable.="<td bgcolor='#".($emailok?"c0ffc0":"ffc0c0")."'>".sanitizeHTML($mail)."</td>";
194
195 $uidok=0;
196 if($bits[1]=="r")
197 {
198 $rmessage=_("Error: UID is revoked");
199 }
200 elseif($uidformatwrong==1)
201 {
202 $rmessage=_("The format of the UID was not recognized. Please use 'Name (comment) &lt;email@domain>'");
203 }
204 elseif($mail=="" and $name=="")
205 {
206 $rmessage=_("Error: Both Name and Email address are empty");
207 }
208 elseif($emailok and $nameok)
209 {
210 $uidok=1;
211 $rmessage=_("Name and Email OK.");
212 }
213 elseif(!$emailok and !$nameok)
214 {
215 $rmessage=_("Name and Email both cannot be matched with your account.");
216 }
217 elseif($emailok and $name=="")
218 {
219 $uidok=1;
220 $rmessage=_("The email is OK. The name is empty.");
221 }
222 elseif($nameok and $mail=="")
223 {
224 $uidok=1;
225 $rmessage=_("The name is OK. The email is empty.");
226 }
227 elseif(!$emailok)
228 {
229 $rmessage=_("The email address has not been registered and verified in your account. Please add the email address to your account first.");
230 }
231 elseif(!$nameok)
232 {
233 $rmessage=_("The name in the UID does not match the name in your account. Please verify the name.");
234 }
235
236 else
237 {
238 $rmessage=_("Error");
239 }
240 if($uidok)
241 {
242 $nok++;
243 $resulttable.="<td>$rmessage</td>";
244 $lastvalidemail=$mail;
245 }
246 else
247 {
248 $nerr++;
249 //$ToBeDeleted[]=$i;
250 //echo "Adding UID $i\n";
251 $resulttable.="<td bgcolor='#ffc0c0'>$rmessage</td>";
252 }
253 $resulttable.="</tr>\n";
254
255 if($emailok) $multiple++;
256 }
257 $resulttable.="</table>";
258
259 if($nok==0)
260 {
261 showheader(_("Welcome to CAcert.org"));
262 echo $resulttable;
263
264 echo "<font color='#ff0000'>"._("No valid UIDs found on your key")."</font>";
265 unset($_REQUEST['process']);
266 $id = $oldid;
267 unset($oldid);
268 exit();
269 }
270 elseif($nerr)
271 {
272 $resulttable.=_("The unverified UIDs have been removed, the verified UIDs have been signed.");
273 }
274
275
276 }
277
278
279 if($oldid == "0" && $CSR != "")
280 {
281 //set variable for comment
282 if(trim($_REQUEST['description']) == ""){
283 $description= "";
284 }else{
285 $description= trim(mysql_real_escape_string(stripslashes($_REQUEST['description'])));
286 }
287
288 $query = "insert into `gpg` set `memid`='".intval($_SESSION['profile']['id'])."',
289 `email`='".mysql_real_escape_string($lastvalidemail)."',
290 `level`='1',
291 `expires`='".mysql_real_escape_string($expires)."',
292 `multiple`='".mysql_real_escape_string($multiple)."',
293 `keyid`='".mysql_real_escape_string($keyid)."',
294 `description`='".mysql_real_escape_string($description)."'";
295 mysql_query($query);
296 $id = mysql_insert_id();
297
298
299 $cwd = '/tmp/gpgspace'.$id;
300 mkdir($cwd,0755);
301
302 $fp = fopen("$cwd/gpg.csr", "w");
303 fputs($fp, clean_gpgcsr($CSR));
304 fclose($fp);
305
306
307 system("gpg --homedir $cwd --import $cwd/gpg.csr");
308
309
310 $gpg = trim(`gpg --homedir $cwd --with-colons --fixed-list-mode --list-keys $keyid 2>&1`);
311 $lines = "";
312 $gpgarr = explode("\n", $gpg);
313 foreach($gpgarr as $line)
314 {
315 //echo "Line[]: $line <br/>\n";
316 if(substr($line, 0, 4) == "uid:")
317 {
318 $name = $comment = "";
319 $bits = explode(":", $line);
320
321 $pos = strpos($bits[9], "(") - 1;
322 $nocomment = 0;
323 if($pos < 0)
324 {
325 $nocomment = 1;
326 $pos = strpos($bits[9], "<") - 1;
327 }
328 if($pos < 0)
329 {
330 $pos = strlen($bits[9]);
331 }
332
333 $name = trim(hex2bin(trim(substr($bits[9], 0, $pos))));
334 $nameok=verifyName($name);
335 if($nocomment == 0)
336 {
337 $pos += 2;
338 $pos2 = strpos($bits[9], ")");
339 $comm = trim(hex2bin(trim(substr($bits[9], $pos, $pos2 - $pos))));
340 if($comm != "")
341 $comment[] = $comm;
342 $pos = $pos2 + 3;
343 } else {
344 $pos = strpos($bits[9], "<") + 1;
345 }
346
347 $mail="";
348 if (preg_match("/<([\w.-]*\@[\w.-]*)>/", $bits[9],$match)) {
349 //echo "Found: ".$match[1];
350 $mail = trim(hex2bin($match[1]));
351 }
352 else
353 {
354 //echo "Not found!\n";
355 }
356
357 $emailok=verifyEmail($mail);
358
359 $uidid=$bits[7];
360
361 if($bits[1]=="r")
362 {
363 $ToBeDeleted[]=$uidid;
364 }
365 elseif($mail=="" and $name=="")
366 {
367 //echo "$uidid will be deleted\n";
368 $ToBeDeleted[]=$uidid;
369 }
370 elseif($emailok and $nameok)
371 {
372 }
373 elseif($emailok and $name=="")
374 {
375 }
376 elseif($nameok and $mail=="")
377 {
378 }
379 elseif(!$emailok and !$nameok)
380 {
381 //echo "$uidid will be deleted\n";
382 $ToBeDeleted[]=$uidid;
383 }
384 elseif(!$emailok)
385 {
386 //echo "$uidid will be deleted\n";
387 $ToBeDeleted[]=$uidid;
388 }
389 elseif(!$nameok)
390 {
391 //echo "$uidid will be deleted\n";
392 $ToBeDeleted[]=$uidid;
393 }
394
395 }
396 }
397
398 if(count($ToBeDeleted)>0)
399 {
400 $descriptorspec = array(
401 0 => array("pipe", "r"), // stdin is a pipe that the child will read from
402 1 => array("pipe", "w"), // stdout is a pipe that the child will write to
403 2 => array("pipe", "w") // stderr is a file to write to
404 );
405
406 $stderr = fopen('php://stderr', 'w');
407
408 //echo "Keyid: $keyid\n";
409
410 $process = proc_open("/usr/bin/gpg --homedir $cwd --no-tty --command-fd 0 --status-fd 1 --logger-fd 2 --edit-key $keyid", $descriptorspec, $pipes);
411
412 //echo "Process: $process\n";
413 //fputs($stderr,"Process: $process\n");
414
415 if (is_resource($process)) {
416 //echo("it is a resource\n");
417 // $pipes now looks like this:
418 // 0 => writeable handle connected to child stdin
419 // 1 => readable handle connected to child stdout
420 // Any error output will be appended to /tmp/error-output.txt
421 while (!feof($pipes[1]))
422 {
423 $buffer = fgets($pipes[1], 4096);
424 //echo $buffer;
425
426 if($buffer == "[GNUPG:] GET_BOOL keyedit.sign_all.okay\n")
427 {
428 fputs($pipes[0],"yes\n");
429 }
430 elseif($buffer == "[GNUPG:] GOT_IT\n")
431 {
432 }
433 elseif(ereg("^\[GNUPG:\] GET_BOOL keyedit\.remove\.uid\.okay\s*",$buffer))
434 {
435 fputs($pipes[0],"yes\n");
436 }
437 elseif(ereg("^\[GNUPG:\] GET_LINE keyedit\.prompt\s*",$buffer))
438 {
439 if(count($ToBeDeleted)>0)
440 {
441 $delthisuid=array_pop($ToBeDeleted);
442 //echo "Deleting an UID $delthisuid\n";
443 fputs($pipes[0],"uid ".$delthisuid."\n");
444 }
445 else
446 {
447 //echo "Saving\n";
448 fputs($pipes[0],$state?"save\n":"deluid\n");
449 $state++;
450 }
451 }
452 elseif($buffer == "[GNUPG:] GOOD_PASSPHRASE\n")
453 {
454 }
455 elseif(ereg("^\[GNUPG:\] KEYEXPIRED ",$buffer))
456 {
457 echo "Key expired!\n";
458 exit;
459 }
460 elseif($buffer == "")
461 {
462 //echo "Empty!\n";
463 }
464 else
465 {
466 echo "ERROR: UNKNOWN $buffer\n";
467 }
468
469
470 }
471 //echo "Fertig\n";
472 fclose($pipes[0]);
473
474 //echo stream_get_contents($pipes[1]);
475 fclose($pipes[1]);
476
477 // It is important that you close any pipes before calling
478 // proc_close in order to avoid a deadlock
479 $return_value = proc_close($process);
480
481 //echo "command returned $return_value\n";
482 }
483 else
484 {
485 echo "Keine ressource!\n";
486 }
487
488
489 }
490
491
492 $csrname=generatecertpath("csr","gpg",$id);
493 $do=`gpg --homedir $cwd --batch --export-options export-minimal --export $keyid >$csrname`;
494
495 mysql_query("update `gpg` set `csr`='$csrname' where `id`='$id'");
496 waitForResult('gpg', $id);
497
498 showheader(_("Welcome to CAcert.org"));
499 echo $resulttable;
500 $query = "select * from `gpg` where `id`='$id' and `crt`!=''";
501 $res = mysql_query($query);
502 if(mysql_num_rows($res) <= 0)
503 {
504 echo _("Your certificate request has failed to be processed correctly, please try submitting it again.")."<br>\n";
505 echo _("If this is a re-occuring problem, please send a copy of the key you are trying to signed to support@cacert.org. Thank you.");
506 } else {
507 echo "<pre>";
508 readfile(generatecertpath("crt","gpg",$id));
509 echo "</pre>";
510 }
511
512 showfooter();
513 exit;
514 }
515
516 if($oldid == 2 && array_key_exists('change',$_REQUEST) && $_REQUEST['change'] != "")
517 {
518 showheader(_("My CAcert.org Account!"));
519 foreach($_REQUEST as $id => $val)
520 {
521 if(substr($id,0,14)=="check_comment_")
522 {
523 $cid = intval(substr($id,14));
524 $comment=trim(mysql_real_escape_string(stripslashes($_REQUEST['comment_'.$cid])));
525 mysql_query("update `gpg` set `description`='$comment' where `id`='$cid' and `memid`='".$_SESSION['profile']['id']."'");
526 }
527 }
528 echo(_("Certificate settings have been changed.")."<br/>\n");
529 showfooter();
530 exit;
531 }
532
533 $id = intval($id);
534
535 showheader(_("Welcome to CAcert.org"));
536 includeit($id, "gpg");
537 showfooter();
538 ?>