Skip to content

Commit dc32794

Browse files
committed
run_part: use execveat(2) to avoid toctou issues
Pin the script to execute before gathering its information to avoid any potential time-of-check-time-of-use issues.
1 parent 0534a1d commit dc32794

File tree

3 files changed

+69
-12
lines changed

3 files changed

+69
-12
lines changed

configure.ac

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ AC_CHECK_HEADERS(crypt.h utmp.h \
4545
dnl shadow now uses the libc's shadow implementation
4646
AC_CHECK_HEADER([shadow.h],,[AC_MSG_ERROR([You need a libc with shadow.h])])
4747

48-
AC_CHECK_FUNCS(arc4random_buf futimes \
48+
AC_CHECK_FUNCS(arc4random_buf execveat futimes \
4949
getentropy getrandom getspnam getusershell \
5050
initgroups lckpwdf lutimes mempcpy \
5151
setgroups updwtmp updwtmpx innetgr \

lib/run_part.c

Lines changed: 68 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
#include <config.h>
2+
13
#include <dirent.h>
24
#include <errno.h>
5+
#include <fcntl.h>
36
#include <stdio.h>
47
#include <stdlib.h>
58
#include <string.h>
@@ -13,12 +16,16 @@
1316
#include "run_part.h"
1417
#include "shadowlog_internal.h"
1518

16-
int run_part (char *script_path, const char *name, const char *action)
19+
#ifdef HAVE_EXECVEAT
20+
int run_part (int script_fd, const char *script_name, const char *name, const char *action)
21+
#else
22+
int run_part (const char *script_path, const char *script_name, const char *name, const char *action)
23+
#endif /* HAVE_EXECVEAT */
1724
{
1825
int pid;
1926
int wait_status;
2027
int pid_status;
21-
char *args[] = { script_path, NULL };
28+
char *args[] = { (char *) script_name, NULL };
2229

2330
pid=fork();
2431
if (pid==-1) {
@@ -28,7 +35,11 @@ int run_part (char *script_path, const char *name, const char *action)
2835
if (pid==0) {
2936
setenv ("ACTION",action,1);
3037
setenv ("SUBJECT",name,1);
31-
execv (script_path,args);
38+
#ifdef HAVE_EXECVEAT
39+
execveat (script_fd, "", args, environ, AT_EMPTY_PATH);
40+
#else
41+
execv (script_path, args);
42+
#endif /* HAVE_EXECVEAT */
3243
perror ("execv");
3344
exit(1);
3445
}
@@ -49,16 +60,38 @@ int run_parts (const char *directory, const char *name, const char *action)
4960
int n;
5061
int execute_result = 0;
5162

63+
#ifdef HAVE_EXECVEAT
64+
int dfd = open (directory, O_PATH | O_DIRECTORY | O_CLOEXEC);
65+
if (dfd == -1) {
66+
perror ("open");
67+
return (1);
68+
}
69+
#endif /* HAVE_EXECVEAT */
70+
5271
scanlist = scandir (directory, &namelist, 0, alphasort);
5372
if (scanlist<=0) {
73+
#ifdef HAVE_EXECVEAT
74+
(void) close (dfd);
75+
#endif /* HAVE_EXECVEAT */
5476
return (0);
5577
}
5678

5779
for (n=0; n<scanlist; n++) {
58-
int path_length;
5980
struct stat sb;
6081

61-
path_length=strlen(directory) + strlen(namelist[n]->d_name) + 2;
82+
#ifdef HAVE_EXECVEAT
83+
int fd = openat (dfd, namelist[n]->d_name, O_PATH | O_CLOEXEC);
84+
if (fd == -1) {
85+
perror ("open");
86+
for (; n<scanlist; n++) {
87+
free (namelist[n]);
88+
}
89+
free (namelist);
90+
(void) close (dfd);
91+
return (1);
92+
}
93+
#else
94+
int path_length=strlen(directory) + strlen(namelist[n]->d_name) + 2;
6295
char *s = MALLOCARRAY(path_length, char);
6396
if (!s) {
6497
fprintf (shadow_logfd, "could not allocate memory\n");
@@ -69,11 +102,20 @@ int run_parts (const char *directory, const char *name, const char *action)
69102
return (1);
70103
}
71104
snprintf (s, path_length, "%s/%s", directory, namelist[n]->d_name);
105+
#endif /* HAVE_EXECVEAT */
72106

73-
execute_result = 0;
107+
#ifdef HAVE_EXECVEAT
108+
if (fstat (fd, &sb) == -1) {
109+
#else
74110
if (stat (s, &sb) == -1) {
111+
#endif /* HAVE_EXECVEAT */
75112
perror ("stat");
113+
#ifdef HAVE_EXECVEAT
114+
(void) close (fd);
115+
(void) close (dfd);
116+
#else
76117
free (s);
118+
#endif /* HAVE_EXECVEAT */
77119
for (; n<scanlist; n++) {
78120
free (namelist[n]);
79121
}
@@ -82,7 +124,11 @@ int run_parts (const char *directory, const char *name, const char *action)
82124
}
83125

84126
if (!S_ISREG (sb.st_mode)) {
85-
free(s);
127+
#ifdef HAVE_EXECVEAT
128+
(void) close (fd);
129+
#else
130+
free (s);
131+
#endif /* HAVE_EXECVEAT */
86132
free (namelist[n]);
87133
continue;
88134
}
@@ -92,14 +138,22 @@ int run_parts (const char *directory, const char *name, const char *action)
92138
(sb.st_mode & 0002)) {
93139
fprintf (shadow_logfd, "skipping %s due to insecure ownership/permission\n",
94140
namelist[n]->d_name);
95-
free(s);
141+
#ifdef HAVE_EXECVEAT
142+
(void) close (fd);
143+
#else
144+
free (s);
145+
#endif /* HAVE_EXECVEAT */
96146
free (namelist[n]);
97147
continue;
98148
}
99149

100-
execute_result = run_part (s, name, action);
101-
150+
#ifdef HAVE_EXECVEAT
151+
execute_result = run_part (fd, namelist[n]->d_name, name, action);
152+
(void) close (fd);
153+
#else
154+
execute_result = run_part (s, namelist[n]->d_name, name, action);
102155
free (s);
156+
#endif /* HAVE_EXECVEAT */
103157

104158
if (execute_result!=0) {
105159
fprintf (shadow_logfd,
@@ -115,6 +169,10 @@ int run_parts (const char *directory, const char *name, const char *action)
115169
}
116170
free (namelist);
117171

172+
#ifdef HAVE_EXECVEAT
173+
(void) close (dfd);
174+
#endif /* HAVE_EXECVEAT */
175+
118176
return (execute_result);
119177
}
120178

lib/run_part.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
#ifndef _RUN_PART_H
22
#define _RUN_PART_H
33

4-
int run_part (char *script_path, const char *name, const char *action);
54
int run_parts (const char *directory, const char *name, const char *action);
65

76
#endif /* _RUN_PART_H */

0 commit comments

Comments
 (0)