smbd: some write time fixes
[tprouty/samba.git] / source / smbd / utmp.c
index 486c7fe06ee9f2bd2162e00b5da0d1d0082a6f3e..de6d707eafdc0f403424ff634247486d8c47654b 100644 (file)
@@ -1,13 +1,12 @@
 /* 
-   Unix SMB/Netbios implementation.
-   Version 2.0
+   Unix SMB/CIFS implementation.
    utmp routines
    Copyright (C) T.D.Lee@durham.ac.uk 1999
    Heavily modified by Andrew Bartlett and Tridge, April 2001
    
    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
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
 
-#ifdef WITH_UTMP
-
 /****************************************************************************
 Reflect connection status in utmp/wtmp files.
        T.D.Lee@durham.ac.uk  September 1999
@@ -82,11 +78,11 @@ lastlog:
 
 Notes:
        Each connection requires a small number (starting at 0, working up)
-       to represent the line (unum).  This must be unique within and across
-       all smbd processes.
+       to represent the line.  This must be unique within and across all
+       smbd processes.  It is the 'id_num' from Samba's session.c code.
 
        The 4 byte 'ut_id' component is vital to distinguish connections,
-       of which there could be several hundered or even thousand.
+       of which there could be several hundred or even thousand.
        Entries seem to be printable characters, with optional NULL pads.
 
        We need to be distinct from other entries in utmp/wtmp.
@@ -106,14 +102,28 @@ Notes:
        Arbitrarily I have chosen to use a distinctive 'SM' for the
        first two bytes.
 
-       The remaining two encode the "unum" (see above).
-
-       For "utmp consolidate" the suggestion was made to encode the pid into
-       those remaining two bytes (16 bits).  But recent UNIX (e.g Solaris 8)
-       is migrating to pids > 16 bits, so we ought not to do this.
+       The remaining two bytes encode the session 'id_num' (see above).
+       Our caller (session.c) should note our 16-bit limitation.
 
 ****************************************************************************/
 
+#ifndef WITH_UTMP
+/*
+ * Not WITH_UTMP?  Simply supply dummy routines.
+ */
+
+void sys_utmp_claim(const char *username, const char *hostname,
+                       const char *ip_addr_str,
+                       const char *id_str, int id_num)
+{}
+
+void sys_utmp_yield(const char *username, const char *hostname,
+                       const char *ip_addr_str,
+                       const char *id_str, int id_num)
+{}
+
+#else /* WITH_UTMP */
+
 #include <utmp.h>
 
 #ifdef HAVE_UTMPX_H
@@ -127,35 +137,9 @@ Notes:
 #endif
 
 /****************************************************************************
-obtain/release a small number (0 upwards) unique within and across smbds
+ Default paths to various {u,w}tmp{,x} files.
 ****************************************************************************/
-/*
- * Need a "small" number to represent this connection, unique within this
- * smbd and across all smbds.
- *
- * claim:
- *     Start at 0, hunt up for free, unique number "unum" by attempting to
- *     store it as a key in a tdb database:
- *             key: unum               data: pid+conn  
- *     Also store its inverse, ready for yield function:
- *             key: pid+conn           data: unum
- *
- * yield:
- *     Find key: pid+conn; data is unum;  delete record
- *     Find key: unum ; delete record.
- *
- * Comment:
- *     The claim algorithm (a "for" loop attempting to store numbers in a tdb
- *     database) will be increasingly inefficient with larger numbers of
- *     connections.  Is it possible to write a suitable primitive within tdb?
- *
- *     However, by also storing the inverse key/data pair, we at least make
- *     the yield algorithm efficient.
- */
 
-/****************************************************************************
-Default paths to various {u,w}tmp{,x} files
-****************************************************************************/
 #ifdef HAVE_UTMPX_H
 
 static const char *ux_pathname =
@@ -223,30 +207,36 @@ static const char *ll_pathname =
  * utmp{,x}:  try "utmp dir", then default (a define)
  * wtmp{,x}:  try "wtmp dir", then "utmp dir", then default (a define)
  */
