/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 plugin-runner.c

  • Committer: Teddy Hogeborn
  • Date: 2026-05-17 23:56:03 UTC
  • Revision ID: teddy@recompile.se-20260517235603-5ygymkxkh32rxjkf
Remove white space at end of most lines

Remove white space at end of most lines.  Except for where it is
significant, like in shell script "here documents" where the delimiter
word is prefixed by "-", in block comments, and in config file example
settings.  Also not in README files, the INSTALL file, the DBUS-API
file, or the COPYING file (the last of which was copied verbatim from
FSF).

* dbus-mandos.conf: Remove white space at end of lines.
* debian/mandos-client.postinst: - '' -
* debian/mandos-client.templates: - '' -
* debian/mandos.postinst: - '' -
* dracut-module/password-agent.c: - '' -
* dracut-module/password-agent.xml: - '' -
* intro.xml: - '' -
* legalnotice.xml: - '' -
* mandos-clients.conf.xml: - '' -
* mandos-ctl.xml: - '' -
* mandos-keygen: - '' -
* mandos-keygen.xml: - '' -
* mandos-monitor.xml: - '' -
* mandos-options.xml: - '' -
* mandos.conf.xml: - '' -
* mandos.xml: - '' -
* network-hooks.d/wireless: - '' -
* plugin-helpers/mandos-client-iprouteadddel.c: - '' -
* plugin-runner.c: - '' -
* plugin-runner.xml: - '' -
* plugins.d/askpass-fifo.c: - '' -
* plugins.d/askpass-fifo.xml: - '' -
* plugins.d/mandos-client.c: - '' -
* plugins.d/mandos-client.xml: - '' -
* plugins.d/password-prompt.c: - '' -
* plugins.d/password-prompt.xml: - '' -
* plugins.d/plymouth.c: - '' -
* plugins.d/plymouth.xml: - '' -
* plugins.d/splashy.c: - '' -
* plugins.d/splashy.xml: - '' -
* plugins.d/usplash.c: - '' -
* plugins.d/usplash.xml: - '' -

Show diffs side-by-side

added added

removed removed

Lines of Context:
103
103
  char **environ;
104
104
  int envc;
105
105
  bool disabled;
106
 
  
 
106
 
107
107
  /* Variables used for running processes*/
108
108
  pid_t pid;
109
109
  int fd;
149
149
      return NULL;
150
150
    }
151
151
  }
152
 
  
 
152
 
153
153
  *new_plugin = (plugin){ .name = copy_name,
154
154
                          .argc = 1,
155
155
                          .disabled = false,
156
156
                          .next = plugin_list };
157
 
  
 
157
 
158
158
  do {
159
159
    new_plugin->argv = malloc(sizeof(char *) * 2);
160
160
  } while(new_plugin->argv == NULL and errno == EINTR);
167
167
  }
168
168
  new_plugin->argv[0] = copy_name;
169
169
  new_plugin->argv[1] = NULL;
170
 
  
 
170
 
171
171
  do {
172
172
    new_plugin->environ = malloc(sizeof(char *));
173
173
  } while(new_plugin->environ == NULL and errno == EINTR);
180
180
    return NULL;
181
181
  }
182
182
  new_plugin->environ[0] = NULL;
183
 
  
 
183
 
184
184
  /* Append the new plugin to the list */
185
185
  plugin_list = new_plugin;
186
186
  return new_plugin;
305
305
      }
306
306
      error(0, errno, "waitpid");
307
307
    }
308
 
    
 
308
 
309
309
    /* A child exited, find it in process_list */
310
310
    while(proc != NULL and proc->pid != pid){
311
311
      proc = proc->next;
337
337
/* Removes and free a plugin from the plugin list */
338
338
__attribute__((nonnull))
339
339
static void free_plugin(plugin *plugin_node){
340
 
  
 
340
 
341
341
  for(char **arg = (plugin_node->argv)+1; *arg != NULL; arg++){
342
342
    free(*arg);
343
343
  }
348
348
  }
349
349
  free(plugin_node->environ);
350
350
  free(plugin_node->buffer);
351
 
  
 
351
 
352
352
  /* Removes the plugin from the singly-linked list */
353
353
  if(plugin_node == plugin_list){
354
354
    /* First one - simple */
362
362
      }
363
363
    }
364
364
  }
365
 
  
 
365
 
366
366
  free(plugin_node);
367
367
}
368
368
 
