/* SMArT List local/NSS users for the WebUI. PAM itself cannot enumerate users. User enumeration is done through NSS getpwent(), so /etc/nsswitch.conf is honored (files, sss, ldap, nis, ...). Optionally, each user can be checked with pam_acct_mgmt() against the "smart" PAM service. Output format: usernameuidgidgecoshomeshell */ #include #include #include #include #include #include #include #include static int empty_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) { struct pam_response *reply; (void) msg; (void) appdata_ptr; if (num_msg <= 0) { *resp = NULL; return PAM_SUCCESS; } reply = calloc((size_t) num_msg, sizeof(struct pam_response)); if (reply == NULL) { return PAM_BUF_ERR; } *resp = reply; return PAM_SUCCESS; } static int pam_account_ok(const char *service, const char *user) { struct pam_conv conv = { empty_conv, NULL }; pam_handle_t *pamh = NULL; int rc; rc = pam_start(service, user, &conv, &pamh); if (rc == PAM_SUCCESS) { rc = pam_acct_mgmt(pamh, PAM_SILENT); } if (pamh != NULL) { pam_end(pamh, rc); } return rc == PAM_SUCCESS; } static void print_sanitized(const char *s) { const unsigned char *p = (const unsigned char *) (s != NULL ? s : ""); while (*p != '\0') { if (*p == '\t' || *p == '\n' || *p == '\r') { putchar(' '); } else { putchar((int) *p); } p++; } } static int is_safe_name(const char *s) { const unsigned char *p = (const unsigned char *) s; if (s == NULL || *s == '\0') { return 0; } while (*p != '\0') { if (!( (*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '_' || *p == '-' || *p == '.' )) { return 0; } p++; } return 1; } int main(int argc, char **argv) { struct passwd *pw; uid_t min_uid = 1000; int include_system = 0; int pam_check = 0; const char *pam_service = "smart"; int i; for (i = 1; i < argc; i++) { if (strcmp(argv[i], "--all") == 0) { include_system = 1; min_uid = 0; } else if (strcmp(argv[i], "--min-uid") == 0 && i + 1 < argc) { char *end = NULL; unsigned long v = strtoul(argv[++i], &end, 10); if (end == NULL || *end != '\0') { fprintf(stderr, "Invalid --min-uid value\n"); return 2; } min_uid = (uid_t) v; } else if (strcmp(argv[i], "--pam-check") == 0) { pam_check = 1; } else if (strcmp(argv[i], "--pam-service") == 0 && i + 1 < argc) { pam_service = argv[++i]; } else { fprintf(stderr, "Usage: %s [--all] [--min-uid UID] [--pam-check] [--pam-service SERVICE]\n", argv[0]); return 2; } } errno = 0; setpwent(); while ((pw = getpwent()) != NULL) { if (!is_safe_name(pw->pw_name)) { continue; } if (!include_system && pw->pw_uid < min_uid) { continue; } if (!include_system && (strcmp(pw->pw_name, "root") == 0 || strcmp(pw->pw_name, "nobody") == 0)) { continue; } if (pam_check && !pam_account_ok(pam_service, pw->pw_name)) { continue; } print_sanitized(pw->pw_name); printf("\t%lu\t%lu\t", (unsigned long) pw->pw_uid, (unsigned long) pw->pw_gid); print_sanitized(pw->pw_gecos); putchar('\t'); print_sanitized(pw->pw_dir); putchar('\t'); print_sanitized(pw->pw_shell); putchar('\n'); } endpwent(); if (errno != 0) { perror("getpwent"); return 1; } return 0; }