Coverage Report

Created: 2026-10-17 07:24

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-2026 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 char   *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 *          // O - PEM-encoded certificate(s)
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, (void *)credentials, credentials ? (unsigned)strlen(credentials) : 0, (void *)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 char *       // O - Trusted root certificate chain or `NULL` otherwise
310
http_check_roots(const char *creds) // I - Credentials
311
0
{
312
0
  char  *ret = NULL;     // Return value
313
314
315
0
  DEBUG_printf("4http_check_roots(creds=\"%u bytes...\")", (unsigned)strlen(creds));
316
317
#ifdef __APPLE__
318
  // Apple hides all of the keychain stuff (all deprecated) so the best we can
319
  // do is use the SecTrust API to evaluate the certificate...
320
  CFMutableArrayRef certs = NULL; // Certificates from credentials
321
  SecCertificateRef cert;
322
  char      *tcreds = NULL, // Copy of credentials string
323
      *tstart,  // Start of certificate data
324
      *tend,    // End of certificate data
325
      *der = NULL;  // DER-encoded fragment buffer
326
  size_t    dersize,  // Size of DER buffer
327
      derlen;   // Length of DER data
328
  SecPolicyRef    policy;   // X.509 policy
329
  SecTrustRef   trust;    // Trust evaluator
330
331
332
  // Convert PEM-encoded credentials to an array of DER-encoded certificates...
333
  if ((tcreds = strdup(creds)) == NULL)
334
  {
335
    DEBUG_puts("5http_check_roots: Unable to duplicate credentials.");
336
    goto done;
337
  }
338
339
  if ((certs = CFArrayCreateMutable(kCFAllocatorDefault, /*capacity*/0, &kCFTypeArrayCallBacks)) == NULL)
340
  {
341
    DEBUG_puts("5http_check_roots: Unable to create credentials array.");
342
    goto done;
343
  }
344
345
  dersize = 3 * strlen(tcreds) / 4;
346
  if ((der = malloc(dersize)) == NULL)
347
  {
348
    DEBUG_printf("5http_check_roots: Unable to allocate %u byte buffer for certificate.", (unsigned)dersize);
349
    goto done;
350
  }
351
352
  for (tstart = strstr(tcreds, "-----BEGIN CERTIFICATE-----\n"); tstart; tstart = strstr(tend, "-----BEGIN CERTIFICATE-----\n"))
353
  {
354
    // Find the end of the certificate data...
355
    tstart += 28;     // Skip "-----BEGIN CERTIFICATE-----\n"
356
    if ((tend = strstr(tstart, "-----END CERTIFICATE-----\n")) == NULL)
357
      break;        // Missing end...
358
359
    // Nul-terminate the cert data...
360
    *tend++ = '\0';
361
362
    // Convert to DER format
363
    derlen = dersize;
364
    if (httpDecode64(der, &derlen, tstart, /*end*/NULL))
365
    {
366
      // Create a CFData object for the data...
367
      DEBUG_printf("5http_check_roots: Allocating %u bytes for certificate.", (unsigned)derlen);
368
      CFDataRef data = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)der, (CFIndex)derlen);
369
370
      if (data)
371
      {
372
        // Create a certificate from the DER data...
373
        if ((cert = SecCertificateCreateWithData(kCFAllocatorDefault, data)) != NULL)
374
        {
375
          // Add certificate to the array...
376
          CFArrayAppendValue(certs, cert);
377
          CFRelease(cert);
378
  }
379
  else
380
  {
381
    DEBUG_puts("5http_check_roots: Unable to create certificate.");
382
  }
383
384
        CFRelease(data);
385
      }
386
      else
387
      {
388
  DEBUG_puts("5http_check_roots: Unable to create certificate data.");
389
      }
390
    }
391
  }
392
393
  // Test the certificate list against the macOS/iOS trust store...
394
  if ((policy = SecPolicyCreateBasicX509()) != NULL)
