Added stubs for AccessCheckByType, AddAuditAccessAce,
[wine] / dlls / ntdll / loadorder.c
1 /*
2  * Dlls load order support
3  *
4  * Copyright 1999 Bertho Stultiens
5  * Copyright 2003 Alexandre Julliard
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <stdarg.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <assert.h>
29
30 #include "windef.h"
31 #include "winbase.h"
32 #include "winerror.h"
33 #include "winreg.h"
34 #include "winternl.h"
35 #include "ntdll_misc.h"
36
37 #include "wine/debug.h"
38 #include "wine/unicode.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(module);
41
42 #define LOADORDER_ALLOC_CLUSTER 32      /* Allocate with 32 entries at a time */
43
44 typedef struct module_loadorder
45 {
46     const WCHAR        *modulename;
47     enum loadorder_type loadorder[LOADORDER_NTYPES];
48 } module_loadorder_t;
49
50 struct loadorder_list
51 {
52     int                 count;
53     int                 alloc;
54     module_loadorder_t *order;
55 };
56
57 /* dll to load as builtins if not explicitly specified otherwise */
58 /* the list must remain sorted by dll name */
59 static const WCHAR default_builtins[][16] =
60 {
61     { 'g','d','i','3','2',0 },
62     { 'i','c','m','p',0 },
63     { 'k','e','r','n','e','l','3','2',0 },
64     { 'm','m','s','y','s','t','e','m',0 },
65     { 'n','t','d','l','l',0 },
66     { 'o','d','b','c','3','2',0 },
67     { 's','o','u','n','d',0 },
68     { 't','t','y','d','r','v',0 },
69     { 'u','s','e','r','3','2',0 },
70     { 'w','3','2','s','k','r','n','l',0 },
71     { 'w','3','2','s','y','s',0 },
72     { 'w','i','n','3','2','s','1','6',0 },
73     { 'w','i','n','a','s','p','i',0 },
74     { 'w','i','n','e','d','o','s',0 },
75     { 'w','i','n','e','p','s','1','6','.','d','r','v',0 },
76     { 'w','i','n','e','p','s',0 },
77     { 'w','i','n','m','m',0 },
78     { 'w','i','n','s','o','c','k',0 },
79     { 'w','n','a','s','p','i','3','2',0 },
80     { 'w','o','w','3','2',0 },
81     { 'w','s','2','_','3','2',0 },
82     { 'w','s','o','c','k','3','2',0 },
83     { 'x','1','1','d','r','v',0 }
84 };
85
86 /* default if nothing else specified */
87 static const enum loadorder_type default_loadorder[LOADORDER_NTYPES] =
88 {
89     LOADORDER_BI, LOADORDER_DLL, 0
90 };
91
92 /* default for modules with an explicit path */
93 static const enum loadorder_type default_path_loadorder[LOADORDER_NTYPES] =
94 {
95     LOADORDER_DLL, LOADORDER_BI, 0
96 };
97
98 static const WCHAR separatorsW[] = {',',' ','\t',0};
99
100 static int init_done;
101 static struct loadorder_list env_list;
102
103
104 /***************************************************************************
105  *      cmp_sort_func   (internal, static)
106  *
107  * Sorting and comparing function used in sort and search of loadorder
108  * entries.
109  */
110 static int cmp_sort_func(const void *s1, const void *s2)
111 {
112     return strcmpiW(((module_loadorder_t *)s1)->modulename, ((module_loadorder_t *)s2)->modulename);
113 }
114
115
116 /***************************************************************************
117  *      strcmp_func
118  */
119 static int strcmp_func(const void *s1, const void *s2)
120 {
121     return strcmpiW( (WCHAR *)s1, (WCHAR *)s2 );
122 }
123
124
125 /***************************************************************************
126  *      get_basename
127  *
128  * Return the base name of a file name (i.e. remove the path components).
129  */
130 static const WCHAR *get_basename( const WCHAR *name )
131 {
132     const WCHAR *ptr;
133
134     if (name[0] && name[1] == ':') name += 2;  /* strip drive specification */
135     if ((ptr = strrchrW( name, '\\' ))) name = ptr + 1;
136     if ((ptr = strrchrW( name, '/' ))) name = ptr + 1;
137     return name;
138 }
139
140 /***************************************************************************
141  *      remove_dll_ext
142  *
143  * Remove extension if it is ".dll".
144  */
145 static inline void remove_dll_ext( WCHAR *ext )
146 {
147     if (ext[0] == '.' &&
148         toupperW(ext[1]) == 'D' &&
149         toupperW(ext[2]) == 'L' &&
150         toupperW(ext[3]) == 'L' &&
151         !ext[4]) ext[0] = 0;
152 }
153
154
155 /***************************************************************************
156  *      debugstr_loadorder
157  *
158  * Return a loadorder in printable form.
159  */
160 static const char *debugstr_loadorder( enum loadorder_type lo[] )
161 {
162     int i;
163     char buffer[LOADORDER_NTYPES*3+1];
164
165     buffer[0] = 0;
166     for(i = 0; i < LOADORDER_NTYPES; i++)
167     {
168         if (lo[i] == LOADORDER_INVALID) break;
169         switch(lo[i])
170         {
171         case LOADORDER_DLL: strcat( buffer, "n," ); break;
172         case LOADORDER_BI:  strcat( buffer, "b," ); break;
173         default:            strcat( buffer, "?," ); break;
174         }
175     }
176     if (buffer[0]) buffer[strlen(buffer)-1] = 0;
177     return debugstr_a(buffer);
178 }
179
180
181 /***************************************************************************
182  *      append_load_order
183  *
184  * Append a load order to the list if necessary.
185  */
186 static void append_load_order(enum loadorder_type lo[], enum loadorder_type append)
187 {
188     int i;
189
190     for (i = 0; i < LOADORDER_NTYPES; i++)
191     {
192         if (lo[i] == LOADORDER_INVALID)  /* append it here */
193         {
194             lo[i++] = append;
195             lo[i] = LOADORDER_INVALID;
196             return;
197         }
198         if (lo[i] == append) return;  /* already in the list */
199     }
200     assert(0);  /* cannot get here */
201 }
202
203
204 /***************************************************************************
205  *      parse_load_order
206  *
207  * Parses the loadorder options from the configuration and puts it into
208  * a structure.
209  */
210 static void parse_load_order( const WCHAR *order, enum loadorder_type lo[] )
211 {
212     lo[0] = LOADORDER_INVALID;
213     while (*order)
214     {
215         order += strspnW( order, separatorsW );
216         switch(*order)
217         {
218         case 'N':       /* Native */
219         case 'n':
220             append_load_order( lo, LOADORDER_DLL );
221             break;
222         case 'B':       /* Builtin */
223         case 'b':
224             append_load_order( lo, LOADORDER_BI );
225             break;
226         }
227         order += strcspnW( order, separatorsW );
228     }
229 }
230
231
232 /***************************************************************************
233  *      add_load_order
234  *
235  * Adds an entry in the list of environment overrides.
236  */
237 static void add_load_order( const module_loadorder_t *plo )
238 {
239     int i;
240
241     for(i = 0; i < env_list.count; i++)
242     {
243         if(!cmp_sort_func(plo, &env_list.order[i] ))
244         {
245             /* replace existing option */
246             memcpy( env_list.order[i].loadorder, plo->loadorder, sizeof(plo->loadorder));
247             return;
248         }
249     }
250
251     if (i >= env_list.alloc)
252     {
253         /* No space in current array, make it larger */
254         env_list.alloc += LOADORDER_ALLOC_CLUSTER;
255         if (env_list.order)
256             env_list.order = RtlReAllocateHeap(GetProcessHeap(), 0, env_list.order,
257                                                env_list.alloc * sizeof(module_loadorder_t));
258         else
259             env_list.order = RtlAllocateHeap(GetProcessHeap(), 0,
260                                              env_list.alloc * sizeof(module_loadorder_t));
261         if(!env_list.order)
262         {
263             MESSAGE("Virtual memory exhausted\n");
264             exit(1);
265         }
266     }
267     memcpy(env_list.order[i].loadorder, plo->loadorder, sizeof(plo->loadorder));
268     env_list.order[i].modulename = plo->modulename;
269     env_list.count++;
270 }
271
272
273 /***************************************************************************
274  *      add_load_order_set
275  *
276  * Adds a set of entries in the list of command-line overrides from the key parameter.
277  */
278 static void add_load_order_set( WCHAR *entry )
279 {
280     module_loadorder_t ldo;
281     WCHAR *end = strchrW( entry, '=' );
282
283     if (!end) return;
284     *end++ = 0;
285     parse_load_order( end, ldo.loadorder );
286
287     while (*entry)
288     {
289         entry += strspnW( entry, separatorsW );
290         end = entry + strcspnW( entry, separatorsW );
291         if (*end) *end++ = 0;
292         if (*entry)
293         {
294             WCHAR *ext = strrchrW(entry, '.');
295             if (ext) remove_dll_ext( ext );
296             ldo.modulename = entry;
297             add_load_order( &ldo );
298             entry = end;
299         }
300     }
301 }
302
303
304 /***************************************************************************
305  *      init_load_order
306  */
307 static void init_load_order(void)
308 {
309     const char *order = getenv( "WINEDLLOVERRIDES" );
310     UNICODE_STRING strW;
311     WCHAR *entry, *next;
312
313     init_done = 1;
314     if (!order) return;
315
316     if (!strcmp( order, "help" ))
317     {
318         MESSAGE( "Syntax:\n"
319                  "  WINEDLLOVERRIDES=\"entry;entry;entry...\"\n"
320                  "    where each entry is of the form:\n"
321                  "        module[,module...]={native|builtin}[,{b|n}]\n"
322                  "\n"
323                  "    Only the first letter of the override (native or builtin)\n"
324                  "    is significant.\n\n"
325                  "Example:\n"
326                  "  WINEDLLOVERRIDES=\"comdlg32,commdlg=n,b;shell,shell32=b\"\n" );
327         exit(0);
328     }
329
330     RtlCreateUnicodeStringFromAsciiz( &strW, order );
331     entry = strW.Buffer;
332     while (*entry)
333     {
334         while (*entry && *entry == ';') entry++;
335         if (!*entry) break;
336         next = strchrW( entry, ';' );
337         if (next) *next++ = 0;
338         else next = entry + strlenW(entry);
339         add_load_order_set( entry );
340         entry = next;
341     }
342
343     /* sort the array for quick lookup */
344     if (env_list.count)
345         qsort(env_list.order, env_list.count, sizeof(env_list.order[0]), cmp_sort_func);
346
347     /* Note: we don't free the Unicode string because the
348      * stored module names point inside it */
349 }
350
351
352 /***************************************************************************
353  *      get_env_load_order
354  *
355  * Get the load order for a given module from the WINEDLLOVERRIDES environment variable.
356  */
357 static inline BOOL get_env_load_order( const WCHAR *module, enum loadorder_type lo[] )
358 {
359     module_loadorder_t tmp, *res = NULL;
360
361     tmp.modulename = module;
362     /* some bsearch implementations (Solaris) are buggy when the number of items is 0 */
363     if (env_list.count &&
364         (res = bsearch(&tmp, env_list.order, env_list.count, sizeof(env_list.order[0]), cmp_sort_func)))
365         memcpy( lo, res->loadorder, sizeof(res->loadorder) );
366     return (res != NULL);
367 }
368
369
370 /***************************************************************************
371  *      get_default_load_order
372  *
373  * Get the load order for a given module from the default list.
374  */
375 static inline BOOL get_default_load_order( const WCHAR *module, enum loadorder_type lo[] )
376 {
377     const int count = sizeof(default_builtins) / sizeof(default_builtins[0]);
378     if (!bsearch( module, default_builtins, count, sizeof(default_builtins[0]), strcmp_func ))
379         return FALSE;
380     lo[0] = LOADORDER_BI;
381     lo[1] = LOADORDER_INVALID;
382     return TRUE;
383 }
384
385
386 /***************************************************************************
387  *      open_app_key
388  *
389  * Open the registry key to the app-specific DllOverrides list.
390  */
391 static HKEY open_app_key( const WCHAR *app_name, const WCHAR *module )
392 {
393     OBJECT_ATTRIBUTES attr;
394     UNICODE_STRING nameW;
395     HKEY hkey;
396     WCHAR *str;
397     static const WCHAR AppDefaultsW[] = {'M','a','c','h','i','n','e','\\',
398                                          'S','o','f','t','w','a','r','e','\\',
399                                          'W','i','n','e','\\',
400                                          'W','i','n','e','\\',
401                                          'C','o','n','f','i','g','\\',
402                                          'A','p','p','D','e','f','a','u','l','t','s','\\',0};
403     static const WCHAR DllOverridesW[] = {'\\','D','l','l','O','v','e','r','r','i','d','e','s',0};
404
405     str = RtlAllocateHeap( GetProcessHeap(), 0,
406                            sizeof(AppDefaultsW) + sizeof(DllOverridesW) +
407                            strlenW(app_name) * sizeof(WCHAR) );
408     if (!str) return 0;
409     strcpyW( str, AppDefaultsW );
410     strcatW( str, app_name );
411     strcatW( str, DllOverridesW );
412
413     TRACE( "searching %s in %s\n", debugstr_w(module), debugstr_w(str) );
414
415     attr.Length = sizeof(attr);
416     attr.RootDirectory = 0;
417     attr.ObjectName = &nameW;
418     attr.Attributes = 0;
419     attr.SecurityDescriptor = NULL;
420     attr.SecurityQualityOfService = NULL;
421     RtlInitUnicodeString( &nameW, str );
422
423     if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) hkey = 0;
424     RtlFreeHeap( GetProcessHeap(), 0, str );
425     return hkey;
426 }
427
428
429 /***************************************************************************
430  *      get_registry_value
431  *
432  * Load the registry loadorder value for a given module.
433  */
434 static BOOL get_registry_value( HKEY hkey, const WCHAR *module, enum loadorder_type lo[] )
435 {
436     UNICODE_STRING valueW;
437     char buffer[80];
438     DWORD count;
439     BOOL ret;
440
441     RtlInitUnicodeString( &valueW, module );
442
443     if ((ret = !NtQueryValueKey( hkey, &valueW, KeyValuePartialInformation,
444                                  buffer, sizeof(buffer), &count )))
445     {
446         int i, n = 0;
447         WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)buffer)->Data;
448
449         while (*str)
450         {
451             enum loadorder_type type = LOADORDER_INVALID;
452
453             while (*str == ',' || isspaceW(*str)) str++;
454             if (!*str) break;
455
456             switch(tolowerW(*str))
457             {
458             case 'n': type = LOADORDER_DLL; break;
459             case 'b': type = LOADORDER_BI; break;
460             case 's': break;  /* no longer supported, ignore */
461             case 0:   break;  /* end of string */
462             default:
463                 ERR("Invalid load order module-type %s, ignored\n", debugstr_w(str));
464                 break;
465             }
466             if (type != LOADORDER_INVALID)
467             {
468                 for (i = 0; i < n; i++) if (lo[i] == type) break;  /* already specified */
469                 if (i == n) lo[n++] = type;
470             }
471             while (*str && *str != ',' && !isspaceW(*str)) str++;
472         }
473         lo[n] = LOADORDER_INVALID;
474     }
475     return ret;
476 }
477
478
479 /***************************************************************************
480  *      MODULE_GetSystemDirectory
481  *
482  * Retrieve the system directory. The string must be freed by the caller.
483  */
484 BOOL MODULE_GetSystemDirectory( UNICODE_STRING *sysdir )
485 {
486     static const WCHAR winsysdirW[] = {'w','i','n','s','y','s','d','i','r',0};
487     UNICODE_STRING name;
488
489     RtlInitUnicodeString( &name, winsysdirW );
490     sysdir->MaximumLength = 0;
491     if (RtlQueryEnvironmentVariable_U( NULL, &name, sysdir ) != STATUS_BUFFER_TOO_SMALL)
492         return FALSE;
493     sysdir->MaximumLength = sysdir->Length + sizeof(WCHAR);
494     if (!(sysdir->Buffer = RtlAllocateHeap( GetProcessHeap(), 0, sysdir->MaximumLength )))
495         return FALSE;
496     if (RtlQueryEnvironmentVariable_U( NULL, &name, sysdir ) == STATUS_SUCCESS)
497         return TRUE;
498     RtlFreeUnicodeString( sysdir );
499     return FALSE;
500 }
501
502
503 /***************************************************************************
504  *      MODULE_GetLoadOrderW    (internal)
505  *
506  * Locate the loadorder of a module.
507  * Any path is stripped from the path-argument and so are the extension
508  * '.dll' and '.exe'. A lookup in the table can yield an override for
509  * the specific dll. Otherwise the default load order is returned.
510  */
511 void MODULE_GetLoadOrderW( enum loadorder_type loadorder[], const WCHAR *app_name,
512                           const WCHAR *path, BOOL win32 )
513 {
514     static const WCHAR DllOverridesW[] = {'M','a','c','h','i','n','e','\\',
515                                           'S','o','f','t','w','a','r','e','\\',
516                                           'W','i','n','e','\\',
517                                           'W','i','n','e','\\',
518                                           'C','o','n','f','i','g','\\',
519                                           'D','l','l','O','v','e','r','r','i','d','e','s',0};
520
521     static HKEY std_key = (HKEY)-1;  /* key to standard section, cached */
522     HKEY app_key = 0;
523     WCHAR *module, *basename;
524     int len;
525
526     if (!init_done) init_load_order();
527
528     TRACE("looking for %s\n", debugstr_w(path));
529
530     loadorder[0] = LOADORDER_INVALID;  /* in case something bad happens below */
531
532     /* Strip path information if the module resides in the system directory
533      * (path is already stripped by caller for 16-bit modules)
534      */
535     if (win32)
536     {
537         UNICODE_STRING path_str, sysdir;
538
539         if (!MODULE_GetSystemDirectory( &sysdir )) return;
540         RtlInitUnicodeString( &path_str, path );
541         if (RtlPrefixUnicodeString( &sysdir, &path_str, TRUE ))
542         {
543             const WCHAR *p = path + sysdir.Length / sizeof(WCHAR);
544             while (*p == '\\' || *p == '/') p++;
545             if (!strchrW( p, '\\' ) && !strchrW( p, '/' )) path = p;
546         }
547         RtlFreeUnicodeString( &sysdir );
548     }
549
550     if (!(len = strlenW(path))) return;
551     if (!(module = RtlAllocateHeap( GetProcessHeap(), 0, (len + 2) * sizeof(WCHAR) ))) return;
552     strcpyW( module+1, path );  /* reserve module[0] for the wildcard char */
553
554     if (len >= 4) remove_dll_ext( module + 1 + len - 4 );
555
556     /* check environment variable first */
557     if (get_env_load_order( module+1, loadorder ))
558     {
559         TRACE( "got environment %s for %s\n",
560                debugstr_loadorder(loadorder), debugstr_w(path) );
561         goto done;
562     }
563
564     /* then explicit module name in AppDefaults */
565     if (app_name)
566     {
567         app_key = open_app_key( app_name, module+1 );
568         if (app_key && get_registry_value( app_key, module+1, loadorder ))
569         {
570             TRACE( "got app defaults %s for %s\n",
571                    debugstr_loadorder(loadorder), debugstr_w(path) );
572             goto done;
573         }
574     }
575
576     /* then explicit module name in standard section */
577     if (std_key == (HKEY)-1)
578     {
579         OBJECT_ATTRIBUTES attr;
580         UNICODE_STRING nameW;
581
582         attr.Length = sizeof(attr);
583         attr.RootDirectory = 0;
584         attr.ObjectName = &nameW;
585         attr.Attributes = 0;
586         attr.SecurityDescriptor = NULL;
587         attr.SecurityQualityOfService = NULL;
588         RtlInitUnicodeString( &nameW, DllOverridesW );
589
590         if (NtOpenKey( &std_key, KEY_ALL_ACCESS, &attr )) std_key = 0;
591     }
592
593     if (std_key && get_registry_value( std_key, module+1, loadorder ))
594     {
595         TRACE( "got standard entry %s for %s\n",
596                debugstr_loadorder(loadorder), debugstr_w(path) );
597         goto done;
598     }
599
600     /* then module basename preceded by '*' in AppDefaults */
601     basename = (WCHAR *)get_basename( module+1 );
602     basename[-1] = '*';
603     if (app_key && get_registry_value( app_key, basename-1, loadorder ))
604     {
605         TRACE( "got app defaults basename %s for %s\n",
606                debugstr_loadorder(loadorder), debugstr_w(path) );
607         goto done;
608     }
609
610     /* then module name preceded by '*' in standard section */
611     if (std_key && get_registry_value( std_key, basename-1, loadorder ))
612     {
613         TRACE( "got standard base name %s for %s\n",
614                debugstr_loadorder(loadorder), debugstr_w(path) );
615         goto done;
616     }
617
618     /* then base name matching compiled-in defaults */
619     if (get_default_load_order( basename, loadorder ))
620     {
621         TRACE( "got compiled-in default %s for %s\n",
622                debugstr_loadorder(loadorder), debugstr_w(path) );
623         goto done;
624     }
625
626     if (basename == module+1)
627     {
628         static const WCHAR wildcardW[] = {'*',0};
629
630         /* then wildcard entry in AppDefaults (only if no explicit path) */
631         if (app_key && get_registry_value( app_key, wildcardW, loadorder ))
632         {
633             TRACE( "got app defaults wildcard %s for %s\n",
634                    debugstr_loadorder(loadorder), debugstr_w(path) );
635             goto done;
636         }
637
638         /* then wildcard entry in standard section (only if no explicit path) */
639         if (std_key && get_registry_value( std_key, wildcardW, loadorder ))
640         {
641             TRACE( "got standard wildcard %s for %s\n",
642                    debugstr_loadorder(loadorder), debugstr_w(path) );
643             goto done;
644         }
645
646         /* and last the hard-coded default */
647         memcpy( loadorder, default_loadorder, sizeof(default_loadorder) );
648         TRACE( "got hardcoded default %s for %s\n",
649                debugstr_loadorder(loadorder), debugstr_w(path) );
650     }
651     else  /* module contains an explicit path */
652     {
653         memcpy( loadorder, default_path_loadorder, sizeof(default_path_loadorder) );
654         TRACE( "got hardcoded path default %s for %s\n",
655                debugstr_loadorder(loadorder), debugstr_w(path) );
656     }
657
658  done:
659     if (app_key) NtClose( app_key );
660     RtlFreeHeap( GetProcessHeap(), 0, module );
661 }
662
663
664 /***************************************************************************
665  *      MODULE_GetLoadOrderA    (internal)
666  *
667  * Locate the loadorder of a module.
668  * Any path is stripped from the path-argument and so are the extension
669  * '.dll' and '.exe'. A lookup in the table can yield an override for
670  * the specific dll. Otherwise the default load order is returned.
671  *
672  * FIXME: should be removed, everything should be Unicode.
673  */
674 void MODULE_GetLoadOrderA( enum loadorder_type loadorder[], const WCHAR *app_name,
675                            const char *path, BOOL win32 )
676 {
677     UNICODE_STRING pathW;
678
679     RtlCreateUnicodeStringFromAsciiz( &pathW, path );
680     MODULE_GetLoadOrderW( loadorder, app_name, pathW.Buffer, win32 );
681     RtlFreeUnicodeString( &pathW );
682 }