Coverage Report

Created: 2025-07-11 06:21

/src/libcups/cups/tls.c
Line
Count
Source (jump to first uncovered line)
1
//
2
// TLS routines for CUPS.
3
//
4
// Copyright © 2021-2025 by OpenPrinting.
5
// Copyright @ 2007-2014 by Apple Inc.
6
// Copyright @ 1997-2007 by Easy Software Products, all rights reserved.
7
//
8
// Licensed under Apache License v2.0.  See the file "LICENSE" for more
9
// information.
10
//
11
12
#include "cups-private.h"
13
#include "dir.h"
14
#include <fcntl.h>
15
#include <math.h>
16
#include <sys/stat.h>
17
#ifdef __APPLE__
18
#  include <Security/Security.h>
19
#endif // __APPLE__
20
#ifdef _WIN32
21
#  pragma comment(lib, "crypt32.lib") // Link in crypt32 library...
22
#  include <tchar.h>
23
#  include <wincrypt.h>
24
#else
25
#  include <poll.h>
26
#  include <signal.h>
27
#  include <sys/time.h>
28
#  include <sys/resource.h>
29
#endif // _WIN32
30
31
32
//
33
// Local globals...
34
//
35
36
static bool   tls_auto_create = false;
37
          // Auto-create self-signed certs?
38
static char   *tls_common_name = NULL;
39
          // Default common name
40
static char   *tls_keypath = NULL;
41
          // Certificate store path
42
static cups_mutex_t tls_mutex = CUPS_MUTEX_INITIALIZER;
43
          // Mutex for certificates
44
static int    tls_options = -1,// Options for TLS connections
45
      tls_min_version = _HTTP_TLS_1_2,
46
      tls_max_version = _HTTP_TLS_MAX;
47
#ifndef __APPLE__
48
static cups_array_t *tls_root_certs = NULL;
49
          // List of known root CAs
50
#endif // __APPLE__
51
52
53
//
54
// Local functions...
55
//
56
57
static bool   http_check_roots(const char *creds);
58
static char   *http_copy_file(const char *path, const char *common_name, const char *ext);
59
static const char *http_default_path(char *buffer, size_t bufsize);
60
static bool   http_default_san_cb(const char *common_name, const char *subject_alt_name, void *data);
61
#if defined(_WIN32) || defined(HAVE_GNUTLS)
62
static char   *http_der_to_pem(const unsigned char *der, size_t dersize);
63
#endif // _WIN32 || HAVE_GNUTLS
64
static const char *http_make_path(char *buffer, size_t bufsize, const char *dirname, const char *filename, const char *ext);
65
static bool   http_save_file(const char *path, const char *common_name, const char *ext, const char *value);
66
67
68
//
69
// Include platform-specific TLS code...
70
//
71
72
#ifdef HAVE_OPENSSL
73
#  include "tls-openssl.c"
74
#else // HAVE_GNUTLS
75
#  include "tls-gnutls.c"
76
#endif // HAVE_OPENSSL
77
78
79
//
80
// 'cupsCopyCredentials()' - Copy the X.509 certificate chain to a string.
81
//
82
83
char *
84
cupsCopyCredentials(
85
    const char *path,     // I - Directory path for certificate/key store or `NULL` for default
86
    const char *common_name)    // I - Common name
87
0
{
88
0
  return (http_copy_file(path, common_name, "crt"));
89
0
}
90
91
92
//
93
// 'cupsCopyCredentialsKey()' - Copy the private key to a string.
94
//
95
96
char *
97
cupsCopyCredentialsKey(
98
    const char *path,     // I - Directory path for certificate/key store or `NULL` for default
99
    const char *common_name)    // I - Common name
100
0
{
101
0
  return (http_copy_file(path, common_name, "key"));
102
0
}
103
104
105
//
106
// 'cupsCopyCredentialsPublicKey()' - Copy the public key for a X.509 certificate request.
107
//
108
109
char *          // O - PEM-encoded public key
110
cupsCopyCredentialsPublicKey(
111
    const char *path,     // I - Directory path for certificate/key store or `NULL` for default
112
    const char *common_name)    // I - Common name
