Native Helper Logging
This commit is contained in:
20
README.md
20
README.md
@@ -431,6 +431,26 @@ Typical access URLs:
|
||||
|
||||
For production use, HTTPS should be preferred.
|
||||
|
||||
## Native helper logging
|
||||
|
||||
The native helper programs `check_login` and `smart_userlist` read their log
|
||||
destination and verbosity from `smart.conf` when called by the WebUI.
|
||||
|
||||
They use the same Perl frontend settings:
|
||||
|
||||
```perl
|
||||
$smart_log_path = '/var/log/mars_nwe/smart.log';
|
||||
$smart_debug_level = 'info';
|
||||
```
|
||||
|
||||
The generated `config.h` also provides fallback defaults for these values, so
|
||||
the helpers can still write useful diagnostics when they are executed manually
|
||||
or before `smart.conf` could be loaded.
|
||||
|
||||
`check_login` logs authentication and authorization results, but never logs the
|
||||
submitted password. `smart_userlist` keeps its tab-separated user-list output
|
||||
on stdout unchanged and writes diagnostics only to the configured log file.
|
||||
|
||||
## Unix user discovery helper
|
||||
|
||||
The WebUI user editor can assign a MARS_NWE bindery user to a local Unix user.
|
||||
|
||||
292
check_login.c
292
check_login.c
@@ -4,28 +4,264 @@
|
||||
Check username/password combination using PAM and require membership in
|
||||
the configured SMArT administrator Unix group.
|
||||
|
||||
Copyright 2001 Wilmer van der Gaast
|
||||
Updated for MARS_NWE SMArT group-restricted login.
|
||||
Usage:
|
||||
check_login <user> <password> <admin-group> [smart.conf]
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
Passwords are never written to the log.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
#include <security/pam_appl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
int my_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr);
|
||||
static int user_in_group(const char *username, const char *groupname);
|
||||
|
||||
|
||||
#define SMART_LOG_ERROR 0
|
||||
#define SMART_LOG_WARNING 1
|
||||
#define SMART_LOG_INFO 2
|
||||
#define SMART_LOG_DEBUG 3
|
||||
#define SMART_LOG_TRACE 4
|
||||
|
||||
typedef struct {
|
||||
char log_path[512];
|
||||
char debug_level[64];
|
||||
char admin_group[256];
|
||||
int level;
|
||||
} smart_helper_config_t;
|
||||
|
||||
static void trim(char *s)
|
||||
{
|
||||
char *p = s;
|
||||
size_t len;
|
||||
|
||||
while (*p && isspace((unsigned char)*p)) {
|
||||
p++;
|
||||
}
|
||||
|
||||
if (p != s) {
|
||||
memmove(s, p, strlen(p) + 1);
|
||||
}
|
||||
|
||||
len = strlen(s);
|
||||
while (len > 0 && isspace((unsigned char)s[len - 1])) {
|
||||
s[len - 1] = '\0';
|
||||
len--;
|
||||
}
|
||||
}
|
||||
|
||||
static void strip_quotes(char *s)
|
||||
{
|
||||
size_t len = strlen(s);
|
||||
|
||||
if (len >= 2) {
|
||||
if ((s[0] == '\'' && s[len - 1] == '\'') ||
|
||||
(s[0] == '"' && s[len - 1] == '"')) {
|
||||
memmove(s, s + 1, len - 2);
|
||||
s[len - 2] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_perl_assignment(const char *line, char *key, size_t ksz, char *val, size_t vsz)
|
||||
{
|
||||
const char *p = line;
|
||||
size_t ki = 0;
|
||||
size_t vi = 0;
|
||||
|
||||
while (*p && isspace((unsigned char)*p)) {
|
||||
p++;
|
||||
}
|
||||
if (*p != '$') {
|
||||
return 0;
|
||||
}
|
||||
p++;
|
||||
|
||||
while (*p && (isalnum((unsigned char)*p) || *p == '_')) {
|
||||
if (ki + 1 < ksz) {
|
||||
key[ki++] = *p;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
key[ki] = '\0';
|
||||
|
||||
while (*p && isspace((unsigned char)*p)) {
|
||||
p++;
|
||||
}
|
||||
if (*p != '=') {
|
||||
return 0;
|
||||
}
|
||||
p++;
|
||||
|
||||
while (*p && isspace((unsigned char)*p)) {
|
||||
p++;
|
||||
}
|
||||
|
||||
while (*p && *p != ';' && *p != '\n' && *p != '\r') {
|
||||
if (vi + 1 < vsz) {
|
||||
val[vi++] = *p;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
val[vi] = '\0';
|
||||
|
||||
trim(key);
|
||||
trim(val);
|
||||
strip_quotes(val);
|
||||
|
||||
return key[0] != '\0';
|
||||
}
|
||||
|
||||
static int parse_log_level(const char *value)
|
||||
{
|
||||
char buf[64];
|
||||
size_t i;
|
||||
|
||||
if (value == NULL || value[0] == '\0') {
|
||||
return SMART_LOG_INFO;
|
||||
}
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s", value);
|
||||
trim(buf);
|
||||
|
||||
for (i = 0; buf[i]; i++) {
|
||||
buf[i] = (char)tolower((unsigned char)buf[i]);
|
||||
}
|
||||
|
||||
if (strcmp(buf, "error") == 0 || strcmp(buf, "err") == 0 || strcmp(buf, "0") == 0) {
|
||||
return SMART_LOG_ERROR;
|
||||
}
|
||||
if (strcmp(buf, "warning") == 0 || strcmp(buf, "warn") == 0 || strcmp(buf, "1") == 0) {
|
||||
return SMART_LOG_WARNING;
|
||||
}
|
||||
if (strcmp(buf, "info") == 0 || strcmp(buf, "2") == 0) {
|
||||
return SMART_LOG_INFO;
|
||||
}
|
||||
if (strcmp(buf, "debug") == 0 || strcmp(buf, "3") == 0) {
|
||||
return SMART_LOG_DEBUG;
|
||||
}
|
||||
if (strcmp(buf, "trace") == 0 || strcmp(buf, "4") == 0) {
|
||||
return SMART_LOG_TRACE;
|
||||
}
|
||||
|
||||
return SMART_LOG_INFO;
|
||||
}
|
||||
|
||||
static const char *level_name(int level)
|
||||
{
|
||||
if (level <= SMART_LOG_ERROR) {
|
||||
return "ERROR";
|
||||
}
|
||||
if (level == SMART_LOG_WARNING) {
|
||||
return "WARNING";
|
||||
}
|
||||
if (level == SMART_LOG_DEBUG) {
|
||||
return "DEBUG";
|
||||
}
|
||||
if (level >= SMART_LOG_TRACE) {
|
||||
return "TRACE";
|
||||
}
|
||||
return "INFO";
|
||||
}
|
||||
|
||||
static void smart_cfg_init(smart_helper_config_t *cfg)
|
||||
{
|
||||
memset(cfg, 0, sizeof(*cfg));
|
||||
snprintf(cfg->log_path, sizeof(cfg->log_path), "%s", DEFAULT_SMART_LOG_PATH);
|
||||
snprintf(cfg->debug_level, sizeof(cfg->debug_level), "%s", DEFAULT_SMART_LOG_LEVEL);
|
||||
snprintf(cfg->admin_group, sizeof(cfg->admin_group), "%s", "root");
|
||||
cfg->level = parse_log_level(cfg->debug_level);
|
||||
}
|
||||
|
||||
static void smart_cfg_load(smart_helper_config_t *cfg, const char *path)
|
||||
{
|
||||
FILE *fh;
|
||||
char line[2048];
|
||||
|
||||
if (path == NULL || path[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
fh = fopen(path, "r");
|
||||
if (fh == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (fgets(line, sizeof(line), fh) != NULL) {
|
||||
char key[256];
|
||||
char val[1024];
|
||||
|
||||
if (!parse_perl_assignment(line, key, sizeof(key), val, sizeof(val))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(key, "smart_log_path") == 0) {
|
||||
snprintf(cfg->log_path, sizeof(cfg->log_path), "%s", val);
|
||||
} else if (strcmp(key, "smart_debug_level") == 0 ||
|
||||
strcmp(key, "smart_log_level") == 0) {
|
||||
snprintf(cfg->debug_level, sizeof(cfg->debug_level), "%s", val);
|
||||
cfg->level = parse_log_level(val);
|
||||
} else if (strcmp(key, "smart_admin_group") == 0) {
|
||||
snprintf(cfg->admin_group, sizeof(cfg->admin_group), "%s", val);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fh);
|
||||
}
|
||||
|
||||
static void helper_log(smart_helper_config_t *cfg, const char *component, int level, const char *fmt, ...)
|
||||
{
|
||||
FILE *fh = stderr;
|
||||
int close_fh = 0;
|
||||
time_t now;
|
||||
struct tm tm_now;
|
||||
char tbuf[64];
|
||||
va_list ap;
|
||||
|
||||
if (cfg != NULL && level > cfg->level) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cfg != NULL && cfg->log_path[0] != '\0') {
|
||||
fh = fopen(cfg->log_path, "a");
|
||||
if (fh != NULL) {
|
||||
close_fh = 1;
|
||||
} else {
|
||||
fh = stderr;
|
||||
}
|
||||
}
|
||||
|
||||
now = time(NULL);
|
||||
localtime_r(&now, &tm_now);
|
||||
strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S", &tm_now);
|
||||
|
||||
fprintf(fh, "[%s] [%s] [SMArT helper] [%s] ", tbuf, level_name(level), component);
|
||||
|
||||
va_start(ap, fmt);
|
||||
vfprintf(fh, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
fputc('\n', fh);
|
||||
fflush(fh);
|
||||
|
||||
if (close_fh) {
|
||||
fclose(fh);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static struct pam_conv conv = {
|
||||
my_conv,
|
||||
NULL
|
||||
@@ -39,10 +275,14 @@ int main( int argc, char **argv )
|
||||
pam_handle_t *pamh = NULL;
|
||||
int retval, st = 1;
|
||||
const char *admin_group;
|
||||
const char *smart_conf = DEFAULT_SMART_CONF;
|
||||
smart_helper_config_t cfg;
|
||||
|
||||
smart_cfg_init(&cfg);
|
||||
|
||||
if( argc < 4 )
|
||||
{
|
||||
fprintf( stderr, "Usage: %s <user> <password> <admin-group>\n", argv[0] );
|
||||
fprintf( stderr, "Usage: %s <user> <password> <admin-group> [smart.conf]\n", argv[0] );
|
||||
return( 3 );
|
||||
}
|
||||
|
||||
@@ -50,27 +290,50 @@ int main( int argc, char **argv )
|
||||
pass = argv[2];
|
||||
admin_group = argv[3];
|
||||
|
||||
if (argc >= 5 && argv[4] != NULL && argv[4][0] != '\0') {
|
||||
smart_conf = argv[4];
|
||||
}
|
||||
|
||||
smart_cfg_load(&cfg, smart_conf);
|
||||
|
||||
if (admin_group == NULL || admin_group[0] == '\0' || strcmp(admin_group, "-") == 0) {
|
||||
admin_group = cfg.admin_group;
|
||||
}
|
||||
|
||||
if( user == NULL || user[0] == '\0' ||
|
||||
pass == NULL ||
|
||||
admin_group == NULL || admin_group[0] == '\0' )
|
||||
{
|
||||
helper_log(&cfg, "check_login", SMART_LOG_ERROR, "invalid helper arguments");
|
||||
return( 3 );
|
||||
}
|
||||
|
||||
helper_log(&cfg, "check_login", SMART_LOG_INFO, "authentication requested user='%s' admin_group='%s'", user, admin_group);
|
||||
|
||||
retval = pam_start( "smart", user, &conv, &pamh );
|
||||
if( retval == PAM_SUCCESS )
|
||||
retval = pam_authenticate( pamh, PAM_SILENT );
|
||||
if( retval == PAM_SUCCESS )
|
||||
st = retval = pam_acct_mgmt( pamh, PAM_SILENT );
|
||||
|
||||
if( pamh != NULL && pam_end( pamh, retval ) != PAM_SUCCESS )
|
||||
if( pamh != NULL && pam_end( pamh, retval ) != PAM_SUCCESS ) {
|
||||
helper_log(&cfg, "check_login", SMART_LOG_ERROR, "pam_end failed user='%s'", user);
|
||||
return( 1 );
|
||||
}
|
||||
|
||||
if( st != PAM_SUCCESS )
|
||||
if( st != PAM_SUCCESS ) {
|
||||
helper_log(&cfg, "check_login", SMART_LOG_WARNING, "pam authentication failed user='%s' pam_status=%d", user, st);
|
||||
return( 1 );
|
||||
}
|
||||
|
||||
if( ! user_in_group( user, admin_group ) )
|
||||
helper_log(&cfg, "check_login", SMART_LOG_DEBUG, "pam authentication ok user='%s'", user);
|
||||
|
||||
if( ! user_in_group( user, admin_group ) ) {
|
||||
helper_log(&cfg, "check_login", SMART_LOG_WARNING, "group authorization failed user='%s' required_group='%s'", user, admin_group);
|
||||
return( 2 );
|
||||
}
|
||||
|
||||
helper_log(&cfg, "check_login", SMART_LOG_INFO, "login accepted user='%s' required_group='%s'", user, admin_group);
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
@@ -103,10 +366,6 @@ static int user_in_group(const char *username, const char *groupname)
|
||||
}
|
||||
|
||||
#if defined(__linux__) || defined(__GLIBC__)
|
||||
/*
|
||||
getgrouplist() asks NSS for supplementary groups, so files, LDAP, SSSD,
|
||||
NIS, etc. follow the local nsswitch.conf configuration.
|
||||
*/
|
||||
getgrouplist( username, pw->pw_gid, NULL, &ngroups );
|
||||
|
||||
if( ngroups > 0 )
|
||||
@@ -132,9 +391,6 @@ static int user_in_group(const char *username, const char *groupname)
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
Portable fallback: check the group's explicit member list.
|
||||
*/
|
||||
if( gr->gr_mem != NULL )
|
||||
{
|
||||
for( i = 0; gr->gr_mem[i] != NULL; i++ )
|
||||
@@ -165,7 +421,7 @@ int my_conv(int num_msg, const struct pam_message **msg, struct pam_response **r
|
||||
|
||||
for( i = 0; i < num_msg; i ++ )
|
||||
{
|
||||
reply[i].resp = (char *) strdup( pass ); /* Just give the password... It's all we know */
|
||||
reply[i].resp = (char *) strdup( pass );
|
||||
reply[i].resp_retcode = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
|
||||
#define LOG_PATH_DEFAULT "@MARS_NWE_LOG_DIR@/nwwebui.log"
|
||||
|
||||
#define DEFAULT_SMART_LOG_PATH "@MARS_NWE_LOG_DIR@/smart.log"
|
||||
#define DEFAULT_SMART_LOG_LEVEL "info"
|
||||
|
||||
#define LOG_LEVEL_ERROR 0
|
||||
#define LOG_LEVEL_WARNING 1
|
||||
#define LOG_LEVEL_INFO 2
|
||||
|
||||
@@ -1086,9 +1086,10 @@ sub smart_unix_users_for_select()
|
||||
my @users = ();
|
||||
my %seen = ();
|
||||
|
||||
my $helper = '/usr/lib64/mars_nwe/smart_userlist';
|
||||
my $helper = defined( $smart_userlist_path ) && $smart_userlist_path ne '' ? $smart_userlist_path : '@MARS_NWE_INSTALL_FULL_LIBEXECDIR@/smart_userlist';
|
||||
my $conf_path = defined( $smart_conf_path ) && $smart_conf_path ne '' ? $smart_conf_path : '@MARS_NWE_INSTALL_FULL_CONFDIR@/smart.conf';
|
||||
|
||||
if( -x $helper && open( my $fh, '-|', $helper ) )
|
||||
if( -x $helper && open( my $fh, '-|', $helper, '--config', $conf_path ) )
|
||||
{
|
||||
while( my $line = <$fh> )
|
||||
{
|
||||
|
||||
@@ -448,7 +448,8 @@ sub check_login_password( $$ )
|
||||
|
||||
my $admin_group = defined( $smart_admin_group ) && $smart_admin_group ne '' ? $smart_admin_group : 'root';
|
||||
|
||||
my $rc = system( $smart_check_login, $user, $pass, $admin_group );
|
||||
my $conf_path = defined( $smart_conf_path ) && $smart_conf_path ne '' ? $smart_conf_path : '@MARS_NWE_INSTALL_FULL_CONFDIR@/smart.conf';
|
||||
my $rc = system( $smart_check_login, $user, $pass, $admin_group, $conf_path );
|
||||
|
||||
if( $rc == 0 )
|
||||
{
|
||||
|
||||
@@ -64,6 +64,9 @@ $smart_log_path = '@MARS_NWE_LOG_DIR@/smart.log';
|
||||
# Path to the PAM-based login helper used for SMArT authentication.
|
||||
$smart_check_login = '@MARS_NWE_INSTALL_FULL_LIBEXECDIR@/check_login';
|
||||
|
||||
# Path to the native Unix-user enumeration helper used by the user editor.
|
||||
$smart_userlist_path = '@MARS_NWE_INSTALL_FULL_LIBEXECDIR@/smart_userlist';
|
||||
|
||||
# Unix group allowed to log in to the SMArT/nwwebui admin interface.
|
||||
#
|
||||
# Authentication is still done through PAM service "smart", but a user must
|
||||
|
||||
279
smart_userlist.c
279
smart_userlist.c
@@ -3,24 +3,262 @@
|
||||
|
||||
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.
|
||||
Usage:
|
||||
smart_userlist [--config /etc/mars_nwe/smart.conf] [--all]
|
||||
[--min-uid UID] [--pam-check] [--pam-service SERVICE]
|
||||
|
||||
Output format:
|
||||
Output format on stdout stays unchanged:
|
||||
username<TAB>uid<TAB>gid<TAB>gecos<TAB>home<TAB>shell
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <pwd.h>
|
||||
#include <security/pam_appl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
|
||||
#define SMART_LOG_ERROR 0
|
||||
#define SMART_LOG_WARNING 1
|
||||
#define SMART_LOG_INFO 2
|
||||
#define SMART_LOG_DEBUG 3
|
||||
#define SMART_LOG_TRACE 4
|
||||
|
||||
typedef struct {
|
||||
char log_path[512];
|
||||
char debug_level[64];
|
||||
char admin_group[256];
|
||||
int level;
|
||||
} smart_helper_config_t;
|
||||
|
||||
static void trim(char *s)
|
||||
{
|
||||
char *p = s;
|
||||
size_t len;
|
||||
|
||||
while (*p && isspace((unsigned char)*p)) {
|
||||
p++;
|
||||
}
|
||||
|
||||
if (p != s) {
|
||||
memmove(s, p, strlen(p) + 1);
|
||||
}
|
||||
|
||||
len = strlen(s);
|
||||
while (len > 0 && isspace((unsigned char)s[len - 1])) {
|
||||
s[len - 1] = '\0';
|
||||
len--;
|
||||
}
|
||||
}
|
||||
|
||||
static void strip_quotes(char *s)
|
||||
{
|
||||
size_t len = strlen(s);
|
||||
|
||||
if (len >= 2) {
|
||||
if ((s[0] == '\'' && s[len - 1] == '\'') ||
|
||||
(s[0] == '"' && s[len - 1] == '"')) {
|
||||
memmove(s, s + 1, len - 2);
|
||||
s[len - 2] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_perl_assignment(const char *line, char *key, size_t ksz, char *val, size_t vsz)
|
||||
{
|
||||
const char *p = line;
|
||||
size_t ki = 0;
|
||||
size_t vi = 0;
|
||||
|
||||
while (*p && isspace((unsigned char)*p)) {
|
||||
p++;
|
||||
}
|
||||
if (*p != '$') {
|
||||
return 0;
|
||||
}
|
||||
p++;
|
||||
|
||||
while (*p && (isalnum((unsigned char)*p) || *p == '_')) {
|
||||
if (ki + 1 < ksz) {
|
||||
key[ki++] = *p;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
key[ki] = '\0';
|
||||
|
||||
while (*p && isspace((unsigned char)*p)) {
|
||||
p++;
|
||||
}
|
||||
if (*p != '=') {
|
||||
return 0;
|
||||
}
|
||||
p++;
|
||||
|
||||
while (*p && isspace((unsigned char)*p)) {
|
||||
p++;
|
||||
}
|
||||
|
||||
while (*p && *p != ';' && *p != '\n' && *p != '\r') {
|
||||
if (vi + 1 < vsz) {
|
||||
val[vi++] = *p;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
val[vi] = '\0';
|
||||
|
||||
trim(key);
|
||||
trim(val);
|
||||
strip_quotes(val);
|
||||
|
||||
return key[0] != '\0';
|
||||
}
|
||||
|
||||
static int parse_log_level(const char *value)
|
||||
{
|
||||
char buf[64];
|
||||
size_t i;
|
||||
|
||||
if (value == NULL || value[0] == '\0') {
|
||||
return SMART_LOG_INFO;
|
||||
}
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s", value);
|
||||
trim(buf);
|
||||
|
||||
for (i = 0; buf[i]; i++) {
|
||||
buf[i] = (char)tolower((unsigned char)buf[i]);
|
||||
}
|
||||
|
||||
if (strcmp(buf, "error") == 0 || strcmp(buf, "err") == 0 || strcmp(buf, "0") == 0) {
|
||||
return SMART_LOG_ERROR;
|
||||
}
|
||||
if (strcmp(buf, "warning") == 0 || strcmp(buf, "warn") == 0 || strcmp(buf, "1") == 0) {
|
||||
return SMART_LOG_WARNING;
|
||||
}
|
||||
if (strcmp(buf, "info") == 0 || strcmp(buf, "2") == 0) {
|
||||
return SMART_LOG_INFO;
|
||||
}
|
||||
if (strcmp(buf, "debug") == 0 || strcmp(buf, "3") == 0) {
|
||||
return SMART_LOG_DEBUG;
|
||||
}
|
||||
if (strcmp(buf, "trace") == 0 || strcmp(buf, "4") == 0) {
|
||||
return SMART_LOG_TRACE;
|
||||
}
|
||||
|
||||
return SMART_LOG_INFO;
|
||||
}
|
||||
|
||||
static const char *level_name(int level)
|
||||
{
|
||||
if (level <= SMART_LOG_ERROR) {
|
||||
return "ERROR";
|
||||
}
|
||||
if (level == SMART_LOG_WARNING) {
|
||||
return "WARNING";
|
||||
}
|
||||
if (level == SMART_LOG_DEBUG) {
|
||||
return "DEBUG";
|
||||
}
|
||||
if (level >= SMART_LOG_TRACE) {
|
||||
return "TRACE";
|
||||
}
|
||||
return "INFO";
|
||||
}
|
||||
|
||||
static void smart_cfg_init(smart_helper_config_t *cfg)
|
||||
{
|
||||
memset(cfg, 0, sizeof(*cfg));
|
||||
snprintf(cfg->log_path, sizeof(cfg->log_path), "%s", DEFAULT_SMART_LOG_PATH);
|
||||
snprintf(cfg->debug_level, sizeof(cfg->debug_level), "%s", DEFAULT_SMART_LOG_LEVEL);
|
||||
snprintf(cfg->admin_group, sizeof(cfg->admin_group), "%s", "root");
|
||||
cfg->level = parse_log_level(cfg->debug_level);
|
||||
}
|
||||
|
||||
static void smart_cfg_load(smart_helper_config_t *cfg, const char *path)
|
||||
{
|
||||
FILE *fh;
|
||||
char line[2048];
|
||||
|
||||
if (path == NULL || path[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
fh = fopen(path, "r");
|
||||
if (fh == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (fgets(line, sizeof(line), fh) != NULL) {
|
||||
char key[256];
|
||||
char val[1024];
|
||||
|
||||
if (!parse_perl_assignment(line, key, sizeof(key), val, sizeof(val))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(key, "smart_log_path") == 0) {
|
||||
snprintf(cfg->log_path, sizeof(cfg->log_path), "%s", val);
|
||||
} else if (strcmp(key, "smart_debug_level") == 0 ||
|
||||
strcmp(key, "smart_log_level") == 0) {
|
||||
snprintf(cfg->debug_level, sizeof(cfg->debug_level), "%s", val);
|
||||
cfg->level = parse_log_level(val);
|
||||
} else if (strcmp(key, "smart_admin_group") == 0) {
|
||||
snprintf(cfg->admin_group, sizeof(cfg->admin_group), "%s", val);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fh);
|
||||
}
|
||||
|
||||
static void helper_log(smart_helper_config_t *cfg, const char *component, int level, const char *fmt, ...)
|
||||
{
|
||||
FILE *fh = stderr;
|
||||
int close_fh = 0;
|
||||
time_t now;
|
||||
struct tm tm_now;
|
||||
char tbuf[64];
|
||||
va_list ap;
|
||||
|
||||
if (cfg != NULL && level > cfg->level) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cfg != NULL && cfg->log_path[0] != '\0') {
|
||||
fh = fopen(cfg->log_path, "a");
|
||||
if (fh != NULL) {
|
||||
close_fh = 1;
|
||||
} else {
|
||||
fh = stderr;
|
||||
}
|
||||
}
|
||||
|
||||
now = time(NULL);
|
||||
localtime_r(&now, &tm_now);
|
||||
strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S", &tm_now);
|
||||
|
||||
fprintf(fh, "[%s] [%s] [SMArT helper] [%s] ", tbuf, level_name(level), component);
|
||||
|
||||
va_start(ap, fmt);
|
||||
vfprintf(fh, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
fputc('\n', fh);
|
||||
fflush(fh);
|
||||
|
||||
if (close_fh) {
|
||||
fclose(fh);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int empty_conv(int num_msg, const struct pam_message **msg,
|
||||
struct pam_response **resp, void *appdata_ptr)
|
||||
{
|
||||
@@ -103,7 +341,13 @@ int main(int argc, char **argv)
|
||||
int include_system = 0;
|
||||
int pam_check = 0;
|
||||
const char *pam_service = "smart";
|
||||
const char *smart_conf = DEFAULT_SMART_CONF;
|
||||
int i;
|
||||
unsigned long emitted = 0;
|
||||
unsigned long skipped = 0;
|
||||
smart_helper_config_t cfg;
|
||||
|
||||
smart_cfg_init(&cfg);
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "--all") == 0) {
|
||||
@@ -113,6 +357,8 @@ int main(int argc, char **argv)
|
||||
char *end = NULL;
|
||||
unsigned long v = strtoul(argv[++i], &end, 10);
|
||||
if (end == NULL || *end != '\0') {
|
||||
smart_cfg_load(&cfg, smart_conf);
|
||||
helper_log(&cfg, "smart_userlist", SMART_LOG_ERROR, "invalid --min-uid value");
|
||||
fprintf(stderr, "Invalid --min-uid value\n");
|
||||
return 2;
|
||||
}
|
||||
@@ -121,32 +367,46 @@ int main(int argc, char **argv)
|
||||
pam_check = 1;
|
||||
} else if (strcmp(argv[i], "--pam-service") == 0 && i + 1 < argc) {
|
||||
pam_service = argv[++i];
|
||||
} else if (strcmp(argv[i], "--config") == 0 && i + 1 < argc) {
|
||||
smart_conf = argv[++i];
|
||||
} else {
|
||||
smart_cfg_load(&cfg, smart_conf);
|
||||
helper_log(&cfg, "smart_userlist", SMART_LOG_ERROR, "invalid command line");
|
||||
fprintf(stderr,
|
||||
"Usage: %s [--all] [--min-uid UID] [--pam-check] [--pam-service SERVICE]\n",
|
||||
"Usage: %s [--config FILE] [--all] [--min-uid UID] [--pam-check] [--pam-service SERVICE]\n",
|
||||
argv[0]);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
smart_cfg_load(&cfg, smart_conf);
|
||||
|
||||
helper_log(&cfg, "smart_userlist", SMART_LOG_DEBUG,
|
||||
"user enumeration started include_system=%d min_uid=%lu pam_check=%d pam_service='%s'",
|
||||
include_system, (unsigned long) min_uid, pam_check, pam_service);
|
||||
|
||||
errno = 0;
|
||||
setpwent();
|
||||
|
||||
while ((pw = getpwent()) != NULL) {
|
||||
if (!is_safe_name(pw->pw_name)) {
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!include_system && pw->pw_uid < min_uid) {
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!include_system &&
|
||||
(strcmp(pw->pw_name, "root") == 0 || strcmp(pw->pw_name, "nobody") == 0)) {
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pam_check && !pam_account_ok(pam_service, pw->pw_name)) {
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -158,14 +418,21 @@ int main(int argc, char **argv)
|
||||
putchar('\t');
|
||||
print_sanitized(pw->pw_shell);
|
||||
putchar('\n');
|
||||
|
||||
emitted++;
|
||||
}
|
||||
|
||||
endpwent();
|
||||
|
||||
if (errno != 0) {
|
||||
helper_log(&cfg, "smart_userlist", SMART_LOG_ERROR, "getpwent failed: %s", strerror(errno));
|
||||
perror("getpwent");
|
||||
return 1;
|
||||
}
|
||||
|
||||
helper_log(&cfg, "smart_userlist", SMART_LOG_DEBUG,
|
||||
"user enumeration finished emitted=%lu skipped=%lu",
|
||||
emitted, skipped);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user