Coverage Report

Created: 2024-11-04 06:16

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