392
392
  char **custom_argv = NULL;
393
393
  int custom_argc = 0;
394
394
  int dir_fd = -1;
395
 
  
 
395
 
396
396
  /* Establish a signal handler */
397
397
  sigemptyset(&sigchld_action.sa_mask);
398
398
  ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD);
407
407
    exitstatus = EX_OSERR;
408
408
    goto fallback;
409
409
  }
410
 
  
 
410
 
411
411
  /* The options we understand. */
412
412
  struct argp_option options[] = {
413
413
    { .name = "global-options", .key = 'g',
457
457
      .doc = "Print program version", .group = -1 },
458
458
    { .name = NULL }
459
459
  };
460
 
  
 
460
 
461
461
  __attribute__((nonnull(3)))
462
462
  error_t parse_opt(int key, char *arg, struct argp_state *state){
463
463
    errno = 0;
621
621
    }
622
622
    return errno;               /* Set to 0 at start */
623
623
  }
624
 
  
 
624
 
625
625
  /* This option parser is the same as parse_opt() above, except it
626
626
     ignores everything but the --config-file option. */
627
627
  error_t parse_opt_config_file(int key, char *arg,
658
658
    }
659
659
    return errno;
660
660
  }
661
 
  
 
661
 
662
662
  struct argp argp = { .options = options,
663
663
                       .parser = parse_opt_config_file,
664
664
                       .args_doc = "",
665
665
                       .doc = "Mandos plugin runner -- Run plugins" };
666
 
  
 
666
 
667
667
  /* Parse using parse_opt_config_file() in order to get the custom
668
668
     config file location, if any. */
669
669
  ret = argp_parse(&argp, argc, argv,
682
682
    exitstatus = EX_USAGE;
683
683
    goto fallback;
684
684
  }
685
 
  
 
685
 
686
686
  /* Reset to the normal argument parser */
687
687
  argp.parser = parse_opt;
688
 
  
 
688
 
689
689
  /* Open the configfile if available */
690
690
  if(argfile == NULL){
691
691
    conffp = fopen(AFILE, "r");
698
698
    size_t size = 0;
699
699
    const char whitespace_delims[] = " \r\t\f\v\n";
700
700
    const char comment_delim[] = "#";
701
 
    
 
701
 
702
702
    custom_argc = 1;
703
703
    custom_argv = malloc(sizeof(char*) * 2);
704
704
    if(custom_argv == NULL){
708
708
    }
709
709
    custom_argv[0] = argv[0];
710
710
    custom_argv[1] = NULL;
711
 
    
 
711
 
712
712
    /* for each line in the config file, strip whitespace and ignore
713
713
       commented text */
714
714
    while(true){
716
716
      if(sret == -1){
717
717
        break;
718
718
      }
719
 
      
 
719
 
720
720
      line = org_line;
721
721
      arg = strsep(&line, comment_delim);
722
722
      while((p = strsep(&arg, whitespace_delims)) != NULL){
730
730
          free(org_line);
731
731
          goto fallback;
732
732
        }
733
 
        
 
733
 
734
734
        custom_argc += 1;
735
735
        {
736
736
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 26)
799
799
      goto fallback;
800
800
    }
801
801
  }
802
 
  
 
802
 
803
803
  /* Parse actual command line arguments, to let them override the
804
804
     config file */
805
805
  ret = argp_parse(&argp, argc, argv,
818
818
    exitstatus = EX_USAGE;
819
819
    goto fallback;
820
820
  }
821
 
  
 
821
 
822
822
  {
823
823
    char *pluginhelperenv;
824
824
    bool bret = true;
836
836
      free(pluginhelperenv);
837
837
    }
838
838
  }
839
 
  
 
839
 
840
840
  if(debug){
841
841
    for(plugin *p = plugin_list; p != NULL; p = p->next){
842
842
      fprintf(stderr, "Plugin: %s has %d arguments\n",
850
850
      }
851
851
    }
852
852
  }
853
 
  
 
853
 
854
854
  if(getuid() == 0){
855
855
    /* Work around Debian bug #633582:
856
856
       <https://bugs.debian.org/633582> */
883
883
      }
884
884
    }
885
885
  }
886
 
  
 
886
 
887
887
  /* Lower permissions */
888
888
  ret = setgid(gid);
