Signed-off-by: Maximilian Wilhelm <m...@rfc2324.org>
---
 wtmp.c |  333 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 wtmp.h |   27 +++++
 2 files changed, 360 insertions(+), 0 deletions(-)
 create mode 100644 wtmp.c
 create mode 100644 wtmp.h

diff --git a/wtmp.c b/wtmp.c
new file mode 100644
index 0000000..39822df
--- /dev/null
+++ b/wtmp.c
@@ -0,0 +1,333 @@
+/*
+ * OpenVPN wtmp support
+ *
+ * Copyright: 2007 Maximilian Wilhelm <m...@rfc2324.org>
+ *
+ */
+
+#include "config.h"
+#include "syshead.h"
+
+#ifdef ENABLE_WTMP
+
+#define _GNU_SOURCE
+
+#include <assert.h>
+#include <dirent.h>    /* opendir() */
+#include <stdio.h>     /* fopen() */
+#include <stdlib.h>    /* *alloc(), free() */
+#include <string.h>    /* strncpy(), strlen() */
+#include <sys/stat.h>  /* stat() */
+#include <sys/time.h>  /* struct timeval */
+#include <sys/types.h> /* opendir() */
+#include <utmp.h>      /* utmp foo */
+
+#include "openvpn.h"   /* struct context* */
+#include "forward.h"
+#include "forward-inline.h"
+#include "multi.h"     /* struct multi_instance */
+#include "buffer.h"    /* struct buffer */
+
+#include "wtmp.h"
+
+int wtmp_active = 0;
+
+#define HAVE_ENDUTENT 1
+
+#define OVPN_WTMP_DEBUG 1
+
+/*
+ * Initialize wtmp subsystem
+ */
+int
+wtmp_init (unsigned int server_id, const char *wtmp_file_path)
+{
+#ifdef OVPN_WTMP_DEBUG
+       fprintf (stderr, "%s() called.\n", __FUNCTION__);
+#endif
+
+       struct stat wtmp_stat;
+       char *wtmp_file_dirname;
+       char *wtmp_file_path_copy;
+
+       /* Only initialize once */
+       if (wtmp_active == 1)
+               return 1;
+
+#ifdef OVPN_WTMP_DEBUG
+       fprintf (stderr, "wtmp_file_path = %s\n", wtmp_file_path);
+#endif
+
+       wtmp_file_path_copy = strdup (wtmp_file_path);
+       wtmp_file_dirname = dirname (wtmp_file_path_copy);
+
+       /* Check if WTMP_DIR exists and try to create it if not */
+       if (! opendir (wtmp_file_dirname)) {
+               fprintf (stderr, "%s: wtmp_file_dirname %s does not exist. 
Trying to create it.\n",
+                       __FUNCTION__, wtmp_file_dirname);
+
+               if (mkdir (wtmp_file_dirname, 0700)) {
+                       fprintf (stderr, "%s: Could not create directory %s\n",
+                               __FUNCTION__, wtmp_file_dirname);
+                       wtmp_active = 0;
+                       goto out;
+               }
+
+               fprintf (stderr, "%s: Successfully created wtmp_file_dirname 
%s.\n",
+                       __FUNCTION__, wtmp_file_dirname);
+       }
+
+       /* Check if wtmp_file_path exists and try to create it if not */
+       if (stat (wtmp_file_path, &wtmp_stat)) {
+               fprintf (stderr, "%s: wtmp_file %s does not exist or is not 
accessable. Trying to create it.\n",
+                       __FUNCTION__, wtmp_file_path);
+
+               /* Touch wtmp_file */
+               if (! fopen (wtmp_file_path, "a")) {
+                       fprintf (stderr, "%s: Could not create file %s.\n",
+                               __FUNCTION__, wtmp_file_path);
+                               wtmp_active = 0;
+                               goto out;
+               }
+
+               fprintf (stderr, "%s: Successfully created wtmp_file %s.\n",
+                       __FUNCTION__, wtmp_file_path);
+       } /* [ ! -f wtmp_file ] */
+
+       /* OK wtmp_file is (now) there, use it */
+       utmpname (wtmp_file_path);
+
+       /* Everything is fine */
+       wtmp_active = 1;
+
+out:
+       free (wtmp_file_path_copy);
+
+       return wtmp_active;
+}
+
+
+void
+wtmp_start (const struct multi_instance *mi)
+{
+#ifdef OVPN_WTMP_DEBUG
+       fprintf (stderr, "%s() called.\n", __FUNCTION__);
+#endif
+
+       struct utmp entry;
+        struct sockaddr_in *remote_addr;
+
+       char *temp;
+
+       if (wtmp_active == 0)
+               return;
+
+       /* Get IP address of this connection
+        * FIXME: IPv6?!
+        */
+       remote_addr = get_remote_sockaddr_from_multi_instance (mi);
+       if (! remote_addr) {
+               fprintf (stderr, "%s: get_remote_sockaddr_from_multi_instance 
(mi) failed.\n",
+                       __FUNCTION__);
+       }
+
+       /* Clear entry */
+       memset(&entry, '\0', sizeof (entry));
+
+       /* This entry is a user process */
+       entry.ut_type = USER_PROCESS;
+
+       /* The connection started now, so set epoch time */
+       set_utmp_time (&entry);
+
+       /* Set the user name */
+       set_utmp_username (mi, &entry);
+
+       /* Set the line */
+       set_utmp_line (mi, &entry);
+
+       /* Store the IP we gave this client as the hostname */
+       set_utmp_hostname (mi, &entry);
+
+       /* Use the first 4 chars of the session ID as id */
+       set_utmp_id (mi, &entry);
+
+       /* Store the remote client IP(v4), if available */
+       entry.ut_addr = remote_addr->sin_addr.s_addr;
+
+#ifdef OVPN_WTMP_DEBUG
+fprintf (stderr, "%s: entry.ut_line = \"%s\"\n", __FUNCTION__, entry.ut_line);
+fprintf (stderr, "%s: entry.ut_id = \"%s\"\n",   __FUNCTION__, entry.ut_id);
+fprintf (stderr, "%s: entry.ut_user = \"%s\"\n", __FUNCTION__, entry.ut_user);
+fprintf (stderr, "%s: entry.ut_host = \"%s\"\n", __FUNCTION__, entry.ut_host);
+
+unsigned int ip = ntohl (entry.ut_addr);
+fprintf (stderr, "%s: entry.ut_addr = %d.%d.%d.%d\n", __FUNCTION__,  ip >> 24, 
(ip >> 16) & 0xff, (ip >> 8) & 0xff, (ip >> 0) & 0xff);
+#endif
+
+       updwtmp (mi->context.options.wtmp_file, &entry);
+}
+
+
+void
+wtmp_stop (const struct multi_instance *mi)
+{
+#ifdef OVPN_WTMP_DEBUG
+       fprintf (stderr, "%s() called.\n", __FUNCTION__);
+#endif
+
+       struct utmp entry;
+
+       if (wtmp_active == 0)
+               return;
+
+       /* Clear entry */
+       memset (&entry, '\0', sizeof (entry));
+
+       /* The connection stopped now, so set epoch time */
+       set_utmp_time (&entry);
+
+       /* He's dead jim */
+       entry.ut_type = DEAD_PROCESS;
+
+       /* Set current epoch time */
+       set_utmp_time (&entry);
+
+       /* Set the line */
+       set_utmp_line (mi, &entry);
+
+       /* Use the first 4 chars of the session ID as id */
+       set_utmp_id (mi, &entry);
+
+       entry.ut_host[0] = 0;
+
+#ifdef OVPN_WTMP_DEBUG
+fprintf (stderr, "%s: entry.ut_line = %s\n", __FUNCTION__, entry.ut_line);
+fprintf (stderr, "%s: entry.ut_id = %s\n", __FUNCTION__, entry.ut_id);
+#endif
+
+       updwtmp (mi->context.options.wtmp_file, &entry);
+}
+
+
+
+
+
+/* Build up and store an utmp line */
+void
+set_utmp_line (const struct multi_instance *mi, struct utmp *entry)
+{
+       struct gc_arena gc = gc_new ();
+       char *temp;
+
+       char line[UT_LINESIZE];
+
+       /* Should be config option */
+       unsigned int server_num = 1;
+
+       assert (mi);
+       assert (entry);
+
+       temp = format_hex_ex 
(mi->context.c2.tls_multi->session[TM_ACTIVE].session_id.id, SID_SIZE, 0, 4, 
"", &gc);
+
+       if (temp) {
+               snprintf (entry->ut_line, UT_LINESIZE, "ovpn%02d-%s", 
server_num, temp);
+       } else {
+               snprintf (entry->ut_line, UT_LINESIZE, "ovpn%02d", server_num);
+       }
+}
+
+
+/* Store the first 4 chars of the session_id as the utmp id */
+void
+set_utmp_id (const struct multi_instance *mi, struct utmp *entry)
+{
+       struct gc_arena gc = gc_new ();
+       char *temp;
+
+       assert (mi);
+       assert (entry);
+
+       temp = format_hex_ex 
(mi->context.c2.tls_multi->session[TM_ACTIVE].session_id.id, SID_SIZE, 0, 4, " 
", &gc);
+       if (temp) {
+               snprintf (entry->ut_id, 4, "%s", temp);
+       }
+}
+
+
+/* Store a human readable format of the VPN ip address we gave to the client 
as utmp hostname */
+void
+set_utmp_hostname (const struct multi_instance *mi, struct utmp *entry)
+{
+       unsigned int ip;
+
+       assert (mi);
+       assert (entry);
+
+       memset (entry->ut_host, '\0', UT_HOSTSIZE);
+
+       ip =  mi->context.c2.push_ifconfig_local;
+       snprintf (entry->ut_host, UT_HOSTSIZE, "%d.%d.%d.%d", ip >> 24, (ip >> 
16) & 0xff, (ip >> 8) & 0xff, (ip >> 0) & 0xff);
+}
+
+
+/* Store the username of the current session in the utmp entry */
+void
+set_utmp_username (const struct multi_instance *mi, struct utmp *entry)
+{
+       const char *username;
+
+       assert (mi);
+       assert (entry);
+
+       username = tls_common_name (mi->context.c2.tls_multi, false);
+       if (username) {
+               snprintf (entry->ut_user, UT_NAMESIZE, "%s", username);
+       } else {
+               snprintf (entry->ut_user, UT_NAMESIZE, "unknown");
+       }
+}
+
+
+/* Get a sockaddr_in struct containing the real IP of the remote host */
+static struct sockaddr_in *
+get_remote_sockaddr_from_multi_instance (const struct multi_instance *mi)
+{
+       struct buffer buf ;
+       struct link_socket_actual *lsa;
+
+       assert (mi);
+
+       lsa = calloc (1, sizeof (struct link_socket_actual));
+       if (! lsa)
+               return NULL;
+
+       link_socket_get_outgoing_addr (&buf, get_link_socket_info 
(&mi->context), &lsa);
+
+       return &lsa->dest.sa;
+}
+
+
+/*
+ * Some helper functions, partly stolen from OpenSSH/loginrec.c
+ *
+ */
+
+
+/* Store the current time in utmp struct */
+static void
+set_utmp_time (struct utmp *ut)
+{
+       struct timeval tv;
+
+       assert (ut);
+
+       gettimeofday(&tv, NULL);
+       ut->ut_tv.tv_sec = tv.tv_sec;
+       ut->ut_tv.tv_usec = tv.tv_usec;
+
+       ut->ut_time = tv.tv_sec;
+}
+#endif /* ENABLE_WTMP */
+
+
diff --git a/wtmp.h b/wtmp.h
new file mode 100644
index 0000000..c43b472
--- /dev/null
+++ b/wtmp.h
@@ -0,0 +1,27 @@
+#ifndef _OPENVPN_WTMP_H
+#define _OPENVPN_WTMP_H
+
+#include <utmp.h>
+
+#include "multi.h"
+
+#define OVPN_WTMP_DIR  "/tmp"
+#define OVPN_WTMP_FILENAME "ovpnwtmp"
+
+enum local_remote { LOCAL, REMOTE };
+
+int wtmp_init ();
+
+void wtmp_start (const struct multi_instance *mi);
+void wtmp_stop (const struct multi_instance *mi);
+
+static void set_utmp_time (struct utmp *ut);
+
+void set_utmp_hostname (const struct multi_instance *mi, struct utmp *entry);
+void set_utmp_id (const struct multi_instance *mi, struct utmp *entry);
+void set_utmp_line (const struct multi_instance *mi, struct utmp *entry);
+void set_utmp_username (const struct multi_instance *mi, struct utmp *entry);
+
+static struct sockaddr_in * get_remote_sockaddr_from_multi_instance (const 
struct multi_instance *mi);
+
+#endif /* _OPENVPN_WTMP_H */
-- 
1.5.2.4


Reply via email to