113
0
{
114
0
  return (http_copy_file(path, common_name, "pub"));
115
0
}
116
117
118
//
119
// 'cupsCopyCredentialsRequest()' - Copy the X.509 certificate signing request to a string.
120
//
121
122
char *          // O - PEM-encoded X.509 certificate signing request
123
cupsCopyCredentialsRequest(
124
    const char *path,     // I - Directory path for certificate/key store or `NULL` for default
125
    const char *common_name)    // I - Common name
126
0
{
127
0
  return (http_copy_file(path, common_name, "csr"));
128
0
}
129
130
131
//
132
// 'cupsSaveCredentials()' - Save the credentials associated with a printer/server.
133
//
134
// This function saves the the PEM-encoded X.509 certificate chain string and
135
// private key (if not `NULL`) to the directory "path" or, if "path" is `NULL`,
136
// in a per-user or system-wide (when running as root) certificate/key store.
137
//
138
139
bool          // O - `true` on success, `false` on failure
140
cupsSaveCredentials(
141
    const char *path,     // I - Directory path for certificate/key store or `NULL` for default
142
    const char *common_name,    // I - Common name for certificate
143
    const char *credentials,    // I - PEM-encoded certificate chain or `NULL` to remove
144
    const char *key)      // I - PEM-encoded private key or `NULL` for none
145
0
{
146
0
  bool  ret = false;      // Return value
147
0
  char  defpath[1024],      // Default path
148
0
  crtfile[1024],      // Certificate filename
149
0
  keyfile[1024],      // Key filename
150
0
  ktmfile[1024];      // Temporary key filename
151
152
153
  // Validate input...
154
0
  DEBUG_printf("cupsSaveCredentials(path=\"%s\", common_name=\"%s\", credentials=%p(%u), key=%p(%u))", path, common_name, credentials, credentials ? (unsigned)strlen(credentials) : 0, key, key ? (unsigned)strlen(key) : 0);
155
156
0
  if (!path)
157
0
    path = http_default_path(defpath, sizeof(defpath));
158
159
0
  if (credentials)
160
0
  {
161
    // Make sure it looks like a PEM-encoded cert...
162
0
    if (strncmp(credentials, "-----BEGIN CERTIFICATE-----", 27) || strstr(credentials, "-----END CERTIFICATE-----") == NULL)
163
0
    {
164
0
      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad certificate."), true);
165
0
      return (false);
166
0
    }
167
0
  }
168
169
0
  if (key)
170
0
  {
171
    // Make sure it looks like a PEM-encoded private key...
172
0
    if (strncmp(key, "-----BEGIN PRIVATE KEY-----", 27) || strstr(key, "-----END PRIVATE KEY-----") == NULL)
173
0
    {
174
0
      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad private key."), true);
175
0
      return (false);
176
0
    }
177
0
  }
178
179
  // Save or delete credentials...
180
0
  http_make_path(crtfile, sizeof(crtfile), path, common_name, "crt");
181
0
  http_make_path(keyfile, sizeof(keyfile), path, common_name, "key");
182
0
  http_make_path(ktmfile, sizeof(ktmfile), path, common_name, "ktm");
183
184
0
  DEBUG_printf("1cupsSaveCredentials: crtfile=\"%s\"", crtfile);
185
0
  DEBUG_printf("1cupsSaveCredentials: keyfile=\"%s\"", keyfile);
186
0
  DEBUG_printf("1cupsSaveCredentials: ktmfile=\"%s\"", ktmfile);
187
188
0
  if (!credentials && !key)
189
0
  {
190
    // Delete credentials...
191
0
    if (!unlink(crtfile) && !unlink(keyfile))
192
0
      ret = true;
193
0
    else
194
0
      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), false);
195
0
  }
196
0
  else if (!credentials && key)
197
0
  {
198
    // Bad arguments...
199
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), false);
200
0
  }
201
0
  else if (!key && access(keyfile, 0) && access(ktmfile, 0))
202
0
  {
203
    // Missing key file...
204
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), false);
205
206
0
    DEBUG_printf("1cupsSaveCredentials: access(\"%s\", 0)=%d", keyfile, access(keyfile, 0));
