summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-03-01 18:47:14 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-03-01 18:47:14 +0000
commit3eaf4237194e25804f221af93c269d3d97e2809d (patch)
treecdedf3fc954b0727b0b34aa9b0b211cc18f854eb /src
downloadsmartcardauth-3eaf4237194e25804f221af93c269d3d97e2809d.tar.gz
smartcardauth-3eaf4237194e25804f221af93c269d3d97e2809d.zip
Added my SmartCard login/session lock/unlock utility
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/smartcardauth@1097604 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src')
-rw-r--r--src/Makefile16
-rw-r--r--src/ckpasswd.c366
-rw-r--r--src/messages.c493
-rw-r--r--src/xmalloc.c163
4 files changed, 1038 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..e71a413
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,16 @@
+all: ckpasswd.o xmalloc.o messages.o ckpasswd
+
+ckpasswd.o: ckpasswd.c
+ gcc ckpasswd.c -c
+
+xmalloc.o: xmalloc.c
+ gcc xmalloc.c -c
+
+messages.o: messages.c
+ gcc messages.c -c
+
+ckpasswd: ckpasswd.o
+ gcc ckpasswd.o xmalloc.o messages.o -o ckpasswd -lpam -lcrypt
+
+clean:
+ rm -f ckpasswd.o xmalloc.o messages.o ckpasswd
diff --git a/src/ckpasswd.c b/src/ckpasswd.c
new file mode 100644
index 0000000..04b4b64
--- /dev/null
+++ b/src/ckpasswd.c
@@ -0,0 +1,366 @@
+/* $Id: ckpasswd.c 7565 2006-08-28 02:42:54Z eagle $
+**
+** The default username/password authenticator.
+**
+** This program is intended to be run by nnrpd and handle usernames and
+** passwords. It can authenticate against a regular flat file (the type
+** managed by htpasswd), a DBM file, the system password file or shadow file,
+** or PAM.
+*/
+
+/* Used for unused parameters to silence gcc warnings. */
+#define UNUSED __attribute__((__unused__))
+
+/* Make available the bool type. */
+#if INN_HAVE_STDBOOL_H
+# include <stdbool.h>
+#else
+# undef true
+# undef false
+# define true (1)
+# define false (0)
+# ifndef __cplusplus
+# define bool int
+# endif
+#endif /* INN_HAVE_STDBOOL_H */
+
+#include <stdlib.h>
+#include <string.h>
+#include <crypt.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <grp.h>
+
+#define DB_DBM_HSEARCH 1
+#include <db.h>
+#define OPT_DBM "d:"
+
+#if HAVE_GETSPNAM
+# include <shadow.h>
+# define OPT_SHADOW "s"
+#else
+# define OPT_SHADOW ""
+#endif
+
+/* The functions are actually macros so that we can pick up the file and line
+ number information for debugging error messages without the user having to
+ pass those in every time. */
+#define xcalloc(n, size) x_calloc((n), (size), __FILE__, __LINE__)
+#define xmalloc(size) x_malloc((size), __FILE__, __LINE__)
+#define xrealloc(p, size) x_realloc((p), (size), __FILE__, __LINE__)
+#define xstrdup(p) x_strdup((p), __FILE__, __LINE__)
+#define xstrndup(p, size) x_strndup((p), (size), __FILE__, __LINE__)
+
+#include <security/pam_appl.h>
+
+/* Holds the authentication information from nnrpd. */
+struct auth_info {
+ char *username;
+ char *password;
+};
+
+/*
+** The PAM conversation function.
+**
+** Since we already have all the information and can't ask the user
+** questions, we can't quite follow the real PAM protocol. Instead, we just
+** return the password in response to every question that PAM asks. There
+** appears to be no generic way to determine whether the message in question
+** is indeed asking for the password....
+**
+** This function allocates an array of struct pam_response to return to the
+** PAM libraries that's never freed. For this program, this isn't much of an
+** issue, since it will likely only be called once and then the program will
+** exit. This function uses malloc and strdup instead of xmalloc and xstrdup
+** intentionally so that the PAM conversation will be closed cleanly if we
+** run out of memory rather than simply terminated.
+**
+** appdata_ptr contains the password we were given.
+*/
+static int pass_conv(int num_msg, const struct pam_message **msgm UNUSED, struct pam_response **response, void *appdata_ptr)
+{
+ int i;
+
+ *response = malloc(num_msg * sizeof(struct pam_response));
+ if (*response == NULL)
+ return PAM_CONV_ERR;
+ for (i = 0; i < num_msg; i++) {
+ (*response)[i].resp = strdup((char *)appdata_ptr);
+ (*response)[i].resp_retcode = 0;
+ }
+ return PAM_SUCCESS;
+}
+
+
+/*
+** Authenticate a user via PAM.
+**
+** Attempts to authenticate a user with PAM, returning true if the user
+** successfully authenticates and false otherwise. Note that this function
+** doesn't attempt to handle any remapping of the authenticated user by the
+** PAM stack, but just assumes that the authenticated user was the same as
+** the username given.
+**
+** Right now, all failures are handled via die. This may be worth revisiting
+** in case we want to try other authentication methods if this fails for a
+** reason other than the system not having PAM support.
+*/
+
+static bool auth_pam(const char *username, char *password)
+{
+ pam_handle_t *pamh;
+ struct pam_conv conv;
+ int status;
+
+ conv.conv = pass_conv;
+ conv.appdata_ptr = password;
+ status = pam_start("nnrpd", username, &conv, &pamh);
+ if (status != PAM_SUCCESS)
+ die("pam_start failed: %s", pam_strerror(pamh, status));
+ status = pam_authenticate(pamh, PAM_SILENT);
+ if (status != PAM_SUCCESS)
+ die("pam_authenticate failed: %s", pam_strerror(pamh, status));
+ status = pam_acct_mgmt(pamh, PAM_SILENT);
+ if (status != PAM_SUCCESS)
+ die("pam_acct_mgmt failed: %s", pam_strerror(pamh, status));
+ status = pam_end(pamh, status);
+ if (status != PAM_SUCCESS)
+ die("pam_end failed: %s", pam_strerror(pamh, status));
+
+ /* If we get to here, the user successfully authenticated. */
+ return true;
+}
+
+
+/*
+** Try to get a password out of a dbm file. The dbm file should have the
+** username for the key and the crypted password as the value. The crypted
+** password, if found, is returned as a newly allocated string; otherwise,
+** NULL is returned.
+*/
+#if !(defined(HAVE_DBM) || defined(HAVE_BDB_DBM))
+static char *
+password_dbm(char *user UNUSED, const char *file UNUSED)
+{
+ return NULL;
+}
+#else
+static char *
+password_dbm(char *name, const char *file)
+{
+ datum key, value;
+ DBM *database;
+ char *password;
+
+ database = dbm_open(file, O_RDONLY, 0600);
+ if (database == NULL)
+ return NULL;
+ key.dptr = name;
+ key.dsize = strlen(name);
+ value = dbm_fetch(database, key);
+ if (value.dptr == NULL) {
+ dbm_close(database);
+ return NULL;
+ }
+ password = xmalloc(value.dsize + 1);
+ strlcpy(password, value.dptr, value.dsize + 1);
+ dbm_close(database);
+ return password;
+}
+#endif /* HAVE_DBM || HAVE_BDB_DBM */
+
+
+/*
+** Try to get a password out of the system /etc/shadow file. The crypted
+** password, if found, is returned as a newly allocated string; otherwise,
+** NULL is returned.
+*/
+#if !HAVE_GETSPNAM
+static char *
+password_shadow(const char *user UNUSED)
+{
+ return NULL;
+}
+#else
+static char *
+password_shadow(const char *user)
+{
+ struct spwd *spwd;
+
+ spwd = getspnam(user);
+ if (spwd != NULL)
+ return xstrdup(spwd->sp_pwdp);
+ return NULL;
+}
+#endif /* HAVE_GETSPNAM */
+
+
+/*
+** Try to get a password out of the system password file. The crypted
+** password, if found, is returned as a newly allocated string; otherwise,
+** NULL is returned.
+*/
+static char *
+password_system(const char *username)
+{
+ struct passwd *pwd;
+
+ pwd = getpwnam(username);
+ if (pwd != NULL)
+ return xstrdup(pwd->pw_passwd);
+ return NULL;
+}
+
+
+/*
+** Try to get the name of a user's primary group out of the system group
+** file. The group, if found, is returned as a newly allocated string;
+** otherwise, NULL is returned. If the username is not found, NULL is
+** returned.
+*/
+static char *
+group_system(const char *username)
+{
+ struct passwd *pwd;
+ struct group *gr;
+
+ pwd = getpwnam(username);
+ if (pwd == NULL)
+ return NULL;
+ gr = getgrgid(pwd->pw_gid);
+ if (gr == NULL)
+ return NULL;
+ return xstrdup(gr->gr_name);
+}
+
+
+/*
+** Output username (and group, if desired) in correct return format.
+*/
+static void
+output_user(const char *username, bool wantgroup)
+{
+ if (wantgroup) {
+ char *group = group_system(username);
+ if (group == NULL)
+ die("group info for user %s not available", username);
+ printf("User:%s@%s\n", username, group);
+ }
+ else
+ printf("User:%s\n", username);
+}
+
+
+/*
+** Main routine.
+**
+** We handle the variences between systems with #if blocks above, so that
+** this code can look fairly clean.
+*/
+int
+main(int argc, char *argv[])
+{
+ enum authtype { AUTH_NONE, AUTH_SHADOW, AUTH_FILE, AUTH_DBM };
+
+ int opt;
+ enum authtype type = AUTH_NONE;
+ bool wantgroup = false;
+ const char *filename = NULL;
+ struct auth_info *authinfo = NULL;
+ char *password = NULL;
+
+ //message_program_name = "ckpasswd";
+
+ while ((opt = getopt(argc, argv, "gf:u:p:" OPT_DBM OPT_SHADOW)) != -1) {
+ switch (opt) {
+ case 'g':
+ if (type == AUTH_DBM || type == AUTH_FILE)
+ die("-g option is incompatible with -d or -f");
+ wantgroup = true;
+ break;
+ case 'd':
+ if (type != AUTH_NONE)
+ die("only one of -s, -f, or -d allowed");
+ if (wantgroup)
+ die("-g option is incompatible with -d or -f");
+ type = AUTH_DBM;
+ filename = optarg;
+ break;
+ case 'f':
+ if (type != AUTH_NONE)
+ die("only one of -s, -f, or -d allowed");
+ if (wantgroup)
+ die("-g option is incompatible with -d or -f");
+ type = AUTH_FILE;
+ filename = optarg;
+ break;
+ case 's':
+ if (type != AUTH_NONE)
+ die("only one of -s, -f, or -d allowed");
+ type = AUTH_SHADOW;
+ break;
+ case 'u':
+ if (authinfo == NULL) {
+ authinfo = xmalloc(sizeof(struct auth_info));
+ authinfo->password = NULL;
+ }
+ authinfo->username = optarg;
+ break;
+ case 'p':
+ if (authinfo == NULL) {
+ authinfo = xmalloc(sizeof(struct auth_info));
+ authinfo->username = NULL;
+ }
+ authinfo->password = optarg;
+ break;
+ default:
+ exit(1);
+ }
+ }
+ if (argc != optind)
+ die("extra arguments given");
+ if (authinfo != NULL && authinfo->username == NULL)
+ die("-u option is required if -p option is given");
+ if (authinfo != NULL && authinfo->password == NULL)
+ die("-p option is required if -u option is given");
+
+// /* Unless a username or password was given on the command line, assume
+// we're being run by nnrpd. */
+// if (authinfo == NULL)
+// authinfo = get_auth_info(stdin);
+// if (authinfo == NULL)
+// die("no authentication information from nnrpd");
+// if (authinfo->username[0] == '\0')
+// die("null username");
+
+ /* Run the appropriate authentication routines. */
+ switch (type) {
+ case AUTH_SHADOW:
+ password = password_shadow(authinfo->username);
+ if (password == NULL)
+ password = password_system(authinfo->username);
+ break;
+// case AUTH_FILE:
+// password = password_file(authinfo->username, filename);
+// break;
+ case AUTH_DBM:
+ password = password_dbm(authinfo->username, filename);
+ break;
+ case AUTH_NONE:
+ if (auth_pam(authinfo->username, authinfo->password)) {
+ output_user(authinfo->username, wantgroup);
+ exit(0);
+ }
+ password = password_system(authinfo->username);
+ break;
+ }
+
+ if (password == NULL)
+ die("user %s unknown", authinfo->username);
+ if (strcmp(password, crypt(authinfo->password, password)) != 0)
+ die("invalid password for user %s", authinfo->username);
+
+ /* The password matched. */
+ output_user(authinfo->username, wantgroup);
+ exit(0);
+}
diff --git a/src/messages.c b/src/messages.c
new file mode 100644
index 0000000..7a54e0a
--- /dev/null
+++ b/src/messages.c
@@ -0,0 +1,493 @@
+/* $Id: messages.c 5496 2002-06-07 13:59:06Z alexk $
+**
+** Message and error reporting (possibly fatal).
+**
+** Usage:
+**
+** extern int cleanup(void);
+** extern void log(int, const char *, va_list, int);
+**
+** message_fatal_cleanup = cleanup;
+** message_program_name = argv[0];
+**
+** warn("Something horrible happened at %lu", time);
+** syswarn("Couldn't unlink temporary file %s", tmpfile);
+**
+** die("Something fatal happened at %lu", time);
+** sysdie("open of %s failed", filename);
+**
+** debug("Some debugging message about %s", string);
+** trace(TRACE_PROGRAM, "Program trace output");
+** notice("Informational notices");
+**
+** message_handlers_warn(1, log);
+** warn("This now goes through our log function");
+**
+** These functions implement message reporting through user-configurable
+** handler functions. debug() only does something if DEBUG is defined,
+** trace() supports sending trace messages in one of a number of configurable
+** classes of traces so that they can be turned on or off independently, and
+** notice() and warn() just output messages as configured. die() similarly
+** outputs a message but then exits, normally with a status of 1.
+**
+** The sys* versions do the same, but append a colon, a space, and the
+** results of strerror(errno) to the end of the message. All functions
+** accept printf-style formatting strings and arguments.
+**
+** If message_fatal_cleanup is non-NULL, it is called before exit by die and
+** sysdie and its return value is used as the argument to exit. It is a
+** pointer to a function taking no arguments and returning an int, and can be
+** used to call cleanup functions or to exit in some alternate fashion (such
+** as by calling _exit).
+**
+** If message_program_name is non-NULL, the string it points to, followed by
+** a colon and a space, is prepended to all error messages logged through the
+** message_log_stdout and message_log_stderr message handlers (the former is
+** the default for notice, and the latter is the default for warn and die).
+**
+** Honoring error_program_name and printing to stderr is just the default
+** handler; with message_handlers_* the handlers for any message function can
+** be changed. By default, notice prints to stdout, warn and die print to
+** stderr, and the others don't do anything at all. These functions take a
+** count of handlers and then that many function pointers, each one to a
+** function that takes a message length (the number of characters snprintf
+** generates given the format and arguments), a format, an argument list as a
+** va_list, and the applicable errno value (if any).
+*/
+
+/* Used for unused parameters to silence gcc warnings. */
+#define UNUSED __attribute__((__unused__))
+
+/* Make available the bool type. */
+#if INN_HAVE_STDBOOL_H
+# include <stdbool.h>
+#else
+# undef true
+# undef false
+# define true (1)
+# define false (0)
+# ifndef __cplusplus
+# define bool int
+# endif
+#endif /* INN_HAVE_STDBOOL_H */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <errno.h>
+#include <syslog.h>
+#include <crypt.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <grp.h>
+
+/* The functions are actually macros so that we can pick up the file and line
+ number information for debugging error messages without the user having to
+ pass those in every time. */
+#define xcalloc(n, size) x_calloc((n), (size), __FILE__, __LINE__)
+#define xmalloc(size) x_malloc((size), __FILE__, __LINE__)
+#define xrealloc(p, size) x_realloc((p), (size), __FILE__, __LINE__)
+#define xstrdup(p) x_strdup((p), __FILE__, __LINE__)
+#define xstrndup(p, size) x_strndup((p), (size), __FILE__, __LINE__)
+
+/* These are the currently-supported types of traces. */
+enum message_trace {
+ TRACE_NETWORK, /* Network traffic. */
+ TRACE_PROGRAM, /* Stages of program execution. */
+ TRACE_ALL /* All traces; this must be last. */
+};
+
+/* The reporting functions. The ones prefaced by "sys" add a colon, a space,
+ and the results of strerror(errno) to the output and are intended for
+ reporting failures of system calls. */
+extern void trace(enum message_trace, const char *, ...)
+ __attribute__((__format__(printf, 2, 3)));
+extern void notice(const char *, ...)
+ __attribute__((__format__(printf, 1, 2)));
+extern void sysnotice(const char *, ...)
+ __attribute__((__format__(printf, 1, 2)));
+extern void warn(const char *, ...)
+ __attribute__((__format__(printf, 1, 2)));
+extern void syswarn(const char *, ...)
+ __attribute__((__format__(printf, 1, 2)));
+extern void die(const char *, ...)
+ __attribute__((__noreturn__, __format__(printf, 1, 2)));
+extern void sysdie(const char *, ...)
+ __attribute__((__noreturn__, __format__(printf, 1, 2)));
+
+/* Debug is handled specially, since we want to make the code disappear
+ completely unless we're built with -DDEBUG. We can only do that with
+ support for variadic macros, though; otherwise, the function just won't do
+ anything. */
+#if !defined(DEBUG) && (INN_HAVE_C99_VAMACROS || INN_HAVE_GNU_VAMACROS)
+# if INN_HAVE_C99_VAMACROS
+# define debug(format, ...) /* empty */
+# elif INN_HAVE_GNU_VAMACROS
+# define debug(format, args...) /* empty */
+# endif
+#else
+extern void debug(const char *, ...)
+ __attribute__((__format__(printf, 1, 2)));
+#endif
+
+/* Set the handlers for various message functions. All of these functions
+ take a count of the number of handlers and then function pointers for each
+ of those handlers. These functions are not thread-safe; they set global
+ variables. */
+extern void message_handlers_debug(int count, ...);
+extern void message_handlers_trace(int count, ...);
+extern void message_handlers_notice(int count, ...);
+extern void message_handlers_warn(int count, ...);
+extern void message_handlers_die(int count, ...);
+
+/* Enable or disable tracing for particular classes of messages. */
+extern void message_trace_enable(enum message_trace, bool);
+
+/* Some useful handlers, intended to be passed to message_handlers_*. All
+ handlers take the length of the formatted message, the format, a variadic
+ argument list, and the errno setting if any. */
+extern void message_log_stdout(int, const char *, va_list, int);
+extern void message_log_stderr(int, const char *, va_list, int);
+extern void message_log_syslog_debug(int, const char *, va_list, int);
+extern void message_log_syslog_info(int, const char *, va_list, int);
+extern void message_log_syslog_notice(int, const char *, va_list, int);
+extern void message_log_syslog_warning(int, const char *, va_list, int);
+extern void message_log_syslog_err(int, const char *, va_list, int);
+extern void message_log_syslog_crit(int, const char *, va_list, int);
+
+/* The type of a message handler. */
+typedef void (*message_handler_func)(int, const char *, va_list, int);
+
+/* If non-NULL, called before exit and its return value passed to exit. */
+extern int (*message_fatal_cleanup)(void);
+
+/* If non-NULL, prepended (followed by ": ") to all messages printed by either
+ message_log_stdout or message_log_stderr. */
+extern const char *message_program_name;
+
+/* The default handler lists. */
+static message_handler_func stdout_handlers[2] = {
+ message_log_stdout, NULL
+};
+static message_handler_func stderr_handlers[2] = {
+ message_log_stderr, NULL
+};
+
+/* The list of logging functions currently in effect. */
+static message_handler_func *debug_handlers = NULL;
+static message_handler_func *trace_handlers = NULL;
+static message_handler_func *notice_handlers = stdout_handlers;
+static message_handler_func *warn_handlers = stderr_handlers;
+static message_handler_func *die_handlers = stderr_handlers;
+
+/* If non-NULL, called before exit and its return value passed to exit. */
+int (*message_fatal_cleanup)(void) = NULL;
+
+/* If non-NULL, prepended (followed by ": ") to messages. */
+const char *message_program_name = NULL;
+
+/* Whether or not we're currently outputting a particular type of trace. */
+static bool tracing[TRACE_ALL] = { false /* false, ... */ };
+
+
+/*
+** Set the handlers for a particular message function. Takes a pointer to
+** the handler list, the count of handlers, and the argument list.
+*/
+static void
+message_handlers(message_handler_func **list, int count, va_list args)
+{
+ int i;
+
+ if (*list != stdout_handlers && *list != stderr_handlers)
+ free(*list);
+ *list = xmalloc(sizeof(message_handler_func) * (count + 1));
+ for (i = 0; i < count; i++)
+ (*list)[i] = (message_handler_func) va_arg(args, message_handler_func);
+ (*list)[count] = NULL;
+}
+
+
+/*
+** There's no good way of writing these handlers without a bunch of code
+** duplication since we can't assume variadic macros, but I can at least make
+** it easier to write and keep them consistent.
+*/
+#define HANDLER_FUNCTION(type) \
+ void \
+ message_handlers_ ## type(int count, ...) \
+ { \
+ va_list args; \
+ \
+ va_start(args, count); \
+ message_handlers(& type ## _handlers, count, args); \
+ va_end(args); \
+ }
+HANDLER_FUNCTION(debug)
+HANDLER_FUNCTION(trace)
+HANDLER_FUNCTION(notice)
+HANDLER_FUNCTION(warn)
+HANDLER_FUNCTION(die)
+
+
+/*
+** Print a message to stdout, supporting message_program_name.
+*/
+void
+message_log_stdout(int len UNUSED, const char *fmt, va_list args, int err)
+{
+ if (message_program_name != NULL)
+ fprintf(stdout, "%s: ", message_program_name);
+ vfprintf(stdout, fmt, args);
+ if (err)
+ fprintf(stdout, ": %s", strerror(err));
+ fprintf(stdout, "\n");
+}
+
+
+/*
+** Print a message to stderr, supporting message_program_name. Also flush
+** stdout so that errors and regular output occur in the right order.
+*/
+void
+message_log_stderr(int len UNUSED, const char *fmt, va_list args, int err)
+{
+ fflush(stdout);
+ if (message_program_name != NULL)
+ fprintf(stderr, "%s: ", message_program_name);
+ vfprintf(stderr, fmt, args);
+ if (err)
+ fprintf(stderr, ": %s", strerror(err));
+ fprintf(stderr, "\n");
+}
+
+
+/*
+** Log a message to syslog. This is a helper function used to implement all
+** of the syslog message log handlers. It takes the same arguments as a
+** regular message handler function but with an additional priority
+** argument.
+*/
+static void
+message_log_syslog(int pri, int len, const char *fmt, va_list args, int err)
+{
+ char *buffer;
+
+ buffer = malloc(len + 1);
+ if (buffer == NULL) {
+ fprintf(stderr, "failed to malloc %u bytes at %s line %d: %s",
+ len + 1, __FILE__, __LINE__, strerror(errno));
+ exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1);
+ }
+ vsnprintf(buffer, len + 1, fmt, args);
+ syslog(pri, err ? "%s: %m" : "%s", buffer);
+ free(buffer);
+}
+
+
+/*
+** Do the same sort of wrapper to generate all of the separate syslog logging
+** functions.
+*/
+#define SYSLOG_FUNCTION(name, type) \
+ void \
+ message_log_syslog_ ## name(int l, const char *f, va_list a, int e) \
+ { \
+ message_log_syslog(LOG_ ## type, l, f, a, e); \
+ }
+SYSLOG_FUNCTION(debug, DEBUG)
+SYSLOG_FUNCTION(info, INFO)
+SYSLOG_FUNCTION(notice, NOTICE)
+SYSLOG_FUNCTION(warning, WARNING)
+SYSLOG_FUNCTION(err, ERR)
+SYSLOG_FUNCTION(crit, CRIT)
+
+
+/*
+** Enable or disable tracing for particular classes of messages.
+*/
+void
+message_trace_enable(enum message_trace type, bool enable)
+{
+ if (type > TRACE_ALL)
+ return;
+ if (type == TRACE_ALL) {
+ int i;
+
+ for (i = 0; i < TRACE_ALL; i++)
+ tracing[i] = enable;
+ } else {
+ tracing[type] = enable;
+ }
+}
+
+
+/*
+** All of the message handlers. There's a lot of code duplication here too,
+** but each one is still *slightly* different and va_start has to be called
+** multiple times, so it's hard to get rid of the duplication.
+*/
+
+#ifdef DEBUG
+void
+debug(const char *format, ...)
+{
+ va_list args;
+ message_handler_func *log;
+ int length;
+
+ if (debug_handlers == NULL)
+ return;
+ va_start(args, format);
+ length = vsnprintf(NULL, 0, format, args);
+ va_end(args);
+ if (length < 0)
+ return;
+ for (log = debug_handlers; *log != NULL; log++) {
+ va_start(args, format);
+ (**log)(length, format, args, 0);
+ va_end(args);
+ }
+}
+#elif !INN_HAVE_C99_VAMACROS && !INN_HAVE_GNU_VAMACROS
+void debug(const char *format UNUSED, ...) { }
+#endif
+
+void
+trace(enum message_trace type, const char *format, ...)
+{
+ va_list args;
+ message_handler_func *log;
+ int length;
+
+ if (trace_handlers == NULL || !tracing[type])
+ return;
+ va_start(args, format);
+ length = vsnprintf(NULL, 0, format, args);
+ va_end(args);
+ if (length < 0)
+ return;
+ for (log = trace_handlers; *log != NULL; log++) {
+ va_start(args, format);
+ (**log)(length, format, args, 0);
+ va_end(args);
+ }
+}
+
+void
+notice(const char *format, ...)
+{
+ va_list args;
+ message_handler_func *log;
+ int length;
+
+ va_start(args, format);
+ length = vsnprintf(NULL, 0, format, args);
+ va_end(args);
+ if (length < 0)
+ return;
+ for (log = notice_handlers; *log != NULL; log++) {
+ va_start(args, format);
+ (**log)(length, format, args, 0);
+ va_end(args);
+ }
+}
+
+void
+sysnotice(const char *format, ...)
+{
+ va_list args;
+ message_handler_func *log;
+ int length;
+ int error = errno;
+
+ va_start(args, format);
+ length = vsnprintf(NULL, 0, format, args);
+ va_end(args);
+ if (length < 0)
+ return;
+ for (log = notice_handlers; *log != NULL; log++) {
+ va_start(args, format);
+ (**log)(length, format, args, error);
+ va_end(args);
+ }
+}
+
+void
+warn(const char *format, ...)
+{
+ va_list args;
+ message_handler_func *log;
+ int length;
+
+ va_start(args, format);
+ length = vsnprintf(NULL, 0, format, args);
+ va_end(args);
+ if (length < 0)
+ return;
+ for (log = warn_handlers; *log != NULL; log++) {
+ va_start(args, format);
+ (**log)(length, format, args, 0);
+ va_end(args);
+ }
+}
+
+void
+syswarn(const char *format, ...)
+{
+ va_list args;
+ message_handler_func *log;
+ int length;
+ int error = errno;
+
+ va_start(args, format);
+ length = vsnprintf(NULL, 0, format, args);
+ va_end(args);
+ if (length < 0)
+ return;
+ for (log = warn_handlers; *log != NULL; log++) {
+ va_start(args, format);
+ (**log)(length, format, args, error);
+ va_end(args);
+ }
+}
+
+void
+die(const char *format, ...)
+{
+ va_list args;
+ message_handler_func *log;
+ int length;
+
+ va_start(args, format);
+ length = vsnprintf(NULL, 0, format, args);
+ va_end(args);
+ if (length >= 0)
+ for (log = die_handlers; *log != NULL; log++) {
+ va_start(args, format);
+ (**log)(length, format, args, 0);
+ va_end(args);
+ }
+ exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1);
+}
+
+void
+sysdie(const char *format, ...)
+{
+ va_list args;
+ message_handler_func *log;
+ int length;
+ int error = errno;
+
+ va_start(args, format);
+ length = vsnprintf(NULL, 0, format, args);
+ va_end(args);
+ if (length >= 0)
+ for (log = die_handlers; *log != NULL; log++) {
+ va_start(args, format);
+ (**log)(length, format, args, error);
+ va_end(args);
+ }
+ exit(message_fatal_cleanup ? (*message_fatal_cleanup)() : 1);
+}
diff --git a/src/xmalloc.c b/src/xmalloc.c
new file mode 100644
index 0000000..4d00c52
--- /dev/null
+++ b/src/xmalloc.c
@@ -0,0 +1,163 @@
+/* $Id: xmalloc.c 5381 2002-03-31 22:35:47Z rra $
+**
+** malloc routines with failure handling.
+**
+** Usage:
+**
+** extern xmalloc_handler_t memory_error;
+** extern const char *string;
+** char *buffer;
+**
+** xmalloc_error_handler = memory_error;
+** buffer = xmalloc(1024);
+** xrealloc(buffer, 2048);
+** free(buffer);
+** buffer = xcalloc(1024);
+** free(buffer);
+** buffer = xstrdup(string);
+** free(buffer);
+** buffer = xstrndup(string, 25);
+**
+** xmalloc, xcalloc, xrealloc, and xstrdup behave exactly like their C
+** library counterparts without the leading x except that they will never
+** return NULL. Instead, on error, they call xmalloc_error_handler,
+** passing it the name of the function whose memory allocation failed, the
+** amount of the allocation, and the file and line number where the
+** allocation function was invoked (from __FILE__ and __LINE__). This
+** function may do whatever it wishes, such as some action to free up
+** memory or a call to sleep to hope that system resources return. If the
+** handler returns, the interrupted memory allocation function will try its
+** allocation again (calling the handler again if it still fails).
+**
+** xstrndup behaves like xstrdup but only copies the given number of
+** characters. It allocates an additional byte over its second argument and
+** always nul-terminates the string.
+**
+** The default error handler, if none is set by the caller, prints an error
+** message to stderr and exits with exit status 1. An error handler must
+** take a const char * (function name), size_t (bytes allocated), const
+** char * (file), and int (line).
+**
+** xmalloc will return a pointer to a valid memory region on an xmalloc of 0
+** bytes, ensuring this by allocating space for one character instead of 0
+** bytes.
+**
+** The functions defined here are actually x_malloc, x_realloc, etc. The
+** header file defines macros named xmalloc, etc. that pass the file name
+** and line number to these functions.
+*/
+
+/* Used for unused parameters to silence gcc warnings. */
+#define UNUSED __attribute__((__unused__))
+
+/* Make available the bool type. */
+#if INN_HAVE_STDBOOL_H
+# include <stdbool.h>
+#else
+# undef true
+# undef false
+# define true (1)
+# define false (0)
+# ifndef __cplusplus
+# define bool int
+# endif
+#endif /* INN_HAVE_STDBOOL_H */
+
+#include <stdlib.h>
+#include <string.h>
+#include <crypt.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <grp.h>
+
+/* Failure handler takes the function, the size, the file, and the line. */
+typedef void (*xmalloc_handler_t)(const char *, size_t, const char *, int);
+
+/* Assign to this variable to choose a handler other than the default, which
+ just calls sysdie. */
+extern xmalloc_handler_t xmalloc_error_handler;
+
+/* The default error handler. */
+void
+xmalloc_fail(const char *function, size_t size, const char *file, int line)
+{
+ sysdie("failed to %s %lu bytes at %s line %d", function,
+ (unsigned long) size, file, line);
+}
+
+/* Assign to this variable to choose a handler other than the default. */
+xmalloc_handler_t xmalloc_error_handler = xmalloc_fail;
+
+void *
+x_malloc(size_t size, const char *file, int line)
+{
+ void *p;
+ size_t real_size;
+
+ real_size = (size > 0) ? size : 1;
+ p = malloc(real_size);
+ while (p == NULL) {
+ (*xmalloc_error_handler)("malloc", size, file, line);
+ p = malloc(real_size);
+ }
+ return p;
+}
+
+void *
+x_calloc(size_t n, size_t size, const char *file, int line)
+{
+ void *p;
+
+ n = (n > 0) ? n : 1;
+ size = (size > 0) ? size : 1;
+ p = calloc(n, size);
+ while (p == NULL) {
+ (*xmalloc_error_handler)("calloc", n * size, file, line);
+ p = calloc(n, size);
+ }
+ return p;
+}
+
+void *
+x_realloc(void *p, size_t size, const char *file, int line)
+{
+ void *newp;
+
+ newp = realloc(p, size);
+ while (newp == NULL && size > 0) {
+ (*xmalloc_error_handler)("realloc", size, file, line);
+ newp = realloc(p, size);
+ }
+ return newp;
+}
+
+char *
+x_strdup(const char *s, const char *file, int line)
+{
+ char *p;
+ size_t len;
+
+ len = strlen(s) + 1;
+ p = malloc(len);
+ while (p == NULL) {
+ (*xmalloc_error_handler)("strdup", len, file, line);
+ p = malloc(len);
+ }
+ memcpy(p, s, len);
+ return p;
+}
+
+char *
+x_strndup(const char *s, size_t size, const char *file, int line)
+{
+ char *p;
+
+ p = malloc(size + 1);
+ while (p == NULL) {
+ (*xmalloc_error_handler)("strndup", size + 1, file, line);
+ p = malloc(size + 1);
+ }
+ memcpy(p, s, size);
+ p[size] = '\0';
+ return p;
+}