]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ssl/gadgets.cc
merge from SslServerCertValidator r12332
[thirdparty/squid.git] / src / ssl / gadgets.cc
1 #include "squid.h"
2 #include "ssl/gadgets.h"
3 #if HAVE_OPENSSL_X509V3_H
4 #include <openssl/x509v3.h>
5 #endif
6
7 EVP_PKEY * Ssl::createSslPrivateKey()
8 {
9 Ssl::EVP_PKEY_Pointer pkey(EVP_PKEY_new());
10
11 if (!pkey)
12 return NULL;
13
14 Ssl::RSA_Pointer rsa(RSA_generate_key(1024, RSA_F4, NULL, NULL));
15
16 if (!rsa)
17 return NULL;
18
19 if (!EVP_PKEY_assign_RSA(pkey.get(), (rsa.get())))
20 return NULL;
21
22 rsa.release();
23 return pkey.release();
24 }
25
26 /**
27 \ingroup ServerProtocolSSLInternal
28 * Set serial random serial number or set random serial number.
29 */
30 static bool setSerialNumber(ASN1_INTEGER *ai, BIGNUM const* serial)
31 {
32 if (!ai)
33 return false;
34 Ssl::BIGNUM_Pointer bn(BN_new());
35 if (serial) {
36 bn.reset(BN_dup(serial));
37 } else {
38 if (!bn)
39 return false;
40
41 if (!BN_pseudo_rand(bn.get(), 64, 0, 0))
42 return false;
43 }
44
45 if (ai && !BN_to_ASN1_INTEGER(bn.get(), ai))
46 return false;
47 return true;
48 }
49
50 bool Ssl::writeCertAndPrivateKeyToMemory(Ssl::X509_Pointer const & cert, Ssl::EVP_PKEY_Pointer const & pkey, std::string & bufferToWrite)
51 {
52 bufferToWrite.clear();
53 if (!pkey || !cert)
54 return false;
55 BIO_Pointer bio(BIO_new(BIO_s_mem()));
56 if (!bio)
57 return false;
58
59 if (!PEM_write_bio_X509 (bio.get(), cert.get()))
60 return false;
61
62 if (!PEM_write_bio_PrivateKey(bio.get(), pkey.get(), NULL, NULL, 0, NULL, NULL))
63 return false;
64
65 char *ptr = NULL;
66 long len = BIO_get_mem_data(bio.get(), &ptr);
67 if (!ptr)
68 return false;
69
70 bufferToWrite = std::string(ptr, len);
71 return true;
72 }
73
74 bool Ssl::appendCertToMemory(Ssl::X509_Pointer const & cert, std::string & bufferToWrite)
75 {
76 if (!cert)
77 return false;
78
79 BIO_Pointer bio(BIO_new(BIO_s_mem()));
80 if (!bio)
81 return false;
82
83 if (!PEM_write_bio_X509 (bio.get(), cert.get()))
84 return false;
85
86 char *ptr = NULL;
87 long len = BIO_get_mem_data(bio.get(), &ptr);
88 if (!ptr)
89 return false;
90
91 if (!bufferToWrite.empty())
92 bufferToWrite.append(" "); // add a space...
93
94 bufferToWrite.append(ptr, len);
95 return true;
96 }
97
98 bool Ssl::writeCertAndPrivateKeyToFile(Ssl::X509_Pointer const & cert, Ssl::EVP_PKEY_Pointer const & pkey, char const * filename)
99 {
100 if (!pkey || !cert)
101 return false;
102
103 Ssl::BIO_Pointer bio(BIO_new(BIO_s_file_internal()));
104 if (!bio)
105 return false;
106 if (!BIO_write_filename(bio.get(), const_cast<char *>(filename)))
107 return false;
108
109 if (!PEM_write_bio_X509(bio.get(), cert.get()))
110 return false;
111
112 if (!PEM_write_bio_PrivateKey(bio.get(), pkey.get(), NULL, NULL, 0, NULL, NULL))
113 return false;
114
115 return true;
116 }
117
118 bool Ssl::readCertAndPrivateKeyFromMemory(Ssl::X509_Pointer & cert, Ssl::EVP_PKEY_Pointer & pkey, char const * bufferToRead)
119 {
120 Ssl::BIO_Pointer bio(BIO_new(BIO_s_mem()));
121 BIO_puts(bio.get(), bufferToRead);
122
123 X509 * certPtr = NULL;
124 cert.reset(PEM_read_bio_X509(bio.get(), &certPtr, 0, 0));
125 if (!cert)
126 return false;
127
128 EVP_PKEY * pkeyPtr = NULL;
129 pkey.reset(PEM_read_bio_PrivateKey(bio.get(), &pkeyPtr, 0, 0));
130 if (!pkey)
131 return false;
132
133 return true;
134 }
135
136 bool Ssl::readCertFromMemory(X509_Pointer & cert, char const * bufferToRead)
137 {
138 Ssl::BIO_Pointer bio(BIO_new(BIO_s_mem()));
139 BIO_puts(bio.get(), bufferToRead);
140
141 X509 * certPtr = NULL;
142 cert.reset(PEM_read_bio_X509(bio.get(), &certPtr, 0, 0));
143 if (!cert)
144 return false;
145
146 return true;
147 }
148
149 // According to RFC 5280 (Section A.1), the common name length in a certificate
150 // can be at most 64 characters
151 static const size_t MaxCnLen = 64;
152
153 // Replace certs common name with the given
154 static bool replaceCommonName(Ssl::X509_Pointer & cert, std::string const &rawCn)
155 {
156 std::string cn = rawCn;
157
158 if (cn.length() > MaxCnLen) {
159 // In the case the length od CN is more than the maximum supported size
160 // try to use the first upper level domain.
161 size_t pos = 0;
162 do {
163 pos = cn.find('.', pos + 1);
164 } while (pos != std::string::npos && (cn.length() - pos + 2) > MaxCnLen);
165
166 // If no short domain found or this domain is a toplevel domain
167 // we failed to find a good cn name.
168 if (pos == std::string::npos || cn.find('.', pos + 1) == std::string::npos)
169 return false;
170
171 std::string fixedCn(1, '*');
172 fixedCn.append(cn.c_str() + pos);
173 cn = fixedCn;
174 }
175
176 // Assume [] surround an IPv6 address and strip them because browsers such
177 // as Firefox, Chromium, and Safari prefer bare IPv6 addresses in CNs.
178 if (cn.length() > 2 && *cn.begin() == '[' && *cn.rbegin() == ']')
179 cn = cn.substr(1, cn.size()-2);
180
181 X509_NAME *name = X509_get_subject_name(cert.get());
182 if (!name)
183 return false;
184 // Remove the CN part:
185 int loc = X509_NAME_get_index_by_NID(name, NID_commonName, -1);
186 if (loc >=0) {
187 X509_NAME_ENTRY *tmp = X509_NAME_get_entry(name, loc);
188 X509_NAME_delete_entry(name, loc);
189 X509_NAME_ENTRY_free(tmp);
190 }
191
192 // Add a new CN
193 return X509_NAME_add_entry_by_NID(name, NID_commonName, MBSTRING_ASC,
194 (unsigned char *)(cn.c_str()), -1, -1, 0);
195 }
196
197 const char *Ssl::CertSignAlgorithmStr[] = {
198 "signTrusted",
199 "signUntrusted",
200 "signSelf",
201 NULL
202 };
203
204 const char *Ssl::CertAdaptAlgorithmStr[] = {
205 "setValidAfter",
206 "setValidBefore",
207 "setCommonName",
208 NULL
209 };
210
211 Ssl::CertificateProperties::CertificateProperties():
212 setValidAfter(false),
213 setValidBefore(false),
214 setCommonName(false),
215 signAlgorithm(Ssl::algSignEnd)
216 {}
217
218 std::string & Ssl::CertificateProperties::dbKey() const
219 {
220 static std::string certKey;
221 certKey.clear();
222 certKey.reserve(4096);
223 if (mimicCert.get()) {
224 char buf[1024];
225 certKey.append(X509_NAME_oneline(X509_get_subject_name(mimicCert.get()), buf, sizeof(buf)));
226 }
227
228 if (certKey.empty()) {
229 certKey.append("/CN=", 4);
230 certKey.append(commonName);
231 }
232
233 if (setValidAfter)
234 certKey.append("+SetValidAfter=on", 17);
235
236 if (setValidBefore)
237 certKey.append("+SetValidBefore=on", 18);
238
239 if (setCommonName) {
240 certKey.append("+SetCommonName=", 15);
241 certKey.append(commonName);
242 }
243
244 if (signAlgorithm != Ssl::algSignEnd) {
245 certKey.append("+Sign=", 6);
246 certKey.append(certSignAlgorithm(signAlgorithm));
247 }
248
249 return certKey;
250 }
251
252 static bool buildCertificate(Ssl::X509_Pointer & cert, Ssl::CertificateProperties const &properties)
253 {
254 // not an Ssl::X509_NAME_Pointer because X509_REQ_get_subject_name()
255 // returns a pointer to the existing subject name. Nothing to clean here.
256 if (properties.mimicCert.get()) {
257 // Leave subject empty if we cannot extract it from true cert.
258 if (X509_NAME *name = X509_get_subject_name(properties.mimicCert.get())) {
259 // X509_set_subject_name will call X509_dup for name
260 X509_set_subject_name(cert.get(), name);
261 }
262 }
263
264 if (properties.setCommonName || !properties.mimicCert.get()) {
265 // In this case the CN of the certificate given by the user
266 // Ignore errors: it is better to make a certificate with no CN
267 // than to quit ssl_crtd because we cannot make a certificate.
268 // Most errors are caused by user input such as huge domain names.
269 (void)replaceCommonName(cert, properties.commonName);
270 }
271
272 // We should get caCert notBefore and notAfter fields and do not allow
273 // notBefore/notAfter values from certToMimic before/after notBefore/notAfter
274 // fields from caCert.
275 // Currently there is not any way in openssl tollkit to compare two ASN1_TIME
276 // objects.
277 ASN1_TIME *aTime = NULL;
278 if (!properties.setValidBefore && properties.mimicCert.get())
279 aTime = X509_get_notBefore(properties.mimicCert.get());
280 if (!aTime && properties.signWithX509.get())
281 aTime = X509_get_notBefore(properties.signWithX509.get());
282
283 if (aTime) {
284 if (!X509_set_notBefore(cert.get(), aTime))
285 return false;
286 } else if (!X509_gmtime_adj(X509_get_notBefore(cert.get()), (-2)*24*60*60))
287 return false;
288
289 aTime = NULL;
290 if (!properties.setValidAfter && properties.mimicCert.get())
291 aTime = X509_get_notAfter(properties.mimicCert.get());
292 if (!aTime && properties.signWithX509.get())
293 aTime = X509_get_notAfter(properties.signWithX509.get());
294 if (aTime) {
295 if (!X509_set_notAfter(cert.get(), aTime))
296 return false;
297 } else if (!X509_gmtime_adj(X509_get_notAfter(cert.get()), 60*60*24*356*3))
298 return false;
299
300 // mimic the alias and possibly subjectAltName
301 if (properties.mimicCert.get()) {
302 unsigned char *alStr;
303 int alLen;
304 alStr = X509_alias_get0(properties.mimicCert.get(), &alLen);
305 if (alStr) {
306 X509_alias_set1(cert.get(), alStr, alLen);
307 }
308
309 // Mimic subjectAltName unless we used a configured CN: browsers reject
310 // certificates with CN unrelated to subjectAltNames.
311 if (!properties.setCommonName) {
312 int pos=X509_get_ext_by_NID (properties.mimicCert.get(), OBJ_sn2nid("subjectAltName"), -1);
313 X509_EXTENSION *ext=X509_get_ext(properties.mimicCert.get(), pos);
314 if (ext) {
315 X509_add_ext(cert.get(), ext, -1);
316 /* According the RFC 5280 using extensions requires version 3
317 certificate.
318 Set version value to 2 for version 3 certificates.
319 */
320 X509_set_version(cert.get(), 2);
321 }
322 }
323 }
324
325 return true;
326 }
327
328 static bool generateFakeSslCertificate(Ssl::X509_Pointer & certToStore, Ssl::EVP_PKEY_Pointer & pkeyToStore, Ssl::CertificateProperties const &properties, Ssl::BIGNUM_Pointer const &serial)
329 {
330 Ssl::EVP_PKEY_Pointer pkey;
331 // Use signing certificates private key as generated certificate private key
332 if (properties.signWithPkey.get())
333 pkey.resetAndLock(properties.signWithPkey.get());
334 else // if not exist generate one
335 pkey.reset(Ssl::createSslPrivateKey());
336
337 if (!pkey)
338 return false;
339
340 Ssl::X509_Pointer cert(X509_new());
341 if (!cert)
342 return false;
343
344 // Set pub key and serial given by the caller
345 if (!X509_set_pubkey(cert.get(), pkey.get()))
346 return false;
347 if (!setSerialNumber(X509_get_serialNumber(cert.get()), serial.get()))
348 return false;
349
350 // Fill the certificate with the required properties
351 if (!buildCertificate(cert, properties))
352 return false;
353
354 int ret = 0;
355 // Set issuer name, from CA or our subject name for self signed cert
356 if (properties.signAlgorithm != Ssl::algSignSelf && properties.signWithX509.get())
357 ret = X509_set_issuer_name(cert.get(), X509_get_subject_name(properties.signWithX509.get()));
358 else // Self signed certificate, set issuer to self
359 ret = X509_set_issuer_name(cert.get(), X509_get_subject_name(cert.get()));
360 if (!ret)
361 return false;
362
363 /*Now sign the request */
364 if (properties.signAlgorithm != Ssl::algSignSelf && properties.signWithPkey.get())
365 ret = X509_sign(cert.get(), properties.signWithPkey.get(), EVP_sha1());
366 else //else sign with self key (self signed request)
367 ret = X509_sign(cert.get(), pkey.get(), EVP_sha1());
368
369 if (!ret)
370 return false;
371
372 certToStore.reset(cert.release());
373 pkeyToStore.reset(pkey.release());
374 return true;
375 }
376
377 static BIGNUM *createCertSerial(unsigned char *md, unsigned int n)
378 {
379
380 assert(n == 20); //for sha1 n is 20 (for md5 n is 16)
381
382 BIGNUM *serial = NULL;
383 serial = BN_bin2bn(md, n, NULL);
384
385 // if the serial is "0" set it to '1'
386 if (BN_is_zero(serial))
387 BN_one(serial);
388
389 // serial size does not exceed 20 bytes
390 assert(BN_num_bits(serial) <= 160);
391
392 // According the RFC 5280, serial is an 20 bytes ASN.1 INTEGER (a signed big integer)
393 // and the maximum value for X.509 certificate serial number is 2^159-1 and
394 // the minimum 0. If the first bit of the serial is '1' ( eg 2^160-1),
395 // will result to a negative integer.
396 // To handle this, if the produced serial is greater than 2^159-1
397 // truncate the last bit
398 if (BN_is_bit_set(serial, 159))
399 BN_clear_bit(serial, 159);
400
401 return serial;
402 }
403
404 /// Return the SHA1 digest of the DER encoded version of the certificate
405 /// stored in a BIGNUM
406 static BIGNUM *x509Digest(Ssl::X509_Pointer const & cert)
407 {
408 unsigned int n;
409 unsigned char md[EVP_MAX_MD_SIZE];
410
411 if (!X509_digest(cert.get(),EVP_sha1(),md,&n))
412 return NULL;
413
414 return createCertSerial(md, n);
415 }
416
417 static BIGNUM *x509Pubkeydigest(Ssl::X509_Pointer const & cert)
418 {
419 unsigned int n;
420 unsigned char md[EVP_MAX_MD_SIZE];
421
422 if (!X509_pubkey_digest(cert.get(),EVP_sha1(),md,&n))
423 return NULL;
424
425 return createCertSerial(md, n);
426 }
427
428 /// Generate a unique serial number based on a Ssl::CertificateProperties object
429 /// for a new generated certificate
430 static bool createSerial(Ssl::BIGNUM_Pointer &serial, Ssl::CertificateProperties const &properties)
431 {
432 Ssl::EVP_PKEY_Pointer fakePkey;
433 Ssl::X509_Pointer fakeCert;
434
435 serial.reset(x509Pubkeydigest(properties.signWithX509));
436 if (!serial.get()) {
437 serial.reset(BN_new());
438 BN_zero(serial.get());
439 }
440
441 if (!generateFakeSslCertificate(fakeCert, fakePkey, properties, serial))
442 return false;
443
444 // The x509Fingerprint return an SHA1 hash.
445 // both SHA1 hash and maximum serial number size are 20 bytes.
446 BIGNUM *r = x509Digest(fakeCert);
447 if (!r)
448 return false;
449
450 serial.reset(r);
451 return true;
452 }
453
454 bool Ssl::generateSslCertificate(Ssl::X509_Pointer & certToStore, Ssl::EVP_PKEY_Pointer & pkeyToStore, Ssl::CertificateProperties const &properties)
455 {
456 Ssl::BIGNUM_Pointer serial;
457
458 if (!createSerial(serial, properties))
459 return false;
460
461 return generateFakeSslCertificate(certToStore, pkeyToStore, properties, serial);
462 }
463
464 /**
465 \ingroup ServerProtocolSSLInternal
466 * Read certificate from file.
467 */
468 static X509 * readSslX509Certificate(char const * certFilename)
469 {
470 if (!certFilename)
471 return NULL;
472 Ssl::BIO_Pointer bio(BIO_new(BIO_s_file_internal()));
473 if (!bio)
474 return NULL;
475 if (!BIO_read_filename(bio.get(), certFilename))
476 return NULL;
477 X509 *certificate = PEM_read_bio_X509(bio.get(), NULL, NULL, NULL);
478 return certificate;
479 }
480
481 EVP_PKEY * Ssl::readSslPrivateKey(char const * keyFilename, pem_password_cb *passwd_callback)
482 {
483 if (!keyFilename)
484 return NULL;
485 Ssl::BIO_Pointer bio(BIO_new(BIO_s_file_internal()));
486 if (!bio)
487 return NULL;
488 if (!BIO_read_filename(bio.get(), keyFilename))
489 return NULL;
490 EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio.get(), NULL, passwd_callback, NULL);
491 return pkey;
492 }
493
494 void Ssl::readCertAndPrivateKeyFromFiles(Ssl::X509_Pointer & cert, Ssl::EVP_PKEY_Pointer & pkey, char const * certFilename, char const * keyFilename)
495 {
496 if (keyFilename == NULL)
497 keyFilename = certFilename;
498 pkey.reset(readSslPrivateKey(keyFilename));
499 cert.reset(readSslX509Certificate(certFilename));
500 if (!pkey || !cert || !X509_check_private_key(cert.get(), pkey.get())) {
501 pkey.reset(NULL);
502 cert.reset(NULL);
503 }
504 }
505
506 bool Ssl::sslDateIsInTheFuture(char const * date)
507 {
508 ASN1_UTCTIME tm;
509 tm.flags = 0;
510 tm.type = 23;
511 tm.data = (unsigned char *)date;
512 tm.length = strlen(date);
513
514 return (X509_cmp_current_time(&tm) > 0);
515 }
516
517 /// Print the time represented by a ASN1_TIME struct to a string using GeneralizedTime format
518 static bool asn1timeToGeneralizedTimeStr(ASN1_TIME *aTime, char *buf, int bufLen)
519 {
520 // ASN1_Time holds time to UTCTime or GeneralizedTime form.
521 // UTCTime has the form YYMMDDHHMMSS[Z | [+|-]offset]
522 // GeneralizedTime has the form YYYYMMDDHHMMSS[Z | [+|-] offset]
523
524 // length should have space for data plus 2 extra bytes for the two extra year fields
525 // plus the '\0' char.
526 if ((aTime->length + 3) > bufLen)
527 return false;
528
529 char *str;
530 if (aTime->type == V_ASN1_UTCTIME) {
531 if (aTime->data[0] > '5') { // RFC 2459, section 4.1.2.5.1
532 buf[0] = '1';
533 buf[1] = '9';
534 } else {
535 buf[0] = '2';
536 buf[1] = '0';
537 }
538 str = buf +2;
539 } else // if (aTime->type == V_ASN1_GENERALIZEDTIME)
540 str = buf;
541
542 memcpy(str, aTime->data, aTime->length);
543 str[aTime->length] = '\0';
544 return true;
545 }
546
547 static int asn1time_cmp(ASN1_TIME *asnTime1, ASN1_TIME *asnTime2)
548 {
549 char strTime1[64], strTime2[64];
550 if (!asn1timeToGeneralizedTimeStr(asnTime1, strTime1, sizeof(strTime1)))
551 return -1;
552 if (!asn1timeToGeneralizedTimeStr(asnTime2, strTime2, sizeof(strTime2)))
553 return -1;
554
555 return strcmp(strTime1, strTime2);
556 }
557
558 bool Ssl::certificateMatchesProperties(X509 *cert, CertificateProperties const &properties)
559 {
560 assert(cert);
561
562 // For non self-signed certificates we have to check if the signing certificate changed
563 if (properties.signAlgorithm != Ssl::algSignSelf) {
564 assert(properties.signWithX509.get());
565 if (X509_check_issued(properties.signWithX509.get(), cert) != X509_V_OK)
566 return false;
567 }
568
569 X509 *cert2 = properties.mimicCert.get();
570 // If there is not certificate to mimic stop here
571 if (!cert2)
572 return true;
573
574 if (!properties.setCommonName) {
575 X509_NAME *cert1_name = X509_get_subject_name(cert);
576 X509_NAME *cert2_name = X509_get_subject_name(cert2);
577 if (X509_NAME_cmp(cert1_name, cert2_name) != 0)
578 return false;
579 } else if (properties.commonName != CommonHostName(cert))
580 return false;
581
582 if (!properties.setValidBefore) {
583 ASN1_TIME *aTime = X509_get_notBefore(cert);
584 ASN1_TIME *bTime = X509_get_notBefore(cert2);
585 if (asn1time_cmp(aTime, bTime) != 0)
586 return false;
587 } else if (X509_cmp_current_time(X509_get_notBefore(cert)) >= 0) {
588 // notBefore does not exist (=0) or it is in the future (>0)
589 return false;
590 }
591
592 if (!properties.setValidAfter) {
593 ASN1_TIME *aTime = X509_get_notAfter(cert);
594 ASN1_TIME *bTime = X509_get_notAfter(cert2);
595 if (asn1time_cmp(aTime, bTime) != 0)
596 return false;
597 } else if (X509_cmp_current_time(X509_get_notAfter(cert)) <= 0) {
598 // notAfter does not exist (0) or it is in the past (<0)
599 return false;
600 }
601
602 char *alStr1;
603 int alLen;
604 alStr1 = (char *)X509_alias_get0(cert, &alLen);
605 char *alStr2 = (char *)X509_alias_get0(cert2, &alLen);
606 if ((!alStr1 && alStr2) || (alStr1 && !alStr2) ||
607 (alStr1 && alStr2 && strcmp(alStr1, alStr2)) != 0)
608 return false;
609
610 // Compare subjectAltName extension
611 STACK_OF(GENERAL_NAME) * cert1_altnames;
612 cert1_altnames = (STACK_OF(GENERAL_NAME)*)X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
613 STACK_OF(GENERAL_NAME) * cert2_altnames;
614 cert2_altnames = (STACK_OF(GENERAL_NAME)*)X509_get_ext_d2i(cert2, NID_subject_alt_name, NULL, NULL);
615 bool match = true;
616 if (cert1_altnames) {
617 int numalts = sk_GENERAL_NAME_num(cert1_altnames);
618 for (int i = 0; match && i < numalts; ++i) {
619 const GENERAL_NAME *aName = sk_GENERAL_NAME_value(cert1_altnames, i);
620 match = sk_GENERAL_NAME_find(cert2_altnames, aName);
621 }
622 } else if (cert2_altnames)
623 match = false;
624
625 sk_GENERAL_NAME_pop_free(cert1_altnames, GENERAL_NAME_free);
626 sk_GENERAL_NAME_pop_free(cert2_altnames, GENERAL_NAME_free);
627
628 return match;
629 }
630
631 static const char *getSubjectEntry(X509 *x509, int nid)
632 {
633 static char name[1024] = ""; // stores common name (CN)
634
635 if (!x509)
636 return NULL;
637
638 // TODO: What if the entry is a UTF8String? See X509_NAME_get_index_by_NID(3ssl).
639 const int nameLen = X509_NAME_get_text_by_NID(
640 X509_get_subject_name(x509),
641 nid, name, sizeof(name));
642
643 if (nameLen > 0)
644 return name;
645
646 return NULL;
647 }
648
649 const char *Ssl::CommonHostName(X509 *x509)
650 {
651 return getSubjectEntry(x509, NID_commonName);
652 }
653
654 const char *Ssl::getOrganization(X509 *x509)
655 {
656 return getSubjectEntry(x509, NID_organizationName);
657 }
658