207
0
    DEBUG_printf("1cupsSaveCredentials: access(\"%s\", 0)=%d", ktmfile, access(ktmfile, 0));
208
0
  }
209
0
  else if (http_save_file(path, common_name, "crt", credentials))
210
0
  {
211
    // Certificate saved, save or rename key file as needed...
212
0
    if (key)
213
0
    {
214
0
      ret = http_save_file(path, common_name, "key", key);
215
0
    }
216
0
    else if (!access(ktmfile, 0) && rename(ktmfile, keyfile))
217
0
    {
218
0
      _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), false);
219
220
0
      DEBUG_printf("1cupsSaveCredentials: rename(\"%s\", \"%s\") failed.", ktmfile, keyfile);
221
0
    }
222
0
    else
223
0
    {
224
0
      ret = true;
225
0
    }
226
0
  }
227
228
0
  return (ret);
229
0
}
230
231
232
//
233
// 'cupsSetServerCredentials()' - Set the default server credentials.
234
//
235
// Note: The server credentials are used by all threads in the running process.
236
// This function is threadsafe.
237
//
238
239
bool          // O - `true` on success, `false` on failure
240
cupsSetServerCredentials(
241
    const char *path,     // I - Directory path for certificate/key store or `NULL` for default
242
    const char *common_name,    // I - Default common name for server
243
    bool       auto_create)   // I - `true` = automatically create self-signed certificates
244
0
{
245
0
  char  temp[1024];     // Default path buffer
246
247
248
0
  DEBUG_printf("cupsSetServerCredentials(path=\"%s\", common_name=\"%s\", auto_create=%d)", path, common_name, auto_create);
249
250
  // Use defaults as needed...
251
0
  if (!path)
252
0
    path = http_default_path(temp, sizeof(temp));
253
254
  // Range check input...
255
0
  if (!path || !common_name)
256
0
  {
257
0
    _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
258
0
    return (0);
259
0
  }
260
261
0
  cupsMutexLock(&tls_mutex);
262
263
  // Free old values...
264
0
  if (tls_keypath)
265
0
    _cupsStrFree(tls_keypath);
266
267
0
  if (tls_common_name)
268
0
    _cupsStrFree(tls_common_name);
269
270
  // Save the new values...
271
0
  tls_keypath     = _cupsStrAlloc(path);
272
0
  tls_auto_create = auto_create;
273
0
  tls_common_name = _cupsStrAlloc(common_name);
274
275
0
  cupsMutexUnlock(&tls_mutex);
276
277
0
  return (1);
278
0
}
279
280
281
//
282
// '_httpTLSSetOptions()' - Set TLS protocol and cipher suite options.
283
//
284
285
void
286
_httpTLSSetOptions(int options,   // I - Options
287
                   int min_version, // I - Minimum TLS version
288
                   int max_version) // I - Maximum TLS version
