172 lines
3.5 KiB
C
172 lines
3.5 KiB
C
/*
|
|
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:
|
|
username<TAB>uid<TAB>gid<TAB>gecos<TAB>home<TAB>shell
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <pwd.h>
|
|
#include <security/pam_appl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
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;
|
|
}
|