Files
mars-smart/smart_userlist.c
Mario Fetka 7ce22da3ea Userlist
2026-05-21 20:48:37 +02:00

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;
}