289
1
{
290
1
  if (!(options & _HTTP_TLS_SET_DEFAULT) || tls_options < 0)
291
1
  {
292
1
    tls_options     = options;
293
1
    tls_min_version = min_version;
294
1
    tls_max_version = max_version;
295
1
  }
296
1
}
297
298
299
//
300
// 'http_check_roots()' - Check whether the supplied credentials use a trusted root CA.
301
//
302
303
static bool       // O - `true` if they use a trusted root, `false` otherwise
304
http_check_roots(const char *creds) // I - Credentials
305
0
{
306
0
  bool    ret = false;    // Return value
307
308
309
#ifdef __APPLE__
310
  // Apple hides all of the keychain stuff (all deprecated) so the best we can
311
  // do is use the SecTrust API to evaluate the certificate...
312
  CFMutableArrayRef certs = NULL; // Certificates from credentials
313
  SecCertificateRef cert;
314
  char      *tcreds = NULL, // Copy of credentials string
315
      *tstart,  // Start of certificate data
316
      *tend,    // End of certificate data
317
      *der = NULL;  // DER-encoded fragment buffer
318
  size_t    dersize,  // Size of DER buffer
319
      derlen;   // Length of DER data
320
  SecPolicyRef    policy;   // X.509 policy
321
  SecTrustRef   trust;    // Trust evaluator
322
323
324
  // Convert PEM-encoded credentials to an array of DER-encoded certificates...
325
  if ((tcreds = strdup(creds)) == NULL)
326
    goto done;
327
328
  if ((certs = CFArrayCreateMutable(kCFAllocatorDefault, /*capacity*/0, &kCFTypeArrayCallBacks)) == NULL)
329
    goto done;
330
331
  dersize = 3 * strlen(tcreds) / 4;
332
  if ((der = malloc(dersize)) == NULL)
333
    goto done;
334
335
  for (tstart = strstr(tcreds, "-----BEGIN CERTIFICATE-----\n"); tstart; tstart = strstr(tend, "-----BEGIN CERTIFICATE-----\n"))
336
  {
337
    // Find the end of the certificate data...
338
    tstart += 28;     // Skip "-----BEGIN CERTIFICATE-----\n"
339
    if ((tend = strstr(tstart, "-----END CERTIFICATE-----\n")) == NULL)
340
      break;        // Missing end...
341
342
    // Nul-terminate the cert data...
343
    *tend++ = '\0';
344
345
    // Convert to DER format
346
    derlen = dersize;
347
    if (httpDecode64(der, &derlen, tstart, /*end*/NULL))
348
    {
349
      // Create a CFData object for the data...
350
      CFDataRef data = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)der, (CFIndex)derlen);
351
352
      if (data)
353
      {
354
        // Create a certificate from the DER data...
355
        if ((cert = SecCertificateCreateWithData(kCFAllocatorDefault, data)) != NULL)
356
        {
357
          // Add certificate to the array...
358
          CFArrayAppendValue(certs, cert);
359
          CFRelease(cert);
360
  }
361
362
        CFRelease(data);
363
      }
364
    }
365
  }
366
367
  // Test the certificate list against the macOS/iOS trust store...
368
  if ((policy = SecPolicyCreateBasicX509()) != NULL)
369
  {
370
    if (SecTrustCreateWithCertificates(certs, policy, &trust) == noErr)
371
    {
372
      ret = SecTrustEvaluateWithError(trust, NULL);
373
      CFRelease(trust);
374
    }
375
376
    CFRelease(policy);
377
  }
378
379
  done:
380
381
  free(tcreds);
382
  free(der);
383
384
  if (certs)
385
    CFRelease(certs);
386
387
#else
388
0
  size_t  credslen;   // Length of credentials string
389
0
  const char  *rcreds;    // Current root credential
390
0
  size_t  rcredslen;    // Length of current root credential
391
392
393
0
  cupsMutexLock(&tls_mutex);
394
395
  // Load root certificates as needed...
396
0
  if (!tls_root_certs)
397
0
  {
398
    // Load root certificates...
399
0
    tls_root_certs = cupsArrayNew(/*cb*/NULL, /*cb_data*/NULL, /*hash_cb*/NULL, /*hash_size*/0, /*copy_cb*/NULL, /*free_cb*/NULL);
400
401
#  ifdef _WIN32
402
    int     i;    // Looping var
403
    HCERTSTORE    store;    // Certificate store
404
    CERT_CONTEXT  *cert;    // Current certificate
405
406
    // Add certificates in both the "ROOT" and "CA" stores...
407
    for (i = 0; i < 2; i ++)
408
    {
409
      if ((store = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_CURRENT_USER, i ? L"CA" : L"ROOT")) == NULL)
410
        continue;
411
412
      // Loop through certificates...
413
      for (cert = CertEnumCertificatesInStore(store, NULL); cert; cert = CertEnumCertificatesInStore(store, cert))
414
      {
415
        if (cert->dwCertEncodingType == X509_ASN_ENCODING)
416
        {
417
          // Convert DER to PEM and add to the list...
418
          char * pem = http_der_to_pem(cert->pbCertEncoded, cert->cbCertEncoded);
419
420
          if (pem)
421
            cupsArrayAdd(tls_root_certs, pem);
422
  }
423
      }
424
425
      CertCloseStore(store, 0);
426
    }
