/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 plugbasedclient.c

  • Committer: Teddy Hogeborn
  • Date: 2008-08-02 10:48:24 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080802104824-fx0miwp9o4g9r31e
* plugbasedclient.c (struct process): New fields "eof", "completed",
                                      and "status".
  (handle_sigchld): New function.
  (main): Initialize "dir" to NULL to only closedir() it if necessary.
          Move "process_list" to be a global variable to be accessible
          by "handle_sigchld".  Make "handle_sigchld" handle SIGCHLD.
          Remove redundant check for NULL "dir".  Free "filename" when
          no longer used.  Block SIGCHLD around fork()/exec().
          Restore normal signals in child.  Only loop while running
          processes exist.  Print process buffer when the process is
          done and it has emitted EOF, not when it only emits EOF.
          Remove processes from list which exit non-cleanly.  In
          cleaning up, closedir() if necessary.  Bug fix: set next
          pointer correctly when freeing process list.

* plugins.d/passprompt.c (main): Do not ignore SIGQUIT.

Show diffs side-by-side

added added

removed removed

Lines of Context:
23
23
 
24
24
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY() */
25
25
 
26
 
#include <stddef.h>             /* size_t, NULL */
27
 
#include <stdlib.h>             /* malloc(), exit(), EXIT_FAILURE,
28
 
                                   EXIT_SUCCESS, realloc() */
29
 
#include <stdbool.h>            /* bool, true, false */
30
 
#include <stdio.h>              /* perror, popen(), fileno(),
31
 
                                   fprintf(), stderr, STDOUT_FILENO */
32
 
#include <sys/types.h>          /* DIR, opendir(), stat(), struct
33
 
                                   stat, waitpid(), WIFEXITED(),
34
 
                                   WEXITSTATUS(), wait(), pid_t,
35
 
                                   uid_t, gid_t, getuid(), getgid(),
36
 
                                   dirfd() */
37
 
#include <sys/select.h>         /* fd_set, select(), FD_ZERO(),
38
 
                                   FD_SET(), FD_ISSET(), FD_CLR */
39
 
#include <sys/wait.h>           /* wait(), waitpid(), WIFEXITED(),
40
 
                                   WEXITSTATUS() */
41
 
#include <sys/stat.h>           /* struct stat, stat(), S_ISREG() */
 
26
#include <stdio.h>              /* popen(), fileno(), fprintf(),
 
27
                                   stderr, STDOUT_FILENO */
42
28
#include <iso646.h>             /* and, or, not */
 
29
#include <sys/types.h>          /* DIR, opendir(), stat(),
 
30
                                   struct stat, waitpid(),
 
31
                                   WIFEXITED(), WEXITSTATUS(),
 
32
                                   wait() */
 
33
#include <sys/wait.h>           /* wait() */
43
34
#include <dirent.h>             /* DIR, struct dirent, opendir(),
44
 
                                   readdir(), closedir(), dirfd() */
 
35
                                   readdir(), closedir() */
 
36
#include <sys/stat.h>           /* struct stat, stat(), S_ISREG() */
45
37
#include <unistd.h>             /* struct stat, stat(), S_ISREG(),
46
 
                                   fcntl(), setuid(), setgid(),
47
 
                                   F_GETFD, F_SETFD, FD_CLOEXEC,
48
 
                                   access(), pipe(), fork(), close()
49
 
                                   dup2, STDOUT_FILENO, _exit(),
50
 
                                   execv(), write(), read(),
51
 
                                   close() */
52
 
#include <fcntl.h>              /* fcntl(), F_GETFD, F_SETFD,
53
 
                                   FD_CLOEXEC */
54
 
#include <string.h>             /* strtok, strlen(), strcpy(),
55
 
                                   strcat() */
 
38
                                   fcntl() */
 
39
#include <fcntl.h>              /* fcntl() */
 
40
#include <stddef.h>             /* NULL */
 
41
#include <stdlib.h>             /* EXIT_FAILURE */
 
42
#include <sys/select.h>         /* fd_set, select(), FD_ZERO(),
 
43
                                   FD_SET(), FD_ISSET() */
 
44
#include <string.h>             /* strlen(), strcpy(), strcat() */
 
45
#include <stdbool.h>            /* true */
 
46
#include <sys/wait.h>           /* waitpid(), WIFEXITED(),
 
