Coverage Report

Created: 2025-12-05 06:50

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