427
428
#  else
429
0
    size_t    i;    // Looping var
430
0
    cups_dir_t    *dir;   // Directory
431
0
    cups_dentry_t *dent;    // Directory entry
432
0
    const char    *ext;   // Pointer to filename extension
433
0
    static const char * const root_dirs[] =
434
0
    {         // Root certificate stores
435
0
      "/etc/ssl/certs",
436
0
      "/system/etc/security/cacerts/",
437
438
0
    };
439
440
0
    for (i = 0, dir = NULL; i < (sizeof(root_dirs) / sizeof(root_dirs[0])); i ++)
441
0
    {
442
0
      if ((dir = cupsDirOpen(root_dirs[i])) != NULL)
443
0
        break;
444
0
    }
445
446
0
    if (dir)
447
0
    {
448
0
      while ((dent = cupsDirRead(dir)) != NULL)
449
0
      {
450
0
        if ((ext = strrchr(dent->filename, '.')) != NULL && !strcmp(ext, ".pem"))
451
0
        {
452
0
          char  filename[1024],   // Certificate filename
453
0
    *cert;      // Certificate data
454
0
    int fd;     // File descriptor
455
456
0
    snprintf(filename, sizeof(filename), "%s/%s", root_dirs[i], dent->filename);
457
0
          if ((fd = open(filename, O_RDONLY)) >= 0)
458
0
          {
459
0
            if ((cert = calloc(1, (size_t)(dent->fileinfo.st_size + 1))) != NULL)
460
0
      {
461
0
        read(fd, cert, (size_t)dent->fileinfo.st_size);
462
0
        cupsArrayAdd(tls_root_certs, cert);
463
0
      }
464
465
0
      close(fd);
466
0
    }
467
0
  }
468
0
      }
469
0
    }
470
0
#  endif // _WIN32
471
0
  }
472
473
  // Check all roots
474
0
  credslen = strlen(creds);
475
476
0
  DEBUG_printf("4http_check_roots: %lu root certificates to check.", (unsigned long)cupsArrayGetCount(tls_root_certs));
477
478
0
  for (rcreds = (const char *)cupsArrayGetFirst(tls_root_certs); rcreds && !ret; rcreds = (const char *)cupsArrayGetNext(tls_root_certs))
479
0
  {
480
    // Compare the root against the tail of the current credentials...
481
0
    rcredslen = strlen(rcreds);
482
483
0
    if (credslen >= rcredslen && !strcmp(creds + (credslen - rcredslen), rcreds))
484
0
      ret = true;
485
0
  }
486
487
  // Unlock access and return...
488
0
  cupsMutexUnlock(&tls_mutex);
489
0
#endif // __APPLE__
490
491
0
  return (ret);
492
0
}
493
494
495
//
496
// 'http_copy_file()' - Copy the contents of a file to a string.
497
//
498
499
static char *       // O - Contents of file or `NULL` on error
500
http_copy_file(const char *path,  // I - Directory
501
               const char *common_name, // I - Common name
502
               const char *ext)   // I - Extension
503
0
{
504
0
  char    *s = NULL;    // String
505
0
  int   fd;     // File descriptor
506
0
  char    defpath[1024],    // Default path
507
0
    filename[1024];   // Filename
508
0
  struct stat fileinfo;   // File information
509
510
511
0
  if (!common_name)
512
0
    return (NULL);
513
514
0
  if (!path)
515
0
    path = http_default_path(defpath, sizeof(defpath));
516
517
0
  if ((fd = open(http_make_path(filename, sizeof(filename), path, common_name, ext), O_RDONLY)) < 0)
518
0
    return (NULL);
519
520
0
  if (fstat(fd, &fileinfo))
521
0
    goto done;
522
523
0
  if (fileinfo.st_size > 65536)
524
0
  {
525
0
    close(fd);
526
0
    return (NULL);
527
0
  }
528
529
0
  if ((s = calloc(1, (size_t)fileinfo.st_size + 1)) == NULL)
530
0
  {
531
0
    close(fd);
532
0
    return (NULL);
533
0
  }
534
535
0
  if (read(fd, s, (size_t)fileinfo.st_size) < 0)
536
0
  {
537
0
    free(s);
538
0
    s = NULL;
539
0
  }
540
541
0
  done:
542
543
0
  close(fd);
544
545
0
  return (s);
546
0
}
547
548
549
//
550
// 'http_default_path()' - Get the default credential store path.
551
//
552
553
static const char *     // O - Path or NULL on error
554
http_default_path(
555
    char   *buffer,     // I - Path buffer
556
    size_t bufsize)     // I - Size of path buffer