889
889
  if(ret == -1){
893
893
  if(ret == -1){
894
894
    error(0, errno, "setuid");
895
895
  }
896
 
  
 
896
 
897
897
  /* Open plugin directory with close_on_exec flag */
898
898
  {
899
899
    dir_fd = open(plugindir != NULL ? plugindir : PDIR, O_RDONLY |
908
908
      exitstatus = EX_UNAVAILABLE;
909
909
      goto fallback;
910
910
    }
911
 
    
 
911
 
912
912
#ifndef O_CLOEXEC
913
913
  /* Set the FD_CLOEXEC flag on the directory */
914
914
    ret = set_cloexec_flag(dir_fd);
919
919
    }
920
920
#endif  /* O_CLOEXEC */
921
921
  }
922
 
  
 
922
 
923
923
  int good_name(const struct dirent * const dirent){
924
924
    const char * const patterns[] = { ".*", "#*#", "*~", "*.dpkg-new",
925
925
                                      "*.dpkg-old", "*.dpkg-bak",
944
944
    }
945
945
    return 1;
946
946
  }
947
 
  
 
947
 
948
948
  int numplugins = scandirat(dir_fd, ".", &direntries, good_name,
949
949
                             alphasort);
950
950
  if(numplugins == -1){
953
953
    exitstatus = EX_OSERR;
954
954
    goto fallback;
955
955
  }
956
 
  
 
956
 
957
957
  FD_ZERO(&rfds_all);
958
 
  
 
958
 
959
959
  /* Read and execute any executable in the plugin directory*/
960
960
  for(int i = 0; i < numplugins; i++){
961
 
    
 
961
 
962
962
    int plugin_fd = openat(dir_fd, direntries[i]->d_name, O_RDONLY);
963
963
    if(plugin_fd == -1){
964
964
      error(0, errno, "Could not open plugin");
972
972
      free(direntries[i]);
973
973
      continue;
974
974
    }
975
 
    
 
975
 
976
976
    /* Ignore non-executable files */
977
977
    if(not S_ISREG(st.st_mode)
978
978
       or (TEMP_FAILURE_RETRY(faccessat(dir_fd, direntries[i]->d_name,
987
987
      free(direntries[i]);
988
988
      continue;
989
989
    }
990
 
    
 
990
 
991
991
    plugin *p = getplugin(direntries[i]->d_name);
992
992
    if(p == NULL){
993
993
      error(0, errno, "getplugin");
1030
1030
        }
1031
1031
      }
1032
1032
    }
1033
 
    
 
1033
 
1034
1034
    int pipefd[2];
1035
1035
#ifndef O_CLOEXEC
1036
1036
    ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
1110
1110
        error(0, errno, "sigprocmask");
1111
1111
        _exit(EX_OSERR);
1112
1112
      }
1113
 
      
 
1113
 
1114
1114
      ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
1115
1115
      if(ret == -1){
1116
1116
        error(0, errno, "dup2");
1117
1117
        _exit(EX_OSERR);
1118
1118
      }
1119
 
      
 
1119
 
1120
1120
      if(fexecve(plugin_fd, p->argv,
1121
1121
                (p->environ[0] != NULL) ? p->environ : environ) < 0){
1122
1122
        error(0, errno, "fexecve for %s/%s",
1143
1143
      goto fallback;
1144
1144
    }
1145
1145
    free(direntries[i]);
1146
 
    
 
1146
 
1147
1147
    new_plugin->pid = pid;
1148
1148
    new_plugin->fd = pipefd[0];
1149
1149
 
1162
1162
      exitstatus = EX_OSERR;
1163
1163
      goto fallback;
1164
1164
    }
1165
 
    
 
1165
 
1166
1166
    FD_SET(new_plugin->fd, &rfds_all);
1167
 
    
 
1167
 
1168
1168
    if(maxfd < new_plugin->fd){
1169
1169
      maxfd = new_plugin->fd;
1170
1170
    }
1171
1171
  }
1172
 
  
 
1172
 
1173
1173
  free(direntries);
1174
1174
  direntries = NULL;
1175
1175
  close(dir_fd);
1176
1176
  dir_fd = -1;
1177
1177
  free_plugin(getplugin(NULL));
1178
 
  
 
1178
 
1179
1179
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1180
1180
    if(p->pid != 0){
1181
1181
      break;
1186
1186
      free_plugin_list();
1187
1187
    }
1188
1188
  }
1189
 
  
 
1189
 
1190
1190
  /* Main loop while running plugins exist */
