]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/tls-gnutls.c
Remove all of the Subversion keywords from various source files.
[thirdparty/cups.git] / cups / tls-gnutls.c
CommitLineData
2c85b752 1/*
2c85b752
MS
2 * TLS support code for CUPS using GNU TLS.
3 *
ef146940 4 * Copyright 2007-2015 by Apple Inc.
2c85b752
MS
5 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
6 *
7 * These coded instructions, statements, and computer programs are the
8 * property of Apple Inc. and are protected by Federal copyright
9 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
10 * which should have been included with this file. If this file is
11 * file is missing or damaged, see the license at "http://www.cups.org/".
12 *
13 * This file is subject to the Apple OS-Developed Software exception.
14 */
15
ebb24a07 16/**** This file is included from tls.c ****/
2c85b752 17
d0facf48
MS
18/*
19 * Include necessary headers...
20 */
21
22#include <sys/stat.h>
23
24
07623986
MS
25/*
26 * Local globals...
27 */
28
29static int tls_auto_create = 0;
30 /* Auto-create self-signed certs? */
31static char *tls_common_name = NULL;
32 /* Default common name */
33static char *tls_keypath = NULL;
34 /* Server cert keychain path */
35static _cups_mutex_t tls_mutex = _CUPS_MUTEX_INITIALIZER;
36 /* Mutex for keychain/certs */
b37d45d9 37static int tls_options = -1;/* Options for TLS connections */
07623986
MS
38
39
2c85b752
MS
40/*
41 * Local functions...
42 */
43
ff82e169 44static gnutls_x509_crt_t http_gnutls_create_credential(http_credential_t *credential);
d0facf48 45static const char *http_gnutls_default_path(char *buffer, size_t bufsize);
a2751f30 46static const char *http_gnutls_make_path(char *buffer, size_t bufsize, const char *dirname, const char *filename, const char *ext);
d0facf48
MS
47static ssize_t http_gnutls_read(gnutls_transport_ptr_t ptr, void *data, size_t length);
48static ssize_t http_gnutls_write(gnutls_transport_ptr_t ptr, const void *data, size_t length);
dd332638
MS
49
50
07623986
MS
51/*
52 * 'cupsMakeServerCredentials()' - Make a self-signed certificate and private key pair.
53 *
e1f19878 54 * @since CUPS 2.0/OS 10.10@
07623986
MS
55 */
56
57int /* O - 1 on success, 0 on failure */
58cupsMakeServerCredentials(
59 const char *path, /* I - Path to keychain/directory */
60 const char *common_name, /* I - Common name */
61 int num_alt_names, /* I - Number of subject alternate names */
62 const char **alt_names, /* I - Subject Alternate Names */
63 time_t expiration_date) /* I - Expiration date */
64{
f394e0f7
MS
65 gnutls_x509_crt_t crt; /* Self-signed certificate */
66 gnutls_x509_privkey_t key; /* Encryption private key */
67 char temp[1024], /* Temporary directory name */
68 crtfile[1024], /* Certificate filename */
172bdf5d
MS
69 keyfile[1024]; /* Private key filename */
70 cups_lang_t *language; /* Default language info */
71 cups_file_t *fp; /* Key/cert file */
72 unsigned char buffer[8192]; /* Buffer for x509 data */
73 size_t bytes; /* Number of bytes of data */
74 unsigned char serial[4]; /* Serial number buffer */
75 time_t curtime; /* Current time */
76 int result; /* Result of GNU TLS calls */
07623986 77
172bdf5d
MS
78
79 DEBUG_printf(("cupsMakeServerCredentials(path=\"%s\", common_name=\"%s\", num_alt_names=%d, alt_names=%p, expiration_date=%d)", path, common_name, num_alt_names, alt_names, (int)expiration_date));
80
81 /*
82 * Filenames...
83 */
84
85 if (!path)
d0facf48 86 path = http_gnutls_default_path(temp, sizeof(temp));
172bdf5d 87
d0facf48
MS
88 if (!path || !common_name)
89 {
90 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
91 return (0);
172bdf5d 92 }
26435c51
MS
93
94 http_gnutls_make_path(crtfile, sizeof(crtfile), path, common_name, "crt");
95 http_gnutls_make_path(keyfile, sizeof(keyfile), path, common_name, "key");
172bdf5d
MS
96
97 /*
98 * Create the encryption key...
99 */
100
d0facf48
MS
101 DEBUG_puts("1cupsMakeServerCredentials: Creating key pair.");
102
172bdf5d
MS
103 gnutls_x509_privkey_init(&key);
104 gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 2048, 0);
105
d0facf48
MS
106 DEBUG_puts("1cupsMakeServerCredentials: Key pair created.");
107
172bdf5d
MS
108 /*
109 * Save it...
110 */
111
112 bytes = sizeof(buffer);
113
d0facf48 114 if ((result = gnutls_x509_privkey_export(key, GNUTLS_X509_FMT_PEM, buffer, &bytes)) < 0)
172bdf5d 115 {
d0facf48 116 DEBUG_printf(("1cupsMakeServerCredentials: Unable to export private key: %s", gnutls_strerror(result)));
172bdf5d
MS
117 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, gnutls_strerror(result), 0);
118 gnutls_x509_privkey_deinit(key);
119 return (0);
120 }
121 else if ((fp = cupsFileOpen(keyfile, "w")) != NULL)
122 {
d0facf48 123 DEBUG_printf(("1cupsMakeServerCredentials: Writing private key to \"%s\".", keyfile));
172bdf5d
MS
124 cupsFileWrite(fp, (char *)buffer, bytes);
125 cupsFileClose(fp);
126 }
127 else
128 {
d0facf48 129 DEBUG_printf(("1cupsMakeServerCredentials: Unable to create private key file \"%s\": %s", keyfile, strerror(errno)));
172bdf5d
MS
130 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
131 gnutls_x509_privkey_deinit(key);
132 return (0);
133 }
134
135 /*
136 * Create the self-signed certificate...
137 */
138
d0facf48 139 DEBUG_puts("1cupsMakeServerCredentials: Generating self-signed X.509 certificate.");
172bdf5d
MS
140
141 language = cupsLangDefault();
142 curtime = time(NULL);
143 serial[0] = curtime >> 24;
144 serial[1] = curtime >> 16;
145 serial[2] = curtime >> 8;
146 serial[3] = curtime;
147
148 gnutls_x509_crt_init(&crt);
149 if (strlen(language->language) == 5)
150 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0,
151 language->language + 3, 2);
152 else
153 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0,
154 "US", 2);
155 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COMMON_NAME, 0,
156 common_name, strlen(common_name));
157 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATION_NAME, 0,
158 common_name, strlen(common_name));
159 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME,
160 0, "Unknown", 7);
161 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0,
162 "Unknown", 7);
163 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_LOCALITY_NAME, 0,
164 "Unknown", 7);
165/* gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_PKCS9_EMAIL, 0,
166 ServerAdmin, strlen(ServerAdmin));*/
167 gnutls_x509_crt_set_key(crt, key);
168 gnutls_x509_crt_set_serial(crt, serial, sizeof(serial));
169 gnutls_x509_crt_set_activation_time(crt, curtime);
170 gnutls_x509_crt_set_expiration_time(crt, curtime + 10 * 365 * 86400);
171 gnutls_x509_crt_set_ca_status(crt, 0);
172 if (num_alt_names > 0)
173 gnutls_x509_crt_set_subject_alternative_name(crt, GNUTLS_SAN_DNSNAME, alt_names[0]);
174 gnutls_x509_crt_set_key_purpose_oid(crt, GNUTLS_KP_TLS_WWW_SERVER, 0);
175 gnutls_x509_crt_set_key_usage(crt, GNUTLS_KEY_KEY_ENCIPHERMENT);
176 gnutls_x509_crt_set_version(crt, 3);
177
178 bytes = sizeof(buffer);
179 if (gnutls_x509_crt_get_key_id(crt, 0, buffer, &bytes) >= 0)
180 gnutls_x509_crt_set_subject_key_id(crt, buffer, bytes);
181
182 gnutls_x509_crt_sign(crt, crt, key);
183
184 /*
185 * Save it...
186 */
187
188 bytes = sizeof(buffer);
189 if ((result = gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM, buffer, &bytes)) < 0)
190 {
d0facf48 191 DEBUG_printf(("1cupsMakeServerCredentials: Unable to export public key and X.509 certificate: %s", gnutls_strerror(result)));
172bdf5d
MS
192 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, gnutls_strerror(result), 0);
193 gnutls_x509_crt_deinit(crt);
194 gnutls_x509_privkey_deinit(key);
195 return (0);
196 }
197 else if ((fp = cupsFileOpen(crtfile, "w")) != NULL)
198 {
d0facf48 199 DEBUG_printf(("1cupsMakeServerCredentials: Writing public key and X.509 certificate to \"%s\".", crtfile));
172bdf5d
MS
200 cupsFileWrite(fp, (char *)buffer, bytes);
201 cupsFileClose(fp);
202 }
203 else
204 {
d0facf48 205 DEBUG_printf(("1cupsMakeServerCredentials: Unable to create public key and X.509 certificate file \"%s\": %s", crtfile, strerror(errno)));
172bdf5d
MS
206 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
207 gnutls_x509_crt_deinit(crt);
208 gnutls_x509_privkey_deinit(key);
209 return (0);
210 }
211
212 /*
213 * Cleanup...
214 */
215
216 gnutls_x509_crt_deinit(crt);
217 gnutls_x509_privkey_deinit(key);
218
d0facf48
MS
219 DEBUG_puts("1cupsMakeServerCredentials: Successfully created credentials.");
220
172bdf5d 221 return (1);
07623986
MS
222}
223
224
225/*
226 * 'cupsSetServerCredentials()' - Set the default server credentials.
227 *
228 * Note: The server credentials are used by all threads in the running process.
229 * This function is threadsafe.
230 *
e1f19878 231 * @since CUPS 2.0/OS 10.10@
07623986
MS
232 */
233
234int /* O - 1 on success, 0 on failure */
235cupsSetServerCredentials(
236 const char *path, /* I - Path to keychain/directory */
237 const char *common_name, /* I - Default common name for server */
238 int auto_create) /* I - 1 = automatically create self-signed certificates */
239{
f394e0f7 240 char temp[1024]; /* Default path buffer */
172bdf5d
MS
241
242
243 DEBUG_printf(("cupsSetServerCredentials(path=\"%s\", common_name=\"%s\", auto_create=%d)", path, common_name, auto_create));
244
07623986 245 /*
d0facf48 246 * Use defaults as needed...
07623986
MS
247 */
248
d0facf48
MS
249 if (!path)
250 path = http_gnutls_default_path(temp, sizeof(temp));
07623986 251
172bdf5d 252 /*
d0facf48 253 * Range check input...
172bdf5d
MS
254 */
255
d0facf48 256 if (!path || !common_name)
172bdf5d 257 {
d0facf48
MS
258 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
259 return (0);
260 }
172bdf5d 261
d0facf48
MS
262 _cupsMutexLock(&tls_mutex);
263
264 /*
265 * Free old values...
266 */
172bdf5d 267
d0facf48
MS
268 if (tls_keypath)
269 _cupsStrFree(tls_keypath);
172bdf5d 270
d0facf48
MS
271 if (tls_common_name)
272 _cupsStrFree(tls_common_name);
172bdf5d 273
07623986
MS
274 /*
275 * Save the new values...
276 */
277
278 tls_keypath = _cupsStrAlloc(path);
279 tls_auto_create = auto_create;
280 tls_common_name = _cupsStrAlloc(common_name);
281
282 _cupsMutexUnlock(&tls_mutex);
283
284 return (1);
285}
286
287
dd332638
MS
288/*
289 * 'httpCopyCredentials()' - Copy the credentials associated with the peer in
290 * an encrypted connection.
291 *
292 * @since CUPS 1.5/OS X 10.7@
293 */
294
295int /* O - Status of call (0 = success) */
296httpCopyCredentials(
297 http_t *http, /* I - Connection to server */
298 cups_array_t **credentials) /* O - Array of credentials */
299{
bdc8d1ad
MS
300 unsigned count; /* Number of certificates */
301 const gnutls_datum_t *certs; /* Certificates */
302
303
304 DEBUG_printf(("httpCopyCredentials(http=%p, credentials=%p)", http, credentials));
305
dd332638
MS
306 if (credentials)
307 *credentials = NULL;
308
309 if (!http || !http->tls || !credentials)
310 return (-1);
311
bdc8d1ad
MS
312 *credentials = cupsArrayNew(NULL, NULL);
313 certs = gnutls_certificate_get_peers(http->tls, &count);
314
315 DEBUG_printf(("1httpCopyCredentials: certs=%p, count=%u", certs, count));
316
317 if (certs && count)
318 {
319 while (count > 0)
320 {
321 httpAddCredential(*credentials, certs->data, certs->size);
322 certs ++;
323 count --;
324 }
325 }
326
dd332638
MS
327 return (0);
328}
329
330
331/*
332 * '_httpCreateCredentials()' - Create credentials in the internal format.
333 */
334
335http_tls_credentials_t /* O - Internal credentials */
336_httpCreateCredentials(
337 cups_array_t *credentials) /* I - Array of credentials */
338{
339 (void)credentials;
340
341 return (NULL);
342}
343
344
345/*
346 * '_httpFreeCredentials()' - Free internal credentials.
347 */
348
349void
350_httpFreeCredentials(
351 http_tls_credentials_t credentials) /* I - Internal credentials */
352{
353 (void)credentials;
354}
355
356
a15960a1
MS
357/*
358 * 'httpCredentialsAreValidForName()' - Return whether the credentials are valid for the given name.
359 *
e1f19878 360 * @since CUPS 2.0/OS 10.10@
a15960a1
MS
361 */
362
363int /* O - 1 if valid, 0 otherwise */
364httpCredentialsAreValidForName(
365 cups_array_t *credentials, /* I - Credentials */
366 const char *common_name) /* I - Name to check */
367{
ff82e169
MS
368 gnutls_x509_crt_t cert; /* Certificate */
369 int result = 0; /* Result */
a15960a1
MS
370
371
ff82e169
MS
372 cert = http_gnutls_create_credential((http_credential_t *)cupsArrayFirst(credentials));
373 if (cert)
a15960a1 374 {
ff82e169
MS
375 result = gnutls_x509_crt_check_hostname(cert, common_name) != 0;
376 gnutls_x509_crt_deinit(cert);
a15960a1 377 }
a15960a1 378
ff82e169 379 return (result);
a15960a1
MS
380}
381
382
383/*
384 * 'httpCredentialsGetTrust()' - Return the trust of credentials.
385 *
e1f19878 386 * @since CUPS 2.0/OS 10.10@
a15960a1
MS
387 */
388
389http_trust_t /* O - Level of trust */
390httpCredentialsGetTrust(
391 cups_array_t *credentials, /* I - Credentials */
392 const char *common_name) /* I - Common name for trust lookup */
393{
394 http_trust_t trust = HTTP_TRUST_OK;
395 /* Trusted? */
ff82e169 396 gnutls_x509_crt_t cert; /* Certificate */
a15960a1
MS
397 cups_array_t *tcreds = NULL; /* Trusted credentials */
398 _cups_globals_t *cg = _cupsGlobals();
399 /* Per-thread globals */
400
401
402 if (!common_name)
403 return (HTTP_TRUST_UNKNOWN);
404
ff82e169 405 if ((cert = http_gnutls_create_credential((http_credential_t *)cupsArrayFirst(credentials))) == NULL)
a15960a1
MS
406 return (HTTP_TRUST_UNKNOWN);
407
3abb875b
MS
408 if (cg->any_root < 0)
409 _cupsSetDefaults();
410
a15960a1
MS
411 /*
412 * Look this common name up in the default keychains...
413 */
414
415 httpLoadCredentials(NULL, &tcreds, common_name);
416
417 if (tcreds)
418 {
419 char credentials_str[1024], /* String for incoming credentials */
420 tcreds_str[1024]; /* String for saved credentials */
421
422 httpCredentialsString(credentials, credentials_str, sizeof(credentials_str));
423 httpCredentialsString(tcreds, tcreds_str, sizeof(tcreds_str));
424
425 if (strcmp(credentials_str, tcreds_str))
426 {
427 /*
428 * Credentials don't match, let's look at the expiration date of the new
429 * credentials and allow if the new ones have a later expiration...
430 */
431
432 if (httpCredentialsGetExpiration(credentials) <= httpCredentialsGetExpiration(tcreds) ||
433 !httpCredentialsAreValidForName(credentials, common_name))
434 {
435 /*
436 * Either the new credentials are not newly issued, or the common name
437 * does not match the issued certificate...
438 */
439
440 trust = HTTP_TRUST_INVALID;
441 }
442 else if (httpCredentialsGetExpiration(tcreds) < time(NULL))
443 {
444 /*
445 * Save the renewed credentials...
446 */
447
448 trust = HTTP_TRUST_RENEWED;
449
450 httpSaveCredentials(NULL, credentials, common_name);
451 }
452 }
453
454 httpFreeCredentials(tcreds);
455 }
456 else if (cg->validate_certs && !httpCredentialsAreValidForName(credentials, common_name))
457 trust = HTTP_TRUST_INVALID;
458
ff82e169
MS
459 if (trust == HTTP_TRUST_OK && !cg->expired_certs)
460 {
461 time_t curtime; /* Current date/time */
462
463 time(&curtime);
464 if (curtime < gnutls_x509_crt_get_activation_time(cert) ||
465 curtime > gnutls_x509_crt_get_expiration_time(cert))
466 trust = HTTP_TRUST_EXPIRED;
467 }
468
469 if (trust == HTTP_TRUST_OK && !cg->any_root && cupsArrayCount(credentials) == 1)
a15960a1
MS
470 trust = HTTP_TRUST_INVALID;
471
ff82e169 472 gnutls_x509_crt_deinit(cert);
a15960a1
MS
473
474 return (trust);
475}
476
477
478/*
479 * 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
480 *
e1f19878 481 * @since CUPS 2.0/OS 10.10@
a15960a1
MS
482 */
483
484time_t /* O - Expiration date of credentials */
485httpCredentialsGetExpiration(
486 cups_array_t *credentials) /* I - Credentials */
487{
ff82e169
MS
488 gnutls_x509_crt_t cert; /* Certificate */
489 time_t result = 0; /* Result */
490
491
492 cert = http_gnutls_create_credential((http_credential_t *)cupsArrayFirst(credentials));
493 if (cert)
494 {
bdc8d1ad 495 result = gnutls_x509_crt_get_expiration_time(cert);
ff82e169
MS
496 gnutls_x509_crt_deinit(cert);
497 }
a15960a1 498
ff82e169 499 return (result);
a15960a1
MS
500}
501
502
503/*
504 * 'httpCredentialsString()' - Return a string representing the credentials.
505 *
e1f19878 506 * @since CUPS 2.0/OS 10.10@
a15960a1
MS
507 */
508
509size_t /* O - Total size of credentials string */
510httpCredentialsString(
511 cups_array_t *credentials, /* I - Credentials */
512 char *buffer, /* I - Buffer or @code NULL@ */
513 size_t bufsize) /* I - Size of buffer */
514{
ff82e169
MS
515 http_credential_t *first; /* First certificate */
516 gnutls_x509_crt_t cert; /* Certificate */
517
518
a15960a1
MS
519 DEBUG_printf(("httpCredentialsString(credentials=%p, buffer=%p, bufsize=" CUPS_LLFMT ")", credentials, buffer, CUPS_LLCAST bufsize));
520
521 if (!buffer)
522 return (0);
523
524 if (buffer && bufsize > 0)
525 *buffer = '\0';
526
a15960a1 527 if ((first = (http_credential_t *)cupsArrayFirst(credentials)) != NULL &&
ff82e169 528 (cert = http_gnutls_create_credential(first)) != NULL)
a15960a1 529 {
a15960a1 530 char name[256]; /* Common name associated with cert */
bdc8d1ad 531 size_t namelen; /* Length of name */
a15960a1
MS
532 time_t expiration; /* Expiration date of cert */
533 _cups_md5_state_t md5_state; /* MD5 state */
534 unsigned char md5_digest[16]; /* MD5 result */
535
bdc8d1ad
MS
536 namelen = sizeof(name) - 1;
537 if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, name, &namelen) >= 0)
538 name[namelen] = '\0';
539 else
a15960a1
MS
540 strlcpy(name, "unknown", sizeof(name));
541
ff82e169 542 expiration = gnutls_x509_crt_get_expiration_time(cert);
a15960a1
MS
543
544 _cupsMD5Init(&md5_state);
545 _cupsMD5Append(&md5_state, first->data, (int)first->datalen);
546 _cupsMD5Finish(&md5_state, md5_digest);
547
548 snprintf(buffer, bufsize, "%s / %s / %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", name, httpGetDateString(expiration), md5_digest[0], md5_digest[1], md5_digest[2], md5_digest[3], md5_digest[4], md5_digest[5], md5_digest[6], md5_digest[7], md5_digest[8], md5_digest[9], md5_digest[10], md5_digest[11], md5_digest[12], md5_digest[13], md5_digest[14], md5_digest[15]);
549
ff82e169 550 gnutls_x509_crt_deinit(cert);
a15960a1 551 }
a15960a1
MS
552
553 DEBUG_printf(("1httpCredentialsString: Returning \"%s\".", buffer));
554
555 return (strlen(buffer));
556}
557
558
559/*
560 * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
561 *
e1f19878 562 * @since CUPS 2.0/OS 10.10@
a15960a1
MS
563 */
564
565int /* O - 0 on success, -1 on error */
566httpLoadCredentials(
567 const char *path, /* I - Keychain/PKCS#12 path */
568 cups_array_t **credentials, /* IO - Credentials */
569 const char *common_name) /* I - Common name for credentials */
570{
ff82e169
MS
571 cups_file_t *fp; /* Certificate file */
572 char filename[1024], /* filename.crt */
573 temp[1024], /* Temporary string */
574 line[256]; /* Base64-encoded line */
575 unsigned char *data = NULL; /* Buffer for cert data */
576 size_t alloc_data = 0, /* Bytes allocated */
577 num_data = 0; /* Bytes used */
578 int decoded; /* Bytes decoded */
579
580
581 if (!credentials || !common_name)
582 return (-1);
583
584 if (!path)
585 path = http_gnutls_default_path(temp, sizeof(temp));
586 if (!path)
587 return (-1);
588
26435c51 589 http_gnutls_make_path(filename, sizeof(filename), path, common_name, "crt");
ff82e169
MS
590
591 if ((fp = cupsFileOpen(filename, "r")) == NULL)
592 return (-1);
593
594 while (cupsFileGets(fp, line, sizeof(line)))
595 {
596 if (!strcmp(line, "-----BEGIN CERTIFICATE-----"))
597 {
598 if (num_data)
599 {
600 /*
601 * Missing END CERTIFICATE...
602 */
603
604 httpFreeCredentials(*credentials);
605 *credentials = NULL;
606 break;
607 }
608 }
609 else if (!strcmp(line, "-----END CERTIFICATE-----"))
610 {
611 if (!num_data)
612 {
613 /*
614 * Missing data...
615 */
a15960a1 616
ff82e169
MS
617 httpFreeCredentials(*credentials);
618 *credentials = NULL;
619 break;
620 }
621
622 if (!*credentials)
623 *credentials = cupsArrayNew(NULL, NULL);
624
625 if (httpAddCredential(*credentials, data, num_data))
626 {
627 httpFreeCredentials(*credentials);
628 *credentials = NULL;
629 break;
630 }
631
632 num_data = 0;
633 }
634 else
635 {
636 if (alloc_data == 0)
637 {
638 data = malloc(2048);
639 alloc_data = 2048;
640
641 if (!data)
642 break;
643 }
644 else if ((num_data + strlen(line)) >= alloc_data)
645 {
646 unsigned char *tdata = realloc(data, alloc_data + 1024);
647 /* Expanded buffer */
648
649 if (!tdata)
650 {
651 httpFreeCredentials(*credentials);
652 *credentials = NULL;
653 break;
654 }
655
656 data = tdata;
657 alloc_data += 1024;
658 }
659
660 decoded = alloc_data - num_data;
bdc8d1ad
MS
661 httpDecode64_2((char *)data + num_data, &decoded, line);
662 num_data += (size_t)decoded;
ff82e169
MS
663 }
664 }
665
666 cupsFileClose(fp);
667
668 if (num_data)
669 {
670 /*
671 * Missing END CERTIFICATE...
672 */
673
674 httpFreeCredentials(*credentials);
675 *credentials = NULL;
676 }
677
678 if (data)
679 free(data);
680
681 return (*credentials ? 0 : -1);
a15960a1
MS
682}
683
684
685/*
686 * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
687 *
e1f19878 688 * @since CUPS 2.0/OS 10.10@
a15960a1
MS
689 */
690
691int /* O - -1 on error, 0 on success */
692httpSaveCredentials(
693 const char *path, /* I - Keychain/PKCS#12 path */
694 cups_array_t *credentials, /* I - Credentials */
695 const char *common_name) /* I - Common name for credentials */
696{
ff82e169
MS
697 cups_file_t *fp; /* Certificate file */
698 char filename[1024], /* filename.crt */
699 nfilename[1024],/* filename.crt.N */
700 temp[1024], /* Temporary string */
bdc8d1ad 701 line[256]; /* Base64-encoded line */
ff82e169 702 const unsigned char *ptr; /* Pointer into certificate */
bdc8d1ad 703 ssize_t remaining; /* Bytes left */
ff82e169
MS
704 http_credential_t *cred; /* Current credential */
705
706
707 if (!credentials || !common_name)
708 return (-1);
709
710 if (!path)
711 path = http_gnutls_default_path(temp, sizeof(temp));
712 if (!path)
713 return (-1);
714
26435c51
MS
715 http_gnutls_make_path(filename, sizeof(filename), path, common_name, "crt");
716 snprintf(nfilename, sizeof(nfilename), "%s.N", filename);
ff82e169
MS
717
718 if ((fp = cupsFileOpen(nfilename, "w")) == NULL)
719 return (-1);
720
721 fchmod(cupsFileNumber(fp), 0600);
722
723 for (cred = (http_credential_t *)cupsArrayFirst(credentials);
724 cred;
725 cred = (http_credential_t *)cupsArrayNext(credentials))
726 {
727 cupsFilePuts(fp, "-----BEGIN CERTIFICATE-----\n");
bdc8d1ad 728 for (ptr = cred->data, remaining = (ssize_t)cred->datalen; remaining > 0; remaining -= 45, ptr += 45)
ff82e169 729 {
bdc8d1ad 730 httpEncode64_2(line, sizeof(line), (char *)ptr, remaining > 45 ? 45 : remaining);
ff82e169
MS
731 cupsFilePrintf(fp, "%s\n", line);
732 }
733 cupsFilePuts(fp, "-----END CERTIFICATE-----\n");
734 }
735
736 cupsFileClose(fp);
737
738 return (rename(nfilename, filename));
739}
740
741
742/*
743 * 'http_gnutls_create_credential()' - Create a single credential in the internal format.
744 */
745
746static gnutls_x509_crt_t /* O - Certificate */
747http_gnutls_create_credential(
748 http_credential_t *credential) /* I - Credential */
749{
bdc8d1ad
MS
750 int result; /* Result from GNU TLS */
751 gnutls_x509_crt_t cert; /* Certificate */
ff82e169
MS
752 gnutls_datum_t datum; /* Data record */
753
754
bdc8d1ad
MS
755 DEBUG_printf(("3http_gnutls_create_credential(credential=%p)", credential));
756
ff82e169
MS
757 if (!credential)
758 return (NULL);
759
bdc8d1ad
MS
760 if ((result = gnutls_x509_crt_init(&cert)) < 0)
761 {
762 DEBUG_printf(("4http_gnutls_create_credential: init error: %s", gnutls_strerror(result)));
ff82e169 763 return (NULL);
bdc8d1ad 764 }
ff82e169 765
bdc8d1ad
MS
766 datum.data = credential->data;
767 datum.size = credential->datalen;
ff82e169 768
bdc8d1ad 769 if ((result = gnutls_x509_crt_import(cert, &datum, GNUTLS_X509_FMT_DER)) < 0)
ff82e169 770 {
bdc8d1ad
MS
771 DEBUG_printf(("4http_gnutls_create_credential: import error: %s", gnutls_strerror(result)));
772
ff82e169
MS
773 gnutls_x509_crt_deinit(cert);
774 return (NULL);
775 }
a15960a1 776
ff82e169 777 return (cert);
a15960a1
MS
778}
779
780
d0facf48
MS
781/*
782 * 'http_gnutls_default_path()' - Get the default credential store path.
783 */
784
785static const char * /* O - Path or NULL on error */
786http_gnutls_default_path(char *buffer,/* I - Path buffer */
787 size_t bufsize)/* I - Size of path buffer */
788{
789 const char *home = getenv("HOME"); /* HOME environment variable */
790
791
792 if (getuid() && home)
793 {
794 snprintf(buffer, bufsize, "%s/.cups", home);
795 if (access(buffer, 0))
796 {
797 DEBUG_printf(("1http_gnutls_default_path: Making directory \"%s\".", buffer));
798 if (mkdir(buffer, 0700))
799 {
800 DEBUG_printf(("1http_gnutls_default_path: Failed to make directory: %s", strerror(errno)));
801 return (NULL);
802 }
803 }
804
805 snprintf(buffer, bufsize, "%s/.cups/ssl", home);
806 if (access(buffer, 0))
807 {
808 DEBUG_printf(("1http_gnutls_default_path: Making directory \"%s\".", buffer));
809 if (mkdir(buffer, 0700))
810 {
811 DEBUG_printf(("1http_gnutls_default_path: Failed to make directory: %s", strerror(errno)));
812 return (NULL);
813 }
814 }
815 }
816 else
817 strlcpy(buffer, CUPS_SERVERROOT "/ssl", bufsize);
818
819 DEBUG_printf(("1http_gnutls_default_path: Using default path \"%s\".", buffer));
820
821 return (buffer);
822}
823
824
26435c51
MS
825/*
826 * 'http_gnutls_make_path()' - Format a filename for a certificate or key file.
827 */
828
829static const char * /* O - Filename */
830http_gnutls_make_path(
831 char *buffer, /* I - Filename buffer */
832 size_t bufsize, /* I - Size of buffer */
833 const char *dirname, /* I - Directory */
834 const char *filename, /* I - Filename (usually hostname) */
835 const char *ext) /* I - Extension */
836{
837 char *bufptr, /* Pointer into buffer */
838 *bufend = buffer + bufsize - 1; /* End of buffer */
839
840
841 snprintf(buffer, bufsize, "%s/", dirname);
842 bufptr = buffer + strlen(buffer);
843
844 while (*filename && bufptr < bufend)
845 {
846 if (_cups_isalnum(*filename) || *filename == '-' || *filename == '.')
847 *bufptr++ = *filename;
848 else
849 *bufptr++ = '_';
850
851 filename ++;
852 }
853
854 if (bufptr < bufend)
855 *bufptr++ = '.';
856
a2751f30 857 strlcpy(bufptr, ext, (size_t)(bufend - bufptr + 1));
26435c51
MS
858
859 return (buffer);
860}
861
862
dd332638
MS
863/*
864 * 'http_gnutls_read()' - Read function for the GNU TLS library.
865 */
866
867static ssize_t /* O - Number of bytes read or -1 on error */
868http_gnutls_read(
869 gnutls_transport_ptr_t ptr, /* I - Connection to server */
870 void *data, /* I - Buffer */
871 size_t length) /* I - Number of bytes to read */
872{
873 http_t *http; /* HTTP connection */
874 ssize_t bytes; /* Bytes read */
875
876
877 DEBUG_printf(("6http_gnutls_read(ptr=%p, data=%p, length=%d)", ptr, data, (int)length));
878
879 http = (http_t *)ptr;
880
881 if (!http->blocking)
882 {
883 /*
884 * Make sure we have data before we read...
885 */
886
887 while (!_httpWait(http, http->wait_value, 0))
888 {
889 if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
890 continue;
891
892 http->error = ETIMEDOUT;
893 return (-1);
894 }
895 }
896
897 bytes = recv(http->fd, data, length, 0);
898 DEBUG_printf(("6http_gnutls_read: bytes=%d", (int)bytes));
899 return (bytes);
900}
901
902
903/*
904 * 'http_gnutls_write()' - Write function for the GNU TLS library.
905 */
906
907static ssize_t /* O - Number of bytes written or -1 on error */
908http_gnutls_write(
909 gnutls_transport_ptr_t ptr, /* I - Connection to server */
910 const void *data, /* I - Data buffer */
911 size_t length) /* I - Number of bytes to write */
912{
913 ssize_t bytes; /* Bytes written */
914
915
916 DEBUG_printf(("6http_gnutls_write(ptr=%p, data=%p, length=%d)", ptr, data,
917 (int)length));
dd332638
MS
918 bytes = send(((http_t *)ptr)->fd, data, length, 0);
919 DEBUG_printf(("http_gnutls_write: bytes=%d", (int)bytes));
920
921 return (bytes);
922}
2c85b752
MS
923
924
925/*
172bdf5d 926 * '_httpTLSInitialize()' - Initialize the TLS stack.
2c85b752
MS
927 */
928
172bdf5d
MS
929void
930_httpTLSInitialize(void)
2c85b752 931{
2c85b752
MS
932 /*
933 * Initialize GNU TLS...
934 */
935
936 gnutls_global_init();
dd332638 937}
2c85b752 938
2c85b752 939
dd332638 940/*
172bdf5d 941 * '_httpTLSPending()' - Return the number of pending TLS-encrypted bytes.
dd332638 942 */
2c85b752 943
172bdf5d
MS
944size_t /* O - Bytes available */
945_httpTLSPending(http_t *http) /* I - HTTP connection */
dd332638
MS
946{
947 return (gnutls_record_check_pending(http->tls));
2c85b752
MS
948}
949
950
2c85b752 951/*
172bdf5d 952 * '_httpTLSRead()' - Read from a SSL/TLS connection.
2c85b752
MS
953 */
954
172bdf5d
MS
955int /* O - Bytes read */
956_httpTLSRead(http_t *http, /* I - Connection to server */
957 char *buf, /* I - Buffer to store data */
958 int len) /* I - Length of buffer */
2c85b752 959{
2c85b752
MS
960 ssize_t result; /* Return value */
961
962
07623986 963 result = gnutls_record_recv(http->tls, buf, (size_t)len);
2c85b752
MS
964
965 if (result < 0 && !errno)
966 {
967 /*
968 * Convert GNU TLS error to errno value...
969 */
970
971 switch (result)
972 {
973 case GNUTLS_E_INTERRUPTED :
974 errno = EINTR;
975 break;
976
977 case GNUTLS_E_AGAIN :
978 errno = EAGAIN;
979 break;
980
981 default :
982 errno = EPIPE;
983 break;
984 }
985
986 result = -1;
987 }
988
989 return ((int)result);
dd332638 990}
2c85b752
MS
991
992
dd332638 993/*
172bdf5d 994 * '_httpTLSSetCredentials()' - Set the TLS credentials.
dd332638 995 */
2c85b752 996
172bdf5d
MS
997int /* O - Status of connection */
998_httpTLSSetCredentials(http_t *http) /* I - Connection to server */
dd332638
MS
999{
1000 (void)http;
2c85b752 1001
dd332638 1002 return (0);
2c85b752 1003}
2c85b752
MS
1004
1005
63aefcd5
MS
1006/*
1007 * '_httpTLSSetOptions()' - Set TLS protocol and cipher suite options.
1008 */
1009
1010void
1011_httpTLSSetOptions(int options) /* I - Options */
1012{
1013 tls_options = options;
1014}
1015
1016
2c85b752 1017/*
172bdf5d 1018 * '_httpTLSStart()' - Set up SSL/TLS support on a connection.
2c85b752
MS
1019 */
1020
172bdf5d
MS
1021int /* O - 0 on success, -1 on failure */
1022_httpTLSStart(http_t *http) /* I - Connection to server */
2c85b752
MS
1023{
1024 char hostname[256], /* Hostname */
1025 *hostptr; /* Pointer into hostname */
2c85b752 1026 int status; /* Status of handshake */
172bdf5d 1027 gnutls_certificate_credentials_t *credentials;
2c85b752 1028 /* TLS credentials */
ee6226a5
MS
1029 char priority_string[1024];
1030 /* Priority string */
2c85b752
MS
1031
1032
b37d45d9
MS
1033 DEBUG_printf(("3_httpTLSStart(http=%p)", http));
1034
1035 if (tls_options < 0)
1036 {
1037 DEBUG_puts("4_httpTLSStart: Setting defaults.");
1038 _cupsSetDefaults();
1039 DEBUG_printf(("4_httpTLSStart: tls_options=%x", tls_options));
1040 }
2c85b752 1041
172bdf5d 1042 if (http->mode == _HTTP_MODE_SERVER && !tls_keypath)
2c85b752 1043 {
172bdf5d
MS
1044 DEBUG_puts("4_httpTLSStart: cupsSetServerCredentials not called.");
1045 http->error = errno = EINVAL;
1046 http->status = HTTP_STATUS_ERROR;
1047 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Server credentials not set."), 1);
2c85b752 1048
172bdf5d 1049 return (-1);
2c85b752
MS
1050 }
1051
172bdf5d
MS
1052 credentials = (gnutls_certificate_credentials_t *)
1053 malloc(sizeof(gnutls_certificate_credentials_t));
2c85b752
MS
1054 if (credentials == NULL)
1055 {
172bdf5d 1056 DEBUG_printf(("8_httpStartTLS: Unable to allocate credentials: %s",
2c85b752
MS
1057 strerror(errno)));
1058 http->error = errno;
1059 http->status = HTTP_STATUS_ERROR;
1060 _cupsSetHTTPError(HTTP_STATUS_ERROR);
1061
1062 return (-1);
1063 }
1064
1065 gnutls_certificate_allocate_credentials(credentials);
d0facf48 1066 status = gnutls_init(&http->tls, http->mode == _HTTP_MODE_CLIENT ? GNUTLS_CLIENT : GNUTLS_SERVER);
d0facf48 1067 if (!status)
005f7f1f 1068 status = gnutls_set_default_priority(http->tls);
d0facf48
MS
1069
1070 if (status)
1071 {
1072 http->error = EIO;
1073 http->status = HTTP_STATUS_ERROR;
1074
1075 DEBUG_printf(("4_httpTLSStart: Unable to initialize common TLS parameters: %s", gnutls_strerror(status)));
1076 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, gnutls_strerror(status), 0);
1077
1078 gnutls_deinit(http->tls);
1079 gnutls_certificate_free_credentials(*credentials);
1080 free(credentials);
1081 http->tls = NULL;
1082
1083 return (-1);
1084 }
2c85b752 1085
f394e0f7 1086 if (http->mode == _HTTP_MODE_CLIENT)
172bdf5d
MS
1087 {
1088 /*
1089 * Client: get the hostname to use for TLS...
1090 */
1091
1092 if (httpAddrLocalhost(http->hostaddr))
1093 {
1094 strlcpy(hostname, "localhost", sizeof(hostname));
1095 }
1096 else
1097 {
1098 /*
1099 * Otherwise make sure the hostname we have does not end in a trailing dot.
1100 */
1101
1102 strlcpy(hostname, http->hostname, sizeof(hostname));
1103 if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
1104 *hostptr == '.')
1105 *hostptr = '\0';
1106 }
1107
d0facf48 1108 status = gnutls_server_name_set(http->tls, GNUTLS_NAME_DNS, hostname, strlen(hostname));
172bdf5d
MS
1109 }
1110 else
1111 {
1112 /*
1113 * Server: get certificate and private key...
1114 */
1115
1116 char crtfile[1024], /* Certificate file */
1117 keyfile[1024]; /* Private key file */
1118 int have_creds = 0; /* Have credentials? */
1119
172bdf5d
MS
1120 if (http->fields[HTTP_FIELD_HOST][0])
1121 {
1122 /*
1123 * Use hostname for TLS upgrade...
1124 */
1125
1126 strlcpy(hostname, http->fields[HTTP_FIELD_HOST], sizeof(hostname));
1127 }
1128 else
1129 {
1130 /*
1131 * Resolve hostname from connection address...
1132 */
1133
1134 http_addr_t addr; /* Connection address */
1135 socklen_t addrlen; /* Length of address */
1136
1137 addrlen = sizeof(addr);
1138 if (getsockname(http->fd, (struct sockaddr *)&addr, &addrlen))
1139 {
1140 DEBUG_printf(("4_httpTLSStart: Unable to get socket address: %s", strerror(errno)));
1141 hostname[0] = '\0';
1142 }
1143 else if (httpAddrLocalhost(&addr))
1144 hostname[0] = '\0';
1145 else
1146 {
1147 httpAddrLookup(&addr, hostname, sizeof(hostname));
1148 DEBUG_printf(("4_httpTLSStart: Resolved socket address to \"%s\".", hostname));
1149 }
1150 }
1151
1152 if (isdigit(hostname[0] & 255) || hostname[0] == '[')
1153 hostname[0] = '\0'; /* Don't allow numeric addresses */
1154
1155 if (hostname[0])
1156 {
26435c51
MS
1157 http_gnutls_make_path(crtfile, sizeof(crtfile), tls_keypath, hostname, "crt");
1158 http_gnutls_make_path(keyfile, sizeof(keyfile), tls_keypath, hostname, "key");
172bdf5d
MS
1159
1160 have_creds = !access(crtfile, 0) && !access(keyfile, 0);
1161 }
1162 else if (tls_common_name)
1163 {
26435c51
MS
1164 http_gnutls_make_path(crtfile, sizeof(crtfile), tls_keypath, tls_common_name, "crt");
1165 http_gnutls_make_path(keyfile, sizeof(keyfile), tls_keypath, tls_common_name, "key");
172bdf5d
MS
1166
1167 have_creds = !access(crtfile, 0) && !access(keyfile, 0);
1168 }
1169
1170 if (!have_creds && tls_auto_create && (hostname[0] || tls_common_name))
1171 {
1172 DEBUG_printf(("4_httpTLSStart: Auto-create credentials for \"%s\".", hostname[0] ? hostname : tls_common_name));
1173
1174 if (!cupsMakeServerCredentials(tls_keypath, hostname[0] ? hostname : tls_common_name, 0, NULL, time(NULL) + 365 * 86400))
1175 {
1176 DEBUG_puts("4_httpTLSStart: cupsMakeServerCredentials failed.");
1177 http->error = errno = EINVAL;
1178 http->status = HTTP_STATUS_ERROR;
1179 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create server credentials."), 1);
1180
1181 return (-1);
1182 }
1183 }
1184
d0facf48
MS
1185 DEBUG_printf(("4_httpTLSStart: Using certificate \"%s\" and private key \"%s\".", crtfile, keyfile));
1186
1187 status = gnutls_certificate_set_x509_key_file(*credentials, crtfile, keyfile, GNUTLS_X509_FMT_PEM);
1188 }
1189
1190 if (!status)
1191 status = gnutls_credentials_set(http->tls, GNUTLS_CRD_CERTIFICATE, *credentials);
1192
1193 if (status)
1194 {
1195 http->error = EIO;
1196 http->status = HTTP_STATUS_ERROR;
1197
1198 DEBUG_printf(("4_httpTLSStart: Unable to complete client/server setup: %s", gnutls_strerror(status)));
1199 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, gnutls_strerror(status), 0);
1200
1201 gnutls_deinit(http->tls);
1202 gnutls_certificate_free_credentials(*credentials);
1203 free(credentials);
1204 http->tls = NULL;
1205
1206 return (-1);
172bdf5d
MS
1207 }
1208
838bcb92
MS
1209 strlcpy(priority_string, "NORMAL", sizeof(priority_string));
1210
1211 if (tls_options & _HTTP_TLS_DENY_TLS10)
1212 strlcat(priority_string, ":+VERS-TLS-ALL:-VERS-TLS1.0:-VERS-SSL3.0", sizeof(priority_string));
1213 else if (tls_options & _HTTP_TLS_ALLOW_SSL3)
1214 strlcat(priority_string, ":+VERS-TLS-ALL", sizeof(priority_string));
63aefcd5 1215 else
838bcb92 1216 strlcat(priority_string, ":+VERS-TLS-ALL:-VERS-SSL3.0", sizeof(priority_string));
ee6226a5 1217
838bcb92
MS
1218 if (!(tls_options & _HTTP_TLS_ALLOW_RC4))
1219 strlcat(priority_string, ":-ARCFOUR-128", sizeof(priority_string));
ee6226a5 1220
838bcb92 1221 if (!(tls_options & _HTTP_TLS_ALLOW_DH))
72aa7cb4 1222 strlcat(priority_string, ":!ANON-DH", sizeof(priority_string));
ee6226a5 1223
ee6226a5
MS
1224#ifdef HAVE_GNUTLS_PRIORITY_SET_DIRECT
1225 gnutls_priority_set_direct(http->tls, priority_string, NULL);
63aefcd5 1226
19ba6878
MS
1227#else
1228 gnutls_priority_t priority; /* Priority */
1229
ee6226a5 1230 gnutls_priority_init(&priority, priority_string, NULL);
19ba6878
MS
1231 gnutls_priority_set(http->tls, priority);
1232 gnutls_priority_deinit(priority);
1233#endif /* HAVE_GNUTLS_PRIORITY_SET_DIRECT */
1234
d0facf48
MS
1235 gnutls_transport_set_ptr(http->tls, (gnutls_transport_ptr_t)http);
1236 gnutls_transport_set_pull_function(http->tls, http_gnutls_read);
e7312eb4 1237#ifdef HAVE_GNUTLS_TRANSPORT_SET_PULL_TIMEOUT_FUNCTION
d0facf48 1238 gnutls_transport_set_pull_timeout_function(http->tls, (gnutls_pull_timeout_func)httpWait);
e7312eb4 1239#endif /* HAVE_GNUTLS_TRANSPORT_SET_PULL_TIMEOUT_FUNCTION */
d0facf48
MS
1240 gnutls_transport_set_push_function(http->tls, http_gnutls_write);
1241
2c85b752
MS
1242 while ((status = gnutls_handshake(http->tls)) != GNUTLS_E_SUCCESS)
1243 {
d0facf48 1244 DEBUG_printf(("5_httpStartTLS: gnutls_handshake returned %d (%s)",
2c85b752
MS
1245 status, gnutls_strerror(status)));
1246
1247 if (gnutls_error_is_fatal(status))
1248 {
1249 http->error = EIO;
1250 http->status = HTTP_STATUS_ERROR;
1251
1252 _cupsSetError(IPP_STATUS_ERROR_CUPS_PKI, gnutls_strerror(status), 0);
1253
1254 gnutls_deinit(http->tls);
1255 gnutls_certificate_free_credentials(*credentials);
1256 free(credentials);
1257 http->tls = NULL;
1258
1259 return (-1);
1260 }
1261 }
1262
1263 http->tls_credentials = credentials;
1264
2c85b752
MS
1265 return (0);
1266}
1267
1268
1269/*
172bdf5d 1270 * '_httpTLSStop()' - Shut down SSL/TLS on a connection.
2c85b752
MS
1271 */
1272
172bdf5d
MS
1273void
1274_httpTLSStop(http_t *http) /* I - Connection to server */
2c85b752 1275{
172bdf5d
MS
1276 int error; /* Error code */
1277
2c85b752 1278
f394e0f7 1279 error = gnutls_bye(http->tls, http->mode == _HTTP_MODE_CLIENT ? GNUTLS_SHUT_RDWR : GNUTLS_SHUT_WR);
172bdf5d 1280 if (error != GNUTLS_E_SUCCESS)
f394e0f7 1281 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, gnutls_strerror(errno), 0);
2c85b752 1282
2c85b752 1283 gnutls_deinit(http->tls);
172bdf5d
MS
1284 http->tls = NULL;
1285
d0facf48
MS
1286 if (http->tls_credentials)
1287 {
1288 gnutls_certificate_free_credentials(*(http->tls_credentials));
1289 free(http->tls_credentials);
1290 http->tls_credentials = NULL;
1291 }
2c85b752 1292}
2c85b752
MS
1293
1294
2c85b752 1295/*
172bdf5d 1296 * '_httpTLSWrite()' - Write to a SSL/TLS connection.
2c85b752
MS
1297 */
1298
172bdf5d
MS
1299int /* O - Bytes written */
1300_httpTLSWrite(http_t *http, /* I - Connection to server */
1301 const char *buf, /* I - Buffer holding data */
1302 int len) /* I - Length of buffer */
2c85b752
MS
1303{
1304 ssize_t result; /* Return value */
1305
1306
1307 DEBUG_printf(("2http_write_ssl(http=%p, buf=%p, len=%d)", http, buf, len));
1308
07623986 1309 result = gnutls_record_send(http->tls, buf, (size_t)len);
2c85b752
MS
1310
1311 if (result < 0 && !errno)
1312 {
1313 /*
1314 * Convert GNU TLS error to errno value...
1315 */
1316
1317 switch (result)
1318 {
1319 case GNUTLS_E_INTERRUPTED :
1320 errno = EINTR;
1321 break;
1322
1323 case GNUTLS_E_AGAIN :
1324 errno = EAGAIN;
1325 break;
1326
1327 default :
1328 errno = EPIPE;
1329 break;
1330 }
1331
1332 result = -1;
1333 }
1334
2c85b752
MS
1335 DEBUG_printf(("3http_write_ssl: Returning %d.", (int)result));
1336
1337 return ((int)result);
1338}