557
0
{
558
0
  _cups_globals_t *cg = _cupsGlobals();
559
          // Pointer to library globals
560
561
562
0
  if (cg->userconfig)
563
0
  {
564
0
    snprintf(buffer, bufsize, "%s/ssl", cg->userconfig);
565
566
0
    if (!_cupsDirCreate(buffer, 0700))
567
0
    {
568
0
      DEBUG_printf("1http_default_path: Failed to make directory '%s': %s", buffer, strerror(errno));
569
0
      return (NULL);
570
0
    }
571
0
  }
572
0
  else
573
0
  {
574
0
    snprintf(buffer, bufsize, "%s/ssl", cg->sysconfig);
575
576
0
    if (!_cupsDirCreate(buffer, 0700))
577
0
    {
578
0
      DEBUG_printf("1http_default_path: Failed to make directory '%s': %s", buffer, strerror(errno));
579
0
      return (NULL);
580
0
    }
581
0
  }
582
583
0
  DEBUG_printf("1http_default_path: Using default path \"%s\".", buffer);
584
585
0
  return (buffer);
586
0
}
587
588
589
//
590
// 'http_default_san_cb()' - Validate a subjectAltName value.
591
//
592
593
static bool       // O - `true` if OK, `false` otherwise
594
http_default_san_cb(
595
    const char *common_name,    // I - Common name value
596
    const char *subject_alt_name, // I - subjectAltName value
597
    void       *data)     // I - Callback data (unused)
598
0
{
599
0
  size_t  common_len;   // Common name length
600
601
602
0
  (void)data;
603
604
0
  if (!_cups_strcasecmp(subject_alt_name, common_name) || !_cups_strcasecmp(subject_alt_name, "localhost"))
605
0
    return (true);
606
607
0
  common_len = strlen(common_name);
608
609
0
  return (!_cups_strncasecmp(subject_alt_name, common_name, common_len) && subject_alt_name[common_len] == '.');
610
0
}
611
612
613
#if defined(_WIN32) || defined(HAVE_GNUTLS)
614
//
615
// 'http_der_to_pem()' - Convert DER format certificate data to PEM.
616
//
617
618
static char *       // O - PEM string
619
http_der_to_pem(
620
     const unsigned char *der,    // I - DER-encoded data
621
     size_t              dersize) // I - Size of DER-encoded data
