1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
16: class IMP_Crypt_Smime extends Horde_Crypt_Smime
17: {
18:
19: const PUBKEY_FIELD = 'smimePublicKey';
20:
21:
22: const ENCRYPT = 'smime_encrypt';
23: const SIGN = 'smime_sign';
24: const SIGNENC = 'smime_signenc';
25:
26: 27: 28: 29: 30: 31:
32: public function encryptList()
33: {
34: $ret = array(
35: self::ENCRYPT => _("S/MIME Encrypt Message")
36: );
37:
38: if ($this->getPersonalPrivateKey()) {
39: $ret += array(
40: self::SIGN => _("S/MIME Sign Message"),
41: self::SIGNENC => _("S/MIME Sign/Encrypt Message")
42: );
43: }
44:
45: return $ret;
46: }
47:
48: 49: 50: 51: 52:
53: public function addPersonalPublicKey($key)
54: {
55: $GLOBALS['prefs']->setValue('smime_public_key', (is_array($key)) ? implode('', $key) : $key);
56: }
57:
58: 59: 60: 61: 62:
63: public function addPersonalPrivateKey($key)
64: {
65: $GLOBALS['prefs']->setValue('smime_private_key', (is_array($key)) ? implode('', $key) : $key);
66: }
67:
68: 69: 70: 71: 72:
73: public function addAdditionalCert($key)
74: {
75: $GLOBALS['prefs']->setValue('smime_additional_cert', (is_array($key)) ? implode('', $key) : $key);
76: }
77:
78: 79: 80: 81: 82:
83: public function getPersonalPublicKey()
84: {
85: return $GLOBALS['prefs']->getValue('smime_public_key');
86: }
87:
88: 89: 90: 91: 92:
93: public function getPersonalPrivateKey()
94: {
95: return $GLOBALS['prefs']->getValue('smime_private_key');
96: }
97:
98: 99: 100: 101: 102:
103: public function getAdditionalCert()
104: {
105: return $GLOBALS['prefs']->getValue('smime_additional_cert');
106: }
107:
108: 109: 110:
111: public function deletePersonalKeys()
112: {
113: $GLOBALS['prefs']->setValue('smime_public_key', '');
114: $GLOBALS['prefs']->setValue('smime_private_key', '');
115: $GLOBALS['prefs']->setValue('smime_additional_cert', '');
116: $this->unsetPassphrase();
117: }
118:
119: 120: 121: 122: 123: 124: 125:
126: public function addPublicKey($cert)
127: {
128: list($name, $email) = $this->publicKeyInfo($cert);
129:
130: $GLOBALS['registry']->call('contacts/addField', array($email, $name, self::PUBKEY_FIELD, $cert, $GLOBALS['prefs']->getValue('add_source')));
131: }
132:
133: 134: 135: 136: 137: 138: 139: 140:
141: public function publicKeyInfo($cert)
142: {
143:
144: $key_info = openssl_x509_parse($cert);
145: if (!is_array($key_info) || !isset($key_info['subject'])) {
146: throw new Horde_Crypt_Exception(_("Not a valid public key."));
147: }
148:
149:
150: $email = $this->getEmailFromKey($cert);
151: if (is_null($email)) {
152: throw new Horde_Crypt_Exception(_("No email information located in the public key."));
153: }
154:
155:
156: if (isset($key_info['subject']['CN'])) {
157: $name = $key_info['subject']['CN'];
158: } elseif (isset($key_info['subject']['OU'])) {
159: $name = $key_info['subject']['OU'];
160: } else {
161: $name = $email;
162: }
163:
164: return array($name, $email);
165: }
166:
167: 168: 169: 170: 171: 172: 173: 174: 175:
176: protected function _encryptParameters($address)
177: {
178:
179: $addrOb = Horde_Mime_Address::bareAddress($address, $GLOBALS['session']->get('imp', 'maildomain'), true);
180: $key_addr = array_pop($addrOb);
181:
182: $public_key = $this->getPublicKey($key_addr);
183:
184: return array(
185: 'pubkey' => $public_key,
186: 'type' => 'message'
187: );
188: }
189:
190: 191: 192: 193: 194: 195: 196: 197: 198:
199: public function getPublicKey($address)
200: {
201: try {
202: $key = Horde::callHook('smime_key', array($address), 'imp');
203: if ($key) {
204: return $key;
205: }
206: } catch (Horde_Exception_HookNotSet $e) {}
207:
208: $params = IMP::getAddressbookSearchParams();
209:
210: try {
211: $key = $GLOBALS['registry']->call('contacts/getField', array($address, self::PUBKEY_FIELD, $params['sources'], true, true));
212: } catch (Horde_Exception $e) {
213:
214: $identity = $GLOBALS['injector']->getInstance('IMP_Identity');
215: $personal_pubkey = $this->getPersonalPublicKey();
216: if (!empty($personal_pubkey) && $identity->hasAddress($address)) {
217: return $personal_pubkey;
218: }
219:
220: throw $e;
221: }
222:
223: 224: 225:
226: return is_array($key) ? reset($key) : $key;
227: }
228:
229: 230: 231: 232: 233: 234:
235: public function listPublicKeys()
236: {
237: $params = IMP::getAddressbookSearchParams();
238: if (empty($params['sources'])) {
239: return array();
240: }
241: return $GLOBALS['registry']->call('contacts/getAllAttributeValues', array(self::PUBKEY_FIELD, $params['sources']));
242: }
243:
244: 245: 246: 247: 248: 249: 250:
251: public function deletePublicKey($email)
252: {
253: $params = IMP::getAddressbookSearchParams();
254: $GLOBALS['registry']->call('contacts/deleteField', array($email, self::PUBKEY_FIELD, $params['sources']));
255: }
256:
257: 258: 259: 260: 261:
262: protected function _signParameters()
263: {
264: return array(
265: 'type' => 'signature',
266: 'pubkey' => $this->getPersonalPublicKey(),
267: 'privkey' => $this->getPersonalPrivateKey(),
268: 'passphrase' => $this->getPassphrase(),
269: 'sigtype' => 'detach',
270: 'certs' => $this->getAdditionalCert()
271: );
272: }
273:
274: 275: 276: 277: 278: 279: 280: 281:
282: public function verifySignature($text)
283: {
284: return $this->verify($text, empty($GLOBALS['conf']['openssl']['cafile']) ? array() : $GLOBALS['conf']['openssl']['cafile']);
285: }
286:
287: 288: 289: 290: 291: 292: 293: 294:
295: public function decryptMessage($text)
296: {
297: return $this->decrypt($text, array(
298: 'type' => 'message',
299: 'pubkey' => $this->getPersonalPublicKey(),
300: 'privkey' => $this->getPersonalPrivateKey(),
301: 'passphrase' => $this->getPassphrase()
302: ));
303: }
304:
305: 306: 307: 308: 309: 310: 311:
312: public function getPassphrase()
313: {
314: global $session;
315:
316: $private_key = $GLOBALS['prefs']->getValue('smime_private_key');
317: if (empty($private_key)) {
318: return false;
319: }
320:
321: if ($session->exists('imp', 'smime_passphrase')) {
322: $secret = $GLOBALS['injector']->getInstance('Horde_Secret');
323: return $secret->read($secret->getKey('imp'), $session->get('imp', 'smime_passphrase'));
324: } elseif (!$session->exists('imp', 'smime_null_passphrase')) {
325: $session->set(
326: 'imp',
327: 'smime_null_passphrase',
328: $this->verifyPassphrase($private_key, null)
329: ? null
330: : false
331: );
332: }
333:
334: return $session->get('imp', 'smime_null_passphrase');
335: }
336:
337: 338: 339: 340: 341: 342: 343:
344: public function storePassphrase($passphrase)
345: {
346: if ($this->verifyPassphrase($this->getPersonalPrivateKey(), $passphrase) === false) {
347: return false;
348: }
349:
350: $secret = $GLOBALS['injector']->getInstance('Horde_Secret');
351: $GLOBALS['session']->set('imp', 'smime_passphrase', $secret->write($secret->getKey('imp'), $passphrase));
352:
353: return true;
354: }
355:
356: 357: 358:
359: public function unsetPassphrase()
360: {
361: global $session;
362:
363: $session->remove('imp', 'smime_null_passphrase');
364: $session->remove('imp', 'smime_passphrase');
365: }
366:
367: 368: 369: 370: 371: 372: 373: 374: 375:
376: public function savePublicKeyURL($mailbox, $uid, $id)
377: {
378: $params = array(
379: 'actionID' => 'save_attachment_public_key',
380: 'mailbox' => $mailbox,
381: 'uid' => $uid,
382: 'mime_id' => $id
383: );
384: return Horde::popupJs(Horde::url('smime.php'), array('params' => $params, 'height' => 200, 'width' => 450));
385: }
386:
387: 388: 389: 390: 391: 392: 393: 394: 395: 396:
397: public function IMPencryptMIMEPart($mime_part, $to_address)
398: {
399: return $this->encryptMIMEPart($mime_part, $this->_encryptParameters($to_address));
400: }
401:
402: 403: 404: 405: 406: 407: 408: 409:
410: public function IMPsignMIMEPart($mime_part)
411: {
412: return $this->signMIMEPart($mime_part, $this->_signParameters());
413: }
414:
415: 416: 417: 418: 419: 420: 421: 422: 423: 424:
425: public function IMPsignAndEncryptMIMEPart($mime_part, $to_address)
426: {
427: return $this->signAndEncryptMIMEPart($mime_part, $this->_signParameters(), $this->_encryptParameters($to_address));
428: }
429:
430: 431: 432: 433: 434: 435: 436: 437: 438: 439:
440: public function addFromPKCS12($pkcs12, $password, $pkpass = null)
441: {
442: $sslpath = empty($GLOBALS['conf']['openssl']['path'])
443: ? null
444: : $GLOBALS['conf']['openssl']['path'];
445:
446: $params = array('sslpath' => $sslpath, 'password' => $password);
447: if (!empty($pkpass)) {
448: $params['newpassword'] = $pkpass;
449: }
450:
451: $result = $this->parsePKCS12Data($pkcs12, $params);
452: $this->addPersonalPrivateKey($result->private);
453: $this->addPersonalPublicKey($result->public);
454: $this->addAdditionalCert($result->certs);
455: }
456:
457: 458: 459: 460: 461: 462: 463: 464:
465: public function ($data)
466: {
467: $sslpath = empty($GLOBALS['conf']['openssl']['path'])
468: ? null
469: : $GLOBALS['conf']['openssl']['path'];
470:
471: return parent::extractSignedContents($data, $sslpath);
472: }
473:
474:
475:
476: 477: 478: 479: 480:
481: public function printCertInfo($key = '')
482: {
483: $cert_info = $this->certToHTML($key);
484:
485: if (empty($cert_info)) {
486: $this->textWindowOutput('S/MIME Key Information', _("Invalid key"));
487: } else {
488: $this->textWindowOutput('S/MIME Key Information', $cert_info, true);
489: }
490: }
491:
492: 493: 494: 495: 496: 497: 498:
499: public function textWindowOutput($name, $msg, $html = false)
500: {
501: $GLOBALS['browser']->downloadHeaders($name, $html ? 'text/html' : 'text/plain; charset=' . 'UTF-8', true, strlen($msg));
502: echo $msg;
503: }
504:
505: 506: 507: 508: 509: 510:
511: public function importKeyDialog($target, $reload)
512: {
513: $title = _("Import S/MIME Key");
514: require IMP_TEMPLATES . '/common-header.inc';
515:
516: 517:
518: if (IMP::getViewMode() == 'dimp') {
519: $GLOBALS['notification']->detach('status');
520: $GLOBALS['notification']->attach('status');
521: }
522: IMP::status();
523:
524: $t = $GLOBALS['injector']->createInstance('Horde_Template');
525: $t->setOption('gettext', true);
526: $t->set('selfurl', Horde::url('smime.php'));
527: $t->set('broken_mp_form', $GLOBALS['browser']->hasQuirk('broken_multipart_form'));
528: $t->set('reload', htmlspecialchars($reload));
529: $t->set('target', $target);
530: $t->set('forminput', Horde_Util::formInput());
531: $t->set('import_public_key', $target == 'process_import_public_key');
532: $t->set('import_personal_certs', $target == 'process_import_personal_certs');
533: echo $t->fetch(IMP_TEMPLATES . '/smime/import_key.html');
534: }
535:
536: 537: 538: 539: 540: 541: 542: 543:
544: public function getImportKey($key)
545: {
546: if (!empty($key)) {
547: return $key;
548: }
549:
550: $GLOBALS['browser']->wasFileUploaded('upload_key', _("key"));
551: return file_get_contents($_FILES['upload_key']['tmp_name']);
552: }
553:
554: 555: 556: 557: 558:
559: public function reloadWindow($reload)
560: {
561: global $session;
562:
563: $href = $session->retrieve($reload);
564: $session->purge($reload);
565:
566: echo Horde::wrapInlineScript(array(
567: 'opener.focus();',
568: 'opener.location.href="' . $href . '";',
569: 'window.close();'
570: ));
571: }
572:
573: }
574: