23
23
#define READ_END 0
24
24
#define WRITE_END 1
25
25
26
- #define tohex (n ) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A'))
27
-
28
- struct userinfo {
29
- int uid , gid ;
30
- char * home ;
31
- };
32
-
33
- void free_userinfo (struct userinfo * userinfo ) {
34
-
35
- if (!userinfo )
36
- return ;
37
-
38
- free (userinfo -> home );
39
- free (userinfo );
40
- }
41
-
42
- bool get_userinfo (pam_handle_t * pamh , struct userinfo * * userinfo ) {
43
- const char * user = NULL ;
44
- struct passwd pwd , * result = NULL ;
45
- char * buf = NULL ;
46
- size_t bufsize ;
47
-
48
- * userinfo = NULL ;
49
-
50
- if (pam_get_user (pamh , & user , NULL ) != PAM_SUCCESS || user == NULL ) {
51
- return false;
52
- }
53
-
54
- bufsize = sysconf (_SC_GETPW_R_SIZE_MAX );
55
- if (bufsize == -1 ) {
56
- bufsize = 16384 ;
57
- }
58
-
59
- buf = malloc (bufsize );
60
- if (buf == NULL ) {
61
- return false;
62
- }
63
-
64
- if (getpwnam_r (user , & pwd , buf , bufsize , & result ) != 0 || result == NULL ||
65
- pwd .pw_dir == NULL || pwd .pw_dir [0 ] != '/' ) {
66
- free (buf );
67
- return false;
68
- }
69
-
70
- * userinfo = malloc (sizeof (* userinfo ));
71
- if (* userinfo == NULL ) {
72
- free (buf );
73
- return false;
74
- }
75
-
76
- (* userinfo )-> uid = pwd .pw_uid ;
77
- (* userinfo )-> gid = pwd .pw_gid ;
78
- (* userinfo )-> home = strdup (pwd .pw_dir );
79
- free (buf );
80
-
81
- if ((* userinfo )-> home == NULL ) {
82
- free_userinfo (* userinfo );
83
- * userinfo = NULL ;
84
- return false;
26
+ char tohex (char n ) {
27
+ if (n < 10 ) {
28
+ return n + '0' ;
29
+ } else {
30
+ return n - 10 + 'A' ;
85
31
}
86
-
87
- return true;
88
32
}
89
33
90
34
/* Copied from gnupg */
@@ -126,144 +70,111 @@ void cleanup_token(pam_handle_t *pamh, void *data, int error_status) {
126
70
wipestr (data );
127
71
}
128
72
129
- void close_safe ( int fd )
130
- {
131
- if (fd != -1 ) {
132
- close ( fd ) ;
73
+ bool preset_passphrase ( pam_handle_t * pamh , const char * tok , bool autostart ) {
74
+ const char * user = NULL ;
75
+ if (pam_get_user ( pamh , & user , NULL ) != PAM_SUCCESS || user == NULL ) {
76
+ return false ;
133
77
}
134
- }
135
78
136
- void setup_sigs (struct sigaction * * old ) {
137
- struct sigaction sigchld , sigpipe ;
138
- if ((* old = malloc (2 * sizeof (struct sigaction ))) == NULL ) {
139
- return ;
79
+ struct passwd * pwd = getpwnam (user );
80
+ if (pwd == NULL ) {
81
+ return false;
140
82
}
141
- memset (* old , 0 , 2 * sizeof (struct sigaction ));
83
+
84
+ struct sigaction sigchld , old_sigchld ;
142
85
memset (& sigchld , 0 , sizeof (sigchld ));
143
- memset (& sigpipe , 0 , sizeof (sigpipe ));
86
+ memset (& old_sigchld , 0 , sizeof (old_sigchld ));
144
87
sigchld .sa_handler = SIG_DFL ;
145
- sigpipe .sa_handler = SIG_IGN ;
146
- sigaction (SIGCHLD , & sigchld , * old + 0 );
147
- sigaction (SIGPIPE , & sigpipe , * old + 1 );
148
- }
88
+ sigaction (SIGCHLD , & sigchld , & old_sigchld );
149
89
150
- void restore_sigs (const struct sigaction * old ) {
151
- if (old == NULL ) {
152
- return ;
153
- }
154
- sigaction (SIGCHLD , old + 0 , NULL );
155
- sigaction (SIGPIPE , old + 1 , NULL );
156
- free ((void * ) old );
157
- }
158
-
159
- int run_as_user (const struct userinfo * user , const char * const cmd [], int * input , char * * env ) {
160
- int inp [2 ] = {-1 , -1 };
161
- int pid ;
162
- int dev_null ;
163
-
164
- if (pipe (inp ) < 0 ) {
165
- * input = -1 ;
166
- return 0 ;
90
+ pid_t pid = fork ();
91
+ if (pid == -1 ) {
92
+ sigaction (SIGCHLD , & old_sigchld , NULL );
93
+ return false;
94
+ } else if (pid > 0 ) {
95
+ int status ;
96
+ waitpid (pid , & status , 0 );
97
+ sigaction (SIGCHLD , & old_sigchld , NULL );
98
+ return (WIFEXITED (status ) && WEXITSTATUS (status ) == EXIT_SUCCESS );
167
99
}
168
- * input = inp [WRITE_END ];
169
-
170
- switch (pid = fork ()) {
171
- case -1 :
172
- close_safe (inp [READ_END ]);
173
- close_safe (inp [WRITE_END ]);
174
- * input = -1 ;
175
- return 0 ;
176
100
177
- case 0 :
178
- break ;
101
+ // We're in the child process now. From here on, the function will not return.
179
102
180
- default :
181
- close_safe (inp [READ_END ]);
182
- return pid ;
103
+ if (setregid (pwd -> pw_gid , pwd -> pw_gid ) < 0 || setreuid (pwd -> pw_uid , pwd -> pw_uid ) < 0 ) {
104
+ exit (EXIT_FAILURE );
183
105
}
184
106
185
- /* We're in the child process now */
186
-
187
- if (dup2 (inp [READ_END ], STDIN_FILENO ) < 0 ) {
107
+ int inp [2 ] = {-1 , -1 };
108
+ if (pipe (inp ) < 0 ) {
188
109
exit (EXIT_FAILURE );
189
110
}
190
- close_safe (inp [READ_END ]);
191
- close_safe (inp [WRITE_END ]);
111
+ signal (SIGPIPE , SIG_IGN );
192
112
193
- if ((dev_null = open ("/dev/null" , O_WRONLY )) != -1 ) {
113
+ int dev_null = open ("/dev/null" , O_RDWR );
114
+ if (dev_null != -1 ) {
115
+ dup2 (dev_null , STDIN_FILENO );
194
116
dup2 (dev_null , STDOUT_FILENO );
195
117
dup2 (dev_null , STDERR_FILENO );
196
118
close (dev_null );
197
119
}
198
120
199
- if (seteuid (getuid ()) < 0 || setegid (getgid ()) < 0 ||
200
- setgid (user -> gid ) < 0 || setuid (user -> uid ) < 0 ||
201
- setegid (user -> gid ) < 0 || seteuid (user -> uid ) < 0 ) {
121
+ int dirfd = open (pwd -> pw_dir , O_RDONLY | O_CLOEXEC );
122
+ if (dirfd < 0 ) {
202
123
exit (EXIT_FAILURE );
203
124
}
204
-
205
- if (env != NULL ) {
206
- execve (cmd [0 ], (char * const * ) cmd , env );
207
- } else {
208
- execv (cmd [0 ], (char * const * ) cmd );
209
- }
210
- exit (EXIT_FAILURE );
211
- }
212
-
213
- bool preset_passphrase (pam_handle_t * pamh , const char * tok , bool autostart ) {
214
- bool ret = false;
215
-
216
- struct userinfo * user ;
217
- if (!get_userinfo (pamh , & user )) {
218
- return false;
219
- }
220
-
221
- char * keygrip_file ;
222
- if (asprintf (& keygrip_file , "%s/.pam-gnupg" , user -> home ) < 0 ) {
223
- goto end ;
125
+ int fd = openat (dirfd , ".pam-gnupg" , O_RDONLY | O_CLOEXEC );
126
+ if (fd < 0 ) {
127
+ exit (EXIT_FAILURE );
224
128
}
225
- FILE * file = fopen (keygrip_file , "r" );
226
- free (keygrip_file );
129
+ FILE * file = fdopen (fd , "r" );
227
130
if (file == NULL ) {
228
- return false ;
131
+ exit ( EXIT_FAILURE ) ;
229
132
}
230
133
231
- struct sigaction * handlers = NULL ;
232
- setup_sigs (& handlers );
233
- if (handlers == NULL ) {
234
- goto end ;
235
- }
134
+ pid = fork ();
135
+ if (pid == -1 ) {
136
+ exit (EXIT_FAILURE );
137
+ } else if (pid == 0 ) {
138
+ // Grandchild
139
+ if (dup2 (inp [READ_END ], STDIN_FILENO ) < 0 ) {
140
+ exit (EXIT_FAILURE );
141
+ }
142
+ close (inp [READ_END ]);
143
+ close (inp [WRITE_END ]);
144
+
145
+ // gpg-connect-agent has an option --no-autostart, which *should* return
146
+ // non-zero when the agent is not running. Unfortunately, the exit code is
147
+ // always 0 in version 2.1. Passing an invalid agent program here is a
148
+ // workaround. See https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=797334
149
+ const char * cmd [] = {GPG_CONNECT_AGENT , "--agent-program" , "/dev/null" , NULL };
150
+ if (autostart ) {
151
+ cmd [1 ] = NULL ;
152
+ }
236
153
237
- /* gpg-connect-agent has an option --no-autostart, which *should* return
238
- * non-zero when the agent is not running. Unfortunately, the exit code is
239
- * always 0 in version 2.1. Passing an invalid agent program here is a
240
- * workaround. See https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=797334 */
241
- const char * cmd [] = {GPG_CONNECT_AGENT , "--agent-program" , "/dev/null" , NULL };
242
- if (autostart ) {
243
- cmd [1 ] = NULL ;
154
+ char * * env = pam_getenvlist (pamh );
155
+ if (env != NULL ) {
156
+ execve (cmd [0 ], (char * const * ) cmd , env );
157
+ } else {
158
+ execv (cmd [0 ], (char * const * ) cmd );
159
+ }
244
160
}
245
161
246
- int input ;
247
- char * * env = pam_getenvlist (pamh );
248
- const int pid = run_as_user (user , cmd , & input , env );
249
- if (env != NULL ) {
250
- free (env );
251
- }
252
- if (pid == 0 || input < 0 ) {
253
- goto end ;
254
- }
162
+ close (inp [READ_END ]);
255
163
256
164
char * presetcmd ;
257
165
const int presetlen = asprintf (& presetcmd , "PRESET_PASSPHRASE xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -1 %s\n" , tok );
258
166
if (presetlen < 0 ) {
259
- presetcmd = NULL ;
260
- goto end ;
167
+ exit (EXIT_FAILURE );
261
168
}
262
169
char * const keygrip = presetcmd + 18 ;
263
170
264
171
char * line = NULL ;
265
172
size_t len = 0 ;
266
- while (getline (& line , & len , file ) != -1 ) {
173
+ ssize_t rd ;
174
+ while ((rd = getline (& line , & len , file )) != -1 ) {
175
+ if (line [rd - 1 ] == '\n' ) {
176
+ line [rd - 1 ] = '\0' ;
177
+ }
267
178
const char * cur = line ;
268
179
while (* cur && strchr (" \t\n\r\f\v" , * cur )) {
269
180
cur ++ ;
@@ -273,33 +184,22 @@ bool preset_passphrase(pam_handle_t *pamh, const char *tok, bool autostart) {
273
184
}
274
185
strncpy (keygrip , cur , KEYGRIP_LENGTH );
275
186
if (strlen (keygrip ) < KEYGRIP_LENGTH ) {
276
- /* We hit eol sooner than expected. */
187
+ // We hit an unexpected eol or null byte.
277
188
continue ;
278
189
}
279
- if (write (input , presetcmd , presetlen ) < 0 ) {
280
- /* If anything goes wrong, we just stop here. No attempt is made to
281
- * clean passphrases that were set in a previous iteration. */
282
- close (input );
283
- goto end ;
190
+ if (write (inp [WRITE_END ], presetcmd , presetlen ) < 0 ) {
191
+ exit (EXIT_FAILURE );
284
192
}
285
193
}
286
194
287
195
int status ;
288
- close (input );
196
+ close (inp [ WRITE_END ] );
289
197
waitpid (pid , & status , 0 );
290
- ret = (WIFEXITED (status ) && WEXITSTATUS (status ) == 0 );
291
-
292
- end :
293
- wipestr (presetcmd );
294
- restore_sigs (handlers );
295
- free_userinfo (user );
296
- if (file != NULL ) {
297
- fclose (file );
298
- }
299
- if (line != NULL ) {
300
- free (line );
198
+ if (WIFEXITED (status ) && WEXITSTATUS (status ) == 0 ) {
199
+ exit (EXIT_SUCCESS );
200
+ } else {
201
+ exit (EXIT_FAILURE );
301
202
}
302
- return ret ;
303
203
}
304
204
305
205
int pam_sm_authenticate (pam_handle_t * pamh , int flags , int argc , const char * * argv ) {
0 commit comments