395
  {
396
    DEBUG_puts("5http_check_roots: Created basic X.509 trust policy.");
397
398
    if (SecTrustCreateWithCertificates(certs, policy, &trust) == noErr)
399
    {
400
      DEBUG_puts("5http_check_roots: Created trust object with policy.");
401
402
      if (SecTrustEvaluateWithError(trust, NULL))
403
      {
404
        DEBUG_puts("5http_check_roots: Certificate is trusted.");
405
        ret = strdup("");
406
      }
407
      else
408
      {
409
        DEBUG_puts("5http_check_roots: Certificate is not trusted.");
410
      }
411
412
      CFRelease(trust);
413
    }
414
415
    CFRelease(policy);
416
  }
417
418
  done:
419
420
  free(tcreds);
421
  free(der);
422
423
  if (certs)
424
    CFRelease(certs);
425
426
#else
427
0
  size_t  credslen;   // Length of credentials string
428
0
  const char  *rcreds;    // Current root credential
429
0
  size_t  rcredslen;    // Length of current root credential
430
431
432
0
  cupsMutexLock(&tls_mutex);
433
434
  // Load root certificates as needed...
435
0
  if (!tls_root_certs)
436
0
  {
437
    // Load root certificates...
438
0
    tls_root_certs = cupsArrayNew(/*cb*/NULL, /*cb_data*/NULL, /*hash_cb*/NULL, /*hash_size*/0, /*copy_cb*/NULL, /*free_cb*/NULL);
439
440
#  ifdef _WIN32
441
    int     i;    // Looping var
442
    HCERTSTORE    store;    // Certificate store
443
    CERT_CONTEXT  *cert;    // Current certificate
444
445
    // Add certificates in both the "ROOT" and "CA" stores...
446
    for (i = 0; i < 2; i ++)
447
    {
448
      if ((store = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_CURRENT_USER, i ? L"CA" : L"ROOT")) == NULL)
449
        continue;
450
451
      // Loop through certificates...
452
      for (cert = CertEnumCertificatesInStore(store, NULL); cert; cert = CertEnumCertificatesInStore(store, cert))
453
      {
454
        if (cert->dwCertEncodingType == X509_ASN_ENCODING)
455
        {
456
          // Convert DER to PEM and add to the list...
457
          char * pem = http_der_to_pem(cert->pbCertEncoded, cert->cbCertEncoded);
458
459
          if (pem)
460
            cupsArrayAdd(tls_root_certs, pem);
461
  }
462
      }
463
464
      CertCloseStore(store, 0);
465
    }
466
467
#  else
468
0
    size_t    i;    // Looping var
469
0
    cups_dir_t    *dir;   // Directory
470
0
    cups_dentry_t *dent;    // Directory entry
471
0
    const char    *ext;   // Pointer to filename extension
472
0
    static const char * const root_dirs[] =
473
0
    {         // Root certificate stores
474
0
      "/etc/ssl/certs",
475
0
      "/system/etc/security/cacerts/",
476
477
0
    };
478
479
0
    for (i = 0, dir = NULL; i < (sizeof(root_dirs) / sizeof(root_dirs[0])); i ++)
480
0
    {
481
0
      if ((dir = cupsDirOpen(root_dirs[i])) != NULL)
482
0
        break;
483
0
    }
484
485
0
    if (dir)
486
0
    {
487
0
      while ((dent = cupsDirRead(dir)) != NULL)
488
0
      {
489
0
        if ((ext = strrchr(dent->filename, '.')) != NULL && !strcmp(ext, ".pem"))
490
0
        {
491
0
          char  filename[1024],   // Certificate filename
492
0
    *cert;      // Certificate data
493
0
    int fd;     // File descriptor
494
495
0
    snprintf(filename, sizeof(filename), "%s/%s", root_dirs[i], dent->filename);
496
0
          if ((fd = open(filename, O_RDONLY)) >= 0)
497
0
          {
498
0
            if ((cert = calloc(1, (size_t)(dent->fileinfo.st_size + 1))) != NULL)
499
0
      {
500
0
        read(fd, cert, (size_t)dent->fileinfo.st_size);
501
0
        cupsArrayAdd(tls_root_certs, cert);
502
0
      }
503
504
0
      close(fd);
505
0
    }
506
0
  }
507
0
      }
508
0
    }