622
{
623
  char    *pem,     // PEM-encoded string
624
    *pemptr;    // Pointer into PEM-encoded string
625
  int   col;      // Current column
626
  size_t  pemsize;    // Size of PEM-encoded string
627
  static const char *base64 =   // Base64 alphabet
628
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
629
630
631
  // Calculate the size, accounting for Base64 expansion, line wrapping at
632
  // column 64, and the BEGIN/END CERTIFICATE text...
633
  pemsize = 2 * dersize + /*"-----BEGIN CERTIFICATE-----\n"*/28 + /*"-----END CERTIFICATE-----\n"*/26 + 1;
634
635
  if ((pem = calloc(1, pemsize)) == NULL)
636
    return (NULL);
637
638
  cupsCopyString(pem, "-----BEGIN CERTIFICATE-----\n", pemsize);
639
  for (pemptr = pem + strlen(pem), col = 0; dersize > 0; der += 3)
640
  {
641
    // Encode the up to 3 characters as 4 Base64 numbers...
642
    switch (dersize)
643
    {
644
      case 1 :
645
          *pemptr ++ = base64[(der[0] & 255) >> 2];
646
    *pemptr ++ = base64[((der[0] & 255) << 4) & 63];
647
    *pemptr ++ = '=';
648
    *pemptr ++ = '=';
649
    dersize = 0;
650
          break;
651
      case 2 :
652
          *pemptr ++ = base64[(der[0] & 255) >> 2];
653
    *pemptr ++ = base64[(((der[0] & 255) << 4) | ((der[1] & 255) >> 4)) & 63];
654
    *pemptr ++ = base64[((der[1] & 255) << 2) & 63];
655
    *pemptr ++ = '=';
656
    dersize = 0;
657
          break;
658
      default :
659
          *pemptr ++ = base64[(der[0] & 255) >> 2];
660
    *pemptr ++ = base64[(((der[0] & 255) << 4) | ((der[1] & 255) >> 4)) & 63];
661
    *pemptr ++ = base64[(((der[1] & 255) << 2) | ((der[2] & 255) >> 6)) & 63];
662
    *pemptr ++ = base64[der[2] & 63];
663
    dersize -= 3;
664
          break;
665
    }
666
667
    // Add a newline as needed...
668
    col += 4;
669
    if (col >= 64)
670
    {
671
      *pemptr++ = '\n';
672
      col = 0;
673
    }
674
  }
675
676
  if (col > 0)
677
    *pemptr++ = '\n';
678
  *pemptr = '\0';
679
680
  cupsConcatString(pem, "-----END CERTIFICATE-----\n", pemsize);
681
682
  // Return the encoded string...
683
  return (pem);
684
}
685
#endif // _WIN32 || HAVE_GNUTLS
686
687
688
//
689
// 'http_make_path()' - Format a filename for a certificate or key file.
690
//
691
692
static const char *     // O - Filename
693
http_make_path(
694
    char       *buffer,     // I - Filename buffer
695
    size_t     bufsize,     // I - Size of buffer
696
    const char *dirname,    // I - Directory
697
    const char *filename,   // I - Filename (usually hostname)
698
    const char *ext)      // I - Extension
699
0
{
700
0
  char  *bufptr,      // Pointer into buffer
701
0
  *bufend = buffer + bufsize - 1; // End of buffer
702
703
704
0
  snprintf(buffer, bufsize, "%s/", dirname);
705
0
  bufptr = buffer + strlen(buffer);
706
707
0
  while (*filename && bufptr < bufend)
708
0
  {
709
0
    if (_cups_isalnum(*filename) || *filename == '-' || *filename == '.')
710
0
      *bufptr++ = *filename;
711
0
    else
712
0
      *bufptr++ = '_';
713
714
0
    filename ++;
715
0
  }
716
717
0
  if (bufptr < bufend && filename[-1] != '.')
718
0
    *bufptr++ = '.';
719
720
0
  cupsCopyString(bufptr, ext, (size_t)(bufend - bufptr + 1));
721
722
0
  return (buffer);
723
0
}
724
725
726
//
727
// 'http_save_file()' - Save a string to a file.
728
//
729
730
static bool       // O - `true` on success, `false` on failure
731
http_save_file(const char *path,  // I - Directory path for certificate/key store or `NULL` for default
732
               const char *common_name, // I - Common name
733
               const char *ext,   // I - Extension
734
         const char *value) // I - String value
735
0
{
736
0
  char  defpath[1024],      // Default path
737
0
  filename[1024];     // Output filename
738
0
  int fd;       // File descriptor
739
740
741
  // Range check input...
742
0
  if (!common_name)
743
0
    return (false);
744
745
  // Get default path as needed...
746
0
  if (!path)
747
0
    path = http_default_path(defpath, sizeof(defpath));
748
749
0
  http_make_path(filename, sizeof(filename), path, common_name, ext);
750
751
0
  if (!value)
752
0
  {
753
0
    unlink(filename);
754
0
    return (true);
755
0
  }
756
757
0
  if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) < 0)
758
0
    return (false);
759
760
0
  if (write(fd, value, strlen(value)) < 0)
761
0
  {
762
0
    close(fd);
763
0
    unlink(filename);
764
0
    return (false);
765
0
  }
766
767
0
  close(fd);
768
769
0
  return (true);
770
0
}
771
772