]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/usersys.c
Clean up some build issues on certain platforms.
[thirdparty/cups.git] / cups / usersys.c
1 /*
2 * User, system, and password routines for CUPS.
3 *
4 * Copyright 2007-2019 by Apple Inc.
5 * Copyright 1997-2006 by Easy Software Products.
6 *
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more
8 * information.
9 */
10
11 /*
12 * Include necessary headers...
13 */
14
15 #include "cups-private.h"
16 #include "debug-internal.h"
17 #include <stdlib.h>
18 #include <sys/stat.h>
19 #ifdef _WIN32
20 # include <windows.h>
21 #else
22 # include <pwd.h>
23 # include <termios.h>
24 # include <sys/utsname.h>
25 #endif /* _WIN32 */
26 #ifdef __APPLE__
27 # include <sys/sysctl.h>
28 #endif /* __APPLE__ */
29
30
31 /*
32 * Local constants...
33 */
34
35 #ifdef __APPLE__
36 # if TARGET_OS_OSX
37 # define kCUPSPrintingPrefs CFSTR("org.cups.PrintingPrefs")
38 # define kPREFIX ""
39 # else
40 # define kCUPSPrintingPrefs CFSTR(".GlobalPreferences")
41 # define kPREFIX "AirPrint"
42 # endif /* TARGET_OS_OSX */
43 # define kAllowAnyRootKey CFSTR(kPREFIX "AllowAnyRoot")
44 # define kAllowExpiredCertsKey CFSTR(kPREFIX "AllowExpiredCerts")
45 # define kEncryptionKey CFSTR(kPREFIX "Encryption")
46 # define kGSSServiceNameKey CFSTR(kPREFIX "GSSServiceName")
47 # define kSSLOptionsKey CFSTR(kPREFIX "SSLOptions")
48 # define kTrustOnFirstUseKey CFSTR(kPREFIX "TrustOnFirstUse")
49 # define kValidateCertsKey CFSTR(kPREFIX "ValidateCerts")
50 /* Deprecated */
51 # define kAllowRC4 CFSTR(kPREFIX "AllowRC4")
52 # define kAllowSSL3 CFSTR(kPREFIX "AllowSSL3")
53 # define kAllowDH CFSTR(kPREFIX "AllowDH")
54 #endif /* __APPLE__ */
55
56 #define _CUPS_PASSCHAR '*' /* Character that is echoed for password */
57
58
59 /*
60 * Local types...
61 */
62
63 typedef struct _cups_client_conf_s /**** client.conf config data ****/
64 {
65 #ifdef HAVE_SSL
66 int ssl_options, /* SSLOptions values */
67 ssl_min_version,/* Minimum SSL/TLS version */
68 ssl_max_version;/* Maximum SSL/TLS version */
69 #endif /* HAVE_SSL */
70 int trust_first, /* Trust on first use? */
71 any_root, /* Allow any (e.g., self-signed) root */
72 expired_certs, /* Allow expired certs */
73 validate_certs; /* Validate certificates */
74 http_encryption_t encryption; /* Encryption setting */
75 char user[65], /* User name */
76 server_name[256];
77 /* Server hostname */
78 #ifdef HAVE_GSSAPI
79 char gss_service_name[32];
80 /* Kerberos service name */
81 #endif /* HAVE_GSSAPI */
82 } _cups_client_conf_t;
83
84
85 /*
86 * Local functions...
87 */
88
89 #ifdef __APPLE__
90 static int cups_apple_get_boolean(CFStringRef key, int *value);
91 static int cups_apple_get_string(CFStringRef key, char *value, size_t valsize);
92 #endif /* __APPLE__ */
93 static int cups_boolean_value(const char *value);
94 static void cups_finalize_client_conf(_cups_client_conf_t *cc);
95 static void cups_init_client_conf(_cups_client_conf_t *cc);
96 static void cups_read_client_conf(cups_file_t *fp, _cups_client_conf_t *cc);
97 static void cups_set_default_ipp_port(_cups_globals_t *cg);
98 static void cups_set_encryption(_cups_client_conf_t *cc, const char *value);
99 #ifdef HAVE_GSSAPI
100 static void cups_set_gss_service_name(_cups_client_conf_t *cc, const char *value);
101 #endif /* HAVE_GSSAPI */
102 static void cups_set_server_name(_cups_client_conf_t *cc, const char *value);
103 #ifdef HAVE_SSL
104 static void cups_set_ssl_options(_cups_client_conf_t *cc, const char *value);
105 #endif /* HAVE_SSL */
106 static void cups_set_user(_cups_client_conf_t *cc, const char *value);
107
108
109 /*
110 * 'cupsEncryption()' - Get the current encryption settings.
111 *
112 * The default encryption setting comes from the CUPS_ENCRYPTION
113 * environment variable, then the ~/.cups/client.conf file, and finally the
114 * /etc/cups/client.conf file. If not set, the default is
115 * @code HTTP_ENCRYPTION_IF_REQUESTED@.
116 *
117 * Note: The current encryption setting is tracked separately for each thread
118 * in a program. Multi-threaded programs that override the setting via the
119 * @link cupsSetEncryption@ function need to do so in each thread for the same
120 * setting to be used.
121 */
122
123 http_encryption_t /* O - Encryption settings */
124 cupsEncryption(void)
125 {
126 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
127
128
129 if (cg->encryption == (http_encryption_t)-1)
130 _cupsSetDefaults();
131
132 return (cg->encryption);
133 }
134
135
136 /*
137 * 'cupsGetPassword()' - Get a password from the user.
138 *
139 * Uses the current password callback function. Returns @code NULL@ if the
140 * user does not provide a password.
141 *
142 * Note: The current password callback function is tracked separately for each
143 * thread in a program. Multi-threaded programs that override the setting via
144 * the @link cupsSetPasswordCB@ or @link cupsSetPasswordCB2@ functions need to
145 * do so in each thread for the same function to be used.
146 *
147 * @exclude all@
148 */
149
150 const char * /* O - Password */
151 cupsGetPassword(const char *prompt) /* I - Prompt string */
152 {
153 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
154
155
156 return ((cg->password_cb)(prompt, NULL, NULL, NULL, cg->password_data));
157 }
158
159
160 /*
161 * 'cupsGetPassword2()' - Get a password from the user using the current
162 * password callback.
163 *
164 * Uses the current password callback function. Returns @code NULL@ if the
165 * user does not provide a password.
166 *
167 * Note: The current password callback function is tracked separately for each
168 * thread in a program. Multi-threaded programs that override the setting via
169 * the @link cupsSetPasswordCB2@ function need to do so in each thread for the
170 * same function to be used.
171 *
172 * @since CUPS 1.4/macOS 10.6@
173 */
174
175 const char * /* O - Password */
176 cupsGetPassword2(const char *prompt, /* I - Prompt string */
177 http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
178 const char *method, /* I - Request method ("GET", "POST", "PUT") */
179 const char *resource) /* I - Resource path */
180 {
181 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
182
183
184 if (!http)
185 http = _cupsConnect();
186
187 return ((cg->password_cb)(prompt, http, method, resource, cg->password_data));
188 }
189
190
191 /*
192 * 'cupsServer()' - Return the hostname/address of the current server.
193 *
194 * The default server comes from the CUPS_SERVER environment variable, then the
195 * ~/.cups/client.conf file, and finally the /etc/cups/client.conf file. If not
196 * set, the default is the local system - either "localhost" or a domain socket
197 * path.
198 *
199 * The returned value can be a fully-qualified hostname, a numeric IPv4 or IPv6
200 * address, or a domain socket pathname.
201 *
202 * Note: The current server is tracked separately for each thread in a program.
203 * Multi-threaded programs that override the server via the
204 * @link cupsSetServer@ function need to do so in each thread for the same
205 * server to be used.
206 */
207
208 const char * /* O - Server name */
209 cupsServer(void)
210 {
211 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
212
213
214 if (!cg->server[0])
215 _cupsSetDefaults();
216
217 return (cg->server);
218 }
219
220
221 /*
222 * 'cupsSetClientCertCB()' - Set the client certificate callback.
223 *
224 * Pass @code NULL@ to restore the default callback.
225 *
226 * Note: The current certificate callback is tracked separately for each thread
227 * in a program. Multi-threaded programs that override the callback need to do
228 * so in each thread for the same callback to be used.
229 *
230 * @since CUPS 1.5/macOS 10.7@
231 */
232
233 void
234 cupsSetClientCertCB(
235 cups_client_cert_cb_t cb, /* I - Callback function */
236 void *user_data) /* I - User data pointer */
237 {
238 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
239
240
241 cg->client_cert_cb = cb;
242 cg->client_cert_data = user_data;
243 }
244
245
246 /*
247 * 'cupsSetCredentials()' - Set the default credentials to be used for SSL/TLS
248 * connections.
249 *
250 * Note: The default credentials are tracked separately for each thread in a
251 * program. Multi-threaded programs that override the setting need to do so in
252 * each thread for the same setting to be used.
253 *
254 * @since CUPS 1.5/macOS 10.7@
255 */
256
257 int /* O - Status of call (0 = success) */
258 cupsSetCredentials(
259 cups_array_t *credentials) /* I - Array of credentials */
260 {
261 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
262
263
264 if (cupsArrayCount(credentials) < 1)
265 return (-1);
266
267 #ifdef HAVE_SSL
268 _httpFreeCredentials(cg->tls_credentials);
269 cg->tls_credentials = _httpCreateCredentials(credentials);
270 #endif /* HAVE_SSL */
271
272 return (cg->tls_credentials ? 0 : -1);
273 }
274
275
276 /*
277 * 'cupsSetEncryption()' - Set the encryption preference.
278 *
279 * The default encryption setting comes from the CUPS_ENCRYPTION
280 * environment variable, then the ~/.cups/client.conf file, and finally the
281 * /etc/cups/client.conf file. If not set, the default is
282 * @code HTTP_ENCRYPTION_IF_REQUESTED@.
283 *
284 * Note: The current encryption setting is tracked separately for each thread
285 * in a program. Multi-threaded programs that override the setting need to do
286 * so in each thread for the same setting to be used.
287 */
288
289 void
290 cupsSetEncryption(http_encryption_t e) /* I - New encryption preference */
291 {
292 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
293
294
295 cg->encryption = e;
296
297 if (cg->http)
298 httpEncryption(cg->http, e);
299 }
300
301
302 /*
303 * 'cupsSetPasswordCB()' - Set the password callback for CUPS.
304 *
305 * Pass @code NULL@ to restore the default (console) password callback, which
306 * reads the password from the console. Programs should call either this
307 * function or @link cupsSetPasswordCB2@, as only one callback can be registered
308 * by a program per thread.
309 *
310 * Note: The current password callback is tracked separately for each thread
311 * in a program. Multi-threaded programs that override the callback need to do
312 * so in each thread for the same callback to be used.
313 *
314 * @exclude all@
315 */
316
317 void
318 cupsSetPasswordCB(cups_password_cb_t cb)/* I - Callback function */
319 {
320 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
321
322
323 if (cb == (cups_password_cb_t)0)
324 cg->password_cb = (cups_password_cb2_t)_cupsGetPassword;
325 else
326 cg->password_cb = (cups_password_cb2_t)cb;
327
328 cg->password_data = NULL;
329 }
330
331
332 /*
333 * 'cupsSetPasswordCB2()' - Set the advanced password callback for CUPS.
334 *
335 * Pass @code NULL@ to restore the default (console) password callback, which
336 * reads the password from the console. Programs should call either this
337 * function or @link cupsSetPasswordCB2@, as only one callback can be registered
338 * by a program per thread.
339 *
340 * Note: The current password callback is tracked separately for each thread
341 * in a program. Multi-threaded programs that override the callback need to do
342 * so in each thread for the same callback to be used.
343 *
344 * @since CUPS 1.4/macOS 10.6@
345 */
346
347 void
348 cupsSetPasswordCB2(
349 cups_password_cb2_t cb, /* I - Callback function */
350 void *user_data) /* I - User data pointer */
351 {
352 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
353
354
355 if (cb == (cups_password_cb2_t)0)
356 cg->password_cb = (cups_password_cb2_t)_cupsGetPassword;
357 else
358 cg->password_cb = cb;
359
360 cg->password_data = user_data;
361 }
362
363
364 /*
365 * 'cupsSetServer()' - Set the default server name and port.
366 *
367 * The "server" string can be a fully-qualified hostname, a numeric
368 * IPv4 or IPv6 address, or a domain socket pathname. Hostnames and numeric IP
369 * addresses can be optionally followed by a colon and port number to override
370 * the default port 631, e.g. "hostname:8631". Pass @code NULL@ to restore the
371 * default server name and port.
372 *
373 * Note: The current server is tracked separately for each thread in a program.
374 * Multi-threaded programs that override the server need to do so in each
375 * thread for the same server to be used.
376 */
377
378 void
379 cupsSetServer(const char *server) /* I - Server name */
380 {
381 char *options, /* Options */
382 *port; /* Pointer to port */
383 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
384
385
386 if (server)
387 {
388 strlcpy(cg->server, server, sizeof(cg->server));
389
390 if (cg->server[0] != '/' && (options = strrchr(cg->server, '/')) != NULL)
391 {
392 *options++ = '\0';
393
394 if (!strcmp(options, "version=1.0"))
395 cg->server_version = 10;
396 else if (!strcmp(options, "version=1.1"))
397 cg->server_version = 11;
398 else if (!strcmp(options, "version=2.0"))
399 cg->server_version = 20;
400 else if (!strcmp(options, "version=2.1"))
401 cg->server_version = 21;
402 else if (!strcmp(options, "version=2.2"))
403 cg->server_version = 22;
404 }
405 else
406 cg->server_version = 20;
407
408 if (cg->server[0] != '/' && (port = strrchr(cg->server, ':')) != NULL &&
409 !strchr(port, ']') && isdigit(port[1] & 255))
410 {
411 *port++ = '\0';
412
413 cg->ipp_port = atoi(port);
414 }
415
416 if (!cg->ipp_port)
417 cups_set_default_ipp_port(cg);
418
419 if (cg->server[0] == '/')
420 strlcpy(cg->servername, "localhost", sizeof(cg->servername));
421 else
422 strlcpy(cg->servername, cg->server, sizeof(cg->servername));
423 }
424 else
425 {
426 cg->server[0] = '\0';
427 cg->servername[0] = '\0';
428 cg->server_version = 20;
429 cg->ipp_port = 0;
430 }
431
432 if (cg->http)
433 {
434 httpClose(cg->http);
435 cg->http = NULL;
436 }
437 }
438
439
440 /*
441 * 'cupsSetServerCertCB()' - Set the server certificate callback.
442 *
443 * Pass @code NULL@ to restore the default callback.
444 *
445 * Note: The current credentials callback is tracked separately for each thread
446 * in a program. Multi-threaded programs that override the callback need to do
447 * so in each thread for the same callback to be used.
448 *
449 * @since CUPS 1.5/macOS 10.7@
450 */
451
452 void
453 cupsSetServerCertCB(
454 cups_server_cert_cb_t cb, /* I - Callback function */
455 void *user_data) /* I - User data pointer */
456 {
457 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
458
459
460 cg->server_cert_cb = cb;
461 cg->server_cert_data = user_data;
462 }
463
464
465 /*
466 * 'cupsSetUser()' - Set the default user name.
467 *
468 * Pass @code NULL@ to restore the default user name.
469 *
470 * Note: The current user name is tracked separately for each thread in a
471 * program. Multi-threaded programs that override the user name need to do so
472 * in each thread for the same user name to be used.
473 */
474
475 void
476 cupsSetUser(const char *user) /* I - User name */
477 {
478 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
479
480
481 if (user)
482 strlcpy(cg->user, user, sizeof(cg->user));
483 else
484 cg->user[0] = '\0';
485 }
486
487
488 /*
489 * 'cupsSetUserAgent()' - Set the default HTTP User-Agent string.
490 *
491 * Setting the string to NULL forces the default value containing the CUPS
492 * version, IPP version, and operating system version and architecture.
493 *
494 * @since CUPS 1.7/macOS 10.9@
495 */
496
497 void
498 cupsSetUserAgent(const char *user_agent)/* I - User-Agent string or @code NULL@ */
499 {
500 _cups_globals_t *cg = _cupsGlobals();
501 /* Thread globals */
502 #ifdef _WIN32
503 SYSTEM_INFO sysinfo; /* System information */
504 OSVERSIONINFOA version; /* OS version info */
505 const char *machine; /* Hardware/machine name */
506 #elif defined(__APPLE__)
507 struct utsname name; /* uname info */
508 char version[256]; /* macOS/iOS version */
509 size_t len; /* Length of value */
510 #else
511 struct utsname name; /* uname info */
512 #endif /* _WIN32 */
513
514
515 if (user_agent)
516 {
517 strlcpy(cg->user_agent, user_agent, sizeof(cg->user_agent));
518 return;
519 }
520
521 #ifdef _WIN32
522 /*
523 * Gather Windows version information for the User-Agent string...
524 */
525
526 version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
527 GetVersionExA(&version);
528 GetNativeSystemInfo(&sysinfo);
529
530 switch (sysinfo.wProcessorArchitecture)
531 {
532 case PROCESSOR_ARCHITECTURE_AMD64 :
533 machine = "amd64";
534 break;
535
536 case PROCESSOR_ARCHITECTURE_ARM :
537 machine = "arm";
538 break;
539
540 case PROCESSOR_ARCHITECTURE_IA64 :
541 machine = "ia64";
542 break;
543
544 case PROCESSOR_ARCHITECTURE_INTEL :
545 machine = "intel";
546 break;
547
548 default :
549 machine = "unknown";
550 break;
551 }
552
553 snprintf(cg->user_agent, sizeof(cg->user_agent), CUPS_MINIMAL " (Windows %d.%d; %s) IPP/2.0", version.dwMajorVersion, version.dwMinorVersion, machine);
554
555 #elif defined(__APPLE__)
556 /*
557 * Gather macOS/iOS version information for the User-Agent string...
558 */
559
560 uname(&name);
561
562 len = sizeof(version) - 1;
563 if (!sysctlbyname("kern.osproductversion", version, &len, NULL, 0))
564 version[len] = '\0';
565 else
566 strlcpy(version, "unknown", sizeof(version));
567
568 # if TARGET_OS_OSX
569 snprintf(cg->user_agent, sizeof(cg->user_agent), CUPS_MINIMAL " (macOS %s; %s) IPP/2.0", version, name.machine);
570 # else
571 snprintf(cg->user_agent, sizeof(cg->user_agent), CUPS_MINIMAL " (iOS %s; %s) IPP/2.0", version, name.machine);
572 # endif /* TARGET_OS_OSX */
573
574 #else
575 /*
576 * Gather generic UNIX version information for the User-Agent string...
577 */
578
579 uname(&name);
580
581 snprintf(cg->user_agent, sizeof(cg->user_agent), CUPS_MINIMAL " (%s %s; %s) IPP/2.0", name.sysname, name.release, name.machine);
582 #endif /* _WIN32 */
583 }
584
585
586 /*
587 * 'cupsUser()' - Return the current user's name.
588 *
589 * Note: The current user name is tracked separately for each thread in a
590 * program. Multi-threaded programs that override the user name with the
591 * @link cupsSetUser@ function need to do so in each thread for the same user
592 * name to be used.
593 */
594
595 const char * /* O - User name */
596 cupsUser(void)
597 {
598 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
599
600
601 if (!cg->user[0])
602 _cupsSetDefaults();
603
604 return (cg->user);
605 }
606
607
608 /*
609 * 'cupsUserAgent()' - Return the default HTTP User-Agent string.
610 *
611 * @since CUPS 1.7/macOS 10.9@
612 */
613
614 const char * /* O - User-Agent string */
615 cupsUserAgent(void)
616 {
617 _cups_globals_t *cg = _cupsGlobals(); /* Thread globals */
618
619
620 if (!cg->user_agent[0])
621 cupsSetUserAgent(NULL);
622
623 return (cg->user_agent);
624 }
625
626
627 /*
628 * '_cupsGetPassword()' - Get a password from the user.
629 */
630
631 const char * /* O - Password or @code NULL@ if none */
632 _cupsGetPassword(const char *prompt) /* I - Prompt string */
633 {
634 #ifdef _WIN32
635 HANDLE tty; /* Console handle */
636 DWORD mode; /* Console mode */
637 char passch, /* Current key press */
638 *passptr, /* Pointer into password string */
639 *passend; /* End of password string */
640 DWORD passbytes; /* Bytes read */
641 _cups_globals_t *cg = _cupsGlobals();
642 /* Thread globals */
643
644
645 /*
646 * Disable input echo and set raw input...
647 */
648
649 if ((tty = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE)
650 return (NULL);
651
652 if (!GetConsoleMode(tty, &mode))
653 return (NULL);
654
655 if (!SetConsoleMode(tty, 0))
656 return (NULL);
657
658 /*
659 * Display the prompt...
660 */
661
662 printf("%s ", prompt);
663 fflush(stdout);
664
665 /*
666 * Read the password string from /dev/tty until we get interrupted or get a
667 * carriage return or newline...
668 */
669
670 passptr = cg->password;
671 passend = cg->password + sizeof(cg->password) - 1;
672
673 while (ReadFile(tty, &passch, 1, &passbytes, NULL))
674 {
675 if (passch == 0x0A || passch == 0x0D)
676 {
677 /*
678 * Enter/return...
679 */
680
681 break;
682 }
683 else if (passch == 0x08 || passch == 0x7F)
684 {
685 /*
686 * Backspace/delete (erase character)...
687 */
688
689 if (passptr > cg->password)
690 {
691 passptr --;
692 fputs("\010 \010", stdout);
693 }
694 else
695 putchar(0x07);
696 }
697 else if (passch == 0x15)
698 {
699 /*
700 * CTRL+U (erase line)
701 */
702
703 if (passptr > cg->password)
704 {
705 while (passptr > cg->password)
706 {
707 passptr --;
708 fputs("\010 \010", stdout);
709 }
710 }
711 else
712 putchar(0x07);
713 }
714 else if (passch == 0x03)
715 {
716 /*
717 * CTRL+C...
718 */
719
720 passptr = cg->password;
721 break;
722 }
723 else if ((passch & 255) < 0x20 || passptr >= passend)
724 putchar(0x07);
725 else
726 {
727 *passptr++ = passch;
728 putchar(_CUPS_PASSCHAR);
729 }
730
731 fflush(stdout);
732 }
733
734 putchar('\n');
735 fflush(stdout);
736
737 /*
738 * Cleanup...
739 */
740
741 SetConsoleMode(tty, mode);
742
743 /*
744 * Return the proper value...
745 */
746
747 if (passbytes == 1 && passptr > cg->password)
748 {
749 *passptr = '\0';
750 return (cg->password);
751 }
752 else
753 {
754 memset(cg->password, 0, sizeof(cg->password));
755 return (NULL);
756 }
757
758 #else
759 int tty; /* /dev/tty - never read from stdin */
760 struct termios original, /* Original input mode */
761 noecho; /* No echo input mode */
762 char passch, /* Current key press */
763 *passptr, /* Pointer into password string */
764 *passend; /* End of password string */
765 ssize_t passbytes; /* Bytes read */
766 _cups_globals_t *cg = _cupsGlobals();
767 /* Thread globals */
768
769
770 /*
771 * Disable input echo and set raw input...
772 */
773
774 if ((tty = open("/dev/tty", O_RDONLY)) < 0)
775 return (NULL);
776
777 if (tcgetattr(tty, &original))
778 {
779 close(tty);
780 return (NULL);
781 }
782
783 noecho = original;
784 noecho.c_lflag &= (tcflag_t)~(ICANON | ECHO | ECHOE | ISIG);
785 noecho.c_cc[VMIN] = 1;
786 noecho.c_cc[VTIME] = 0;
787
788 if (tcsetattr(tty, TCSAFLUSH, &noecho))
789 {
790 close(tty);
791 return (NULL);
792 }
793
794 /*
795 * Display the prompt...
796 */
797
798 printf("%s ", prompt);
799 fflush(stdout);
800
801 /*
802 * Read the password string from /dev/tty until we get interrupted or get a
803 * carriage return or newline...
804 */
805
806 passptr = cg->password;
807 passend = cg->password + sizeof(cg->password) - 1;
808
809 while ((passbytes = read(tty, &passch, 1)) == 1)
810 {
811 if (passch == noecho.c_cc[VEOL] ||
812 # ifdef VEOL2
813 passch == noecho.c_cc[VEOL2] ||
814 # endif /* VEOL2 */
815 passch == 0x0A || passch == 0x0D)
816 {
817 /*
818 * Enter/return...
819 */
820
821 break;
822 }
823 else if (passch == noecho.c_cc[VERASE] ||
824 passch == 0x08 || passch == 0x7F)
825 {
826 /*
827 * Backspace/delete (erase character)...
828 */
829
830 if (passptr > cg->password)
831 {
832 passptr --;
833 fputs("\010 \010", stdout);
834 }
835 else
836 putchar(0x07);
837 }
838 else if (passch == noecho.c_cc[VKILL])
839 {
840 /*
841 * CTRL+U (erase line)
842 */
843
844 if (passptr > cg->password)
845 {
846 while (passptr > cg->password)
847 {
848 passptr --;
849 fputs("\010 \010", stdout);
850 }
851 }
852 else
853 putchar(0x07);
854 }
855 else if (passch == noecho.c_cc[VINTR] || passch == noecho.c_cc[VQUIT] ||
856 passch == noecho.c_cc[VEOF])
857 {
858 /*
859 * CTRL+C, CTRL+D, or CTRL+Z...
860 */
861
862 passptr = cg->password;
863 break;
864 }
865 else if ((passch & 255) < 0x20 || passptr >= passend)
866 putchar(0x07);
867 else
868 {
869 *passptr++ = passch;
870 putchar(_CUPS_PASSCHAR);
871 }
872
873 fflush(stdout);
874 }
875
876 putchar('\n');
877 fflush(stdout);
878
879 /*
880 * Cleanup...
881 */
882
883 tcsetattr(tty, TCSAFLUSH, &original);
884 close(tty);
885
886 /*
887 * Return the proper value...
888 */
889
890 if (passbytes == 1 && passptr > cg->password)
891 {
892 *passptr = '\0';
893 return (cg->password);
894 }
895 else
896 {
897 memset(cg->password, 0, sizeof(cg->password));
898 return (NULL);
899 }
900 #endif /* _WIN32 */
901 }
902
903
904 #ifdef HAVE_GSSAPI
905 /*
906 * '_cupsGSSServiceName()' - Get the GSS (Kerberos) service name.
907 */
908
909 const char *
910 _cupsGSSServiceName(void)
911 {
912 _cups_globals_t *cg = _cupsGlobals(); /* Thread globals */
913
914
915 if (!cg->gss_service_name[0])
916 _cupsSetDefaults();
917
918 return (cg->gss_service_name);
919 }
920 #endif /* HAVE_GSSAPI */
921
922
923 /*
924 * '_cupsSetDefaults()' - Set the default server, port, and encryption.
925 */
926
927 void
928 _cupsSetDefaults(void)
929 {
930 cups_file_t *fp; /* File */
931 const char *home; /* Home directory of user */
932 char filename[1024]; /* Filename */
933 _cups_client_conf_t cc; /* client.conf values */
934 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
935
936
937 DEBUG_puts("_cupsSetDefaults()");
938
939 /*
940 * Load initial client.conf values...
941 */
942
943 cups_init_client_conf(&cc);
944
945 /*
946 * Read the /etc/cups/client.conf and ~/.cups/client.conf files, if
947 * present.
948 */
949
950 snprintf(filename, sizeof(filename), "%s/client.conf", cg->cups_serverroot);
951 if ((fp = cupsFileOpen(filename, "r")) != NULL)
952 {
953 cups_read_client_conf(fp, &cc);
954 cupsFileClose(fp);
955 }
956
957 # ifdef HAVE_GETEUID
958 if ((geteuid() == getuid() || !getuid()) && getegid() == getgid() && (home = getenv("HOME")) != NULL)
959 # elif !defined(_WIN32)
960 if (getuid() && (home = getenv("HOME")) != NULL)
961 # else
962 if ((home = getenv("HOME")) != NULL)
963 # endif /* HAVE_GETEUID */
964 {
965 /*
966 * Look for ~/.cups/client.conf...
967 */
968
969 snprintf(filename, sizeof(filename), "%s/.cups/client.conf", home);
970 if ((fp = cupsFileOpen(filename, "r")) != NULL)
971 {
972 cups_read_client_conf(fp, &cc);
973 cupsFileClose(fp);
974 }
975 }
976
977 /*
978 * Finalize things so every client.conf value is set...
979 */
980
981 cups_finalize_client_conf(&cc);
982
983 if (cg->encryption == (http_encryption_t)-1)
984 cg->encryption = cc.encryption;
985
986 if (!cg->server[0] || !cg->ipp_port)
987 cupsSetServer(cc.server_name);
988
989 if (!cg->ipp_port)
990 cups_set_default_ipp_port(cg);
991
992 if (!cg->user[0])
993 strlcpy(cg->user, cc.user, sizeof(cg->user));
994
995 #ifdef HAVE_GSSAPI
996 if (!cg->gss_service_name[0])
997 strlcpy(cg->gss_service_name, cc.gss_service_name, sizeof(cg->gss_service_name));
998 #endif /* HAVE_GSSAPI */
999
1000 if (cg->trust_first < 0)
1001 cg->trust_first = cc.trust_first;
1002
1003 if (cg->any_root < 0)
1004 cg->any_root = cc.any_root;
1005
1006 if (cg->expired_certs < 0)
1007 cg->expired_certs = cc.expired_certs;
1008
1009 if (cg->validate_certs < 0)
1010 cg->validate_certs = cc.validate_certs;
1011
1012 #ifdef HAVE_SSL
1013 _httpTLSSetOptions(cc.ssl_options | _HTTP_TLS_SET_DEFAULT, cc.ssl_min_version, cc.ssl_max_version);
1014 #endif /* HAVE_SSL */
1015 }
1016
1017
1018 #ifdef __APPLE__
1019 /*
1020 * 'cups_apple_get_boolean()' - Get a boolean setting from the CUPS preferences.
1021 */
1022
1023 static int /* O - 1 if set, 0 otherwise */
1024 cups_apple_get_boolean(
1025 CFStringRef key, /* I - Key (name) */
1026 int *value) /* O - Boolean value */
1027 {
1028 Boolean bval, /* Preference value */
1029 bval_set; /* Value is set? */
1030
1031
1032 bval = CFPreferencesGetAppBooleanValue(key, kCUPSPrintingPrefs, &bval_set);
1033
1034 if (bval_set)
1035 *value = (int)bval;
1036
1037 return ((int)bval_set);
1038 }
1039
1040
1041 /*
1042 * 'cups_apple_get_string()' - Get a string setting from the CUPS preferences.
1043 */
1044
1045 static int /* O - 1 if set, 0 otherwise */
1046 cups_apple_get_string(
1047 CFStringRef key, /* I - Key (name) */
1048 char *value, /* O - String value */
1049 size_t valsize) /* I - Size of value buffer */
1050 {
1051 CFStringRef sval; /* String value */
1052
1053
1054 if ((sval = CFPreferencesCopyAppValue(key, kCUPSPrintingPrefs)) != NULL)
1055 {
1056 Boolean result = CFStringGetCString(sval, value, (CFIndex)valsize, kCFStringEncodingUTF8);
1057
1058 CFRelease(sval);
1059
1060 if (result)
1061 return (1);
1062 }
1063
1064 return (0);
1065 }
1066 #endif /* __APPLE__ */
1067
1068
1069 /*
1070 * 'cups_boolean_value()' - Convert a string to a boolean value.
1071 */
1072
1073 static int /* O - Boolean value */
1074 cups_boolean_value(const char *value) /* I - String value */
1075 {
1076 return (!_cups_strcasecmp(value, "yes") || !_cups_strcasecmp(value, "on") || !_cups_strcasecmp(value, "true"));
1077 }
1078
1079
1080 /*
1081 * 'cups_finalize_client_conf()' - Finalize client.conf values.
1082 */
1083
1084 static void
1085 cups_finalize_client_conf(
1086 _cups_client_conf_t *cc) /* I - client.conf values */
1087 {
1088 const char *value; /* Environment variable */
1089
1090
1091 if ((value = getenv("CUPS_TRUSTFIRST")) != NULL)
1092 cc->trust_first = cups_boolean_value(value);
1093
1094 if ((value = getenv("CUPS_ANYROOT")) != NULL)
1095 cc->any_root = cups_boolean_value(value);
1096
1097 if ((value = getenv("CUPS_ENCRYPTION")) != NULL)
1098 cups_set_encryption(cc, value);
1099
1100 if ((value = getenv("CUPS_EXPIREDCERTS")) != NULL)
1101 cc->expired_certs = cups_boolean_value(value);
1102
1103 #ifdef HAVE_GSSAPI
1104 if ((value = getenv("CUPS_GSSSERVICENAME")) != NULL)
1105 cups_set_gss_service_name(cc, value);
1106 #endif /* HAVE_GSSAPI */
1107
1108 if ((value = getenv("CUPS_SERVER")) != NULL)
1109 cups_set_server_name(cc, value);
1110
1111 if ((value = getenv("CUPS_USER")) != NULL)
1112 cups_set_user(cc, value);
1113
1114 if ((value = getenv("CUPS_VALIDATECERTS")) != NULL)
1115 cc->validate_certs = cups_boolean_value(value);
1116
1117 /*
1118 * Then apply defaults for those values that haven't been set...
1119 */
1120
1121 if (cc->trust_first < 0)
1122 cc->trust_first = 1;
1123
1124 if (cc->any_root < 0)
1125 cc->any_root = 1;
1126
1127 if (cc->encryption == (http_encryption_t)-1)
1128 cc->encryption = HTTP_ENCRYPTION_IF_REQUESTED;
1129
1130 if (cc->expired_certs < 0)
1131 cc->expired_certs = 0;
1132
1133 #ifdef HAVE_GSSAPI
1134 if (!cc->gss_service_name[0])
1135 cups_set_gss_service_name(cc, CUPS_DEFAULT_GSSSERVICENAME);
1136 #endif /* HAVE_GSSAPI */
1137
1138 if (!cc->server_name[0])
1139 {
1140 #ifdef CUPS_DEFAULT_DOMAINSOCKET
1141 /*
1142 * If we are compiled with domain socket support, only use the
1143 * domain socket if it exists and has the right permissions...
1144 */
1145
1146 if (!access(CUPS_DEFAULT_DOMAINSOCKET, R_OK))
1147 cups_set_server_name(cc, CUPS_DEFAULT_DOMAINSOCKET);
1148 else
1149 #endif /* CUPS_DEFAULT_DOMAINSOCKET */
1150 cups_set_server_name(cc, "localhost");
1151 }
1152
1153 if (!cc->user[0])
1154 {
1155 #ifdef _WIN32
1156 /*
1157 * Get the current user name from the OS...
1158 */
1159
1160 DWORD size; /* Size of string */
1161
1162 size = sizeof(cc->user);
1163 if (!GetUserNameA(cc->user, &size))
1164 #else
1165 /*
1166 * Try the USER environment variable as the default username...
1167 */
1168
1169 const char *envuser = getenv("USER");
1170 /* Default username */
1171 struct passwd *pw = NULL; /* Account information */
1172
1173 if (envuser)
1174 {
1175 /*
1176 * Validate USER matches the current UID, otherwise don't allow it to
1177 * override things... This makes sure that printing after doing su
1178 * or sudo records the correct username.
1179 */
1180
1181 if ((pw = getpwnam(envuser)) != NULL && pw->pw_uid != getuid())
1182 pw = NULL;
1183 }
1184
1185 if (!pw)
1186 pw = getpwuid(getuid());
1187
1188 if (pw)
1189 strlcpy(cc->user, pw->pw_name, sizeof(cc->user));
1190 else
1191 #endif /* _WIN32 */
1192 {
1193 /*
1194 * Use the default "unknown" user name...
1195 */
1196
1197 strlcpy(cc->user, "unknown", sizeof(cc->user));
1198 }
1199 }
1200
1201 if (cc->validate_certs < 0)
1202 cc->validate_certs = 0;
1203 }
1204
1205
1206 /*
1207 * 'cups_init_client_conf()' - Initialize client.conf values.
1208 */
1209
1210 static void
1211 cups_init_client_conf(
1212 _cups_client_conf_t *cc) /* I - client.conf values */
1213 {
1214 /*
1215 * Clear all values to "not set"...
1216 */
1217
1218 memset(cc, 0, sizeof(_cups_client_conf_t));
1219
1220 #if defined(__APPLE__) && !TARGET_OS_OSX
1221 cups_set_user(cc, "mobile");
1222 #endif /* __APPLE__ && !TARGET_OS_OSX */
1223
1224 #ifdef HAVE_SSL
1225 cc->ssl_min_version = _HTTP_TLS_1_0;
1226 cc->ssl_max_version = _HTTP_TLS_MAX;
1227 #endif /* HAVE_SSL */
1228 cc->encryption = (http_encryption_t)-1;
1229 cc->trust_first = -1;
1230 cc->any_root = -1;
1231 cc->expired_certs = -1;
1232 cc->validate_certs = -1;
1233
1234 /*
1235 * Load settings from the org.cups.PrintingPrefs plist (which trump
1236 * everything...)
1237 */
1238
1239 #if defined(__APPLE__) && defined(HAVE_SSL)
1240 char sval[1024]; /* String value */
1241 int bval; /* Boolean value */
1242
1243 if (cups_apple_get_boolean(kAllowAnyRootKey, &bval))
1244 cc->any_root = bval;
1245
1246 if (cups_apple_get_boolean(kAllowExpiredCertsKey, &bval))
1247 cc->expired_certs = bval;
1248
1249 if (cups_apple_get_string(kEncryptionKey, sval, sizeof(sval)))
1250 cups_set_encryption(cc, sval);
1251
1252 if (cups_apple_get_string(kSSLOptionsKey, sval, sizeof(sval)))
1253 {
1254 cups_set_ssl_options(cc, sval);
1255 }
1256 else
1257 {
1258 sval[0] = '\0';
1259
1260 if (cups_apple_get_boolean(kAllowRC4, &bval) && bval)
1261 strlcat(sval, " AllowRC4", sizeof(sval));
1262 if (cups_apple_get_boolean(kAllowSSL3, &bval) && bval)
1263 strlcat(sval, " AllowSSL3", sizeof(sval));
1264 if (cups_apple_get_boolean(kAllowDH, &bval) && bval)
1265 strlcat(sval, " AllowDH", sizeof(sval));
1266
1267 if (sval[0])
1268 cups_set_ssl_options(cc, sval);
1269 }
1270
1271 if (cups_apple_get_boolean(kTrustOnFirstUseKey, &bval))
1272 cc->trust_first = bval;
1273
1274 if (cups_apple_get_boolean(kValidateCertsKey, &bval))
1275 cc->validate_certs = bval;
1276 #endif /* __APPLE__ && HAVE_SSL */
1277 }
1278
1279
1280 /*
1281 * 'cups_read_client_conf()' - Read a client.conf file.
1282 */
1283
1284 static void
1285 cups_read_client_conf(
1286 cups_file_t *fp, /* I - File to read */
1287 _cups_client_conf_t *cc) /* I - client.conf values */
1288 {
1289 int linenum; /* Current line number */
1290 char line[1024], /* Line from file */
1291 *value; /* Pointer into line */
1292
1293
1294 /*
1295 * Read from the file...
1296 */
1297
1298 linenum = 0;
1299 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
1300 {
1301 if (!_cups_strcasecmp(line, "Encryption") && value)
1302 cups_set_encryption(cc, value);
1303 #ifndef __APPLE__
1304 /*
1305 * The ServerName directive is not supported on macOS due to app
1306 * sandboxing restrictions, i.e. not all apps request network access.
1307 */
1308 else if (!_cups_strcasecmp(line, "ServerName") && value)
1309 cups_set_server_name(cc, value);
1310 #endif /* !__APPLE__ */
1311 else if (!_cups_strcasecmp(line, "User") && value)
1312 cups_set_user(cc, value);
1313 else if (!_cups_strcasecmp(line, "TrustOnFirstUse") && value)
1314 cc->trust_first = cups_boolean_value(value);
1315 else if (!_cups_strcasecmp(line, "AllowAnyRoot") && value)
1316 cc->any_root = cups_boolean_value(value);
1317 else if (!_cups_strcasecmp(line, "AllowExpiredCerts") &&
1318 value)
1319 cc->expired_certs = cups_boolean_value(value);
1320 else if (!_cups_strcasecmp(line, "ValidateCerts") && value)
1321 cc->validate_certs = cups_boolean_value(value);
1322 #ifdef HAVE_GSSAPI
1323 else if (!_cups_strcasecmp(line, "GSSServiceName") && value)
1324 cups_set_gss_service_name(cc, value);
1325 #endif /* HAVE_GSSAPI */
1326 #ifdef HAVE_SSL
1327 else if (!_cups_strcasecmp(line, "SSLOptions") && value)
1328 cups_set_ssl_options(cc, value);
1329 #endif /* HAVE_SSL */
1330 }
1331 }
1332
1333
1334 /*
1335 * 'cups_set_default_ipp_port()' - Set the default IPP port value.
1336 */
1337
1338 static void
1339 cups_set_default_ipp_port(
1340 _cups_globals_t *cg) /* I - Global data */
1341 {
1342 const char *ipp_port; /* IPP_PORT environment variable */
1343
1344
1345 if ((ipp_port = getenv("IPP_PORT")) != NULL)
1346 {
1347 if ((cg->ipp_port = atoi(ipp_port)) <= 0)
1348 cg->ipp_port = CUPS_DEFAULT_IPP_PORT;
1349 }
1350 else
1351 cg->ipp_port = CUPS_DEFAULT_IPP_PORT;
1352 }
1353
1354 /*
1355 * 'cups_set_encryption()' - Set the Encryption value.
1356 */
1357
1358 static void
1359 cups_set_encryption(
1360 _cups_client_conf_t *cc, /* I - client.conf values */
1361 const char *value) /* I - Value */
1362 {
1363 if (!_cups_strcasecmp(value, "never"))
1364 cc->encryption = HTTP_ENCRYPTION_NEVER;
1365 else if (!_cups_strcasecmp(value, "always"))
1366 cc->encryption = HTTP_ENCRYPTION_ALWAYS;
1367 else if (!_cups_strcasecmp(value, "required"))
1368 cc->encryption = HTTP_ENCRYPTION_REQUIRED;
1369 else
1370 cc->encryption = HTTP_ENCRYPTION_IF_REQUESTED;
1371 }
1372
1373
1374 /*
1375 * 'cups_set_gss_service_name()' - Set the GSSServiceName value.
1376 */
1377
1378 #ifdef HAVE_GSSAPI
1379 static void
1380 cups_set_gss_service_name(
1381 _cups_client_conf_t *cc, /* I - client.conf values */
1382 const char *value) /* I - Value */
1383 {
1384 strlcpy(cc->gss_service_name, value, sizeof(cc->gss_service_name));
1385 }
1386 #endif /* HAVE_GSSAPI */
1387
1388
1389 /*
1390 * 'cups_set_server_name()' - Set the ServerName value.
1391 */
1392
1393 static void
1394 cups_set_server_name(
1395 _cups_client_conf_t *cc, /* I - client.conf values */
1396 const char *value) /* I - Value */
1397 {
1398 strlcpy(cc->server_name, value, sizeof(cc->server_name));
1399 }
1400
1401
1402 /*
1403 * 'cups_set_ssl_options()' - Set the SSLOptions value.
1404 */
1405
1406 #ifdef HAVE_SSL
1407 static void
1408 cups_set_ssl_options(
1409 _cups_client_conf_t *cc, /* I - client.conf values */
1410 const char *value) /* I - Value */
1411 {
1412 /*
1413 * SSLOptions [AllowRC4] [AllowSSL3] [AllowDH] [DenyTLS1.0] [None]
1414 */
1415
1416 int options = _HTTP_TLS_NONE, /* SSL/TLS options */
1417 min_version = _HTTP_TLS_1_0, /* Minimum SSL/TLS version */
1418 max_version = _HTTP_TLS_MAX; /* Maximum SSL/TLS version */
1419 char temp[256], /* Copy of value */
1420 *start, /* Start of option */
1421 *end; /* End of option */
1422
1423
1424 strlcpy(temp, value, sizeof(temp));
1425
1426 for (start = temp; *start; start = end)
1427 {
1428 /*
1429 * Find end of keyword...
1430 */
1431
1432 end = start;
1433 while (*end && !_cups_isspace(*end))
1434 end ++;
1435
1436 if (*end)
1437 *end++ = '\0';
1438
1439 /*
1440 * Compare...
1441 */
1442
1443 if (!_cups_strcasecmp(start, "AllowRC4"))
1444 options |= _HTTP_TLS_ALLOW_RC4;
1445 else if (!_cups_strcasecmp(start, "AllowSSL3"))
1446 min_version = _HTTP_TLS_SSL3;
1447 else if (!_cups_strcasecmp(start, "AllowDH"))
1448 options |= _HTTP_TLS_ALLOW_DH;
1449 else if (!_cups_strcasecmp(start, "DenyCBC"))
1450 options |= _HTTP_TLS_DENY_CBC;
1451 else if (!_cups_strcasecmp(start, "DenyTLS1.0"))
1452 min_version = _HTTP_TLS_1_1;
1453 else if (!_cups_strcasecmp(start, "MaxTLS1.0"))
1454 max_version = _HTTP_TLS_1_0;
1455 else if (!_cups_strcasecmp(start, "MaxTLS1.1"))
1456 max_version = _HTTP_TLS_1_1;
1457 else if (!_cups_strcasecmp(start, "MaxTLS1.2"))
1458 max_version = _HTTP_TLS_1_2;
1459 else if (!_cups_strcasecmp(start, "MaxTLS1.3"))
1460 max_version = _HTTP_TLS_1_3;
1461 else if (!_cups_strcasecmp(start, "MinTLS1.0"))
1462 min_version = _HTTP_TLS_1_0;
1463 else if (!_cups_strcasecmp(start, "MinTLS1.1"))
1464 min_version = _HTTP_TLS_1_1;
1465 else if (!_cups_strcasecmp(start, "MinTLS1.2"))
1466 min_version = _HTTP_TLS_1_2;
1467 else if (!_cups_strcasecmp(start, "MinTLS1.3"))
1468 min_version = _HTTP_TLS_1_3;
1469 else if (!_cups_strcasecmp(start, "None"))
1470 options = _HTTP_TLS_NONE;
1471 }
1472
1473 cc->ssl_options = options;
1474 cc->ssl_max_version = max_version;
1475 cc->ssl_min_version = min_version;
1476
1477 DEBUG_printf(("4cups_set_ssl_options(cc=%p, value=\"%s\") options=%x, min_version=%d, max_version=%d", (void *)cc, value, options, min_version, max_version));
1478 }
1479 #endif /* HAVE_SSL */
1480
1481
1482 /*
1483 * 'cups_set_user()' - Set the User value.
1484 */
1485
1486 static void
1487 cups_set_user(
1488 _cups_client_conf_t *cc, /* I - client.conf values */
1489 const char *value) /* I - Value */
1490 {
1491 strlcpy(cc->user, value, sizeof(cc->user));
1492 }