-static void uw_pathname(pstring fname, const char *uw_name, const char *uw_default)
+static char *uw_pathname(TALLOC_CTX *ctx,
+               const char *uw_name,
+               const char *uw_default)
 {
-       pstring dirname;
-
-       pstrcpy(dirname, "");
+       char *dirname = NULL;
 
        /* For w-files, first look for explicit "wtmp dir" */
        if (uw_name[0] == 'w') {
-               pstrcpy(dirname,lp_wtmpdir());
-               trim_string(dirname,"","/");
+               dirname = talloc_strdup(ctx, lp_wtmpdir());
+               if (!dirname) {
+                       return NULL;
+               }
+               trim_char(dirname,'\0','/');
        }
 
        /* For u-files and non-explicit w-dir, look for "utmp dir" */
-       if (dirname == 0 || strlen(dirname) == 0) {
-               pstrcpy(dirname,lp_utmpdir());
-               trim_string(dirname,"","/");
+       if ((dirname == NULL) || (strlen(dirname) == 0)) {
+               dirname = talloc_strdup(ctx, lp_utmpdir());
+               if (!dirname) {
+                       return NULL;
+               }
+               trim_char(dirname,'\0','/');
        }
 
        /* If explicit directory above, use it */
-       if (dirname != 0 && strlen(dirname) != 0) {
-               pstrcpy(fname, dirname);
-               pstrcat(fname, "/");
-               pstrcat(fname, uw_name);
-               return;
+       if (dirname && strlen(dirname) != 0) {
+               return talloc_asprintf(ctx,
+                               "%s/%s",
+                               dirname,
+                               uw_name);
        }
 
        /* No explicit directory: attempt to use default paths */
@@ -256,14 +246,15 @@ static void uw_pathname(pstring fname, const char *uw_name, const char *uw_defau
                 */
                DEBUG(2,("uw_pathname: unable to determine pathname\n"));
        }
-       pstrcpy(fname, uw_default);
+       return talloc_strdup(ctx, uw_default);
 }
 
 #ifndef HAVE_PUTUTLINE
 /****************************************************************************
-Update utmp file directly.  No subroutine interface: probably a BSD system.
+ Update utmp file directly.  No subroutine interface: probably a BSD system.
 ****************************************************************************/
-static void pututline_my(pstring uname, struct utmp *u, BOOL claim)
+
+static void pututline_my(const char *uname, struct utmp *u, bool claim)
 {
        DEBUG(1,("pututline_my: not yet implemented\n"));
        /* BSD implementor: may want to consider (or not) adjusting "lastlog" */
@@ -271,11 +262,13 @@ static void pututline_my(pstring uname, struct utmp *u, BOOL claim)
 #endif /* HAVE_PUTUTLINE */
 
 #ifndef HAVE_UPDWTMP
+
 /****************************************************************************
-Update wtmp file directly.  No subroutine interface: probably a BSD system.
-Credit: Michail Vidiassov <master@iaas.msu.ru>
+ Update wtmp file directly.  No subroutine interface: probably a BSD system.
+ Credit: Michail Vidiassov <master@iaas.msu.ru>
 ****************************************************************************/
-static void updwtmp_my(pstring wname, struct utmp *u, BOOL claim)
+
+static void updwtmp_my(const char *wname, struct utmp *u, bool claim)
 {
        int fd;
        struct stat buf;
@@ -294,8 +287,12 @@ static void updwtmp_my(pstring wname, struct utmp *u, BOOL claim)
                 *      man page appears not to specify (hints non-NULL)
                 *      A correspondent suggest at least ut_name should be NULL
                 */
-               memset((char *)&(u->ut_name), '\0', sizeof(u->ut_name));
-               memset((char *)&(u->ut_host), '\0', sizeof(u->ut_host));
+#if defined(HAVE_UT_UT_NAME)
+               memset((char *)&u->ut_name, '\0', sizeof(u->ut_name));
+#endif
+#if defined(HAVE_UT_UT_HOST)
+               memset((char *)&u->ut_host, '\0', sizeof(u->ut_host));
+#endif
        }
        /* Stolen from logwtmp function in libutil.
         * May be more locking/blocking is needed?
@@ -311,16 +308,21 @@ static void updwtmp_my(pstring wname, struct utmp *u, BOOL claim)
 #endif /* HAVE_UPDWTMP */
 
 /****************************************************************************
-Update via utmp/wtmp (not utmpx/wtmpx)
+ Update via utmp/wtmp (not utmpx/wtmpx).
 ****************************************************************************/
-static void utmp_nox_update(struct utmp *u, const char *host, BOOL claim)
+
+static void utmp_nox_update(struct utmp *u, bool claim)
 {
-       pstring uname, wname;
+       char *uname = NULL;
+       char *wname = NULL;
 #if defined(PUTUTLINE_RETURNS_UTMP)
        struct utmp *urc;
 #endif /* PUTUTLINE_RETURNS_UTMP */
 
-       uw_pathname(uname, "utmp", ut_pathname);
+       uname = uw_pathname(talloc_tos(), "utmp", ut_pathname);
+       if (!uname) {
+               return;
+       }
        DEBUG(2,("utmp_nox_update: uname:%s\n", uname));
 
 #ifdef HAVE_PUTUTLINE
@@ -348,7 +350,10 @@ static void utmp_nox_update(struct utmp *u, const char *host, BOOL claim)
        }
 #endif /* HAVE_PUTUTLINE */
 
-       uw_pathname(wname, "wtmp", wt_pathname);
+       wname = uw_pathname(talloc_tos(), "wtmp", wt_pathname);
+       if (!wname) {
+               return;
+       }
        DEBUG(2,("utmp_nox_update: wname:%s\n", wname));
        if (strlen(wname) != 0) {
 #ifdef HAVE_UPDWTMP
@@ -369,35 +374,68 @@ static void utmp_nox_update(struct utmp *u, const char *host, BOOL claim)
 }
 
 /****************************************************************************
-Update via utmpx/wtmpx (preferred) or via utmp/wtmp
+ Copy a string in the utmp structure.
 ****************************************************************************/
-static void sys_utmp_update(struct utmp *u, const char *hostname, BOOL claim)
+
+static void utmp_strcpy(char *dest, const char *src, size_t n)
+{
+       size_t len = 0;
+
+       memset(dest, '\0', n);
+       if (src)
+               len = strlen(src);
+       if (len >= n) {
+               memcpy(dest, src, n);
+       } else {
+               if (len)
+                       memcpy(dest, src, len);
+       }
+}
+
+/****************************************************************************
+ Update via utmpx/wtmpx (preferred) or via utmp/wtmp.
+****************************************************************************/
+
+static void sys_utmp_update(struct utmp *u, const char *hostname, bool claim)
 {
 #if !defined(HAVE_UTMPX_H)
        /* No utmpx stuff.  Drop to non-x stuff */
-       utmp_nox_update(u, hostname, claim);
+       utmp_nox_update(u, claim);
 #elif !defined(HAVE_PUTUTXLINE)
        /* Odd.  Have utmpx.h but no "pututxline()".  Drop to non-x stuff */
        DEBUG(1,("utmp_update: have utmpx.h but no pututxline() function\n"));
-       utmp_nox_update(u, hostname, claim);
+       utmp_nox_update(u, claim);
 #elif !defined(HAVE_GETUTMPX)
        /* Odd.  Have utmpx.h but no "getutmpx()".  Drop to non-x stuff */
        DEBUG(1,("utmp_update: have utmpx.h but no getutmpx() function\n"));
-       utmp_nox_update(u, hostname, claim);
+       utmp_nox_update(u, claim);
+#elif !defined(HAVE_UPDWTMPX)
+       /* Have utmpx.h but no "updwtmpx()".  Drop to non-x stuff */
+       DEBUG(1,("utmp_update: have utmpx.h but no updwtmpx() function\n"));
+       utmp_nox_update(u, claim);
 #else
-       pstring uname, wname;
+       char *uname = NULL;
+       char *wname = NULL;
        struct utmpx ux, *uxrc;
 
        getutmpx(u, &ux);
 
 #if defined(HAVE_UX_UT_SYSLEN)
-       ux.ut_syslen = strlen(hostname) + 1;    /* include end NULL */
+       if (hostname)
+               ux.ut_syslen = strlen(hostname) + 1;    /* include end NULL */
+       else
+               ux.ut_syslen = 0;
 #endif
-       safe_strcpy(ux.ut_host, hostname, sizeof(ux.ut_host)-1);
+#if defined(HAVE_UT_UT_HOST)
+       utmp_strcpy(ux.ut_host, hostname, sizeof(ux.ut_host));
+#endif
+
+       uname = uw_pathname(talloc_tos(), "utmpx", ux_pathname);
+       wname = uw_pathname(talloc_tos(), "wtmpx", wx_pathname);
+       if (uname && wname) {
+               DEBUG(2,("utmp_update: uname:%s wname:%s\n", uname, wname));
+       }
 
-       uw_pathname(uname, "utmpx", ux_pathname);
-       uw_pathname(wname, "wtmpx", wx_pathname);
-       DEBUG(2,("utmp_update: uname:%s wname:%s\n", uname, wname));
        /*
         * Check for either uname or wname being empty.
         * Some systems, such as Redhat 6, have a "utmpx.h" which doesn't
@@ -405,8 +443,8 @@ static void sys_utmp_update(struct utmp *u, const char *hostname, BOOL claim)
         * Also, our local installation has not provided an override.
         * Drop to non-x method.  (E.g. RH6 has good defaults in "utmp.h".)
         */
-       if ((strlen(uname) == 0) || (strlen(wname) == 0)) {
-               utmp_nox_update(u, hostname, claim);
+       if (!uname || !wname || (strlen(uname) == 0) || (strlen(wname) == 0)) {
+               utmp_nox_update(u, claim);
        } else {
                utmpxname(uname);
                setutxent();
@@ -423,13 +461,14 @@ static void sys_utmp_update(struct utmp *u, const char *hostname, BOOL claim)
 
 #if defined(HAVE_UT_UT_ID)
 /****************************************************************************
-encode the unique connection number into "ut_id"
+ Encode the unique connection number into "ut_id".
 ****************************************************************************/
+
 static int ut_id_encode(int i, char *fourbyte)
 {
        int nbase;
-       char *ut_id_encstr = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
-       
+       const char *ut_id_encstr = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
        fourbyte[0] = 'S';
        fourbyte[1] = 'M';
 
@@ -451,12 +490,13 @@ static int ut_id_encode(int i, char *fourbyte)
 
 
 /*
-  fill a system utmp structure given all the info we can gather 
+  fill a system utmp structure given all the info we can gather
 */
-static BOOL sys_utmp_fill(struct utmp *u,
-                         const char *username, const char *hostname,
-                         const char *id_str, int id_num)
-{                        
+static bool sys_utmp_fill(struct utmp *u,
+                       const char *username, const char *hostname,
+                       const char *ip_addr_str,
+                       const char *id_str, int id_num)
+{
        struct timeval timeval;
 
        /*
@@ -466,51 +506,61 @@ static BOOL sys_utmp_fill(struct utmp *u,
         *      rather than to try to detect and optimise.
         */
 #if defined(HAVE_UT_UT_USER)
-       safe_strcpy(u->ut_user, username, sizeof(u->ut_user)-1);
+       utmp_strcpy(u->ut_user, username, sizeof(u->ut_user));
 #elif defined(HAVE_UT_UT_NAME)
-       safe_strcpy(u->ut_name, username, sizeof(u->ut_name)-1);
+       utmp_strcpy(u->ut_name, username, sizeof(u->ut_name));
 #endif
 
        /*
         * ut_line:
         *      If size limit proves troublesome, then perhaps use "ut_id_encode()".
-        *
-        * Temporary variable "line_tmp" avoids trouble:
-        * o  with unwanted trailing NULL if ut_line full;
-        * o  with overflow if ut_line would be more than full.
         */
        if (strlen(id_str) > sizeof(u->ut_line)) {
-               DEBUG(1,("id_str [%s] is too long for %d char utmp field\n",
-                        id_str, sizeof(u->ut_line)));
+               DEBUG(1,("id_str [%s] is too long for %lu char utmp field\n",
+                        id_str, (unsigned long)sizeof(u->ut_line)));
                return False;
        }
-       memcpy(u->ut_line, id_str, sizeof(u->ut_line));
+       utmp_strcpy(u->ut_line, id_str, sizeof(u->ut_line));
 
 #if defined(HAVE_UT_UT_PID)
        u->ut_pid = sys_getpid();
 #endif
 
 /*
- * ut_time, ut_tv: 
+ * ut_time, ut_tv:
  *     Some have one, some the other.  Many have both, but defined (aliased).
  *     It is easier and clearer simply to let the following take its course.
  *     But note that we do the more precise ut_tv as the final assignment.
  */
 #if defined(HAVE_UT_UT_TIME)
-       gettimeofday(&timeval, NULL);
+       GetTimeOfDay(&timeval);
        u->ut_time = timeval.tv_sec;
 #elif defined(HAVE_UT_UT_TV)
-       gettimeofday(&timeval, NULL);
+       GetTimeOfDay(&timeval);
        u->ut_tv = timeval;
 #else
 #error "with-utmp must have UT_TIME or UT_TV"
 #endif
 
 #if defined(HAVE_UT_UT_HOST)
-       safe_strcpy(u->ut_host, hostname, sizeof(u->ut_host)-1);
+       utmp_strcpy(u->ut_host, hostname, sizeof(u->ut_host));
 #endif
-
-#if defined(HAVE_UT_UT_ADDR)
+#if defined(HAVE_IPV6) && defined(HAVE_UT_UT_ADDR_V6)
+       memset(&u->ut_addr_v6, '\0', sizeof(u->ut_addr_v6));
+       if (ip_addr_str) {
+               struct in6_addr addr;
+               if (inet_pton(AF_INET6, ip_addr_str, &addr) > 0) {
+                       memcpy(&u->ut_addr_v6, &addr, sizeof(addr));
+               }
+       }
+#elif defined(HAVE_UT_UT_ADDR)
+       memset(&u->ut_addr, '\0', sizeof(u->ut_addr));
+       if (ip_addr_str) {
+               struct in_addr addr;
+               if (inet_pton(AF_INET, ip_addr_str, &addr) > 0) {
+                       memcpy(&u->ut_addr, &addr, sizeof(addr));
+               }
+       }
        /*
         * "(unsigned long) ut_addr" apparently exists on at least HP-UX 10.20.
         * Volunteer to implement, please ...
@@ -528,10 +578,12 @@ static BOOL sys_utmp_fill(struct utmp *u,
 }
 
 /****************************************************************************
-close a connection
+ Close a connection.
 ****************************************************************************/
-void sys_utmp_yield(const char *username, const char *hostname, 
-                   const char *id_str, int id_num)
+
+void sys_utmp_yield(const char *username, const char *hostname,
+                       const char *ip_addr_str,
+                       const char *id_str, int id_num)
 {
        struct utmp u;
 
@@ -546,16 +598,19 @@ void sys_utmp_yield(const char *username, const char *hostname,
        u.ut_type = DEAD_PROCESS;
 #endif
 
-       if (!sys_utmp_fill(&u, username, hostname, id_str, id_num)) return;
+       if (!sys_utmp_fill(&u, username, hostname, ip_addr_str, id_str, id_num))
+               return;
 
        sys_utmp_update(&u, NULL, False);
 }
 
 /****************************************************************************
-claim a entry in whatever utmp system the OS uses
+ Claim a entry in whatever utmp system the OS uses.
 ****************************************************************************/
-void sys_utmp_claim(const char *username, const char *hostname, 
-                   const char *id_str, int id_num)
+
+void sys_utmp_claim(const char *username, const char *hostname,
+                       const char *ip_addr_str,
+                       const char *id_str, int id_num)
 {
        struct utmp u;
 
@@ -565,11 +620,10 @@ void sys_utmp_claim(const char *username, const char *hostname,
        u.ut_type = USER_PROCESS;
 #endif
 
-       if (!sys_utmp_fill(&u, username, hostname, id_str, id_num)) return;
+       if (!sys_utmp_fill(&u, username, hostname, ip_addr_str, id_str, id_num))
+               return;
 
        sys_utmp_update(&u, hostname, True);
 }
 
-#else /* WITH_UTMP */
- void dummy_utmp(void) {}
-#endif
+#endif /* WITH_UTMP */