]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/usersys.c
One more spot needs attention.
[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 /*
1141 * If we are compiled with domain socket support, only use the
1142 * domain socket if it exists and has the right permissions...
1143 */
1144
1145 #if defined(__APPLE__) && !TARGET_OS_OSX
1146 cups_set_server_name(cc, "/private/var/run/printd");
1147
1148 #else
1149 # ifdef CUPS_DEFAULT_DOMAINSOCKET
1150 if (!access(CUPS_DEFAULT_DOMAINSOCKET, R_OK))
1151 cups_set_server_name(cc, CUPS_DEFAULT_DOMAINSOCKET);
1152 else
1153 # endif /* CUPS_DEFAULT_DOMAINSOCKET */
1154 cups_set_server_name(cc, "localhost");
1155 #endif /* __APPLE__ && !TARGET_OS_OSX */
1156 }
1157
1158 if (!cc->user[0])
1159 {
1160 #ifdef _WIN32
1161 /*
1162 * Get the current user name from the OS...
1163 */
1164
1165 DWORD size; /* Size of string */
1166
1167 size = sizeof(cc->user);
1168 if (!GetUserNameA(cc->user, &size))
1169 #else
1170 /*
1171 * Try the USER environment variable as the default username...
1172 */
1173
1174 const char *envuser = getenv("USER");
1175 /* Default username */
1176 struct passwd *pw = NULL; /* Account information */
1177
1178 if (envuser)
1179 {
1180 /*
1181 * Validate USER matches the current UID, otherwise don't allow it to
1182 * override things... This makes sure that printing after doing su
1183 * or sudo records the correct username.
1184 */
1185
1186 if ((pw = getpwnam(envuser)) != NULL && pw->pw_uid != getuid())
1187 pw = NULL;
1188 }
1189
1190 if (!pw)
1191 pw = getpwuid(getuid());
1192
1193 if (pw)
1194 strlcpy(cc->user, pw->pw_name, sizeof(cc->user));
1195 else
1196 #endif /* _WIN32 */
1197 {
1198 /*
1199 * Use the default "unknown" user name...
1200 */
1201
1202 strlcpy(cc->user, "unknown", sizeof(cc->user));
1203 }
1204 }
1205
1206 if (cc->validate_certs < 0)
1207 cc->validate_certs = 0;
1208 }
1209
1210
1211 /*
1212 * 'cups_init_client_conf()' - Initialize client.conf values.
1213 */
1214
1215 static void
1216 cups_init_client_conf(
1217 _cups_client_conf_t *cc) /* I - client.conf values */
1218 {
1219 /*
1220 * Clear all values to "not set"...
1221 */
1222
1223 memset(cc, 0, sizeof(_cups_client_conf_t));
1224
1225 #if defined(__APPLE__) && !TARGET_OS_OSX
1226 cups_set_user(cc, "mobile");
1227 #endif /* __APPLE__ && !TARGET_OS_OSX */
1228
1229 #ifdef HAVE_SSL
1230 cc->ssl_min_version = _HTTP_TLS_1_0;
1231 cc->ssl_max_version = _HTTP_TLS_MAX;
1232 #endif /* HAVE_SSL */
1233 cc->encryption = (http_encryption_t)-1;
1234 cc->trust_first = -1;
1235 cc->any_root = -1;
1236 cc->expired_certs = -1;
1237 cc->validate_certs = -1;
1238
1239 /*
1240 * Load settings from the org.cups.PrintingPrefs plist (which trump
1241 * everything...)
1242 */
1243
1244 #if defined(__APPLE__) && defined(HAVE_SSL)
1245 char sval[1024]; /* String value */
1246 int bval; /* Boolean value */
1247
1248 if (cups_apple_get_boolean(kAllowAnyRootKey, &bval))
1249 cc->any_root = bval;
1250
1251 if (cups_apple_get_boolean(kAllowExpiredCertsKey, &bval))
1252 cc->expired_certs = bval;
1253
1254 if (cups_apple_get_string(kEncryptionKey, sval, sizeof(sval)))
1255 cups_set_encryption(cc, sval);
1256
1257 if (cups_apple_get_string(kSSLOptionsKey, sval, sizeof(sval)))
1258 {
1259 cups_set_ssl_options(cc, sval);
1260 }
1261 else
1262 {
1263 sval[0] = '\0';
1264
1265 if (cups_apple_get_boolean(kAllowRC4, &bval) && bval)
1266 strlcat(sval, " AllowRC4", sizeof(sval));
1267 if (cups_apple_get_boolean(kAllowSSL3, &bval) && bval)
1268 strlcat(sval, " AllowSSL3", sizeof(sval));
1269 if (cups_apple_get_boolean(kAllowDH, &bval) && bval)
1270 strlcat(sval, " AllowDH", sizeof(sval));
1271
1272 if (sval[0])
1273 cups_set_ssl_options(cc, sval);
1274 }
1275
1276 if (cups_apple_get_boolean(kTrustOnFirstUseKey, &bval))
1277 cc->trust_first = bval;
1278
1279 if (cups_apple_get_boolean(kValidateCertsKey, &bval))
1280 cc->validate_certs = bval;
1281 #endif /* __APPLE__ && HAVE_SSL */
1282 }
1283
1284
1285 /*
1286 * 'cups_read_client_conf()' - Read a client.conf file.
1287 */
1288
1289 static void
1290 cups_read_client_conf(
1291 cups_file_t *fp, /* I - File to read */
1292 _cups_client_conf_t *cc) /* I - client.conf values */
1293 {
1294 int linenum; /* Current line number */
1295 char line[1024], /* Line from file */
1296 *value; /* Pointer into line */
1297
1298
1299 /*
1300 * Read from the file...
1301 */
1302
1303 linenum = 0;
1304 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
1305 {
1306 if (!_cups_strcasecmp(line, "Encryption") && value)
1307 cups_set_encryption(cc, value);
1308 #ifndef __APPLE__
1309 /*
1310 * The ServerName directive is not supported on macOS due to app
1311 * sandboxing restrictions, i.e. not all apps request network access.
1312 */
1313 else if (!_cups_strcasecmp(line, "ServerName") && value)
1314 cups_set_server_name(cc, value);
1315 #endif /* !__APPLE__ */
1316 else if (!_cups_strcasecmp(line, "User") && value)
1317 cups_set_user(cc, value);
1318 else if (!_cups_strcasecmp(line, "TrustOnFirstUse") && value)
1319 cc->trust_first = cups_boolean_value(value);
1320 else if (!_cups_strcasecmp(line, "AllowAnyRoot") && value)
1321 cc->any_root = cups_boolean_value(value);
1322 else if (!_cups_strcasecmp(line, "AllowExpiredCerts") &&
1323 value)
1324 cc->expired_certs = cups_boolean_value(value);
1325 else if (!_cups_strcasecmp(line, "ValidateCerts") && value)
1326 cc->validate_certs = cups_boolean_value(value);
1327 #ifdef HAVE_GSSAPI
1328 else if (!_cups_strcasecmp(line, "GSSServiceName") && value)
1329 cups_set_gss_service_name(cc, value);
1330 #endif /* HAVE_GSSAPI */
1331 #ifdef HAVE_SSL
1332 else if (!_cups_strcasecmp(line, "SSLOptions") && value)
1333 cups_set_ssl_options(cc, value);
1334 #endif /* HAVE_SSL */
1335 }
1336 }
1337
1338
1339 /*
1340 * 'cups_set_default_ipp_port()' - Set the default IPP port value.
1341 */
1342
1343 static void
1344 cups_set_default_ipp_port(
1345 _cups_globals_t *cg) /* I - Global data */
1346 {
1347 const char *ipp_port; /* IPP_PORT environment variable */
1348
1349
1350 if ((ipp_port = getenv("IPP_PORT")) != NULL)
1351 {
1352 if ((cg->ipp_port = atoi(ipp_port)) <= 0)
1353 cg->ipp_port = CUPS_DEFAULT_IPP_PORT;
1354 }
1355 else
1356 cg->ipp_port = CUPS_DEFAULT_IPP_PORT;
1357 }
1358
1359 /*
1360 * 'cups_set_encryption()' - Set the Encryption value.
1361 */
1362
1363 static void
1364 cups_set_encryption(
1365 _cups_client_conf_t *cc, /* I - client.conf values */
1366 const char *value) /* I - Value */
1367 {
1368 if (!_cups_strcasecmp(value, "never"))
1369 cc->encryption = HTTP_ENCRYPTION_NEVER;
1370 else if (!_cups_strcasecmp(value, "always"))
1371 cc->encryption = HTTP_ENCRYPTION_ALWAYS;
1372 else if (!_cups_strcasecmp(value, "required"))
1373 cc->encryption = HTTP_ENCRYPTION_REQUIRED;
1374 else
1375 cc->encryption = HTTP_ENCRYPTION_IF_REQUESTED;
1376 }
1377
1378
1379 /*
1380 * 'cups_set_gss_service_name()' - Set the GSSServiceName value.
1381 */
1382
1383 #ifdef HAVE_GSSAPI
1384 static void
1385 cups_set_gss_service_name(
1386 _cups_client_conf_t *cc, /* I - client.conf values */
1387 const char *value) /* I - Value */
1388 {
1389 strlcpy(cc->gss_service_name, value, sizeof(cc->gss_service_name));
1390 }
1391 #endif /* HAVE_GSSAPI */
1392
1393
1394 /*
1395 * 'cups_set_server_name()' - Set the ServerName value.
1396 */
1397
1398 static void
1399 cups_set_server_name(
1400 _cups_client_conf_t *cc, /* I - client.conf values */
1401 const char *value) /* I - Value */
1402 {
1403 strlcpy(cc->server_name, value, sizeof(cc->server_name));
1404 }
1405
1406
1407 /*
1408 * 'cups_set_ssl_options()' - Set the SSLOptions value.
1409 */
1410
1411 #ifdef HAVE_SSL
1412 static void
1413 cups_set_ssl_options(
1414 _cups_client_conf_t *cc, /* I - client.conf values */
1415 const char *value) /* I - Value */
1416 {
1417 /*
1418 * SSLOptions [AllowRC4] [AllowSSL3] [AllowDH] [DenyTLS1.0] [None]
1419 */
1420
1421 int options = _HTTP_TLS_NONE, /* SSL/TLS options */
1422 min_version = _HTTP_TLS_1_0, /* Minimum SSL/TLS version */
1423 max_version = _HTTP_TLS_MAX; /* Maximum SSL/TLS version */
1424 char temp[256], /* Copy of value */
1425 *start, /* Start of option */
1426 *end; /* End of option */
1427
1428
1429 strlcpy(temp, value, sizeof(temp));
1430
1431 for (start = temp; *start; start = end)
1432 {
1433 /*
1434 * Find end of keyword...
1435 */
1436
1437 end = start;
1438 while (*end && !_cups_isspace(*end))
1439 end ++;
1440
1441 if (*end)
1442 *end++ = '\0';
1443
1444 /*
1445 * Compare...
1446 */
1447
1448 if (!_cups_strcasecmp(start, "AllowRC4"))
1449 options |= _HTTP_TLS_ALLOW_RC4;
1450 else if (!_cups_strcasecmp(start, "AllowSSL3"))
1451 min_version = _HTTP_TLS_SSL3;
1452 else if (!_cups_strcasecmp(start, "AllowDH"))
1453 options |= _HTTP_TLS_ALLOW_DH;
1454 else if (!_cups_strcasecmp(start, "DenyCBC"))
1455 options |= _HTTP_TLS_DENY_CBC;
1456 else if (!_cups_strcasecmp(start, "DenyTLS1.0"))
1457 min_version = _HTTP_TLS_1_1;
1458 else if (!_cups_strcasecmp(start, "MaxTLS1.0"))
1459 max_version = _HTTP_TLS_1_0;
1460 else if (!_cups_strcasecmp(start, "MaxTLS1.1"))
1461 max_version = _HTTP_TLS_1_1;
1462 else if (!_cups_strcasecmp(start, "MaxTLS1.2"))
1463 max_version = _HTTP_TLS_1_2;
1464 else if (!_cups_strcasecmp(start, "MaxTLS1.3"))
1465 max_version = _HTTP_TLS_1_3;
1466 else if (!_cups_strcasecmp(start, "MinTLS1.0"))
1467 min_version = _HTTP_TLS_1_0;
1468 else if (!_cups_strcasecmp(start, "MinTLS1.1"))
1469 min_version = _HTTP_TLS_1_1;
1470 else if (!_cups_strcasecmp(start, "MinTLS1.2"))
1471 min_version = _HTTP_TLS_1_2;
1472 else if (!_cups_strcasecmp(start, "MinTLS1.3"))
1473 min_version = _HTTP_TLS_1_3;
1474 else if (!_cups_strcasecmp(start, "None"))
1475 options = _HTTP_TLS_NONE;
1476 }
1477
1478 cc->ssl_options = options;
1479 cc->ssl_max_version = max_version;
1480 cc->ssl_min_version = min_version;
1481
1482 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));
1483 }
1484 #endif /* HAVE_SSL */
1485
1486
1487 /*
1488 * 'cups_set_user()' - Set the User value.
1489 */
1490
1491 static void
1492 cups_set_user(
1493 _cups_client_conf_t *cc, /* I - client.conf values */
1494 const char *value) /* I - Value */
1495 {
1496 strlcpy(cc->user, value, sizeof(cc->user));
1497 }