/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to plugins.d/password-prompt.c

  • Committer: Teddy Hogeborn
  • Date: 2016-03-17 20:40:55 UTC
  • Revision ID: teddy@recompile.se-20160317204055-bhsh5xsidq7w5cxu
Client: Fix plymouth agent; broken since 1.7.2.

Fix an very old memory bug in the plymouth agent (which has been
present since its apperance in version 1.2), but which was only
recently detected at run time due to the new -fsanitize=address
compile- time flag, which has been used since version 1.7.2.  This
detection of a memory access violation causes the program to abort,
making the Plymouth graphical boot system unable to accept interactive
input of passwords when using the Mandos client.

* plugins.d/plymouth.c (exec_and_wait): Fix memory allocation bug when
  allocating new_argv.  Also tolerate a zero-length argv.

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
/*
3
3
 * Password-prompt - Read a password from the terminal and print it
4
4
 * 
5
 
 * Copyright © 2008-2010 Teddy Hogeborn
6
 
 * Copyright © 2008-2010 Björn Påhlsson
 
5
 * Copyright © 2008-2016 Teddy Hogeborn
 
6
 * Copyright © 2008-2016 Björn Påhlsson
7
7
 * 
8
8
 * This program is free software: you can redistribute it and/or
9
9
 * modify it under the terms of the GNU General Public License as
19
19
 * along with this program.  If not, see
20
20
 * <http://www.gnu.org/licenses/>.
21
21
 * 
22
 
 * Contact the authors at <mandos@fukt.bsnet.se>.
 
22
 * Contact the authors at <mandos@recompile.se>.
23
23
 */
24
24
 
25
25
#define _GNU_SOURCE             /* getline(), asprintf() */
26
26
 
27
 
#include <termios.h>            /* struct termios, tcsetattr(),
 
27
#include <termios.h>            /* struct termios, tcsetattr(),
28
28
                                   TCSAFLUSH, tcgetattr(), ECHO */
29
29
#include <unistd.h>             /* struct termios, tcsetattr(),
30
30
                                   STDIN_FILENO, TCSAFLUSH,
35
35
                                   SIGQUIT, SIGHUP, SIGTERM,
36
36
                                   raise() */
37
37
#include <stddef.h>             /* NULL, size_t, ssize_t */
38
 
#include <sys/types.h>          /* ssize_t, struct dirent, pid_t, ssize_t */
 
38
#include <sys/types.h>          /* ssize_t, struct dirent, pid_t,
 
39
                                   ssize_t, open() */
39
40
#include <stdlib.h>             /* EXIT_SUCCESS, EXIT_FAILURE,
40
41
                                   getenv(), free() */
41
42
#include <dirent.h>             /* scandir(), alphasort() */
42
43
#include <stdio.h>              /* fprintf(), stderr, getline(),
43
 
                                   stdin, feof(), fputc()
44
 
                                */
 
44
                                   stdin, feof(), fputc(), vfprintf(),
 
45
                                   vasprintf() */
45
46
#include <errno.h>              /* errno, EBADF, ENOTTY, EINVAL,
46
47
                                   EFAULT, EFBIG, EIO, ENOSPC, EINTR
47
48
                                */
49
50
#include <iso646.h>             /* or, not */
50
51
#include <stdbool.h>            /* bool, false, true */
51
52
#include <inttypes.h>           /* strtoumax() */
52
 
#include <sys/stat.h>           /* struct stat, lstat() */
53
 
#include <string.h>             /* strlen, rindex, memcmp */
 
53
#include <sys/stat.h>           /* struct stat, lstat(), open() */
 
54
#include <string.h>             /* strlen, rindex, memcmp, strerror()
 
55
                                 */
54
56
#include <argp.h>               /* struct argp_option, struct
55
57
                                   argp_state, struct argp,
56
58
                                   argp_parse(), error_t,
58
60
                                   ARGP_ERR_UNKNOWN */
59
61
#include <sysexits.h>           /* EX_SOFTWARE, EX_OSERR,
60
62
                                   EX_UNAVAILABLE, EX_IOERR, EX_OK */
 
63
#include <fcntl.h>              /* open() */
 
64
#include <stdarg.h>             /* va_list, va_start(), ... */
61
65
 
62
66
volatile sig_atomic_t quit_now = 0;
63
67
int signal_received;
64
68
bool debug = false;
65
69
const char *argp_program_version = "password-prompt " VERSION;
66
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
67
 
 
68
 
/* Needed for conflic resolution */
69
 
const char plymouthd_path[] = "/sbin/plymouth";
70
 
 
 
70
const char *argp_program_bug_address = "<mandos@recompile.se>";
 
71
 
 
72
/* Needed for conflict resolution */
 