509
0
#  endif // _WIN32
510
0
  }
511
512
  // Check all roots
513
0
  credslen = strlen(creds);
514
515
0
  DEBUG_printf("4http_check_roots: %lu root certificates to check.", (unsigned long)cupsArrayGetCount(tls_root_certs));
516
517
0
  for (rcreds = (const char *)cupsArrayGetFirst(tls_root_certs); rcreds && !ret; rcreds = (const char *)cupsArrayGetNext(tls_root_certs))
518
0
  {
519
    // Compare the root against the tail of the current credentials...
520
0
    rcredslen = strlen(rcreds);
521
522
0
    if (credslen > rcredslen && !strcmp(creds + (credslen - rcredslen), rcreds))
523
0
      ret = strdup(rcreds);
524
0
  }
525
526
  // Unlock access and return...
527
0
  cupsMutexUnlock(&tls_mutex);
528
0
#endif // __APPLE__
529
530
0
  return (ret);
531
0
}
532
533
534
//
535
// 'http_copy_file()' - Copy the contents of a file to a string.
536
//
537
538
static char *       // O - Contents of file or `NULL` on error
539
http_copy_file(const char *path,  // I - Directory
540
               const char *common_name, // I - Common name
541
               const char *ext)   // I - Extension
542
0
{
543
0
  char    *s = NULL;   // String
544
0
  int   fd;     // File descriptor
545
0
  char    defpath[1024],    // Default path
546
0
    filename[1024];   // Filename
547
0
  struct stat fileinfo;   // File information
548
549
550
0
  DEBUG_printf("3http_copy_file(path=\"%s\", common_name=\"%s\", ext=\"%s\")", path, common_name, ext);
551
552
0
  if (!common_name)
553
0
    return (NULL);
554
555
0
  if (!path)
556
0
    path = http_default_path(defpath, sizeof(defpath));
557
558
0
  if ((fd = open(http_make_path(filename, sizeof(filename), path, common_name, ext), O_RDONLY)) < 0)
559
0
  {
560
0
    DEBUG_printf("4http_copy_file: open() failed: %s", strerror(errno));
561
0
    return (NULL);
562
0
  }
563
564
0
  if (fstat(fd, &fileinfo))
565
0
  {
566
0
    DEBUG_printf("4http_copy_file: fstat() failed: %s", strerror(errno));
567
0
    goto done;
568
0
  }
569
570
0
  DEBUG_printf("4http_copy_file: st_size=%lu", (unsigned long)fileinfo.st_size);
571
572
0
  if (fileinfo.st_size > 65536)
573
0
  {
574
0
    close(fd);
575
0
    return (NULL);
576
0
  }
577
578
0
  if ((s = calloc(1, (size_t)fileinfo.st_size + 1)) == NULL)
579
0
  {
580
0
    DEBUG_printf("4http_copy_file: calloc() failed: %s", strerror(errno));
581
0
    close(fd);
582
0
    return (NULL);
583
0
  }
584
585
0
  if (read(fd, s, (size_t)fileinfo.st_size) < 0)
586
0
  {
587
0
    DEBUG_printf("4http_copy_file: read() failed: %s", strerror(errno));
588
0
    free(s);
589
0
    s = NULL;
590
0
  }
591
592
0
  done:
593
594
0
  close(fd);
595
596
0
  DEBUG_printf("4http_copy_file: Returning \"%s\"", s);
597
598
0
  return (s);
599
0
}
600
601
602
//
603
// 'http_default_path()' - Get the default credential store path.
604
//
605
606
static const char *     // O - Path or NULL on error
607
http_default_path(
608
    char   *buffer,     // I - Path buffer
609
    size_t bufsize)     // I - Size of path buffer
