#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #define MAX_IDS 100 long long ids[MAX_IDS] = { 0 }; char nopass[MAX_IDS] = { 0 }; size_t ids_len = 0; read_ids() { FILE *f = fopen("/etc/sus", "r"); if (!f) return 0; while (!feof(f) && ids_len < 99) { char ch; fscanf(f, "%lld", &ids[ids_len++]); if (!feof(f)) { if ((ch = fgetc(f)) == 'n') nopass[ids_len] = 1; else ungetc(ch, f); } } if (ids_len == 0) return 0; return 1; } uid_nopass(uid) { int i; for (i = 0; i < ids_len; i++) { if (ids[i] == uid && nopass[i]) return 1; } return 0; } check_password(pass_buf) char *pass_buf; { struct spwd* shadow_entry; struct passwd* pa; char *p, *correct, *supplied, *salt; pa = getpwuid(getuid()); shadow_entry = getspnam(pa->pw_name); if (shadow_entry == NULL) return 2; supplied = crypt(pass_buf, shadow_entry->sp_pwdp); if (supplied == NULL) return 6; return !!strcmp(supplied, shadow_entry->sp_pwdp); } int getting_pass = 0; ask_password() { getting_pass = 1; int i = 3; while (i--) { char *pass_buf = getpass("Password: "); if (!pass_buf || *pass_buf == '\0') { i++; continue; } int res = check_password(pass_buf); memset(pass_buf, 0, strlen(pass_buf)-1); if (res == 0) { getting_pass = 0; return 1; } else fputs("Wrong password!\n", stderr); sleep(1); } getting_pass = 0; return 0; } int_handler() { if (getting_pass) return; fputs("Cancelled.\n", stderr); exit(0); } is_valid() { int i, j, groups = getgroups(0, NULL); gid_t list[groups]; int ngroups = getgroups(groups, list); if (ngroups < 0) return -1; for (i = 0; i < groups; i++) { struct group* grp; grp = getgrgid(list[i]); if (grp == NULL) return 0; for (j = 0; j < ids_len; j++) if (grp->gr_gid == ids[j]) return 1; } return 0; } main(argc, argv, envp, cstart, uid) char **argv; char **envp; uid_t uid; { cstart = 1; uid = 0; struct sigaction savealrm, saveint, savehup, savequit, saveterm; struct sigaction savetstp, savettin, savettou, savepipe; struct sigaction sa; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = int_handler; sigaction(SIGALRM, &sa, &savealrm); sigaction(SIGHUP, &sa, &savehup); sigaction(SIGINT, &sa, &saveint); sigaction(SIGPIPE, &sa, &savepipe); sigaction(SIGQUIT, &sa, &savequit); sigaction(SIGTERM, &sa, &saveterm); sigaction(SIGTSTP, &sa, &savetstp); sigaction(SIGTTIN, &sa, &savettin); sigaction(SIGTTOU, &sa, &savettou); if (argc < 2) { fprintf(stderr, "Usage: %s [- user] -- command\n", argv[0]); return 0; } if (strcmp(*(argv + cstart), "-") == 0) { if (argc < 4) { fprintf(stderr, "No command or user specified.\n"); return -1; } struct passwd *p = getpwnam(argv[2]); if (!p) { fprintf(stderr, "Invalid user specified.\n"); return -1; } uid = p->pw_uid; char **envp2 = envp; while (*envp2 != NULL) { if (strncmp(*envp2, "USER=", 5) == 0) { char *new_user_string = malloc(strlen(p->pw_name) + 6); sprintf(new_user_string, "USER=%s", p->pw_name); strcpy(*envp2, new_user_string); free(new_user_string); break; } envp2++; } cstart += 2; } if (strcmp(*(argv + cstart), "--") == 0) { struct passwd *p = getpwuid(uid); if (!p) { fprintf(stderr, "UID not found, this should not happen.\n"); return -1; } chdir(p->pw_dir); cstart++; if (argc - cstart < 1) { fprintf(stderr, "Command not found.\n"); return -1; } } if (!read_ids()) { fputs("Failed to read ids file /etc/sus.\n", stderr); return -1; } if (!is_valid()) { fputs("Not in the susers file, this incident won't be reported.\n", stderr); return -1; } if (!uid_nopass(uid)) if (ask_password() != 1) { fputs("Too many attempts.\n", stderr); return -1; } sigaction(SIGALRM, &sa, NULL); sigaction(SIGHUP, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGPIPE, &sa, NULL); sigaction(SIGQUIT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); sigaction(SIGTSTP, &sa, NULL); sigaction(SIGTTIN, &sa, NULL); sigaction(SIGTTOU, &sa, NULL); argv += cstart; setuid(uid); execvpe(argv[0], argv, envp); return 0; }