static int external_passwd_quality (krb5_context context, krb5_principal principal, krb5_data *pwd, const char *opaque, char *message, size_t length) { krb5_error_code ret; const char *program; char *p; pid_t child; int status; char reply[1024]; FILE *in = NULL, *out = NULL, *error = NULL; if (memchr(pwd->data, '\n', pwd->length) != NULL) { snprintf(message, length, "password contains newline, " "not valid for external test"); return 1; } program = krb5_config_get_string(context, NULL, "password_quality", "external_program", NULL); if (program == NULL) { snprintf(message, length, "external password quality " "program not configured"); return 1; } ret = krb5_unparse_name(context, principal, &p); if (ret) { strlcpy(message, "out of memory", length); return 1; } child = pipe_execv(&in, &out, &error, program, program, p, NULL); if (child < 0) { snprintf(message, length, "external password quality " "program failed to execute for principal %s", p); free(p); return 1; } fprintf(in, "principal: %s\n" "new-password: %.*s\n" "end\n", p, (int)pwd->length, (char *)pwd->data); fclose(in); if (fgets(reply, sizeof(reply), out) == NULL) { if (fgets(reply, sizeof(reply), error) == NULL) { snprintf(message, length, "external password quality " "program failed without error"); } else { reply[strcspn(reply, "\n")] = '\0'; snprintf(message, length, "External password quality " "program failed: %s", reply); } fclose(out); fclose(error); wait_for_process(child); return 1; } reply[strcspn(reply, "\n")] = '\0'; fclose(out); fclose(error); status = wait_for_process(child); if (SE_IS_ERROR(status) || SE_PROCSTATUS(status) != 0) { snprintf(message, length, "external program failed: %s", reply); free(p); return 1; } if (strcmp(reply, "APPROVED") != 0) { snprintf(message, length, "%s", reply); free(p); return 1; } free(p); return 0; }
ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL roken_detach_prep(int argc, char **argv, char *special_arg) { pid_t child; char buf[1]; ssize_t bytes; int status; pipefds[0] = -1; pipefds[1] = -1; #ifdef WIN32 if (_pipe(pipefds, 4, O_BINARY) == -1) err(1, "failed to setup to detach daemon (_pipe failed)"); #else if (pipe(pipefds) == -1) err(1, "failed to setup to detach daemon (pipe failed)"); #endif #ifndef WIN32 fflush(stdout); child = fork(); #else { intptr_t child_handle; int write_side; size_t i; char *fildes; char **new_argv; new_argv = calloc(argc + 2, sizeof(*new_argv)); if (new_argv == NULL) err(1, "Out of memory"); write_side = _dup(pipefds[1]); /* The new fd will be inherited */ if (write_side == -1) err(1, "Out of memory"); if (asprintf(&fildes, "%d", write_side) == -1 || fildes == NULL) err(1, "failed to setup to detach daemon (_dup failed)"); new_argv[0] = argv[0]; new_argv[1] = special_arg; new_argv[2] = fildes; for (i = 1; argv[i] != NULL; i++) new_argv[i + 1] = argv[i]; new_argv[argc + 2] = NULL; _flushall(); child_handle = spawnvp(_P_NOWAIT, argv[0], new_argv); if (child_handle == -1) child = (pid_t)-1; else child = GetProcessId((HANDLE)child_handle); } #endif if (child == (pid_t)-1) err(1, "failed to setup to fork daemon (fork failed)"); #ifndef WIN32 if (child == 0) { int fd; (void) close(pipefds[0]); pipefds[0] = -1; /* * Keep stdout/stderr for now so output and errors prior to * detach_finish() can be seen by the user. */ fd = open(_PATH_DEVNULL, O_RDWR, 0); if (fd == -1) err(1, "failed to open /dev/null"); (void) dup2(fd, STDIN_FILENO); if (fd > STDERR_FILENO) (void) close(fd); return; } #endif (void) close(pipefds[1]); pipefds[1] = -1; do { bytes = read(pipefds[0], buf, sizeof(buf)); } while (bytes == -1 && errno == EINTR); (void) close(pipefds[0]); pipefds[0] = -1; if (bytes == -1) { /* * No need to wait for the process. We've killed it. If it * doesn't want to exit, we'd have to wait potentially forever, * but we want to indicate failure to the user as soon as * possible. A wait with timeout would end the same way * (attempting to kill the process). */ err(1, "failed to setup daemon child (read from child pipe)"); } if (bytes == 0) { warnx("daemon child preparation failed, waiting for child"); status = wait_for_process(child); if (SE_IS_ERROR(status) || SE_PROCSTATUS(status) != 0) errx(SE_PROCSTATUS(status), "daemon child preparation failed (child exited)"); } _exit(0); }