610
0
{
611
0
  _cups_globals_t *cg = _cupsGlobals();
612
          // Pointer to library globals
613
614
615
0
  if (cg->userconfig)
616
0
  {
617
0
    snprintf(buffer, bufsize, "%s/ssl", cg->userconfig);
618
619
0
    if (!_cupsDirCreate(buffer, 0700))
620
0
    {
621
0
      DEBUG_printf("4http_default_path: Failed to make directory '%s': %s", buffer, strerror(errno));
622
0
      return (NULL);
623
0
    }
624
0
  }
625
0
  else
626
0
  {
627
0
    snprintf(buffer, bufsize, "%s/ssl", cg->sysconfig);
628
629
0
    if (!_cupsDirCreate(buffer, 0700))
630
0
    {
631
0
      DEBUG_printf("4http_default_path: Failed to make directory '%s': %s", buffer, strerror(errno));
632
0
      return (NULL);
633
0
    }
634
0
  }
635
636
0
  DEBUG_printf("4http_default_path: Using default path \"%s\".", buffer);
637
638
0
  return (buffer);
639
0
}
640
641
642
//
643
// 'http_default_san_cb()' - Validate a subjectAltName value.
644
//
645
646
static bool       // O - `true` if OK, `false` otherwise
647
http_default_san_cb(
648
    const char *common_name,    // I - Common name value
649
    const char *subject_alt_name, // I - subjectAltName value
650
    void       *data)     // I - Callback data (unused)
651
0
{
652
0
  size_t  common_len;   // Common name length
653
654
655
0
  (void)data;
656
657
0
  if (!_cups_strcasecmp(subject_alt_name, common_name) || !_cups_strcasecmp(subject_alt_name, "localhost"))
658
0
    return (true);
659
660
0
  common_len = strlen(common_name);
661
662
0
  return (!_cups_strncasecmp(subject_alt_name, common_name, common_len) && subject_alt_name[common_len] == '.');
663
0
}
664
665
666
#if defined(_WIN32) || defined(HAVE_GNUTLS)
667
//
668
// 'http_der_to_pem()' - Convert DER format certificate data to PEM.
669
//
670
671
static char *       // O - PEM string
672
http_der_to_pem(
673
     const unsigned char *der,    // I - DER-encoded data
674
     size_t              dersize) // I - Size of DER-encoded data
675
{
676
  char    *pem,     // PEM-encoded string
677
    *pemptr;    // Pointer into PEM-encoded string
678
  int   col;      // Current column
679
  size_t  pemsize;    // Size of PEM-encoded string
680
  static const char *base64 =   // Base64 alphabet
681
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
682
683
684
  // Calculate the size, accounting for Base64 expansion, line wrapping at
685
  // column 64, and the BEGIN/END CERTIFICATE text...
686
  pemsize = 2 * dersize + /*"-----BEGIN CERTIFICATE-----\n"*/28 + /*"-----END CERTIFICATE-----\n"*/26 + 1;
687
688
  if ((pem = calloc(1, pemsize)) == NULL)
689
    return (NULL);
690
691
  cupsCopyString(pem, "-----BEGIN CERTIFICATE-----\n", pemsize);
692
  for (pemptr = pem + strlen(pem), col = 0; dersize > 0; der += 3)
693
  {
694
    // Encode the up to 3 characters as 4 Base64 numbers...
695
    switch (dersize)
696
    {
697
      case 1 :
698
          *pemptr ++ = base64[(der[0] & 255) >> 2];
699
    *pemptr ++ = base64[((der[0] & 255) << 4) & 63];
700
    *pemptr ++ = '=';
701
    *pemptr ++ = '=';
702
    dersize = 0;
703
          break;
704
      case 2 :
705
          *pemptr ++ = base64[(der[0] & 255) >> 2];
706
    *pemptr ++ = base64[(((der[0] & 255) << 4) | ((der[1] & 255) >> 4)) & 63];
707
    *pemptr ++ = base64[((der[1] & 255) << 2) & 63];
708
    *pemptr ++ = '=';
709
    dersize = 0;
710
          break;
711
      default :
712
          *pemptr ++ = base64[(der[0] & 255) >> 2];
713
    *pemptr ++ = base64[(((der[0] & 255) << 4) | ((der[1] & 255) >> 4)) & 63];
714
    *pemptr ++ = base64[(((der[1] & 255) << 2) | ((der[2] & 255) >> 6)) & 63];
715
    *pemptr ++ = base64[der[2] & 63];
716
    dersize -= 3;
717
          break;
718
    }
719
720
    // Add a newline as needed...
721
    col += 4;
722
    if (col >= 64)
723
    {
724
      *pemptr++ = '\n';
725
      col = 0;
726
    }
727
  }
728
729
  if (col > 0)
730
    *pemptr++ = '\n';
731
  *pemptr = '\0';
732
733
  cupsConcatString(pem, "-----END CERTIFICATE-----\n", pemsize);
734
735
  // Return the encoded string...
736
  return (pem);
737
}
738
#endif // _WIN32 || HAVE_GNUTLS
739
740
741
//
742
// 'http_make_path()' - Format a filename for a certificate or key file.
743
//
744
745
static const char *     // O - Filename
746
http_make_path(
747
    char       *buffer,     // I - Filename buffer
748
    size_t     bufsize,     // I - Size of buffer
749
    const char *dirname,    // I - Directory
750
    const char *filename,   // I - Filename (usually hostname)
751
    const char *ext)      // I - Extension
