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 | | |
48 | | |
49 | | // |
50 | | // Local functions... |
51 | | // |
52 | | |
53 | | static char *http_copy_file(const char *path, const char *common_name, const char *ext); |
54 | | static const char *http_default_path(char *buffer, size_t bufsize); |
55 | | static bool http_default_san_cb(const char *common_name, const char *subject_alt_name, void *data); |
56 | | #if defined(_WIN32) || defined(HAVE_GNUTLS) |
57 | | static char *http_der_to_pem(const unsigned char *der, size_t dersize); |
58 | | #endif // _WIN32 || HAVE_GNUTLS |
59 | | static const char *http_make_path(char *buffer, size_t bufsize, const char *dirname, const char *filename, const char *ext); |
60 | | static bool http_save_file(const char *path, const char *common_name, const char *ext, const char *value); |
61 | | |
62 | | |
63 | | // |
64 | | // Include platform-specific TLS code... |
65 | | // |
66 | | |
67 | | #ifdef HAVE_OPENSSL |
68 | | # include "tls-openssl.c" |
69 | | #else // HAVE_GNUTLS |
70 | | # include "tls-gnutls.c" |
71 | | #endif // HAVE_OPENSSL |
72 | | |
73 | | |
74 | | // |
75 | | // 'cupsCopyCredentials()' - Copy the X.509 certificate chain to a string. |
76 | | // |
77 | | |
78 | | char * // O - PEM-encoded certificate(s) |
79 | | cupsCopyCredentials( |
80 | | const char *path, // I - Directory path for certificate/key store or `NULL` for default |
81 | | const char *common_name) // I - Common name |
82 | 0 | { |
83 | 0 | return (http_copy_file(path, common_name, "crt")); |
84 | 0 | } |
85 | | |
86 | | |
87 | | // |
88 | | // 'cupsCopyCredentialsKey()' - Copy the private key to a string. |
89 | | // |
90 | | |
91 | | char * // O - PEM-encoded private key |
92 | | cupsCopyCredentialsKey( |
93 | | const char *path, // I - Directory path for certificate/key store or `NULL` for default |
94 | | const char *common_name) // I - Common name |
95 | 0 | { |
96 | 0 | char *key = http_copy_file(path, common_name, "key"); |
97 | | // Private key |
98 | |
|
99 | 0 | if (!key) |
100 | 0 | key = http_copy_file(path, common_name, "ktm"); |
101 | |
|
102 | 0 | return (key); |
103 | 0 | } |
104 | | |
105 | | |
106 | | // |
107 | | // 'cupsCopyCredentialsPublicKey()' - Copy the public key for a X.509 certificate request. |
108 | | // |
109 | | |
110 | | char * // O - PEM-encoded public key |
111 | | cupsCopyCredentialsPublicKey( |
112 | | const char *path, // I - Directory path for certificate/key store or `NULL` for default |
113 | | const char *common_name) // I - Common name |
114 | 0 | { |
115 | 0 | return (http_copy_file(path, common_name, "pub")); |
116 | 0 | } |
117 | | |
118 | | |
119 | | // |
120 | | // 'cupsCopyCredentialsRequest()' - Copy the X.509 certificate signing request to a string. |
121 | | // |
122 | | |
123 | | char * // O - PEM-encoded X.509 certificate signing request |
124 | | cupsCopyCredentialsRequest( |
125 | | const char *path, // I - Directory path for certificate/key store or `NULL` for default |
126 | | const char *common_name) // I - Common name |
127 | 0 | { |
128 | 0 | return (http_copy_file(path, common_name, "csr")); |
129 | 0 | } |
130 | | |
131 | | |
132 | | // |
133 | | // 'cupsSaveCredentials()' - Save the credentials associated with a printer/server. |
134 | | // |
135 | | // This function saves the the PEM-encoded X.509 certificate chain string and |
136 | | // private key (if not `NULL`) to the directory "path" or, if "path" is `NULL`, |
137 | | // in a per-user or system-wide (when running as root) certificate/key store. |
138 | | // |
139 | | |
140 | | bool // O - `true` on success, `false` on failure |
141 | | cupsSaveCredentials( |
142 | | const char *path, // I - Directory path for certificate/key store or `NULL` for default |
143 | | const char *common_name, // I - Common name for certificate |
144 | | const char *credentials, // I - PEM-encoded certificate chain or `NULL` to remove |
145 | | const char *key) // I - PEM-encoded private key or `NULL` for none |
146 | 0 | { |
147 | 0 | bool ret = false; // Return value |
148 | 0 | char defpath[1024], // Default path |
149 | 0 | crtfile[1024], // Certificate filename |
150 | 0 | keyfile[1024], // Key filename |
151 | 0 | ktmfile[1024]; // Temporary key filename |
152 | | |
153 | | |
154 | | // Validate input... |
155 | 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); |
156 | |
|
157 | 0 | if (!path) |
158 | 0 | path = http_default_path(defpath, sizeof(defpath)); |
159 | |
|
160 | 0 | if (credentials) |
161 | 0 | { |
162 | | // Make sure it looks like a PEM-encoded cert... |
163 | 0 | if (strncmp(credentials, "-----BEGIN CERTIFICATE-----", 27) || strstr(credentials, "-----END CERTIFICATE-----") == NULL) |
164 | 0 | { |
165 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad certificate."), true); |
166 | 0 | return (false); |
167 | 0 | } |
168 | 0 | } |
169 | | |
170 | 0 | if (key) |
171 | 0 | { |
172 | | // Make sure it looks like a PEM-encoded private key... |
173 | 0 | if (strncmp(key, "-----BEGIN PRIVATE KEY-----", 27) || strstr(key, "-----END PRIVATE KEY-----") == NULL) |
174 | 0 | { |
175 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad private key."), true); |
176 | 0 | return (false); |
177 | 0 | } |
178 | 0 | } |
179 | | |
180 | | // Save or delete credentials... |
181 | 0 | http_make_path(crtfile, sizeof(crtfile), path, common_name, "crt"); |
182 | 0 | http_make_path(keyfile, sizeof(keyfile), path, common_name, "key"); |
183 | 0 | http_make_path(ktmfile, sizeof(ktmfile), path, common_name, "ktm"); |
184 | |
|
185 | 0 | DEBUG_printf("1cupsSaveCredentials: crtfile=\"%s\"", crtfile); |
186 | 0 | DEBUG_printf("1cupsSaveCredentials: keyfile=\"%s\"", keyfile); |
187 | 0 | DEBUG_printf("1cupsSaveCredentials: ktmfile=\"%s\"", ktmfile); |
188 | |
|
189 | 0 | if (!credentials && !key) |
190 | 0 | { |
191 | | // Delete credentials... |
192 | 0 | if (!unlink(crtfile) && !unlink(keyfile)) |
193 | 0 | ret = true; |
194 | 0 | else |
195 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), false); |
196 | 0 | } |
197 | 0 | else if (!credentials && key) |
198 | 0 | { |
199 | | // Bad arguments... |
200 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), false); |
201 | 0 | } |
202 | 0 | else if (!key && access(keyfile, 0) && access(ktmfile, 0)) |
203 | 0 | { |
204 | | // Missing key file... |
205 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), false); |
206 | |
|
207 | 0 | DEBUG_printf("1cupsSaveCredentials: access(\"%s\", 0)=%d", keyfile, access(keyfile, 0)); |
208 | 0 | DEBUG_printf("1cupsSaveCredentials: access(\"%s\", 0)=%d", ktmfile, access(ktmfile, 0)); |
209 | 0 | } |
210 | 0 | else if (http_save_file(path, common_name, "crt", credentials)) |
211 | 0 | { |
212 | | // Certificate saved, save or rename key file as needed... |
213 | 0 | if (key) |
214 | 0 | { |
215 | 0 | ret = http_save_file(path, common_name, "key", key); |
216 | 0 | } |
217 | 0 | else if (!access(ktmfile, 0) && rename(ktmfile, keyfile)) |
218 | 0 | { |
219 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), false); |
220 | |
|
221 | 0 | DEBUG_printf("1cupsSaveCredentials: rename(\"%s\", \"%s\") failed.", ktmfile, keyfile); |
222 | 0 | } |
223 | 0 | else |
224 | 0 | { |
225 | 0 | ret = true; |
226 | 0 | } |
227 | 0 | } |
228 | |
|
229 | 0 | return (ret); |
230 | 0 | } |
231 | | |
232 | | |
233 | | // |
234 | | // 'cupsSetServerCredentials()' - Set the default server credentials. |
235 | | // |
236 | | // Note: The server credentials are used by all threads in the running process. |
237 | | // This function is threadsafe. |
238 | | // |
239 | | |
240 | | bool // O - `true` on success, `false` on failure |
241 | | cupsSetServerCredentials( |
242 | | const char *path, // I - Directory path for certificate/key store or `NULL` for default |
243 | | const char *common_name, // I - Default common name for server |
244 | | bool auto_create) // I - `true` = automatically create self-signed certificates |
245 | 0 | { |
246 | 0 | char temp[1024]; // Default path buffer |
247 | | |
248 | |
|
249 | 0 | DEBUG_printf("cupsSetServerCredentials(path=\"%s\", common_name=\"%s\", auto_create=%d)", path, common_name, auto_create); |
250 | | |
251 | | // Use defaults as needed... |
252 | 0 | if (!path) |
253 | 0 | path = http_default_path(temp, sizeof(temp)); |
254 | | |
255 | | // Range check input... |
256 | 0 | if (!path || !common_name) |
257 | 0 | { |
258 | 0 | _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0); |
259 | 0 | return (0); |
260 | 0 | } |
261 | | |
262 | 0 | cupsMutexLock(&tls_mutex); |
263 | | |
264 | | // Free old values... |
265 | 0 | if (tls_keypath) |
266 | 0 | _cupsStrFree(tls_keypath); |
267 | |
|
268 | 0 | if (tls_common_name) |
269 | 0 | _cupsStrFree(tls_common_name); |
270 | | |
271 | | // Save the new values... |
272 | 0 | tls_keypath = _cupsStrAlloc(path); |
273 | 0 | tls_auto_create = auto_create; |
274 | 0 | tls_common_name = _cupsStrAlloc(common_name); |
275 | |
|
276 | 0 | cupsMutexUnlock(&tls_mutex); |
277 | |
|
278 | 0 | return (1); |
279 | 0 | } |
280 | | |
281 | | |
282 | | // |
283 | | // '_httpTLSSetOptions()' - Set TLS protocol and cipher suite options. |
284 | | // |
285 | | |
286 | | void |
287 | | _httpTLSSetOptions(int options, // I - Options |
288 | | int min_version, // I - Minimum TLS version |
289 | | int max_version) // I - Maximum TLS version |
290 | 1 | { |
291 | 1 | if (!(options & _HTTP_TLS_SET_DEFAULT) || tls_options < 0) |
292 | 1 | { |
293 | 1 | tls_options = options; |
294 | 1 | tls_min_version = min_version; |
295 | 1 | tls_max_version = max_version; |
296 | 1 | } |
297 | 1 | } |
298 | | |
299 | | |
300 | | // |
301 | | // 'http_copy_file()' - Copy the contents of a file to a string. |
302 | | // |
303 | | |
304 | | static char * // O - Contents of file or `NULL` on error |
305 | | http_copy_file(const char *path, // I - Directory |
306 | | const char *common_name, // I - Common name |
307 | | const char *ext) // I - Extension |
308 | 0 | { |
309 | 0 | char *s = NULL; // String |
310 | 0 | int fd; // File descriptor |
311 | 0 | char defpath[1024], // Default path |
312 | 0 | filename[1024]; // Filename |
313 | 0 | struct stat fileinfo; // File information |
314 | | |
315 | |
|
316 | 0 | DEBUG_printf("3http_copy_file(path=\"%s\", common_name=\"%s\", ext=\"%s\")", path, common_name, ext); |
317 | |
|
318 | 0 | if (!common_name) |
319 | 0 | return (NULL); |
320 | | |
321 | 0 | if (!path) |
322 | 0 | path = http_default_path(defpath, sizeof(defpath)); |
323 | |
|
324 | 0 | if ((fd = open(http_make_path(filename, sizeof(filename), path, common_name, ext), O_RDONLY)) < 0) |
325 | 0 | { |
326 | 0 | DEBUG_printf("4http_copy_file: open() failed: %s", strerror(errno)); |
327 | 0 | return (NULL); |
328 | 0 | } |
329 | | |
330 | 0 | if (fstat(fd, &fileinfo)) |
331 | 0 | { |
332 | 0 | DEBUG_printf("4http_copy_file: fstat() failed: %s", strerror(errno)); |
333 | 0 | goto done; |
334 | 0 | } |
335 | | |
336 | 0 | DEBUG_printf("4http_copy_file: st_size=%lu", (unsigned long)fileinfo.st_size); |
337 | |
|
338 | 0 | if (fileinfo.st_size > 65536) |
339 | 0 | { |
340 | 0 | close(fd); |
341 | 0 | return (NULL); |
342 | 0 | } |
343 | | |
344 | 0 | if ((s = calloc(1, (size_t)fileinfo.st_size + 1)) == NULL) |
345 | 0 | { |
346 | 0 | DEBUG_printf("4http_copy_file: calloc() failed: %s", strerror(errno)); |
347 | 0 | close(fd); |
348 | 0 | return (NULL); |
349 | 0 | } |
350 | | |
351 | 0 | if (read(fd, s, (size_t)fileinfo.st_size) < 0) |
352 | 0 | { |
353 | 0 | DEBUG_printf("4http_copy_file: read() failed: %s", strerror(errno)); |
354 | 0 | free(s); |
355 | 0 | s = NULL; |
356 | 0 | } |
357 | |
|
358 | 0 | done: |
359 | |
|
360 | 0 | close(fd); |
361 | |
|
362 | 0 | DEBUG_printf("4http_copy_file: Returning \"%s\"", s); |
363 | |
|
364 | 0 | return (s); |
365 | 0 | } |
366 | | |
367 | | |
368 | | // |
369 | | // 'http_default_path()' - Get the default credential store path. |
370 | | // |
371 | | |
372 | | static const char * // O - Path or NULL on error |
373 | | http_default_path( |
374 | | char *buffer, // I - Path buffer |
375 | | size_t bufsize) // I - Size of path buffer |
376 | 0 | { |
377 | 0 | _cups_globals_t *cg = _cupsGlobals(); |
378 | | // Pointer to library globals |
379 | | |
380 | |
|
381 | 0 | if (cg->userconfig) |
382 | 0 | { |
383 | 0 | snprintf(buffer, bufsize, "%s/ssl", cg->userconfig); |
384 | |
|
385 | 0 | if (!_cupsDirCreate(buffer, 0700)) |
386 | 0 | { |
387 | 0 | DEBUG_printf("4http_default_path: Failed to make directory '%s': %s", buffer, strerror(errno)); |
388 | 0 | return (NULL); |
389 | 0 | } |
390 | 0 | } |
391 | 0 | else |
392 | 0 | { |
393 | 0 | snprintf(buffer, bufsize, "%s/ssl", cg->sysconfig); |
394 | |
|
395 | 0 | if (!_cupsDirCreate(buffer, 0700)) |
396 | 0 | { |
397 | 0 | DEBUG_printf("4http_default_path: Failed to make directory '%s': %s", buffer, strerror(errno)); |
398 | 0 | return (NULL); |
399 | 0 | } |
400 | 0 | } |
401 | | |
402 | 0 | DEBUG_printf("4http_default_path: Using default path \"%s\".", buffer); |
403 | |
|
404 | 0 | return (buffer); |
405 | 0 | } |
406 | | |
407 | | |
408 | | // |
409 | | // 'http_default_san_cb()' - Validate a subjectAltName value. |
410 | | // |
411 | | |
412 | | static bool // O - `true` if OK, `false` otherwise |
413 | | http_default_san_cb( |
414 | | const char *common_name, // I - Common name value |
415 | | const char *subject_alt_name, // I - subjectAltName value |
416 | | void *data) // I - Callback data (unused) |
417 | 0 | { |
418 | 0 | size_t common_len; // Common name length |
419 | | |
420 | |
|
421 | 0 | (void)data; |
422 | |
|
423 | 0 | if (!_cups_strcasecmp(subject_alt_name, common_name) || !_cups_strcasecmp(subject_alt_name, "localhost")) |
424 | 0 | return (true); |
425 | | |
426 | 0 | common_len = strlen(common_name); |
427 | |
|
428 | 0 | return (!_cups_strncasecmp(subject_alt_name, common_name, common_len) && subject_alt_name[common_len] == '.'); |
429 | 0 | } |
430 | | |
431 | | |
432 | | #if defined(_WIN32) || defined(HAVE_GNUTLS) |
433 | | // |
434 | | // 'http_der_to_pem()' - Convert DER format certificate data to PEM. |
435 | | // |
436 | | |
437 | | static char * // O - PEM string |
438 | | http_der_to_pem( |
439 | | const unsigned char *der, // I - DER-encoded data |
440 | | size_t dersize) // I - Size of DER-encoded data |
441 | | { |
442 | | char *pem, // PEM-encoded string |
443 | | *pemptr; // Pointer into PEM-encoded string |
444 | | int col; // Current column |
445 | | size_t pemsize; // Size of PEM-encoded string |
446 | | static const char *base64 = // Base64 alphabet |
447 | | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
448 | | |
449 | | |
450 | | // Calculate the size, accounting for Base64 expansion, line wrapping at |
451 | | // column 64, and the BEGIN/END CERTIFICATE text... |
452 | | pemsize = 2 * dersize + /*"-----BEGIN CERTIFICATE-----\n"*/28 + /*"-----END CERTIFICATE-----\n"*/26 + 1; |
453 | | |
454 | | if ((pem = calloc(1, pemsize)) == NULL) |
455 | | return (NULL); |
456 | | |
457 | | cupsCopyString(pem, "-----BEGIN CERTIFICATE-----\n", pemsize); |
458 | | for (pemptr = pem + strlen(pem), col = 0; dersize > 0; der += 3) |
459 | | { |
460 | | // Encode the up to 3 characters as 4 Base64 numbers... |
461 | | switch (dersize) |
462 | | { |
463 | | case 1 : |
464 | | *pemptr ++ = base64[(der[0] & 255) >> 2]; |
465 | | *pemptr ++ = base64[((der[0] & 255) << 4) & 63]; |
466 | | *pemptr ++ = '='; |
467 | | *pemptr ++ = '='; |
468 | | dersize = 0; |
469 | | break; |
470 | | case 2 : |
471 | | *pemptr ++ = base64[(der[0] & 255) >> 2]; |
472 | | *pemptr ++ = base64[(((der[0] & 255) << 4) | ((der[1] & 255) >> 4)) & 63]; |
473 | | *pemptr ++ = base64[((der[1] & 255) << 2) & 63]; |
474 | | *pemptr ++ = '='; |
475 | | dersize = 0; |
476 | | break; |
477 | | default : |
478 | | *pemptr ++ = base64[(der[0] & 255) >> 2]; |
479 | | *pemptr ++ = base64[(((der[0] & 255) << 4) | ((der[1] & 255) >> 4)) & 63]; |
480 | | *pemptr ++ = base64[(((der[1] & 255) << 2) | ((der[2] & 255) >> 6)) & 63]; |
481 | | *pemptr ++ = base64[der[2] & 63]; |
482 | | dersize -= 3; |
483 | | break; |
484 | | } |
485 | | |
486 | | // Add a newline as needed... |
487 | | col += 4; |
488 | | if (col >= 64) |
489 | | { |
490 | | *pemptr++ = '\n'; |
491 | | col = 0; |
492 | | } |
493 | | } |
494 | | |
495 | | if (col > 0) |
496 | | *pemptr++ = '\n'; |
497 | | *pemptr = '\0'; |
498 | | |
499 | | cupsConcatString(pem, "-----END CERTIFICATE-----\n", pemsize); |
500 | | |
501 | | // Return the encoded string... |
502 | | return (pem); |
503 | | } |
504 | | #endif // _WIN32 || HAVE_GNUTLS |
505 | | |
506 | | |
507 | | // |
508 | | // 'http_make_path()' - Format a filename for a certificate or key file. |
509 | | // |
510 | | |
511 | | static const char * // O - Filename |
512 | | http_make_path( |
513 | | char *buffer, // I - Filename buffer |
514 | | size_t bufsize, // I - Size of buffer |
515 | | const char *dirname, // I - Directory |
516 | | const char *filename, // I - Filename (usually hostname) |
517 | | const char *ext) // I - Extension |
518 | 0 | { |
519 | 0 | char *bufptr, // Pointer into buffer |
520 | 0 | *bufend = buffer + bufsize - 1; // End of buffer |
521 | | |
522 | |
|
523 | 0 | DEBUG_printf("3http_make_path(buffer=%p, bufsize=%u, dirname=\"%s\", filename=\"%s\", ext=\"%s\")", (void *)buffer, (unsigned)bufsize, dirname, filename, ext); |
524 | |
|
525 | 0 | snprintf(buffer, bufsize, "%s/", dirname); |
526 | 0 | bufptr = buffer + strlen(buffer); |
527 | |
|
528 | 0 | while (*filename && bufptr < bufend) |
529 | 0 | { |
530 | 0 | if (_cups_isalnum(*filename) || *filename == '-' || *filename == '.') |
531 | 0 | *bufptr++ = *filename; |
532 | 0 | else |
533 | 0 | *bufptr++ = '_'; |
534 | |
|
535 | 0 | filename ++; |
536 | 0 | } |
537 | |
|
538 | 0 | if (bufptr < bufend && filename[-1] != '.') |
539 | 0 | *bufptr++ = '.'; |
540 | |
|
541 | 0 | cupsCopyString(bufptr, ext, (size_t)(bufend - bufptr + 1)); |
542 | |
|
543 | 0 | DEBUG_printf("4http_make_path: Returning \"%s\".", buffer); |
544 | |
|
545 | 0 | return (buffer); |
546 | 0 | } |
547 | | |
548 | | |
549 | | // |
550 | | // 'http_save_file()' - Save a string to a file. |
551 | | // |
552 | | |
553 | | static bool // O - `true` on success, `false` on failure |
554 | | http_save_file(const char *path, // I - Directory path for certificate/key store or `NULL` for default |
555 | | const char *common_name, // I - Common name |
556 | | const char *ext, // I - Extension |
557 | | const char *value) // I - String value |
558 | 0 | { |
559 | 0 | char defpath[1024], // Default path |
560 | 0 | filename[1024]; // Output filename |
561 | 0 | int fd; // File descriptor |
562 | | |
563 | | |
564 | | // Range check input... |
565 | 0 | if (!common_name) |
566 | 0 | return (false); |
567 | | |
568 | | // Get default path as needed... |
569 | 0 | if (!path) |
570 | 0 | path = http_default_path(defpath, sizeof(defpath)); |
571 | |
|
572 | 0 | http_make_path(filename, sizeof(filename), path, common_name, ext); |
573 | |
|
574 | 0 | if (!value) |
575 | 0 | { |
576 | 0 | unlink(filename); |
577 | 0 | return (true); |
578 | 0 | } |
579 | | |
580 | 0 | if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, (strcmp(ext, "key") && strcmp(ext, "ktm")) ? 0644 : 0600)) < 0) |
581 | 0 | return (false); |
582 | | |
583 | 0 | if (write(fd, value, strlen(value)) < 0) |
584 | 0 | { |
585 | 0 | close(fd); |
586 | 0 | unlink(filename); |
587 | 0 | return (false); |
588 | 0 | } |
589 | | |
590 | 0 | close(fd); |
591 | |
|
592 | | return (true); |
593 | 0 | } |
594 | | |
595 | | |