1191
1191
  while(plugin_list){
1192
1192
    fd_set rfds = rfds_all;
1205
1205
        if(not WIFEXITED(proc->status)
1206
1206
           or WEXITSTATUS(proc->status) != 0){
1207
1207
          /* Bad exit by plugin */
1208
 
          
 
1208
 
1209
1209
          if(debug){
1210
1210
            if(WIFEXITED(proc->status)){
1211
1211
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] exited with"
1220
1220
                      strsignal(WTERMSIG(proc->status)));
1221
1221
            }
1222
1222
          }
1223
 
          
 
1223
 
1224
1224
          /* Remove the plugin */
1225
1225
          FD_CLR(proc->fd, &rfds_all);
1226
 
          
 
1226
 
1227
1227
          /* Block signal while modifying process_list */
1228
1228
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
1229
1229
                                        (SIG_BLOCK,
1234
1234
            exitstatus = EX_OSERR;
1235
1235
            goto fallback;
1236
1236
          }
1237
 
          
 
1237
 
1238
1238
          plugin *next_plugin = proc->next;
1239
1239
          free_plugin(proc);
1240
1240
          proc = next_plugin;
1241
 
          
 
1241
 
1242
1242
          /* We are done modifying process list, so unblock signal */
1243
1243
          ret = (int)(TEMP_FAILURE_RETRY
1244
1244
                      (sigprocmask(SIG_UNBLOCK,
1248
1248
            exitstatus = EX_OSERR;
1249
1249
            goto fallback;
1250
1250
          }
1251
 
          
 
1251
 
1252
1252
          if(plugin_list == NULL){
1253
1253
            break;
1254
1254
          }
1255
 
          
 
1255
 
1256
1256
          continue;
1257
1257
        }
1258
 
        
 
1258
 
1259
1259
        /* This process exited nicely, so print its buffer */
1260
 
        
 
1260
 
1261
1261
        bool bret = print_out_password(proc->buffer,
1262
1262
                                       proc->buffer_length);
1263
1263
        if(not bret){
1266
1266
        }
1267
1267
        goto fallback;
1268
1268
      }
1269
 
      
 
1269
 
1270
1270
      /* This process has not completed.  Does it have any output? */
1271
1271
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1272
1272
        /* This process had nothing to say at this time */
1303
1303
      }
1304
1304
    }
1305
1305
  }
1306
 
  
1307
 
  
 
1306
 
 
1307
 
1308
1308
 fallback:
1309
 
  
 
1309
 
1310
1310
  if(plugin_list == NULL or (exitstatus != EXIT_SUCCESS
1311
1311
                             and exitstatus != EX_OK)){
1312
1312
    /* Fallback if all plugins failed, none are found or an error
1326
1326
      exitstatus = EX_IOERR;
1327
1327
    }
1328
1328
  }
1329
 
  
 
1329
 
1330
1330
  /* Restore old signal handler */
1331
1331
  ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
1332
1332
  if(ret == -1){
1333
1333
    error(0, errno, "sigaction");
1334
1334
    exitstatus = EX_OSERR;
1335
1335
  }
1336
 
  
 
1336
 
1337
1337
  if(custom_argv != NULL){
1338
1338
    for(char **arg = custom_argv+1; *arg != NULL; arg++){
1339
1339
      free(*arg);
1340
1340
    }
1341
1341
    free(custom_argv);
1342
1342
  }
1343
 
  
 
1343
 
1344
1344
  free(direntries);
1345
 
  
 
1345
 
1346
1346
  if(dir_fd != -1){
1347
1347
    close(dir_fd);
1348
1348
  }
1349
 
  
 
1349
 
1350
1350
  /* Kill the processes */
1351
1351
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1352
1352
    if(p->pid != 0){
1358
1358
      }
1359
1359
    }
1360
1360
  }
1361
 
  
 
1361
 
1362
1362
  /* Wait for any remaining child processes to terminate */
1363
1363
  do {
1364
1364
    ret = wait(NULL);
1366
1366
  if(errno != ECHILD){
1367
1367
    error(0, errno, "wait");
1368
1368
  }
1369
 
  
 
1369
 
1370
1370
  free_plugin_list();
1371
 
  
 
1371
 
1372
1372
  free(plugindir);
1373
1373
  free(pluginhelperdir);
1374
1374
  free(argfile);
1375
 
  
 
1375
 
1376
1376
  return exitstatus;
1377
1377
}