73
const char plymouth_name[] = "plymouthd";
 
74
 
 
75
/* Function to use when printing errors */
 
76
__attribute__((format (gnu_printf, 3, 4)))
 
77
void error_plus(int status, int errnum, const char *formatstring,
 
78
                ...){
 
79
  va_list ap;
 
80
  char *text;
 
81
  int ret;
 
82
  
 
83
  va_start(ap, formatstring);
 
84
  ret = vasprintf(&text, formatstring, ap);
 
85
  if(ret == -1){
 
86
    fprintf(stderr, "Mandos plugin %s: ",
 
87
            program_invocation_short_name);
 
88
    vfprintf(stderr, formatstring, ap);
 
89
    fprintf(stderr, ": %s\n", strerror(errnum));
 
90
    error(status, errno, "vasprintf while printing error");
 
91
    return;
 
92
  }
 
93
  fprintf(stderr, "Mandos plugin ");
 
94
  error(status, errnum, "%s", text);
 
95
  free(text);
 
96
}
71
97
 
72
98
static void termination_handler(int signum){
73
99
  if(quit_now){
79
105
 
80
106
bool conflict_detection(void){
81
107
 
82
 
  /* plymouth conflicts with password-promt since both want to control the
83
 
     associated terminal. Password-prompt exit since plymouth perferms the same
84
 
     functionallity.
 
108
  /* plymouth conflicts with password-prompt since both want to read
 
109
     from the terminal.  Password-prompt will exit if it detects
 
110
     plymouth since plymouth performs the same functionality.
85
111
   */
 
112
  __attribute__((nonnull))
86
113
  int is_plymouth(const struct dirent *proc_entry){
87
114
    int ret;
 
115
    int cl_fd;
88
116
    {
89
 
      uintmax_t maxvalue;
 
117
      uintmax_t proc_id;
90
118
      char *tmp;
91
119
      errno = 0;
92
 
      maxvalue = strtoumax(proc_entry->d_name, &tmp, 10);
 
120
      proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
93
121
      
94
122
      if(errno != 0 or *tmp != '\0'
95
 
         or maxvalue != (uintmax_t)((pid_t)maxvalue)){
 
123
         or proc_id != (uintmax_t)((pid_t)proc_id)){
96
124
        return 0;
97
125
      }
98
126
    }
99
 
    char exe_target[sizeof(plymouthd_path)];
100
 
    char *exe_link;
101
 
    ret = asprintf(&exe_link, "/proc/%s/exe", proc_entry->d_name);
 
127
    
 
128
    char *cmdline_filename;
 
129
    ret = asprintf(&cmdline_filename, "/proc/%s/cmdline",
 
130
                   proc_entry->d_name);
102
131
    if(ret == -1){
103
 
      error(0, errno, "asprintf");
 
132
      error_plus(0, errno, "asprintf");
104
133
      return 0;
105
134
    }
106
 
  
107
 
    struct stat exe_stat;
108
 
    ret = lstat(exe_link, &exe_stat);
109
 
    if(ret == -1){
110
 
      free(exe_link);
 
135
    
 
136
    /* Open /proc/<pid>/cmdline */
 
137
    cl_fd = open(cmdline_filename, O_RDONLY);
 
138
    free(cmdline_filename);
 
139
    if(cl_fd == -1){
111
140
      if(errno != ENOENT){
112
 
        error(0, errno, "lstat");
113
 
      }
114
 
      return 0;
115
 
    }
116
 
  
117
 
    if(not S_ISLNK(exe_stat.st_mode)
118
 
       or exe_stat.st_uid != 0
119
 
       or exe_stat.st_gid != 0){
120
 
      free(exe_link);
121
 
      return 0;
122
 
    }
123
 
  
124
 
    ssize_t sret = readlink(exe_link, exe_target, sizeof(exe_target));
125
 
    free(exe_link);
126
 
    if((sret != (ssize_t)sizeof(plymouthd_path)-1) or
127
 
       (memcmp(plymouthd_path, exe_target,
128
 
               sizeof(plymouthd_path)-1) != 0)){
129
 
      return 0;
130
 
    }
 
141
        error_plus(0, errno, "open");
 
142
      }
 
143
      return 0;
 
144
    }
 
145
    
 
146
    char *cmdline = NULL;
 
147
    {
 
148
      size_t cmdline_len = 0;
 
149
      size_t cmdline_allocated = 0;
 
150
      char *tmp;
 
151
      const size_t blocksize = 1024;
 
152
      ssize_t sret;
 
153
      do {
 
154
        /* Allocate more space? */
 
155
        if(cmdline_len + blocksize + 1 > cmdline_allocated){
 
156
          tmp = realloc(cmdline, cmdline_allocated + blocksize + 1);
 
157
          if(tmp == NULL){
 
158
            error_plus(0, errno, "realloc");
 
159
            free(cmdline);
 
160
            close(cl_fd);
 
161
            return 0;
 
162
          }
 
163
          cmdline = tmp;
 
164
          cmdline_allocated += blocksize;
 
165
        }
 
166
        
 
167
        /* Read data */
 
168
        sret = read(cl_fd, cmdline + cmdline_len,
 
169
                    cmdline_allocated - cmdline_len);
 
170
        if(sret == -1){
 
171
          error_plus(0, errno, "read");
 
172
          free(cmdline);
 
173
          close(cl_fd);
 
174
          return 0;
 
175
        }
 
176
        cmdline_len += (size_t)sret;
 
177
      } while(sret != 0);
 
178
      ret = close(cl_fd);
 
179
      if(ret == -1){
 
180
        error_plus(0, errno, "close");
 
181
        free(cmdline);
 
182
        return 0;
 
183
      }
 
184
      cmdline[cmdline_len] = '\0'; /* Make sure it is terminated */
 
185
    }
 
186
    /* we now have cmdline */
 
187
    
 
188
    /* get basename */
 
189
    char *cmdline_base = strrchr(cmdline, '/');
 
190
    if(cmdline_base != NULL){
 
191
      cmdline_base += 1;                /* skip the slash */
 
192
    } else {
 
193
      cmdline_base = cmdline;
 
194
    }
 
195
    
 
196
    if(strcmp(cmdline_base, plymouth_name) != 0){
 
197
      if(debug){
 
198
        fprintf(stderr, "\"%s\" is not \"%s\"\n", cmdline_base,
 
199
                plymouth_name);
 
200
      }
 
201
      free(cmdline);
 
202
      return 0;
 
203
    }
 
204
    if(debug){
 
205
      fprintf(stderr, "\"%s\" equals \"%s\"\n", cmdline_base,
 
206
              plymouth_name);
 
207
    }
 
208
    free(cmdline);
131
209
    return 1;
132
210
  }
133
 
 
134
 
  struct dirent **direntries;
 
211
  
 
212
  struct dirent **direntries = NULL;
135
213
  int ret;
136
214
  ret = scandir("/proc", &direntries, is_plymouth, alphasort);
137
 
  if (ret == -1){
138
 
    error(1, errno, "scandir");
139
 
  }
140
 
  if (ret < 0){
141
 
    return 1;
142
 
  } else {
143
 
    return 0;
144
 
  }
 
215
  if(ret == -1){
 
216
    error_plus(1, errno, "scandir");
 
217
  }
 
218
  free(direntries);
 
219
  return ret > 0;
145
220
}
146
221
 
147
222
 
175
250
      { .name = NULL }
176
251
    };