752
0
{
753
0
  char  *bufptr,      // Pointer into buffer
754
0
  *bufend = buffer + bufsize - 1; // End of buffer
755
756
757
0
  DEBUG_printf("3http_make_path(buffer=%p, bufsize=%u, dirname=\"%s\", filename=\"%s\", ext=\"%s\")", (void *)buffer, (unsigned)bufsize, dirname, filename, ext);
758
759
0
  snprintf(buffer, bufsize, "%s/", dirname);
760
0
  bufptr = buffer + strlen(buffer);
761
762
0
  while (*filename && bufptr < bufend)
763
0
  {
764
0
    if (_cups_isalnum(*filename) || *filename == '-' || *filename == '.')
765
0
      *bufptr++ = *filename;
766
0
    else
767
0
      *bufptr++ = '_';
768
769
0
    filename ++;
770
0
  }
771
772
0
  if (bufptr < bufend && filename[-1] != '.')
773
0
    *bufptr++ = '.';
774
775
0
  cupsCopyString(bufptr, ext, (size_t)(bufend - bufptr + 1));
776
777
0
  DEBUG_printf("4http_make_path: Returning \"%s\".", buffer);
778
779
0
  return (buffer);
780
0
}
781
782
783
//
784
// 'http_save_file()' - Save a string to a file.
785
//
786
787
static bool       // O - `true` on success, `false` on failure
788
http_save_file(const char *path,  // I - Directory path for certificate/key store or `NULL` for default
789
               const char *common_name, // I - Common name
790
               const char *ext,   // I - Extension
791
         const char *value) // I - String value
792
0
{
793
0
  char  defpath[1024],      // Default path
794
0
  filename[1024];     // Output filename
795
0
  int fd;       // File descriptor
796
797
798
  // Range check input...
799
0
  if (!common_name)
800
0
    return (false);
801
802
  // Get default path as needed...
803
0
  if (!path)
804
0
    path = http_default_path(defpath, sizeof(defpath));
805
806
0
  http_make_path(filename, sizeof(filename), path, common_name, ext);
807
808
0
  if (!value)
809
0
  {
810
0
    unlink(filename);
811
0
    return (true);
812
0
  }
813
814
0
  if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) < 0)
815
0
    return (false);
816
817
0
  if (write(fd, value, strlen(value)) < 0)
818
0
  {
819
0
    close(fd);
820
0
    unlink(filename);
821
0
    return (false);
822
0
  }
823
824
0
  close(fd);
825
826
  return (true);
827
0
}
828
829