1 /*
2  * Global variable access routines for CUPS.
3  *
4  * Copyright © 2007-2019 by Apple Inc.
5  * Copyright © 1997-2007 by Easy Software Products, all rights reserved.
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 #ifndef _WIN32
17 #  include <pwd.h>
18 #endif /* !_WIN32 */
19 
20 
21 /*
22  * Local globals...
23  */
24 
25 #ifdef DEBUG
26 static int		cups_global_index = 0;
27 					/* Next thread number */
28 #endif /* DEBUG */
29 static _cups_threadkey_t cups_globals_key = _CUPS_THREADKEY_INITIALIZER;
30 					/* Thread local storage key */
31 #ifdef HAVE_PTHREAD_H
32 static pthread_once_t	cups_globals_key_once = PTHREAD_ONCE_INIT;
33 					/* One-time initialization object */
34 #endif /* HAVE_PTHREAD_H */
35 #if defined(HAVE_PTHREAD_H) || defined(_WIN32)
36 static _cups_mutex_t	cups_global_mutex = _CUPS_MUTEX_INITIALIZER;
37 					/* Global critical section */
38 #endif /* HAVE_PTHREAD_H || _WIN32 */
39 
40 
41 /*
42  * Local functions...
43  */
44 
45 #ifdef _WIN32
46 static void		cups_fix_path(char *path);
47 #endif /* _WIN32 */
48 static _cups_globals_t	*cups_globals_alloc(void);
49 #if defined(HAVE_PTHREAD_H) || defined(_WIN32)
50 static void		cups_globals_free(_cups_globals_t *g);
51 #endif /* HAVE_PTHREAD_H || _WIN32 */
52 #ifdef HAVE_PTHREAD_H
53 static void		cups_globals_init(void);
54 #endif /* HAVE_PTHREAD_H */
55 
56 
57 /*
58  * '_cupsGlobalLock()' - Lock the global mutex.
59  */
60 
61 void
_cupsGlobalLock(void)62 _cupsGlobalLock(void)
63 {
64 #ifdef HAVE_PTHREAD_H
65   pthread_mutex_lock(&cups_global_mutex);
66 #elif defined(_WIN32)
67   EnterCriticalSection(&cups_global_mutex.m_criticalSection);
68 #endif /* HAVE_PTHREAD_H */
69 }
70 
71 
72 /*
73  * '_cupsGlobals()' - Return a pointer to thread local storage
74  */
75 
76 _cups_globals_t *			/* O - Pointer to global data */
_cupsGlobals(void)77 _cupsGlobals(void)
78 {
79   _cups_globals_t *cg;			/* Pointer to global data */
80 
81 
82 #ifdef HAVE_PTHREAD_H
83  /*
84   * Initialize the global data exactly once...
85   */
86 
87   pthread_once(&cups_globals_key_once, cups_globals_init);
88 #endif /* HAVE_PTHREAD_H */
89 
90  /*
91   * See if we have allocated the data yet...
92   */
93 
94   if ((cg = (_cups_globals_t *)_cupsThreadGetData(cups_globals_key)) == NULL)
95   {
96    /*
97     * No, allocate memory as set the pointer for the key...
98     */
99 
100     if ((cg = cups_globals_alloc()) != NULL)
101       _cupsThreadSetData(cups_globals_key, cg);
102   }
103 
104  /*
105   * Return the pointer to the data...
106   */
107 
108   return (cg);
109 }
110 
111 
112 /*
113  * '_cupsGlobalUnlock()' - Unlock the global mutex.
114  */
115 
116 void
_cupsGlobalUnlock(void)117 _cupsGlobalUnlock(void)
118 {
119 #ifdef HAVE_PTHREAD_H
120   pthread_mutex_unlock(&cups_global_mutex);
121 #elif defined(_WIN32)
122   LeaveCriticalSection(&cups_global_mutex.m_criticalSection);
123 #endif /* HAVE_PTHREAD_H */
124 }
125 
126 
127 #ifdef _WIN32
128 /*
129  * 'DllMain()' - Main entry for library.
130  */
131 
132 BOOL WINAPI				/* O - Success/failure */
DllMain(HINSTANCE hinst,DWORD reason,LPVOID reserved)133 DllMain(HINSTANCE hinst,		/* I - DLL module handle */
134         DWORD     reason,		/* I - Reason */
135         LPVOID    reserved)		/* I - Unused */
136 {
137   _cups_globals_t *cg;			/* Global data */
138 
139 
140   (void)hinst;
141   (void)reserved;
142 
143   switch (reason)
144   {
145     case DLL_PROCESS_ATTACH :		/* Called on library initialization */
146         InitializeCriticalSection(&cups_global_mutex.m_criticalSection);
147 
148         if ((cups_globals_key = TlsAlloc()) == TLS_OUT_OF_INDEXES)
149           return (FALSE);
150         break;
151 
152     case DLL_THREAD_DETACH :		/* Called when a thread terminates */
153         if ((cg = (_cups_globals_t *)TlsGetValue(cups_globals_key)) != NULL)
154           cups_globals_free(cg);
155         break;
156 
157     case DLL_PROCESS_DETACH :		/* Called when library is unloaded */
158         if ((cg = (_cups_globals_t *)TlsGetValue(cups_globals_key)) != NULL)
159           cups_globals_free(cg);
160 
161         TlsFree(cups_globals_key);
162         DeleteCriticalSection(&cups_global_mutex.m_criticalSection);
163         break;
164 
165     default:
166         break;
167   }
168 
169   return (TRUE);
170 }
171 #endif /* _WIN32 */
172 
173 
174 /*
175  * 'cups_globals_alloc()' - Allocate and initialize global data.
176  */
177 
178 static _cups_globals_t *		/* O - Pointer to global data */
cups_globals_alloc(void)179 cups_globals_alloc(void)
180 {
181   _cups_globals_t *cg = malloc(sizeof(_cups_globals_t));
182 					/* Pointer to global data */
183 #ifdef _WIN32
184   HKEY		key;			/* Registry key */
185   DWORD		size;			/* Size of string */
186   static char	installdir[1024] = "",	/* Install directory */
187 		confdir[1024] = "",	/* Server root directory */
188 		localedir[1024] = "";	/* Locale directory */
189 #endif /* _WIN32 */
190 
191 
192   if (!cg)
193     return (NULL);
194 
195  /*
196   * Clear the global storage and set the default encryption and password
197   * callback values...
198   */
199 
200   memset(cg, 0, sizeof(_cups_globals_t));
201   cg->encryption     = (http_encryption_t)-1;
202   cg->password_cb    = (cups_password_cb2_t)_cupsGetPassword;
203   cg->trust_first    = -1;
204   cg->any_root       = -1;
205   cg->expired_certs  = -1;
206   cg->validate_certs = -1;
207 
208 #ifdef DEBUG
209  /*
210   * Friendly thread ID for debugging...
211   */
212 
213   cg->thread_id = ++ cups_global_index;
214 #endif /* DEBUG */
215 
216  /*
217   * Then set directories as appropriate...
218   */
219 
220 #ifdef _WIN32
221   if (!installdir[0])
222   {
223    /*
224     * Open the registry...
225     */
226 
227     strlcpy(installdir, "C:/Program Files/cups.org", sizeof(installdir));
228 
229     if (!RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\cups.org", 0, KEY_READ, &key))
230     {
231      /*
232       * Grab the installation directory...
233       */
234 
235       char  *ptr;			/* Pointer into installdir */
236 
237       size = sizeof(installdir);
238       RegQueryValueExA(key, "installdir", NULL, NULL, installdir, &size);
239       RegCloseKey(key);
240 
241       for (ptr = installdir; *ptr;)
242       {
243         if (*ptr == '\\')
244         {
245           if (ptr[1])
246             *ptr++ = '/';
247           else
248             *ptr = '\0';		/* Strip trailing \ */
249         }
250         else if (*ptr == '/' && !ptr[1])
251           *ptr = '\0';			/* Strip trailing / */
252         else
253           ptr ++;
254       }
255     }
256 
257     snprintf(confdir, sizeof(confdir), "%s/conf", installdir);
258     snprintf(localedir, sizeof(localedir), "%s/locale", installdir);
259   }
260 
261   if ((cg->cups_datadir = getenv("CUPS_DATADIR")) == NULL)
262     cg->cups_datadir = installdir;
263 
264   if ((cg->cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
265     cg->cups_serverbin = installdir;
266 
267   if ((cg->cups_serverroot = getenv("CUPS_SERVERROOT")) == NULL)
268     cg->cups_serverroot = confdir;
269 
270   if ((cg->cups_statedir = getenv("CUPS_STATEDIR")) == NULL)
271     cg->cups_statedir = confdir;
272 
273   if ((cg->localedir = getenv("LOCALEDIR")) == NULL)
274     cg->localedir = localedir;
275 
276   cg->home = getenv("HOME");
277 
278 #else
279 #  ifdef HAVE_GETEUID
280   if ((geteuid() != getuid() && getuid()) || getegid() != getgid())
281 #  else
282   if (!getuid())
283 #  endif /* HAVE_GETEUID */
284   {
285    /*
286     * When running setuid/setgid, don't allow environment variables to override
287     * the directories...
288     */
289 
290     cg->cups_datadir    = CUPS_DATADIR;
291     cg->cups_serverbin  = CUPS_SERVERBIN;
292     cg->cups_serverroot = CUPS_SERVERROOT;
293     cg->cups_statedir   = CUPS_STATEDIR;
294     cg->localedir       = CUPS_LOCALEDIR;
295   }
296   else
297   {
298    /*
299     * Allow directories to be overridden by environment variables.
300     */
301 
302     if ((cg->cups_datadir = getenv("CUPS_DATADIR")) == NULL)
303       cg->cups_datadir = CUPS_DATADIR;
304 
305     if ((cg->cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
306       cg->cups_serverbin = CUPS_SERVERBIN;
307 
308     if ((cg->cups_serverroot = getenv("CUPS_SERVERROOT")) == NULL)
309       cg->cups_serverroot = CUPS_SERVERROOT;
310 
311     if ((cg->cups_statedir = getenv("CUPS_STATEDIR")) == NULL)
312       cg->cups_statedir = CUPS_STATEDIR;
313 
314     if ((cg->localedir = getenv("LOCALEDIR")) == NULL)
315       cg->localedir = CUPS_LOCALEDIR;
316 
317     cg->home = getenv("HOME");
318 
319 #  ifdef __APPLE__ /* Sandboxing now exposes the container as the home directory */
320     if (cg->home && strstr(cg->home, "/Library/Containers/"))
321       cg->home = NULL;
322 #  endif /* !__APPLE__ */
323   }
324 
325   if (!cg->home)
326   {
327     struct passwd	*pw;		/* User info */
328 
329     if ((pw = getpwuid(getuid())) != NULL)
330       cg->home = _cupsStrAlloc(pw->pw_dir);
331   }
332 #endif /* _WIN32 */
333 
334   return (cg);
335 }
336 
337 
338 /*
339  * 'cups_globals_free()' - Free global data.
340  */
341 
342 #if defined(HAVE_PTHREAD_H) || defined(_WIN32)
343 static void
cups_globals_free(_cups_globals_t * cg)344 cups_globals_free(_cups_globals_t *cg)	/* I - Pointer to global data */
345 {
346   _cups_buffer_t	*buffer,	/* Current read/write buffer */
347 			*next;		/* Next buffer */
348 
349 
350   if (cg->last_status_message)
351     _cupsStrFree(cg->last_status_message);
352 
353   for (buffer = cg->cups_buffers; buffer; buffer = next)
354   {
355     next = buffer->next;
356     free(buffer);
357   }
358 
359   cupsArrayDelete(cg->leg_size_lut);
360   cupsArrayDelete(cg->ppd_size_lut);
361   cupsArrayDelete(cg->pwg_size_lut);
362 
363   httpClose(cg->http);
364 
365 #ifdef HAVE_SSL
366   _httpFreeCredentials(cg->tls_credentials);
367 #endif /* HAVE_SSL */
368 
369   cupsFileClose(cg->stdio_files[0]);
370   cupsFileClose(cg->stdio_files[1]);
371   cupsFileClose(cg->stdio_files[2]);
372 
373   cupsFreeOptions(cg->cupsd_num_settings, cg->cupsd_settings);
374 
375   if (cg->raster_error.start)
376     free(cg->raster_error.start);
377 
378   free(cg);
379 }
380 #endif /* HAVE_PTHREAD_H || _WIN32 */
381 
382 
383 #ifdef HAVE_PTHREAD_H
384 /*
385  * 'cups_globals_init()' - Initialize environment variables.
386  */
387 
388 static void
cups_globals_init(void)389 cups_globals_init(void)
390 {
391  /*
392   * Register the global data for this thread...
393   */
394 
395   pthread_key_create(&cups_globals_key, (void (*)(void *))cups_globals_free);
396 }
397 #endif /* HAVE_PTHREAD_H */
398