177
252
    
 
253
    __attribute__((nonnull(3)))
178
254
    error_t parse_opt (int key, char *arg, struct argp_state *state){
179
255
      errno = 0;
180
256
      switch (key){
216
292
    case ENOMEM:
217
293
    default:
218
294
      errno = ret;
219
 
      error(0, errno, "argp_parse");
 
295
      error_plus(0, errno, "argp_parse");
220
296
      return EX_OSERR;
221
297
    case EINVAL:
222
298
      return EX_USAGE;
227
303
    fprintf(stderr, "Starting %s\n", argv[0]);
228
304
  }
229
305
 
230
 
  if (conflict_detection()){
 
306
  if(conflict_detection()){
231
307
    if(debug){
232
 
      fprintf(stderr, "Stopping %s because of conflict", argv[0]);
 
308
      fprintf(stderr, "Stopping %s because of conflict\n", argv[0]);
233
309
    }
234
310
    return EXIT_FAILURE;
235
311
  }
240
316
  
241
317
  if(tcgetattr(STDIN_FILENO, &t_old) != 0){
242
318
    int e = errno;
243
 
    error(0, errno, "tcgetattr");
 
319
    error_plus(0, errno, "tcgetattr");
244
320
    switch(e){
245
321
    case EBADF:
246
322
    case ENOTTY:
253
329
  sigemptyset(&new_action.sa_mask);
254
330
  ret = sigaddset(&new_action.sa_mask, SIGINT);
255
331
  if(ret == -1){
256
 
    error(0, errno, "sigaddset");
 
332
    error_plus(0, errno, "sigaddset");
257
333
    return EX_OSERR;
258
334
  }
259
335
  ret = sigaddset(&new_action.sa_mask, SIGHUP);
260
336
  if(ret == -1){
261
 
    error(0, errno, "sigaddset");
 
337
    error_plus(0, errno, "sigaddset");
262
338
    return EX_OSERR;
263
339
  }
264
340
  ret = sigaddset(&new_action.sa_mask, SIGTERM);
265
341
  if(ret == -1){
266
 
    error(0, errno, "sigaddset");
 
342
    error_plus(0, errno, "sigaddset");
267
343
    return EX_OSERR;
268
344
  }
269
345
  /* Need to check if the handler is SIG_IGN before handling:
272
348
  */
273
349
  ret = sigaction(SIGINT, NULL, &old_action);
274
350
  if(ret == -1){
275
 
    error(0, errno, "sigaction");
 
351
    error_plus(0, errno, "sigaction");
276
352
    return EX_OSERR;
277
353
  }
278
354
  if(old_action.sa_handler != SIG_IGN){
279
355
    ret = sigaction(SIGINT, &new_action, NULL);
280
356
    if(ret == -1){
281
 
      error(0, errno, "sigaction");
 
357
      error_plus(0, errno, "sigaction");
282
358
      return EX_OSERR;
283
359
    }
284
360
  }
285
361
  ret = sigaction(SIGHUP, NULL, &old_action);
286
362
  if(ret == -1){
287
 
    error(0, errno, "sigaction");
 
363
    error_plus(0, errno, "sigaction");
288
364
    return EX_OSERR;
289
365
  }
290
366
  if(old_action.sa_handler != SIG_IGN){
291
367
    ret = sigaction(SIGHUP, &new_action, NULL);
292
368
    if(ret == -1){
293
 
      error(0, errno, "sigaction");
 
369
      error_plus(0, errno, "sigaction");
294
370
      return EX_OSERR;
295
371
    }
296
372
  }
297
373
  ret = sigaction(SIGTERM, NULL, &old_action);
298
374
  if(ret == -1){
299
 
    error(0, errno, "sigaction");
 
375
    error_plus(0, errno, "sigaction");
300
376
    return EX_OSERR;
301
377
  }
302
378
  if(old_action.sa_handler != SIG_IGN){
303
379
    ret = sigaction(SIGTERM, &new_action, NULL);
304
380
    if(ret == -1){
305
 
      error(0, errno, "sigaction");
 
381
      error_plus(0, errno, "sigaction");
306
382
      return EX_OSERR;
307
383
    }
308
384
  }
316
392
  t_new.c_lflag &= ~(tcflag_t)ECHO;
317
393
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
318
394
    int e = errno;
319
 
    error(0, errno, "tcsetattr-echo");
 
395
    error_plus(0, errno, "tcsetattr-echo");
320
396
    switch(e){
321
397
    case EBADF:
322
398
    case ENOTTY:
386
462
        sret = write(STDOUT_FILENO, buffer + written, n - written);
387
463
        if(sret < 0){
388
464
          int e = errno;
389
 
          error(0, errno, "write");
 
465
          error_plus(0, errno, "write");
390
466
          switch(e){
391
467
          case EBADF:
392
468
          case EFAULT:
408
484
      sret = close(STDOUT_FILENO);
409
485
      if(sret == -1){
410
486
        int e = errno;
411
 
        error(0, errno, "close");
 
487
        error_plus(0, errno, "close");
412
488
        switch(e){
413
489
        case EBADF:
414
490
          status = EX_OSFILE;
424
500
    if(sret < 0){
425
501
      int e = errno;
426
502
      if(errno != EINTR and not feof(stdin)){
427
 
        error(0, errno, "getline");
 
503
        error_plus(0, errno, "getline");
428
504
        switch(e){
429
505
        case EBADF:
430
506
          status = EX_UNAVAILABLE;
 
507
          break;
431
508
        case EIO:
432
509
        case EINVAL:
433
510
        default:
437
514
        break;
438
515
      }
439
516
    }
440
 
    /* if(sret == 0), then the only sensible thing to do is to retry to
441
 
       read from stdin */
 
517
    /* if(sret == 0), then the only sensible thing to do is to retry
 
518
       to read from stdin */
442
519
    fputc('\n', stderr);
443
520
    if(debug and not quit_now){
444
521
      /* If quit_now is nonzero, we were interrupted by a signal, and
453
530
    fprintf(stderr, "Restoring terminal attributes\n");
454
531
  }
455
532
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
456
 
    error(0, errno, "tcsetattr+echo");
 
533
    error_plus(0, errno, "tcsetattr+echo");
457
534
  }
458
535
  
459
536
  if(quit_now){
461
538
    old_action.sa_handler = SIG_DFL;
462
539
    ret = sigaction(signal_received, &old_action, NULL);
463
540
    if(ret == -1){
464
 
      error(0, errno, "sigaction");
 
541
      error_plus(0, errno, "sigaction");
465
542
    }
466
543
    raise(signal_received);
467
544
  }