]> granicus.if.org Git - postgresql/blob - src/port/exec.c
37d109ec5cc2b24c48343e84137484872e9ef59d
[postgresql] / src / port / exec.c
1 /*-------------------------------------------------------------------------
2  *
3  * exec.c
4  *              Functions for finding and validating executable files
5  *
6  *
7  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  *
11  * IDENTIFICATION
12  *        $PostgreSQL: pgsql/src/port/exec.c,v 1.59 2008/03/31 01:31:43 tgl Exp $
13  *
14  *-------------------------------------------------------------------------
15  */
16
17 #ifndef FRONTEND
18 #include "postgres.h"
19 #else
20 #include "postgres_fe.h"
21 #endif
22
23 #include <grp.h>
24 #include <pwd.h>
25 #include <signal.h>
26 #include <sys/stat.h>
27 #include <sys/wait.h>
28 #include <unistd.h>
29
30 #ifndef S_IRUSR                                 /* XXX [TRH] should be in a header */
31 #define S_IRUSR          S_IREAD
32 #define S_IWUSR          S_IWRITE
33 #define S_IXUSR          S_IEXEC
34 #define S_IRGRP          ((S_IRUSR)>>3)
35 #define S_IWGRP          ((S_IWUSR)>>3)
36 #define S_IXGRP          ((S_IXUSR)>>3)
37 #define S_IROTH          ((S_IRUSR)>>6)
38 #define S_IWOTH          ((S_IWUSR)>>6)
39 #define S_IXOTH          ((S_IXUSR)>>6)
40 #endif
41
42 #ifndef FRONTEND
43 /* We use only 3-parameter elog calls in this file, for simplicity */
44 /* NOTE: caller must provide gettext call around str! */
45 #define log_error(str, param)   elog(LOG, str, param)
46 #else
47 #define log_error(str, param)   (fprintf(stderr, str, param), fputc('\n', stderr))
48 #endif
49
50 #ifdef WIN32_ONLY_COMPILER
51 #define getcwd(cwd,len)  GetCurrentDirectory(len, cwd)
52 #endif
53
54 static int      validate_exec(const char *path);
55 static int      resolve_symlinks(char *path);
56 static char *pipe_read_line(char *cmd, char *line, int maxsize);
57
58 #ifdef WIN32
59 static BOOL GetUserSid(PSID * ppSidUser, HANDLE hToken);
60 #endif
61
62 /*
63  * validate_exec -- validate "path" as an executable file
64  *
65  * returns 0 if the file is found and no error is encountered.
66  *                -1 if the regular file "path" does not exist or cannot be executed.
67  *                -2 if the file is otherwise valid but cannot be read.
68  */
69 static int
70 validate_exec(const char *path)
71 {
72         struct stat buf;
73
74 #ifndef WIN32
75         uid_t           euid;
76         struct group *gp;
77         struct passwd *pwp;
78         int                     i;
79         int                     in_grp = 0;
80 #else
81         char            path_exe[MAXPGPATH + sizeof(".exe") - 1];
82 #endif
83         int                     is_r;
84         int                     is_x;
85
86 #ifdef WIN32
87         /* Win32 requires a .exe suffix for stat() */
88         if (strlen(path) >= strlen(".exe") &&
89                 pg_strcasecmp(path + strlen(path) - strlen(".exe"), ".exe") != 0)
90         {
91                 strcpy(path_exe, path);
92                 strcat(path_exe, ".exe");
93                 path = path_exe;
94         }
95 #endif
96
97         /*
98          * Ensure that the file exists and is a regular file.
99          *
100          * XXX if you have a broken system where stat() looks at the symlink
101          * instead of the underlying file, you lose.
102          */
103         if (stat(path, &buf) < 0)
104                 return -1;
105
106         if (!S_ISREG(buf.st_mode))
107                 return -1;
108
109         /*
110          * Ensure that we are using an authorized executable.
111          */
112
113         /*
114          * Ensure that the file is both executable and readable (required for
115          * dynamic loading).
116          */
117 #ifdef WIN32
118         is_r = buf.st_mode & S_IRUSR;
119         is_x = buf.st_mode & S_IXUSR;
120         return is_x ? (is_r ? 0 : -2) : -1;
121 #else
122         euid = geteuid();
123
124         /* If owned by us, just check owner bits */
125         if (euid == buf.st_uid)
126         {
127                 is_r = buf.st_mode & S_IRUSR;
128                 is_x = buf.st_mode & S_IXUSR;
129                 return is_x ? (is_r ? 0 : -2) : -1;
130         }
131
132         /* OK, check group bits */
133
134         pwp = getpwuid(euid);           /* not thread-safe */
135         if (pwp)
136         {
137                 if (pwp->pw_gid == buf.st_gid)  /* my primary group? */
138                         ++in_grp;
139                 else if (pwp->pw_name &&
140                                  (gp = getgrgid(buf.st_gid)) != NULL && /* not thread-safe */
141                                  gp->gr_mem != NULL)
142                 {                                               /* try list of member groups */
143                         for (i = 0; gp->gr_mem[i]; ++i)
144                         {
145                                 if (!strcmp(gp->gr_mem[i], pwp->pw_name))
146                                 {
147                                         ++in_grp;
148                                         break;
149                                 }
150                         }
151                 }
152                 if (in_grp)
153                 {
154                         is_r = buf.st_mode & S_IRGRP;
155                         is_x = buf.st_mode & S_IXGRP;
156                         return is_x ? (is_r ? 0 : -2) : -1;
157                 }
158         }
159
160         /* Check "other" bits */
161         is_r = buf.st_mode & S_IROTH;
162         is_x = buf.st_mode & S_IXOTH;
163         return is_x ? (is_r ? 0 : -2) : -1;
164 #endif
165 }
166
167
168 /*
169  * find_my_exec -- find an absolute path to a valid executable
170  *
171  *      argv0 is the name passed on the command line
172  *      retpath is the output area (must be of size MAXPGPATH)
173  *      Returns 0 if OK, -1 if error.
174  *
175  * The reason we have to work so hard to find an absolute path is that
176  * on some platforms we can't do dynamic loading unless we know the
177  * executable's location.  Also, we need a full path not a relative
178  * path because we will later change working directory.  Finally, we want
179  * a true path not a symlink location, so that we can locate other files
180  * that are part of our installation relative to the executable.
181  *
182  * This function is not thread-safe because it calls validate_exec(),
183  * which calls getgrgid().      This function should be used only in
184  * non-threaded binaries, not in library routines.
185  */
186 int
187 find_my_exec(const char *argv0, char *retpath)
188 {
189         char            cwd[MAXPGPATH],
190                                 test_path[MAXPGPATH];
191         char       *path;
192
193         if (!getcwd(cwd, MAXPGPATH))
194         {
195                 log_error(_("could not identify current directory: %s"),
196                                   strerror(errno));
197                 return -1;
198         }
199
200         /*
201          * If argv0 contains a separator, then PATH wasn't used.
202          */
203         if (first_dir_separator(argv0) != NULL)
204         {
205                 if (is_absolute_path(argv0))
206                         StrNCpy(retpath, argv0, MAXPGPATH);
207                 else
208                         join_path_components(retpath, cwd, argv0);
209                 canonicalize_path(retpath);
210
211                 if (validate_exec(retpath) == 0)
212                         return resolve_symlinks(retpath);
213
214                 log_error(_("invalid binary \"%s\""), retpath);
215                 return -1;
216         }
217
218 #ifdef WIN32
219         /* Win32 checks the current directory first for names without slashes */
220         join_path_components(retpath, cwd, argv0);
221         if (validate_exec(retpath) == 0)
222                 return resolve_symlinks(retpath);
223 #endif
224
225         /*
226          * Since no explicit path was supplied, the user must have been relying on
227          * PATH.  We'll search the same PATH.
228          */
229         if ((path = getenv("PATH")) && *path)
230         {
231                 char       *startp = NULL,
232                                    *endp = NULL;
233
234                 do
235                 {
236                         if (!startp)
237                                 startp = path;
238                         else
239                                 startp = endp + 1;
240
241                         endp = first_path_separator(startp);
242                         if (!endp)
243                                 endp = startp + strlen(startp); /* point to end */
244
245                         StrNCpy(test_path, startp, Min(endp - startp + 1, MAXPGPATH));
246
247                         if (is_absolute_path(test_path))
248                                 join_path_components(retpath, test_path, argv0);
249                         else
250                         {
251                                 join_path_components(retpath, cwd, test_path);
252                                 join_path_components(retpath, retpath, argv0);
253                         }
254                         canonicalize_path(retpath);
255
256                         switch (validate_exec(retpath))
257                         {
258                                 case 0: /* found ok */
259                                         return resolve_symlinks(retpath);
260                                 case -1:                /* wasn't even a candidate, keep looking */
261                                         break;
262                                 case -2:                /* found but disqualified */
263                                         log_error(_("could not read binary \"%s\""),
264                                                           retpath);
265                                         break;
266                         }
267                 } while (*endp);
268         }
269
270         log_error(_("could not find a \"%s\" to execute"), argv0);
271         return -1;
272 }
273
274
275 /*
276  * resolve_symlinks - resolve symlinks to the underlying file
277  *
278  * Replace "path" by the absolute path to the referenced file.
279  *
280  * Returns 0 if OK, -1 if error.
281  *
282  * Note: we are not particularly tense about producing nice error messages
283  * because we are not really expecting error here; we just determined that
284  * the symlink does point to a valid executable.
285  */
286 static int
287 resolve_symlinks(char *path)
288 {
289 #ifdef HAVE_READLINK
290         struct stat buf;
291         char            orig_wd[MAXPGPATH],
292                                 link_buf[MAXPGPATH];
293         char       *fname;
294
295         /*
296          * To resolve a symlink properly, we have to chdir into its directory and
297          * then chdir to where the symlink points; otherwise we may fail to
298          * resolve relative links correctly (consider cases involving mount
299          * points, for example).  After following the final symlink, we use
300          * getcwd() to figure out where the heck we're at.
301          *
302          * One might think we could skip all this if path doesn't point to a
303          * symlink to start with, but that's wrong.  We also want to get rid of
304          * any directory symlinks that are present in the given path. We expect
305          * getcwd() to give us an accurate, symlink-free path.
306          */
307         if (!getcwd(orig_wd, MAXPGPATH))
308         {
309                 log_error(_("could not identify current directory: %s"),
310                                   strerror(errno));
311                 return -1;
312         }
313
314         for (;;)
315         {
316                 char       *lsep;
317                 int                     rllen;
318
319                 lsep = last_dir_separator(path);
320                 if (lsep)
321                 {
322                         *lsep = '\0';
323                         if (chdir(path) == -1)
324                         {
325                                 log_error(_("could not change directory to \"%s\""), path);
326                                 return -1;
327                         }
328                         fname = lsep + 1;
329                 }
330                 else
331                         fname = path;
332
333                 if (lstat(fname, &buf) < 0 ||
334                         !S_ISLNK(buf.st_mode))
335                         break;
336
337                 rllen = readlink(fname, link_buf, sizeof(link_buf));
338                 if (rllen < 0 || rllen >= sizeof(link_buf))
339                 {
340                         log_error(_("could not read symbolic link \"%s\""), fname);
341                         return -1;
342                 }
343                 link_buf[rllen] = '\0';
344                 strcpy(path, link_buf);
345         }
346
347         /* must copy final component out of 'path' temporarily */
348         strcpy(link_buf, fname);
349
350         if (!getcwd(path, MAXPGPATH))
351         {
352                 log_error(_("could not identify current directory: %s"),
353                                   strerror(errno));
354                 return -1;
355         }
356         join_path_components(path, path, link_buf);
357         canonicalize_path(path);
358
359         if (chdir(orig_wd) == -1)
360         {
361                 log_error(_("could not change directory to \"%s\""), orig_wd);
362                 return -1;
363         }
364 #endif   /* HAVE_READLINK */
365
366         return 0;
367 }
368
369
370 /*
371  * Find another program in our binary's directory,
372  * then make sure it is the proper version.
373  */
374 int
375 find_other_exec(const char *argv0, const char *target,
376                                 const char *versionstr, char *retpath)
377 {
378         char            cmd[MAXPGPATH];
379         char            line[100];
380
381         if (find_my_exec(argv0, retpath) < 0)
382                 return -1;
383
384         /* Trim off program name and keep just directory */
385         *last_dir_separator(retpath) = '\0';
386         canonicalize_path(retpath);
387
388         /* Now append the other program's name */
389         snprintf(retpath + strlen(retpath), MAXPGPATH - strlen(retpath),
390                          "/%s%s", target, EXE);
391
392         if (validate_exec(retpath) != 0)
393                 return -1;
394
395         snprintf(cmd, sizeof(cmd), "\"%s\" -V 2>%s", retpath, DEVNULL);
396
397         if (!pipe_read_line(cmd, line, sizeof(line)))
398                 return -1;
399
400         if (strcmp(line, versionstr) != 0)
401                 return -2;
402
403         return 0;
404 }
405
406
407 /*
408  * The runtime library's popen() on win32 does not work when being
409  * called from a service when running on windows <= 2000, because
410  * there is no stdin/stdout/stderr.
411  *
412  * Executing a command in a pipe and reading the first line from it
413  * is all we need.
414  */
415 static char *
416 pipe_read_line(char *cmd, char *line, int maxsize)
417 {
418 #ifndef WIN32
419         FILE       *pgver;
420
421         /* flush output buffers in case popen does not... */
422         fflush(stdout);
423         fflush(stderr);
424
425         if ((pgver = popen(cmd, "r")) == NULL)
426                 return NULL;
427
428         if (fgets(line, maxsize, pgver) == NULL)
429         {
430                 perror("fgets failure");
431                 return NULL;
432         }
433
434         if (pclose_check(pgver))
435                 return NULL;
436
437         return line;
438 #else                                                   /* WIN32 */
439
440         SECURITY_ATTRIBUTES sattr;
441         HANDLE          childstdoutrd,
442                                 childstdoutwr,
443                                 childstdoutrddup;
444         PROCESS_INFORMATION pi;
445         STARTUPINFO si;
446         char       *retval = NULL;
447
448         sattr.nLength = sizeof(SECURITY_ATTRIBUTES);
449         sattr.bInheritHandle = TRUE;
450         sattr.lpSecurityDescriptor = NULL;
451
452         if (!CreatePipe(&childstdoutrd, &childstdoutwr, &sattr, 0))
453                 return NULL;
454
455         if (!DuplicateHandle(GetCurrentProcess(),
456                                                  childstdoutrd,
457                                                  GetCurrentProcess(),
458                                                  &childstdoutrddup,
459                                                  0,
460                                                  FALSE,
461                                                  DUPLICATE_SAME_ACCESS))
462         {
463                 CloseHandle(childstdoutrd);
464                 CloseHandle(childstdoutwr);
465                 return NULL;
466         }
467
468         CloseHandle(childstdoutrd);
469
470         ZeroMemory(&pi, sizeof(pi));
471         ZeroMemory(&si, sizeof(si));
472         si.cb = sizeof(si);
473         si.dwFlags = STARTF_USESTDHANDLES;
474         si.hStdError = childstdoutwr;
475         si.hStdOutput = childstdoutwr;
476         si.hStdInput = INVALID_HANDLE_VALUE;
477
478         if (CreateProcess(NULL,
479                                           cmd,
480                                           NULL,
481                                           NULL,
482                                           TRUE,
483                                           0,
484                                           NULL,
485                                           NULL,
486                                           &si,
487                                           &pi))
488         {
489                 /* Successfully started the process */
490                 char       *lineptr;
491
492                 ZeroMemory(line, maxsize);
493
494                 /* Try to read at least one line from the pipe */
495                 /* This may require more than one wait/read attempt */
496                 for (lineptr = line; lineptr < line + maxsize - 1;)
497                 {
498                         DWORD           bytesread = 0;
499
500                         /* Let's see if we can read */
501                         if (WaitForSingleObject(childstdoutrddup, 10000) != WAIT_OBJECT_0)
502                                 break;                  /* Timeout, but perhaps we got a line already */
503
504                         if (!ReadFile(childstdoutrddup, lineptr, maxsize - (lineptr - line),
505                                                   &bytesread, NULL))
506                                 break;                  /* Error, but perhaps we got a line already */
507
508                         lineptr += strlen(lineptr);
509
510                         if (!bytesread)
511                                 break;                  /* EOF */
512
513                         if (strchr(line, '\n'))
514                                 break;                  /* One or more lines read */
515                 }
516
517                 if (lineptr != line)
518                 {
519                         /* OK, we read some data */
520                         int                     len;
521
522                         /* If we got more than one line, cut off after the first \n */
523                         lineptr = strchr(line, '\n');
524                         if (lineptr)
525                                 *(lineptr + 1) = '\0';
526
527                         len = strlen(line);
528
529                         /*
530                          * If EOL is \r\n, convert to just \n. Because stdout is a
531                          * text-mode stream, the \n output by the child process is
532                          * received as \r\n, so we convert it to \n.  The server main.c
533                          * sets setvbuf(stdout, NULL, _IONBF, 0) which has the effect of
534                          * disabling \n to \r\n expansion for stdout.
535                          */
536                         if (len >= 2 && line[len - 2] == '\r' && line[len - 1] == '\n')
537                         {
538                                 line[len - 2] = '\n';
539                                 line[len - 1] = '\0';
540                                 len--;
541                         }
542
543                         /*
544                          * We emulate fgets() behaviour. So if there is no newline at the
545                          * end, we add one...
546                          */
547                         if (len == 0 || line[len - 1] != '\n')
548                                 strcat(line, "\n");
549
550                         retval = line;
551                 }
552
553                 CloseHandle(pi.hProcess);
554                 CloseHandle(pi.hThread);
555         }
556
557         CloseHandle(childstdoutwr);
558         CloseHandle(childstdoutrddup);
559
560         return retval;
561 #endif   /* WIN32 */
562 }
563
564
565 /*
566  * pclose() plus useful error reporting
567  * Is this necessary?  bjm 2004-05-11
568  * It is better here because pipe.c has win32 backend linkage.
569  */
570 int
571 pclose_check(FILE *stream)
572 {
573         int                     exitstatus;
574
575         exitstatus = pclose(stream);
576
577         if (exitstatus == 0)
578                 return 0;                               /* all is well */
579
580         if (exitstatus == -1)
581         {
582                 /* pclose() itself failed, and hopefully set errno */
583                 perror("pclose failed");
584         }
585         else if (WIFEXITED(exitstatus))
586                 log_error(_("child process exited with exit code %d"),
587                                   WEXITSTATUS(exitstatus));
588         else if (WIFSIGNALED(exitstatus))
589 #if defined(WIN32)
590                 log_error(_("child process was terminated by exception 0x%X"),
591                                   WTERMSIG(exitstatus));
592 #elif defined(HAVE_DECL_SYS_SIGLIST) && HAVE_DECL_SYS_SIGLIST
593         {
594                 char            str[256];
595
596                 snprintf(str, sizeof(str), "%d: %s", WTERMSIG(exitstatus),
597                                  WTERMSIG(exitstatus) < NSIG ?
598                                  sys_siglist[WTERMSIG(exitstatus)] : "(unknown)");
599                 log_error(_("child process was terminated by signal %s"), str);
600         }
601 #else
602                 log_error(_("child process was terminated by signal %d"),
603                                   WTERMSIG(exitstatus));
604 #endif
605         else
606                 log_error(_("child process exited with unrecognized status %d"),
607                                   exitstatus);
608
609         return -1;
610 }
611
612
613 /*
614  *      set_pglocale_pgservice
615  *
616  *      Set application-specific locale and service directory
617  *
618  *      This function takes the value of argv[0] rather than a full path.
619  *
620  * (You may be wondering why this is in exec.c.  It requires this module's
621  * services and doesn't introduce any new dependencies, so this seems as
622  * good as anyplace.)
623  */
624 void
625 set_pglocale_pgservice(const char *argv0, const char *app)
626 {
627         char            path[MAXPGPATH];
628         char            my_exec_path[MAXPGPATH];
629         char            env_path[MAXPGPATH + sizeof("PGSYSCONFDIR=")];  /* longer than
630                                                                                                                                  * PGLOCALEDIR */
631
632         /* don't set LC_ALL in the backend */
633         if (strcmp(app, "postgres") != 0)
634                 setlocale(LC_ALL, "");
635
636         if (find_my_exec(argv0, my_exec_path) < 0)
637                 return;
638
639 #ifdef ENABLE_NLS
640         get_locale_path(my_exec_path, path);
641         bindtextdomain(app, path);
642         textdomain(app);
643
644         if (getenv("PGLOCALEDIR") == NULL)
645         {
646                 /* set for libpq to use */
647                 snprintf(env_path, sizeof(env_path), "PGLOCALEDIR=%s", path);
648                 canonicalize_path(env_path + 12);
649                 putenv(strdup(env_path));
650         }
651 #endif
652
653         if (getenv("PGSYSCONFDIR") == NULL)
654         {
655                 get_etc_path(my_exec_path, path);
656
657                 /* set for libpq to use */
658                 snprintf(env_path, sizeof(env_path), "PGSYSCONFDIR=%s", path);
659                 canonicalize_path(env_path + 13);
660                 putenv(strdup(env_path));
661         }
662 }
663
664 #ifdef WIN32
665
666 /*
667  * AddUserToDacl(HANDLE hProcess)
668  *
669  * This function adds the current user account to the default DACL
670  * which gets attached to the restricted token used when we create
671  * a restricted process.
672  *
673  * This is required because of some security changes in Windows
674  * that appeared in patches to XP/2K3 and in Vista/2008.
675  *
676  * On these machines, the Administrator account is not included in
677  * the default DACL - you just get Administrators + System. For
678  * regular users you get User + System. Because we strip Administrators
679  * when we create the restricted token, we are left with only System
680  * in the DACL which leads to access denied errors for later CreatePipe()
681  * and CreateProcess() calls when running as Administrator.
682  *
683  * This function fixes this problem by modifying the DACL of the
684  * specified process and explicitly re-adding the current user account.
685  * This is still secure because the Administrator account inherits it's
686  * privileges from the Administrators group - it doesn't have any of
687  * it's own.
688  */
689 BOOL
690 AddUserToDacl(HANDLE hProcess)
691 {
692         int                     i;
693         ACL_SIZE_INFORMATION asi;
694         ACCESS_ALLOWED_ACE *pace;
695         DWORD           dwNewAclSize;
696         DWORD           dwSize = 0;
697         DWORD           dwTokenInfoLength = 0;
698         DWORD           dwResult = 0;
699         HANDLE          hToken = NULL;
700         PACL            pacl = NULL;
701         PSID            psidUser = NULL;
702         TOKEN_DEFAULT_DACL tddNew;
703         TOKEN_DEFAULT_DACL *ptdd = NULL;
704         TOKEN_INFORMATION_CLASS tic = TokenDefaultDacl;
705         BOOL            ret = FALSE;
706
707         /* Get the token for the process */
708         if (!OpenProcessToken(hProcess, TOKEN_QUERY | TOKEN_ADJUST_DEFAULT, &hToken))
709         {
710                 log_error("could not open process token: %ui", GetLastError());
711                 goto cleanup;
712         }
713
714         /* Figure out the buffer size for the DACL info */
715         if (!GetTokenInformation(hToken, tic, (LPVOID) NULL, dwTokenInfoLength, &dwSize))
716         {
717                 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
718                 {
719                         ptdd = (TOKEN_DEFAULT_DACL *) LocalAlloc(LPTR, dwSize);
720                         if (ptdd == NULL)
721                         {
722                                 log_error("could not allocate %i bytes of memory", dwSize);
723                                 goto cleanup;
724                         }
725
726                         if (!GetTokenInformation(hToken, tic, (LPVOID) ptdd, dwSize, &dwSize))
727                         {
728                                 log_error("could not get token information: %ui", GetLastError());
729                                 goto cleanup;
730                         }
731                 }
732                 else
733                 {
734                         log_error("could not get token information buffer size: %ui", GetLastError());
735                         goto cleanup;
736                 }
737         }
738
739         /* Get the ACL info */
740         if (!GetAclInformation(ptdd->DefaultDacl, (LPVOID) & asi,
741                                                    (DWORD) sizeof(ACL_SIZE_INFORMATION),
742                                                    AclSizeInformation))
743         {
744                 log_error("could not get ACL information: %ui", GetLastError());
745                 goto cleanup;
746         }
747
748         /* Get the SID for the current user. We need to add this to the ACL. */
749         if (!GetUserSid(&psidUser, hToken))
750         {
751                 log_error("could not get user SID: %ui", GetLastError());
752                 goto cleanup;
753         }
754
755         /* Figure out the size of the new ACL */
756         dwNewAclSize = asi.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(psidUser) - sizeof(DWORD);
757
758         /* Allocate the ACL buffer & initialize it */
759         pacl = (PACL) LocalAlloc(LPTR, dwNewAclSize);
760         if (pacl == NULL)
761         {
762                 log_error("could not allocate %i bytes of memory", dwNewAclSize);
763                 goto cleanup;
764         }
765
766         if (!InitializeAcl(pacl, dwNewAclSize, ACL_REVISION))
767         {
768                 log_error("could not initialize ACL: %ui", GetLastError());
769                 goto cleanup;
770         }
771
772         /* Loop through the existing ACEs, and build the new ACL */
773         for (i = 0; i < (int) asi.AceCount; i++)
774         {
775                 if (!GetAce(ptdd->DefaultDacl, i, (LPVOID *) & pace))
776                 {
777                         log_error("could not get ACE: %ui", GetLastError());
778                         goto cleanup;
779                 }
780
781                 if (!AddAce(pacl, ACL_REVISION, MAXDWORD, pace, ((PACE_HEADER) pace)->AceSize))
782                 {
783                         log_error("could not add ACE: %ui", GetLastError());
784                         goto cleanup;
785                 }
786         }
787
788         /* Add the new ACE for the current user */
789         if (!AddAccessAllowedAce(pacl, ACL_REVISION, GENERIC_ALL, psidUser))
790         {
791                 log_error("could not add access allowed ACE: %ui", GetLastError());
792                 goto cleanup;
793         }
794
795         /* Set the new DACL in the token */
796         tddNew.DefaultDacl = pacl;
797
798         if (!SetTokenInformation(hToken, tic, (LPVOID) & tddNew, dwNewAclSize))
799         {
800                 log_error("could not set token information: %ui", GetLastError());
801                 goto cleanup;
802         }
803
804         ret = TRUE;
805
806 cleanup:
807         if (psidUser)
808                 FreeSid(psidUser);
809
810         if (pacl)
811                 LocalFree((HLOCAL) pacl);
812
813         if (ptdd)
814                 LocalFree((HLOCAL) ptdd);
815
816         if (hToken)
817                 CloseHandle(hToken);
818
819         return ret;
820 }
821
822 /*
823  * GetUserSid*PSID *ppSidUser, HANDLE hToken)
824  *
825  * Get the SID for the current user
826  */
827 static BOOL
828 GetUserSid(PSID * ppSidUser, HANDLE hToken)
829 {
830         DWORD           dwLength;
831         DWORD           cbName = 250;
832         DWORD           cbDomainName = 250;
833         PTOKEN_USER pTokenUser = NULL;
834
835
836         if (!GetTokenInformation(hToken,
837                                                          TokenUser,
838                                                          pTokenUser,
839                                                          0,
840                                                          &dwLength))
841         {
842                 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
843                 {
844                         pTokenUser = (PTOKEN_USER) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength);
845
846                         if (pTokenUser == NULL)
847                         {
848                                 log_error("could not allocate %ui bytes of memory", dwLength);
849                                 return FALSE;
850                         }
851                 }
852                 else
853                 {
854                         log_error("could not get token information buffer size: %ui", GetLastError());
855                         return FALSE;
856                 }
857         }
858
859         if (!GetTokenInformation(hToken,
860                                                          TokenUser,
861                                                          pTokenUser,
862                                                          dwLength,
863                                                          &dwLength))
864         {
865                 HeapFree(GetProcessHeap(), 0, pTokenUser);
866                 pTokenUser = NULL;
867
868                 log_error("could not get token information: %ui", GetLastError());
869                 return FALSE;
870         }
871
872         *ppSidUser = pTokenUser->User.Sid;
873         return TRUE;
874 }
875
876 #endif