From c10b8593e8f9bf6438c26f9b7877be4ed4ac032a Mon Sep 17 00:00:00 2001 From: bjk Date: Sat, 30 Jul 2005 13:00:15 +0000 Subject: [PATCH] Updates for 2.1. Fixed memory leak in login.c. --- src/modules/Makefile.am | 20 ++ src/modules/login.c | 850 ++++++++++++++++++++++++++++++++++++++++++++++++ src/modules/login.h | 104 ++++++ src/modules/mail.c | 335 +++++++++++++++++++ src/modules/mail.h | 70 ++++ src/modules/passwd.c | 405 +++++++++++++++++++++++ src/modules/passwd.h | 63 ++++ 7 files changed, 1847 insertions(+) create mode 100644 src/modules/Makefile.am create mode 100644 src/modules/login.c create mode 100644 src/modules/login.h create mode 100644 src/modules/mail.c create mode 100644 src/modules/mail.h create mode 100644 src/modules/passwd.c create mode 100644 src/modules/passwd.h diff --git a/src/modules/Makefile.am b/src/modules/Makefile.am new file mode 100644 index 0000000..6b9381e --- /dev/null +++ b/src/modules/Makefile.am @@ -0,0 +1,20 @@ +pkglib_LTLIBRARIES = login.la mail.la passwd.la +login_la_SOURCES = login.c login.h +if KVM +login_la_LDFLAGS = -module -avoid-version -lkvm +else +login_la_LDFLAGS = -module -avoid-version +endif +mail_la_SOURCES = mail.c mail.h +mail_la_LDFLAGS = -module -avoid-version +passwd_la_SOURCES = passwd.c passwd.h +passwd_la_LDFLAGS = -module -avoid-version + +install-data-hook: + rm -f $(pkglibdir)/*.la + +uninstall-hook: + rm -f $(pkglibdir)/*.so + +indent: + indent *.[ch] && rm -f *.[ch]~ diff --git a/src/modules/login.c b/src/modules/login.c new file mode 100644 index 0000000..e847a4d --- /dev/null +++ b/src/modules/login.c @@ -0,0 +1,850 @@ +/* $Id: login.c,v 1.1 2005-07-30 13:00:15 bjk Exp $ */ +/* + Copyright (C) 2001-2005 Ben Kibbey + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "login.h" + +#ifndef HAVE_STRSEP +#include "strsep.c" +#endif + +#ifndef HAVE_ERR_H +#include "err.c" +#endif + +void ui_module_init(int *chainable) +{ +#ifdef DEBUG + fprintf(stderr, "%s: ui_module_init()\n", __FILE__); +#endif + + *chainable = 0; + time(&now); + return; +} + +void ui_module_exit() +{ +#ifdef DEBUG + fprintf(stderr, "%s: ui_module_exit()\n", __FILE__); +#endif + +#ifdef HAVE_PROCFS + if (procdir) + closedir(procdir); +#endif + +#ifdef HAVE_KVM_H + if (kd) + kvm_close(kd); +#endif + + if (lastlogfd) + close(lastlogfd); + + return; +} + +#ifndef UTMPX_FORMAT +/* This is for *BSD (login process id). */ +#ifdef BSD_KVM +char *ui_module_pid(uid_t uid, int multi) +{ + static int firstrun; + static char line[LINE_MAX]; + int cnt, i; + pid_t pid = 0; + char errbuf[LINE_MAX]; + struct kinfo_proc *kp; + + line[0] = '\0'; + + if (!kd && firstrun) + return "!"; + + if (!kd) { + firstrun = 1; + +#ifdef __NetBSD__ + if ((kd = kvm_openfiles(NULL, NULL, NULL, +#else + if ((kd = kvm_openfiles(_PATH_DEVNULL, _PATH_DEVNULL, _PATH_DEVNULL, +#endif + O_RDONLY, errbuf)) == NULL) { + warnx("%s", errbuf); + return "!"; + } + } + + if ((kp = kvm_getprocs(kd, KERN_PROC_UID, uid, &cnt)) == NULL) { + warnx("kvm_getprocs(): %s", kvm_geterr(kd)); + return "!"; + } + + for (i = 0; i < cnt; i++) { + char buf[32]; + +#if __FreeBSD_version < 500000 + if (kp[i].kp_eproc.e_flag & EPROC_SLEADER && kp[i].kp_eproc.e_tdev != + -1) { + pid = kp[i].kp_eproc.e_ppid; + /* + * pid = kp[i].kp_proc.p_pid; + */ + + if (pid == 1) + continue; +#else + if (kp[i].ki_kiflag & KI_SLEADER && kp[i].ki_tdev != -1) { + pid = kp[i].ki_pid; +#endif + snprintf(buf, sizeof(buf), "%i%c", pid, multi); + strncat(line, buf, sizeof(line)); + } + } + + if (line[0] == '\0') + return "!"; + + line[strlen(line) - 1] = '\0'; + return line; +} + +/* This is for Linux and Solaris. */ +#elif defined(HAVE_PROCFS) +#include +#include + +#ifdef HAVE_DIRENT_H +#include +#endif + +#ifdef __svr4__ +#include +#include +#endif + +char *ui_module_pid(uid_t uid, int multi) +{ + static int firstrun; + struct dirent *ent; + struct stat st; + static char line[LINE_MAX]; + pid_t *pids = 0; + int pid_index = 0; + +#ifdef __svr4__ + int fd; + struct pstatus pstat; +#else + FILE *fp; +#endif + + line[0] = '\0'; + + if (!procdir && firstrun) + return "!"; + + if (!procdir) { + firstrun = 1; + + if ((procdir = opendir("/proc")) == NULL) { + warn("%s", "/proc"); + return "!"; + } + } + + rewinddir(procdir); + + again: + while ((ent = readdir(procdir)) != NULL) { + pid_t pid = -1; + char filename[FILENAME_MAX]; + char buf[LINE_MAX]; + int i; + +#ifndef __svr4__ + char *t; +#endif + + if (!isdigit((unsigned char) *ent->d_name)) + continue; + +#ifdef __linux__ + snprintf(filename, sizeof(filename), "/proc/%s/stat", ent->d_name); +#else + snprintf(filename, sizeof(filename), "/proc/%s/status", ent->d_name); +#endif + + if (stat(filename, &st) == -1) + continue; + + /* + * The current user owns this file (process id). + */ + if (st.st_uid == uid) { +#ifdef __svr4__ + if ((fd = open(filename, O_RDONLY)) == -1) + continue; + + if (pread(fd, &pstat, sizeof(struct pstatus), 0) != + sizeof(struct pstatus)) { + close(fd); + continue; + } + + pid = pstat.pr_ppid; + close(fd); +#else + if ((fp = fopen(filename, "r")) == NULL) + continue; + + if ((t = fgets(buf, sizeof(buf), fp)) == NULL) { + fclose(fp); + continue; + } + +#ifdef __linux__ + if ((i = sscanf(buf, "%*i %*s %*c %*i %*i %i", &pid)) < 1) { +#endif +/* +#else + if ((i = sscanf(buf, "%*s %*i %li", &ppid)) < 1) { +#endif +*/ + fclose(fp); + continue; + } + + fclose(fp); +#endif + + /* + * Skip duplicate pids. + */ + for (i = 0; i < pid_index; i++) { + if (pids[i] == pid) + goto again; + } + + snprintf(buf, sizeof(buf), "%li%c", (unsigned long) pid, multi); + strncat(line, buf, sizeof(line)); + + if ((pids = + realloc(pids, (pid_index + 2) * sizeof(pid_t *))) == NULL) { + warn("realloc()"); + continue; + } + + pids[pid_index++] = pid; + } + } + + if (pid_index) + free(pids); + + if (line[0] == '\0') + return "!"; + + line[strlen(line) - 1] = '\0'; + return line; +} +#else +/* Unsupported OS. */ +char *ui_module_pid(uid_t uid, int multi) +{ + return "!"; +} +#endif +#endif + +/* Break up the last login string into sections and add the sections to the + * output string array if needed. */ +static void last_strings(char *str) +{ + int i = 0; + char *buf; + const char *line, *host, *when; + + line = host = when = (str) ? "-" : "!"; + + while ((buf = strsep(&str, ",")) != NULL) { + if (!buf[0]) + continue; + + switch (i++) { + case 0: + line = buf; + break; + case 1: + host = buf; + break; + case 2: + when = buf; + break; + default: + break; + } + } + + for (i = 0; i < strlen(last_options); i++) { + switch (last_options[i]) { + case 'y': + add_string(&strings, line); + break; + case 'h': + add_string(&strings, host); + break; + case 't': + add_string(&strings, when); + break; + case 'a': + add_string(&strings, line); + add_string(&strings, host); + add_string(&strings, when); + default: + break; + } + } + + return; +} + +/* Get the lastlog structure from the lastlog file. */ +static char *lastlogin(uid_t uid, char *tf) +{ + int count; + long offset; + static char buf[LINE_MAX]; + +#ifdef __NetBSD__ +#ifdef UTMPX_FORMAT + char tmp[64], htmp[UTX_HOSTSIZE + 1]; +#else + char tmp[64], htmp[UT_HOSTSIZE + 1]; +#endif +#else + char tmp[64], htmp[UT_HOSTSIZE + 1]; +#endif + struct lastlog last; + + buf[0] = tmp[0] = htmp[0] = '\0'; + + if (lastlogfd < 0) + return NULL; + + if (!lastlogfd) { + if ((lastlogfd = open(_PATH_LASTLOG, O_RDONLY)) == -1) { + warn("%s", _PATH_LASTLOG); + return NULL; + } + } + + offset = (long) uid *sizeof(struct lastlog); + + if (lseek(lastlogfd, offset, SEEK_SET) == -1) { + warn("%s", _PATH_LASTLOG); + return NULL; + } + + if ((count = read(lastlogfd, &last, sizeof(struct lastlog))) != + sizeof(struct lastlog)) { + if (count == -1) + warn("%s", _PATH_LASTLOG); + + return NULL; + } + + if (last.ll_line[0] == '\0') + strncpy(buf, "!", sizeof(buf)); + else + strncpy(buf, last.ll_line, sizeof(buf)); + + strncat(buf, ",", sizeof(buf)); + strncpy(htmp, last.ll_host, sizeof(htmp)); + +#ifdef __NetBSD__ +#ifdef UTMPX_FORMAT + htmp[UTX_HOSTSIZE] = '\0'; +#else + htmp[UT_HOSTSIZE] = '\0'; +#endif +#else + htmp[UT_HOSTSIZE] = '\0'; +#endif + + if (htmp[0] && isalnum((unsigned char) htmp[0])) + strncat(buf, htmp, sizeof(buf)); + else { + /* + * If a users tty is tty1-n, it must be a console + * * login. + */ + if (last.ll_line[0] && isdigit((unsigned char) last.ll_line[3])) + strncat(buf, "-", sizeof(buf)); + else + strncat(buf, "!", sizeof(buf)); + } + + strncat(buf, ",", sizeof(buf)); + + if (last.ll_time) + strncat(buf, stamp(last.ll_time, tf), sizeof(buf)); + else + strncat(buf, "!", sizeof(buf)); + + return buf; +} + +/* This will return an array of utmp structures if a user is logged in, NULL + * otherwise. We'll try to keep the utmp file descriptor open if possible to + * speed things up a bit. */ +static UTMP **get_utmp(const char *user) +{ + UTMP **logins = NULL; + +#ifdef UTMPX_FORMAT + UTMP *u; +#else + UTMP u; + int count; + static int fd; + + if (fd < 0) + return NULL; + + if (!fd) { + if ((fd = open(_PATH_UTMP, O_RDONLY)) == -1) { + warn("%s", _PATH_UTMP); + return NULL; + } + } +#endif + + login_count = 0; + +#ifdef UTMPX_FORMAT + setutent(); + + while ((u = getutxent()) != NULL) { + if (strcmp(u->ut_name, user) == 0) { +#else + lseek(fd, 0, SEEK_SET); + + while ((count = read(fd, &u, sizeof(UTMP))) == sizeof(UTMP)) { + if (strcmp(u.ut_name, user) == 0) { +#endif + if ((logins = realloc(logins, + (login_count + 2) * sizeof(UTMP *))) == + NULL) { + warn("realloc()"); + return NULL; + } + + if ((logins[login_count] = malloc(sizeof(UTMP))) == NULL) { + warn("malloc()"); + return NULL; + } + +#ifdef UTMPX_FORMAT +#ifdef __NetBSD__ + strncpy(logins[login_count]->ut_name, u->ut_name, UTX_NAMESIZE); + strncpy(logins[login_count]->ut_line, u->ut_line, UTX_LINESIZE); + strncpy(logins[login_count]->ut_host, u->ut_host, UTX_HOSTSIZE); + logins[login_count]->ut_pid = u->ut_pid; +#else + strncpy(logins[login_count]->ut_name, u->ut_name, UT_NAMESIZE); + strncpy(logins[login_count]->ut_line, u->ut_line, UT_LINESIZE); + strncpy(logins[login_count]->ut_host, u->ut_host, UT_HOSTSIZE); + logins[login_count]->ut_tv.tv_sec = u->ut_tv.tv_sec; + logins[login_count]->ut_pid = u->ut_pid; +#endif +#else + strncpy(logins[login_count]->ut_name, u.ut_name, UT_NAMESIZE); + strncpy(logins[login_count]->ut_line, u.ut_line, UT_LINESIZE); + strncpy(logins[login_count]->ut_host, u.ut_host, UT_HOSTSIZE); + logins[login_count]->ut_time = u.ut_time; +#endif + logins[++login_count] = NULL; + } + } + + return logins; +} + +/* The 'mesg' status of the logged in user. */ +static char *msgstat(UTMP ** u, int multi) +{ + static char line[LINE_MAX]; + int i; + + line[0] = '\0'; + + for (i = 0; i < login_count; i++) { + char filename[FILENAME_MAX]; + struct stat st; + char m[2] = { multi, '\0' }; + + snprintf(filename, sizeof(filename), "%s%s", _PATH_DEV, u[i]->ut_line); + + if (stat(filename, &st) == -1) + strncat(line, "!", sizeof(line)); + else + strncat(line, + (st.st_mode & S_IWGRP || st.st_mode & S_IWOTH) ? "1" : "0", + sizeof(line)); + + strncat(line, m, sizeof(line)); + } + + if (line[0] == '\0') + return "!"; + + line[strlen(line) - 1] = '\0'; + return line; +} + +/* Returns the users idle time in seconds. */ +static char *idle(UTMP ** u, int multi) +{ + static char line[LINE_MAX]; + time_t t; + struct stat st; + int i; + + line[0] = '\0'; + + for (i = 0; i < login_count; i++) { + char buf[FILENAME_MAX]; + char m[2] = { multi, '\0' }; + + snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, u[i]->ut_line); + + if (stat(buf, &st) == -1) { + strncat(line, "!", sizeof(line)); + strncat(line, m, sizeof(line)); + continue; + } + +#ifdef UTMPX_FORMAT + if (u[i]->ut_tv.tv_sec > st.st_atime) { +#else + if (u[i]->ut_time > st.st_atime) { +#endif + strncat(line, "-", sizeof(line)); + strncat(line, m, sizeof(line)); + continue; + } + + t = st.st_atime; + +#ifdef UTMPX_FORMAT + if (t < u[i]->ut_tv.tv_sec) + t = u[i]->ut_tv.tv_sec; +#else + if (t < u[i]->ut_time) + t = u[i]->ut_time; +#endif + + snprintf(buf, sizeof(buf), "%lu", (now - t <= 0) ? 0 : (now - t) / 60); + strncat(line, buf, sizeof(line)); + strncat(line, m, sizeof(line)); + } + + if (line[0] == '\0') + return "!"; + + line[strlen(line) - 1] = '\0'; + return line; +} + +/* This is output if the -h command line option is passed to the main program. + */ +void ui_module_help() +{ +#ifdef DEBUG + fprintf(stderr, "%s: ui_module_help()\n", __FILE__); +#endif + + printf(" Login information [-L (-%s)]:\n", LOGIN_OPTION_ORDER); + printf("\t-y tty\t\t\t\t"); + printf("-m message status\n"); + printf("\t-t login time stamp\t\t"); + printf("-d duration in minutes\n"); + printf("\t-h hostname\t\t\t"); + printf("-i minutes idle\n"); + printf("\t-p login process id\n"); + printf("\t-l lastlog information" + " (any of tt[y],[h]ostname,[t]ime, or [a]ll)\n\n"); + return; +} + +/* This is the equivalent to main() only without argc and argv available. */ +int ui_module_exec(char ***s, const struct passwd *pw, const int multi, + const int verbose, char *tf) +{ + char *p = options; + UTMP **u = NULL, **up; + char buf[255]; + + login_count = 0; + u = get_utmp(pw->pw_name); + strings = *s; + + while (*p) { + char line[LINE_MAX] = { '\0' }; + int i; + char m[2] = { multi, '\0' }; +#ifdef __NetBSD__ +#ifdef UTMPX_FORMAT + char htmp[UTX_HOSTSIZE + 1]; +#else + char htmp[UT_HOSTSIZE + 1]; +#endif +#else + char htmp[UT_HOSTSIZE + 1]; +#endif + + switch (*p) { + case 'i': + add_string(&strings, (u) ? idle(u, multi) : "!"); + break; + case 'l': + last_strings(lastlogin(pw->pw_uid, tf)); + break; + case 'h': + for (i = 0; i < login_count; i++) { + if (u[i]->ut_host[0] + && isalnum((unsigned char) u[i]->ut_host[0])) { + strcpy(htmp, u[i]->ut_host); + htmp[sizeof(htmp) - 1] = '\0'; + strncat(line, htmp, sizeof(line)); + } + else { + /* + * If a users tty is tty1-n, it must be a console + * * login. + */ + if (u[i]->ut_line[0] + && isdigit((unsigned char) u[i]->ut_line[3])) + strncat(line, "-", sizeof(line)); + else + strncat(line, "!", sizeof(line)); + } + + strncat(line, m, sizeof(line)); + } + + if (line[0] == '\0') + strncpy(line, "!", sizeof(line)); + else + line[strlen(line) - 1] = '\0'; + + add_string(&strings, line); + break; + case 'y': + for (i = 0; i < login_count; i++) { + if (u[i]->ut_line[0]) + strncat(line, u[i]->ut_line, sizeof(line)); + else + strncat(line, "!", sizeof(line)); + + strncat(line, m, sizeof(line)); + } + + if (line[0] == '\0') + strncpy(line, "!", sizeof(line)); + else + line[strlen(line) - 1] = '\0'; + + add_string(&strings, line); + break; + case 'm': + add_string(&strings, msgstat(u, multi)); + break; + case 't': + for (i = 0; i < login_count; i++) { +#ifdef UTMPX_FORMAT + strncat(line, stamp(u[i]->ut_tv.tv_sec, tf), sizeof(line)); +#else + strncat(line, stamp(u[i]->ut_time, tf), sizeof(line)); +#endif + strncat(line, m, sizeof(line)); + } + + if (line[0] == '\0') + strncpy(line, "!", sizeof(line)); + else + line[strlen(line) - 1] = '\0'; + + add_string(&strings, line); + break; + case 'd': + for (i = 0; i < login_count; i++) { +#ifdef UTMPX_FORMAT + if ((now - u[i]->ut_tv.tv_sec) > 60) { + snprintf(buf, sizeof(buf), "%lu", + ((now - u[i]->ut_tv.tv_sec) / 60)); +#else + if ((now - u[i]->ut_time) > 60) { + snprintf(buf, sizeof(buf), "%lu", + ((now - u[i]->ut_time) / 60)); +#endif + strncat(line, buf, sizeof(line)); + } + else + strncat(line, "-", sizeof(line)); + + strncat(line, m, sizeof(line)); + } + + if (line[0] == '\0') + strncpy(line, "!", sizeof(line)); + else + line[strlen(line) - 1] = '\0'; + + add_string(&strings, line); + break; + case 'p': +#ifdef UTMPX_FORMAT + for (i = 0; i < login_count; i++) { + if (u[i]->ut_pid) { + snprintf(buf, sizeof(buf), "%li", (long) u[i]->ut_pid); + strncat(line, buf, sizeof(line)); + } + else + strncat(line, "!", sizeof(line)); + + strncat(line, m, sizeof(line)); + } + + if (line[0] == '\0') + strncpy(line, "!", sizeof(line)); + else + line[strlen(line) - 1] = '\0'; + + add_string(&strings, line); +#else + add_string(&strings, (u) ? ui_module_pid(pw->pw_uid, multi) : "!"); +#endif + break; + default: + break; + } + + p++; + } + + if (login_count) { + for (up = u; *up; *up++) + free(*up); + + free(u); + } + + *s = strings; + return EXIT_SUCCESS; +} + +/* See if the last login options (-l) are valid. */ +static int parse_last_options(const char *args) +{ + int i = 0; + + for (i = 0; i < strlen(args); i++) { + switch (args[i]) { + case 'y': + case 'h': + case 't': + case 'a': + break; + default: + return 1; + } + } + + return 0; +} + +char *ui_module_options_init(char **defaults) +{ + *defaults = "L"; + return LOGIN_OPTION_STRING; +} + +/* Check module option validity. */ +int ui_module_options(int argc, char **argv) +{ + int opt; + char *p = options; + +#ifdef DEBUG + fprintf(stderr, "%s: ui_module_options()\n", __FILE__); +#endif + + while ((opt = getopt(argc, argv, LOGIN_OPTION_STRING)) != -1) { + switch (opt) { + case 'l': + last_options = optarg; + break; + case 'L': + case 'p': + case 'd': + case 'i': + case 'm': + case 'y': + case 'h': + case 't': + break; + case '?': + warnx("login: invalid option -- %c", optopt); + default: + return 1; + } + + if (opt == 'l') { + if (parse_last_options(last_options)) + return 1; + } + + if (opt == 'L') { + strncpy(options, LOGIN_OPTION_ORDER, sizeof(options)); + last_options = "a"; + break; + } + + *p++ = opt; + *p = '\0'; + } + + return 0; +} diff --git a/src/modules/login.h b/src/modules/login.h new file mode 100644 index 0000000..b43169b --- /dev/null +++ b/src/modules/login.h @@ -0,0 +1,104 @@ +/* $Id: login.h,v 1.1 2005-07-30 13:00:15 bjk Exp $ */ +/* + Copyright (C) 2001-2005 Ben Kibbey + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef LOGIN_H +#define LOGIN_H + +#ifdef HAVE_LIMITS_H +#include +#endif + +#ifdef HAVE_ERR_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_PATHS_H +#include +#else +#ifndef _PATH_DEV +#define _PATH_DEV "/dev/" +#endif +#endif + +#ifdef HAVE_LASTLOG_H +#include +#endif + +#ifndef _PATH_LASTLOG +#define _PATH_LASTLOG "/var/adm/lastlog" +#endif + +#ifdef UTMPX_FORMAT +#include +#ifndef UT_HOSTSIZE +#define UT_HOSTSIZE 256 +#define UT_LINESIZE 32 +#define UT_NAMESIZE 32 +#endif +typedef struct utmpx UTMP; +#else +typedef struct utmp UTMP; +#endif + +#define setutent setutxent +#define ut_xtime ut_tv.tv_sec + +#define TIMEBUFSIZE 64 + +#ifdef HAVE_PROCFS +#ifdef HAVE_DIRENT_H +#include +DIR *procdir; +#endif +#endif + +#ifdef HAVE_KVM_H +#include +#include +#include +#include +static kvm_t *kd; +#endif + +#define LOGIN_OPTION_ORDER "pdimyhtl" +#define LOGIN_OPTION_STRING "Lpdimyhtl:" + +static char options[9]; /* NULL terminated. */ +static char *last_options; +static char **strings; +static int lastlogfd; +static time_t now; +static int login_count; + +char *parentpid(uid_t, int, char *); +void add_string(char ***, const char *); +char *stamp(time_t, const char *); + +#ifdef WITH_DMALLOC +#include +#endif + +#endif diff --git a/src/modules/mail.c b/src/modules/mail.c new file mode 100644 index 0000000..3295993 --- /dev/null +++ b/src/modules/mail.c @@ -0,0 +1,335 @@ +/* $Id: mail.c,v 1.1 2005-07-30 13:00:15 bjk Exp $ */ +/* + Copyright (C) 2001-2005 Ben Kibbey + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "mail.h" + +#ifndef HAVE_ERR_H +#include "err.c" +#endif + +#ifndef HAVE_STRSEP +#include "strsep.c" +#endif + +void ui_module_init(int *chainable) +{ +#ifdef DEBUG + fprintf(stderr, "%s: ui_module_init()\n", __FILE__); +#endif + + *chainable = 0; + return; +} + +void ui_module_exit() +{ +#ifdef DEBUG + fprintf(stderr, "%s: ui_module_exit()\n", __FILE__); +#endif + + if (aliasbuf) + munmap(aliasbuf, strlen(aliasbuf)); + + return; +} + +/* Remove characters (rm) from string (str). */ +static char *stripstr(char *str, char *rm) +{ + static char buf[LINE_MAX]; + char *orm; + int i = 0, c; + + if (rm == NULL || str == NULL) + return str; + + while (*str) { + orm = rm; + + while (*orm) { + if (*str == *orm) { + c = *str++; + continue; + } + + c = *orm++; + } + + buf[i++] = *str++; + } + + buf[i] = '\0'; + return buf; +} + +/* Return a string of mail aliases for the user. Looks in /etc/aliases (or + * whatever was specified at compile-time). The file is read into a buffer + * only once (mmap(2)). */ +static char *aliases(const char *user, const int multi) +{ + char t[LINE_MAX]; + static char aliases[LINE_MAX], *p; + static int firstrun; + int i, n; + struct stat st; + char m[2] = { multi, '\0' }; + int fd; + + aliases[0] = '\0'; + + if ((!aliasbuf && firstrun) || aliasbuf == MAP_FAILED) + return "!"; + + if (!aliasbuf) { + firstrun = 1; + + if (stat(ALIAS_FILE, &st) == -1) + return "!"; + + if ((fd = open(ALIAS_FILE, O_RDONLY)) == -1) + return "!"; + + if ((aliasbuf = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, + 0)) == MAP_FAILED) { + warn("%s", "mmap()"); + return "!"; + } + + close(fd); + } + + for (i = n = 0; aliasbuf[i]; i++) { + char *last, *name, *tmp; + + while (aliasbuf[i] != '\n') + t[n++] = aliasbuf[i++]; + + t[n] = 0; + n = 0; + + if (t[0] == '#' || t[0] == '\0') + continue; + + last = t; + + if ((name = strsep(&last, ":")) == NULL) + continue; + + if (strcmp(user, name) == 0) { + while ((tmp = strsep(&last, ",")) != NULL) { + tmp = stripstr(tmp, " \n\t"); + + strncat(aliases, tmp, sizeof(aliases)); + strncat(aliases, m, sizeof(aliases)); + } + + continue; + } + + while ((tmp = strsep(&last, ",")) != NULL) { + tmp = stripstr(tmp, " \n\t"); + + if (strcmp(user, tmp) == 0) { + strncat(aliases, name, sizeof(aliases)); + strncat(aliases, m, sizeof(aliases)); + } + } + } + + if (aliases[0] == '\0') + return "-"; + else + aliases[strlen(aliases) - 1] = '\0'; + + p = aliases; + return p; +} + +/* Returns a string of forward aliases for the user. Reads ~/.forward if it + * exists and is readable. */ +static char *forwards(const char *dir, const int multi) +{ + FILE *fp; + char buf[LINE_MAX], *s; + static char buf2[FILENAME_MAX]; + int n = 0; + char m[2] = { multi, '\0' }; + + buf[0] = buf2[0] = '\0'; + snprintf(buf2, sizeof(buf2), "%s/.forward", dir); + + if ((fp = fopen(buf2, "r")) == NULL) { + if (errno == ENOENT) + return "-"; + else + return "!"; + } + + buf2[0] = '\0'; + + while ((s = fgets(buf, sizeof(buf), fp)) != NULL) { + if (buf[0] == '\n') + continue; + + if (buf[strlen(buf) - 1] == '\n') + buf[strlen(buf) - 1] = '\0'; + + if (n++) + strncat(buf2, m, sizeof(buf2)); + + strncat(buf2, buf, sizeof(buf2)); + } + + fclose(fp); + + if (!n) + return "-"; + + s = buf2; + return s; +} + +/* /var/mail/username folder size in bytes. */ +static char *foldersize(struct stat st) +{ + static char str[33], *p; + + str[0] = '\0'; + + snprintf(str, sizeof(str), "%lu", (unsigned long) st.st_size); + p = str; + return p; +} + +/* This is output if the -h command line option is passed to the main program. + */ +void ui_module_help() +{ +#ifdef DEBUG + fprintf(stderr, "%s: ui_module_help()\n", __FILE__); +#endif + + printf(" Mail information [-M (-%s)]:\n", MAIL_OPTION_ORDER); + printf("\t-f forwarding addresses\t"); + printf("-a mail aliases\n"); + printf("\t-r folder access (read) time\t"); + printf("-m folder modification time\n"); + printf("\t-s folder size\n\n"); + return; +} + +/* This is the equivalent to main() only without argc and argv available. */ +int ui_module_exec(char ***s, const struct passwd *pw, const int multi_char, + const int verbose, char *tf) +{ + char *p = options; + int gotstat = 0; + struct stat st; + char folder[FILENAME_MAX]; + + strings = *s; + folder[0] = '\0'; + snprintf(folder, sizeof(folder), "%s/%s", _PATH_MAILDIR, pw->pw_name); + + if (stat(folder, &st) != -1) + gotstat = 1; + + while (*p) { + switch (*p) { + case 's': + add_string(&strings, (gotstat) ? foldersize(st) : "!"); + break; + case 'r': + add_string(&strings, (gotstat) ? stamp(st.st_atime, tf) : "!"); + break; + case 'm': + add_string(&strings, (gotstat) ? stamp(st.st_mtime, tf) : "!"); + break; + case 'f': + add_string(&strings, forwards(pw->pw_dir, multi_char)); + break; + case 'a': + add_string(&strings, aliases(pw->pw_name, multi_char)); + break; + default: + break; + } + + p++; + } + + *s = strings; + return EXIT_SUCCESS; +} + +char *ui_module_options_init(char **defaults) +{ + *defaults = "M"; + return MAIL_OPTION_STRING; +} + +/* Check module option validity. */ +int ui_module_options(int argc, char **argv) +{ + int opt; + char *p = options; + +#ifdef DEBUG + fprintf(stderr, "%s: ui_module_options()\n", __FILE__); +#endif + + while ((opt = getopt(argc, argv, MAIL_OPTION_STRING)) != -1) { + switch (opt) { + case 'f': + case 's': + case 'r': + case 'm': + case 'a': + case 'M': + break; + case '?': + warnx("mail: invalid option -- %c", optopt); + default: + return 1; + } + + /* + * This option '-M' sets all available options for this module. + */ + if (opt == 'M') { + strncpy(options, MAIL_OPTION_ORDER, sizeof(options)); + break; + } + + *p++ = opt; + *p = '\0'; + } + + return 0; +} diff --git a/src/modules/mail.h b/src/modules/mail.h new file mode 100644 index 0000000..f617108 --- /dev/null +++ b/src/modules/mail.h @@ -0,0 +1,70 @@ +/* $Id: mail.h,v 1.1 2005-07-30 13:00:15 bjk Exp $ */ +/* + Copyright (C) 2001-2005 Ben Kibbey + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef MAIL_H +#define MAIL_H + +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_SYS_MMAN_H +#include +#endif + +#ifdef HAVE_LIMITS_H +#include +#endif + +#ifdef HAVE_SYS_MMAN_H +#include +#endif + +#ifdef HAVE_ERR_H +#include +#endif + +#ifdef HAVE_PATHS_H +#include +#endif + +#ifndef _PATH_MAILDIR +#define _PATH_MAILDIR "/var/mail" +#endif + +#include + +#define MAIL_OPTION_ORDER "smrfa" +#define MAIL_OPTION_STRING "Mfrsam" + +static char options[6]; /* NULL terminated. */ +static char *aliasbuf; +static char **strings; + +void add_string(char ***, const char *); +char *stamp(time_t, const char *); + +#ifdef WITH_DMALLOC +#include +#endif + +#endif diff --git a/src/modules/passwd.c b/src/modules/passwd.c new file mode 100644 index 0000000..467d2a5 --- /dev/null +++ b/src/modules/passwd.c @@ -0,0 +1,405 @@ +/* $Id: passwd.c,v 1.1 2005-07-30 13:00:15 bjk Exp $ */ +/* + Copyright (C) 2001-2005 Ben Kibbey + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "passwd.h" + +#ifndef HAVE_STRSEP +#include "strsep.c" +#endif + +#ifndef HAVE_ERR_H +#include "err.c" +#endif + +void ui_module_init(int *chainable) +{ +#ifdef DEBUG + fprintf(stderr, "%s: ui_module_init()\n", __FILE__); +#endif + + /* + * Keep the password file open if possible (*BSD). + */ +#ifdef HAVE_SETPASSENT + setpassent(1); +#endif + + if (getuid() == 0) + amroot = 1; + + *chainable = 0; + return; +} + +void ui_module_exit() +{ +#ifdef DEBUG + fprintf(stderr, "%s: ui_module_exit()\n", __FILE__); +#endif + +#ifdef HAVE_GETSPNAM + if (amroot) + endspent(); +#endif + + endpwent(); + endgrent(); + return; +} + +/* See if the gecos options are valid. */ +static int parse_gecos_options(const char *args) +{ + int i = 0; + + for (i = 0; i < strlen(args); i++) { + switch (args[i]) { + case 'n': + case '1': + case '2': + case '3': + case 'a': + break; + default: + return 1; + } + } + + return 0; +} + +/* Break up the gecos string into sections and add the sections to the output + * string array if needed. */ +static void gecos_strings(char *str) +{ + int i = 0; + char *buf; + const char *name, *first, *second, *third; + + name = first = second = third = "-"; + + while ((buf = strsep(&str, ",")) != NULL) { + if (!buf[0]) + continue; + + switch (i++) { + case 0: + name = buf; + break; + case 1: + first = buf; + break; + case 2: + second = buf; + break; + case 3: + third = buf; + break; + default: + break; + } + } + + for (i = 0; i < strlen(gecos_options); i++) { + switch (gecos_options[i]) { + case 'n': + add_string(&strings, name); + break; + case '1': + add_string(&strings, first); + break; + case '2': + add_string(&strings, second); + break; + case '3': + add_string(&strings, third); + break; + case 'a': + add_string(&strings, name); + add_string(&strings, first); + add_string(&strings, second); + add_string(&strings, third); + break; + default: + break; + } + } + + return; +} + +/* Get all groups that a user is a member of. The primary group will be the + * first added. */ +static void groups(const struct passwd *pw, const int multi, + const int verbose) +{ + struct group *grp; + char tmp[255]; + char line[LINE_MAX]; + gid_t primary = -1; + + line[0] = '\0'; + + if ((grp = getgrgid(pw->pw_gid)) == NULL) { + snprintf(tmp, sizeof(tmp), "%li%s%s%s", (long) pw->pw_gid, + (verbose) ? "(" : "", (verbose) ? "!" : "", + (verbose) ? ")" : ""); + add_string(&strings, tmp); + return; + } + + primary = grp->gr_gid; + snprintf(tmp, sizeof(tmp), "%li%s%s%s%c", (long) pw->pw_gid, + (verbose) ? "(" : "", (verbose) ? grp->gr_name : "", + (verbose) ? ")" : "", multi); + strncat(line, tmp, sizeof(line)); + +#ifdef HAVE_SETGROUPENT + setgroupent(1); +#else + setgrent(); +#endif + + while ((grp = getgrent()) != NULL) { + char **members = grp->gr_mem; + + while (*members) { + if (strcmp(*members++, pw->pw_name) == 0) { + if (grp->gr_gid == primary) + continue; + + snprintf(tmp, sizeof(tmp), "%li%s%s%s%c", (long) grp->gr_gid, + (verbose) ? "(" : "", (verbose) ? grp->gr_name : "", + (verbose) ? ")" : "", multi); + strncat(line, tmp, sizeof(line)); + } + } + } + + /* + * Trim the remaining multi-string deliminator. + */ + line[strlen(line) - 1] = '\0'; + + add_string(&strings, line); + return; +} + +/* This is output if the -h command line option is passed to the main program. + */ +void ui_module_help() +{ +#ifdef DEBUG + fprintf(stderr, "%s: ui_module_help()\n", __FILE__); +#endif + + printf(" Password/Group file information [-P (-%s)]:\n", + PASSWD_OPTION_ORDER); + printf("\t-l login name\t\t"); + printf("\t-p encrypted password\n"); + printf("\t-u user id (uid)\t"); + printf("\t-g group id (gid)\n"); + printf("\t-c password change time"); + printf("\t-e password expire time\n"); + printf("\t-d home directory\t"); + printf("\t-m home directory mode\n"); + printf("\t-s login shell\n"); + printf("\t-i gecos (any of [n]ame,[1]st,[2]nd,[3]rd or [a]ll)\n\n"); + return; +} + +/* This is the equivalent to main() only without argc and argv available. */ +int ui_module_exec(char ***s, const struct passwd *pw, const int multi_char, + const int verbose, char *tf) +{ + char *p = options; + struct stat st; + +#ifdef HAVE_GETSPNAM + struct spwd *spwd = NULL; +#endif + +#ifdef DEBUG + fprintf(stderr, "%s: ui_module_exec()\n", __FILE__); +#endif + +#ifdef HAVE_GETSPNAM + if (amroot) { + if ((spwd = getspnam(pw->pw_name)) == NULL) + warnx("%s", "getspnam(): unknown error"); + } +#endif + + strings = *s; + + while (*p) { + char tmp[32]; + + switch (*p) { +#ifdef HAVE_GETSPNAM + case 'c': + if (!amroot) { + add_string(&strings, "!"); + break; + } + + snprintf(tmp, sizeof(tmp), "%li", (long) spwd->sp_max); + add_string(&strings, tmp); + break; + case 'e': + if (!amroot) { + add_string(&strings, "!"); + break; + } + + snprintf(tmp, sizeof(tmp), "%li", (long) spwd->sp_expire); + add_string(&strings, tmp); + break; +#else + case 'c': + snprintf(tmp, sizeof(tmp), "%li", (long) pw->pw_change); + add_string(&strings, tmp); + break; + case 'e': + snprintf(tmp, sizeof(tmp), "%li", (long) pw->pw_expire); + add_string(&strings, tmp); + break; +#endif + case 'l': + add_string(&strings, pw->pw_name); + break; + case 'd': + add_string(&strings, (pw->pw_dir && pw->pw_dir[0]) ? pw->pw_dir : "-"); + break; + case 's': + add_string(&strings, (pw->pw_shell && pw->pw_shell[0]) ? pw->pw_shell : "-"); + break; + case 'p': +#ifdef HAVE_GETSPNAM + if (!amroot) + add_string(&strings, (pw->pw_passwd + && pw->pw_passwd[0]) ? pw->pw_passwd : "-"); + else + add_string(&strings, (spwd->sp_pwdp + && spwd->sp_pwdp[0]) ? spwd->sp_pwdp : "-"); +#else + add_string(&strings, (pw->pw_passwd + && pw->pw_passwd[0]) ? pw->pw_passwd : "-"); +#endif + break; + case 'u': + sprintf(tmp, "%li", (long) pw->pw_uid); + add_string(&strings, tmp); + break; + case 'g': + groups(pw, multi_char, verbose); + break; + case 'm': + if (stat(pw->pw_dir, &st) == -1) { + add_string(&strings, "!"); + break; + } + + sprintf(tmp, "%.4o", (unsigned) st.st_mode & ALLPERMS); + add_string(&strings, tmp); + break; + case 'i': + gecos_strings(pw->pw_gecos); + break; + default: + break; + } + + p++; + } + + *s = strings; + return EXIT_SUCCESS; +} + +char *ui_module_options_init(char **defaults) +{ + *defaults = "P"; + return PASSWD_OPTION_STRING; +} + +/* Check module option validity. */ +int ui_module_options(int argc, char **argv) +{ + int opt; + char *p = options; + +#ifdef DEBUG + fprintf(stderr, "%s: ui_module_options()\n", __FILE__); +#endif + + while ((opt = getopt(argc, argv, PASSWD_OPTION_STRING)) != -1) { + switch (opt) { + case 'i': + gecos_options = optarg; + break; + case 'P': + case 'l': + case 'p': + case 'u': + case 'g': + case 'c': + case 'e': + case 'd': + case 's': + case 'm': + break; + case '?': + warnx("passwd: invalid option -- %c", optopt); + default: + return 1; + } + + if (opt == 'i') { + if (parse_gecos_options(gecos_options)) + return 1; + } + + /* + * This option '-P' sets all available options for this module. + */ + if (opt == 'P') { + strncpy(options, PASSWD_OPTION_ORDER, sizeof(options)); + gecos_options = "a"; + break; + } + + *p++ = opt; + *p = '\0'; + } + + return 0; +} diff --git a/src/modules/passwd.h b/src/modules/passwd.h new file mode 100644 index 0000000..18628ca --- /dev/null +++ b/src/modules/passwd.h @@ -0,0 +1,63 @@ +/* $Id: passwd.h,v 1.1 2005-07-30 13:00:15 bjk Exp $ */ +/* + Copyright (C) 2001-2005 Ben Kibbey + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef PASSWD_H +#define PASSWD_H + +#ifdef HAVE_LIMITS_H +#include +#endif + +#ifdef HAVE_GETOPT_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_GETSPNAM +#include +#endif + +#ifdef HAVE_ERR_H +#include +#endif + +#ifndef ALLPERMS +#define ALLPERMS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) +#endif + +/* This is the order of options when the 'P' option is specified. It selects + * all available options. */ +#define PASSWD_OPTION_ORDER "lpugicedsm" +#define PASSWD_OPTION_STRING "Plpugcedsmi:" + +static int amroot; +static char **strings; +static char options[11]; /* NULL terminated. */ +static char *gecos_options; + +void add_string(char ***, const char *); +char *stamp(time_t, const char *); + +#ifdef WITH_DMALLOC +#include +#endif + +#endif -- 2.11.4.GIT