47
                                   WEXITSTATUS() */
56
48
#include <errno.h>              /* errno */
57
 
#include <argp.h>               /* struct argp_option, struct
58
 
                                   argp_state, struct argp,
59
 
                                   argp_parse(), ARGP_ERR_UNKNOWN,
60
 
                                   ARGP_KEY_END, ARGP_KEY_ARG, error_t */
61
 
#include <signal.h>             /* struct sigaction, sigemptyset(),
62
 
                                   sigaddset(), sigaction(),
63
 
                                   sigprocmask(), SIG_BLOCK, SIGCHLD,
64
 
                                   SIG_UNBLOCK, kill() */
65
 
#include <errno.h>              /* errno, EBADF */
66
 
 
67
 
#define BUFFER_SIZE 256
68
 
 
69
 
const char *argp_program_version = "mandos-client 1.0";
70
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
49
#include <argp.h>               /* struct argp_option,
 
50
                                   struct argp_state, struct argp,
 
51
                                   argp_parse() */
71
52
 
72
53
struct process;
73
54
 
91
72
  struct plugin *next;
92
73
} plugin;
93
74
 
94
 
static plugin *getplugin(char *name, plugin **plugin_list){
 
75
plugin *getplugin(char *name, plugin **plugin_list){
95
76
  for (plugin *p = *plugin_list; p != NULL; p = p->next){
96
77
    if ((p->name == name)
97
78
        or (p->name and name and (strcmp(p->name, name) == 0))){
120
101
  return new_plugin;
121
102
}
122
103
 
123
 
static void addargument(plugin *p, char *arg){
 
104
void addargument(plugin *p, char *arg){
124
105
  p->argv[p->argc] = arg;
125
106
  p->argv = realloc(p->argv, sizeof(char *) * (size_t)(p->argc + 2));
126
107
  if (p->argv == NULL){
136
117
 * Descriptor Flags".
137
118
 * *Note File Descriptor Flags:(libc)Descriptor Flags.
138
119
 */
139
 
static int set_cloexec_flag(int fd)
 
120
int set_cloexec_flag(int fd)
140
121
{
141
122
  int ret = fcntl(fd, F_GETFD, 0);
142
123
  /* If reading the flags failed, return error indication now. */
147
128
  return fcntl(fd, F_SETFD, ret | FD_CLOEXEC);
148
129
}
149
130
 
 
131
#define BUFFER_SIZE 256
 
132
 
 
133
const char *argp_program_version = "plugbasedclient 0.9";
 
134
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
135
 
150
136
process *process_list = NULL;
151
137
 
152
138
/* Mark a process as completed when it exits, and save its exit
155
141
  process *proc = process_list;
156
142
  int status;
157
143
  pid_t pid = wait(&status);
158
 
  if(pid == -1){
159
 
    perror("wait");
160
 
    return;
161
 
  }
162
144
  while(proc != NULL and proc->pid != pid){
163
 
    proc = proc->next;
 
145
    proc = proc->next;    
164
146
  }
165
147
  if(proc == NULL){
166
148
    /* Process not found in process list */
178
160
  struct stat st;
179
161
  fd_set rfds_all;
180
162
  int ret, maxfd = 0;
181
 
  uid_t uid = 65534;
182
 
  gid_t gid = 65534;
183
163
  bool debug = false;
184
164
  int exitstatus = EXIT_SUCCESS;
185
 
  struct sigaction old_sigchld_action;
186
 
  struct sigaction sigchld_action = { .sa_handler = handle_sigchld,
187
 
                                      .sa_flags = SA_NOCLDSTOP };
188
 
  char *plus_options = NULL;
189
 
  char **plus_argv = NULL;
190
165
  
191
166
  /* Establish a signal handler */
 
167
  struct sigaction old_sigchld_action,
 
168
    sigchld_action = { .sa_handler = handle_sigchld,
 
169
                       .sa_flags = SA_NOCLDSTOP };
192
170
  sigemptyset(&sigchld_action.sa_mask);
193
171
  ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD);
194
172
  if(ret < 0){
215
193
    { .name = "plugin-dir", .key = 128,
216
194
      .arg = "DIRECTORY",
217
195
      .doc = "Specify a different plugin directory", .group = 2 },
218
 
    { .name = "userid", .key = 129,
219
 
      .arg = "ID", .flags = 0,
220
 
      .doc = "User ID the plugins will run as", .group = 2 },
221
 
    { .name = "groupid", .key = 130,
222
 
      .arg = "ID", .flags = 0,
223
 
      .doc = "Group ID the plugins will run as", .group = 2 },
224
 
    { .name = "debug", .key = 131,
 
196
    { .name = "debug", .key = 129,
225
197
      .doc = "Debug mode", .group = 3 },
226
198
    { .name = NULL }
227
199
  };
237
209
        do{
238
210
          addargument(getplugin(NULL, plugins), p);
239
211
          p = strtok(NULL, ",");
240
 
        } while (p != NULL);
 
212
        } while (p);
241
213
      }
242
214
      break;
243
215
    case 'o':
244
216
      if (arg != NULL){
245
217
        char *name = strtok(arg, ":");
246
218
        char *p = strtok(NULL, ":");
247
 
        if(p != NULL){
 
219
        if(p){
248
220
          p = strtok(p, ",");
249
221
          do{
250
222
            addargument(getplugin(name, plugins), p);
251
223
            p = strtok(NULL, ",");
252
 
          } while (p != NULL);
 
224
          } while (p);
253
225
        }
254
226
      }
255
227
      break;
262
234
      plugindir = arg;
263
235
      break;
264
236
    case 129:
265
 
      uid = (uid_t)strtol(arg, NULL, 10);
266
 
      break;
267
 
    case 130:
268
 
      gid = (gid_t)strtol(arg, NULL, 10);
269
 
      break;
270
 
    case 131:
271
237
      debug = true;
272
238
      break;
273
239
    case ARGP_KEY_ARG:
274
 
      if(plus_options != NULL or arg == NULL or arg[0] != '+'){
275
 
        argp_usage (state);
276
 
      }
277
 
      plus_options = arg;
 
240
      argp_usage (state);
278
241
      break;
279
242
    case ARGP_KEY_END:
280
243
      break;
287
250
  plugin *plugin_list = NULL;
288
251
  
289
252
  struct argp argp = { .options = options, .parser = parse_opt,
290
 
                       .args_doc = "[+PLUS_SEPARATED_OPTIONS]",
 
253
                       .args_doc = "",
291
254
                       .doc = "Mandos plugin runner -- Run plugins" };
292
255
  
293
 
  ret = argp_parse (&argp, argc, argv, 0, 0, &plugin_list);
294
 
  if (ret == ARGP_ERR_UNKNOWN){
295
 
    fprintf(stderr, "Unkown error while parsing arguments\n");
296
 
    exitstatus = EXIT_FAILURE;
297
 
    goto end;
298
 
  }
299
 
  
300
 
  if(plus_options){
301
 
    /* This is a mangled argument in the form of
302
 
     "+--option+--other-option=parameter+--yet-another-option", etc */
303
 
    /* Make new argc and argv vars, and call argp_parse() again. */
304
 
    plus_options++;             /* skip the first '+' character */
305
 
    const char delims[] = "+";
306
 
    char *arg;
307
 
    int new_argc = 1;
308
 
    plus_argv = malloc(sizeof(char*) * 2);
309
 
    if(plus_argv == NULL){
310
 
      perror("malloc");
311
 
      exitstatus = EXIT_FAILURE;
312
 
      goto end;
313
 
    }
314
 
    plus_argv[0] = argv[0];
315
 
    plus_argv[1] = NULL;
316
 
    arg = strtok(plus_options, delims); /* Get first argument */
317
 
    while(arg != NULL){
318
 
      new_argc++;
319
 
      plus_argv = realloc(plus_argv, sizeof(char *)
320
 
                         * ((unsigned int) new_argc + 1));
321
 
      if(plus_argv == NULL){
322
 
        perror("realloc");
323
 
        exitstatus = EXIT_FAILURE;
324
 
        goto end;
325
 
      }
326
 
      plus_argv[new_argc-1] = arg;
327
 
      plus_argv[new_argc] = NULL;
328
 
      arg = strtok(NULL, delims); /* Get next argument */
329
 
    }
330
 
    ret = argp_parse (&argp, new_argc, plus_argv, 0, 0, &plugin_list);
331
 
    if (ret == ARGP_ERR_UNKNOWN){
332
 
      fprintf(stderr, "Unkown error while parsing arguments\n");
333
 
      exitstatus = EXIT_FAILURE;
334
 
      goto end;
335
 
    }
336
 
  }
 
256
  argp_parse (&argp, argc, argv, 0, 0, &plugin_list);
337
257
  
338
258
  if(debug){
339
259
    for(plugin *p = plugin_list; p != NULL; p=p->next){
345
265
    }
346
266
  }
347
267
  
348
 
  ret = setuid(uid);
349
 
  if (ret == -1){
350
 
    perror("setuid");
351
 
  }
352
 
  
353
 
  setgid(gid);
354
 
  if (ret == -1){
355
 
    perror("setgid");
356
 
  }
357
 
  
358
268
  dir = opendir(plugindir);
359
269
  if(dir == NULL){
360
270
    perror("Could not open plugin dir");
382
292
    
383
293
    // All directory entries have been processed
384
294
    if(dirst == NULL){
385
 
      if (errno == EBADF){
386
 
        perror("readdir");
387
 
        exitstatus = EXIT_FAILURE;
388
 
        goto end;
389
 
      }
390
295
      break;
391
296
    }
392
297
    
447
352
    strcat(filename, "/");      /* Spurious warning */
448
353
    strcat(filename, dirst->d_name); /* Spurious warning */
449
354
    
450
 
    ret = stat(filename, &st);
451
 
    if (ret == -1){
452
 
      perror("stat");
453
 
      exitstatus = EXIT_FAILURE;
454
 
      goto end;
455
 
    }
 
355
    stat(filename, &st);
456
356
    
457
357
    if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
458
358
      if(debug){
518
418
        perror("sigprocmask");
519
419
        _exit(EXIT_FAILURE);
520
420
      }
521
 
 
522
 
      ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
523
 
      if(ret == -1){
524
 
        perror("dup2");
525
 
        _exit(EXIT_FAILURE);
526
 
      }
 
421
      dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
527
422
      
528
423
      if(dirfd(dir) < 0){
529
424
        /* If dir has no file descriptor, we could not set FD_CLOEXEC
581
476
  
582
477
  closedir(dir);
583
478
  dir = NULL;
584
 
    
 
479
  
585
480
  if (process_list == NULL){
586
481
    fprintf(stderr, "No plugin processes started, exiting\n");
587
482
    exitstatus = EXIT_FAILURE;
606
501
          /* Bad exit by plugin */
607
502
          if(debug){
608
503
            if(WIFEXITED(proc->status)){
609
 
              fprintf(stderr, "Plugin %u exited with status %d\n",
610
 
                      (unsigned int) (proc->pid),
611
 
                      WEXITSTATUS(proc->status));
 
504
              fprintf(stderr, "Plugin %d exited with status %d\n",
 
505
                      proc->pid, WEXITSTATUS(proc->status));
612
506
            } else if(WIFSIGNALED(proc->status)) {
613
 
              fprintf(stderr, "Plugin %u killed by signal %d\n",
614
 
                      (unsigned int) (proc->pid),
615
 
                      WTERMSIG(proc->status));
 
507
              fprintf(stderr, "Plugin %d killed by signal %d\n",
 
508
                      proc->pid, WTERMSIG(proc->status));
616
509
            } else if(WCOREDUMP(proc->status)){
617
 
              fprintf(stderr, "Plugin %d dumped core\n",
618
 
                      (unsigned int) (proc->pid));
 
510
              fprintf(stderr, "Plugin %d dumped core\n", proc->pid);
619
511
            }
620
512
          }
621
513
          /* Remove the plugin */
708
600
  /* Restore old signal handler */
709
601
  sigaction(SIGCHLD, &old_sigchld_action, NULL);
710
602
  
711
 
  free(plus_argv);
712
 
  
713
603
  /* Free the plugin list */
714
604
  for(plugin *next; plugin_list != NULL; plugin_list = next){
715
605
    next = plugin_list->next;
725
615
  for(process *next; process_list != NULL; process_list = next){
726
616
    next = process_list->next;
727
617
    close(process_list->fd);
728
 
    ret = kill(process_list->pid, SIGTERM);
729
 
    if(ret == -1 and errno != ESRCH){
730
 
      /* set-uid proccesses migth not get closed */
731
 
      perror("kill");
732
 
    }
 
618
    kill(process_list->pid, SIGTERM);
733
619
    free(process_list->buffer);
734
620
    free(process_list);
735
621
  }