smbd/utmp.c

説明を見る。
00001 /* 
00002    Unix SMB/CIFS implementation.
00003    utmp routines
00004    Copyright (C) T.D.Lee@durham.ac.uk 1999
00005    Heavily modified by Andrew Bartlett and Tridge, April 2001
00006    
00007    This program is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU General Public License as published by
00009    the Free Software Foundation; either version 2 of the License, or
00010    (at your option) any later version.
00011    
00012    This program is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015    GNU General Public License for more details.
00016    
00017    You should have received a copy of the GNU General Public License
00018    along with this program; if not, write to the Free Software
00019    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00020 */
00021 
00022 #include "includes.h"
00023 
00024 /****************************************************************************
00025 Reflect connection status in utmp/wtmp files.
00026         T.D.Lee@durham.ac.uk  September 1999
00027 
00028         With grateful thanks since then to many who have helped port it to
00029         different operating systems.  The variety of OS quirks thereby
00030         uncovered is amazing...
00031 
00032 Hints for porting:
00033         o  Always attempt to use programmatic interface (pututline() etc.)
00034            Indeed, at present only programmatic use is supported.
00035         o  The only currently supported programmatic interface to "wtmp{,x}"
00036            is through "updwtmp*()" routines.
00037         o  The "x" (utmpx/wtmpx; HAVE_UTMPX_H) seems preferable.
00038         o  The HAVE_* items should identify supported features.
00039         o  If at all possible, avoid "if defined(MY-OS)" constructions.
00040 
00041 OS observations and status:
00042         Almost every OS seems to have its own quirks.
00043 
00044         Solaris 2.x:
00045                 Tested on 2.6 and 2.7; should be OK on other flavours.
00046         AIX:
00047                 Apparently has utmpx.h but doesn't implement.
00048         OSF:
00049                 Has utmpx.h, but (e.g.) no "getutmpx()".  (Is this like AIX ?)
00050         Redhat 6:
00051                 utmpx.h seems not to set default filenames.  non-x better.
00052         IRIX 6.5:
00053                 Not tested.  Appears to have "x".
00054         HP-UX 9.x:
00055                 Not tested.  Appears to lack "x".
00056         HP-UX 10.x:
00057                 Not tested.
00058                 "updwtmp*()" routines seem absent, so no current wtmp* support.
00059                 Has "ut_addr": probably trivial to implement (although remember
00060                 that IPv6 is coming...).
00061 
00062         FreeBSD:
00063                 No "putut*()" type of interface.
00064                 No "ut_type" and associated defines. 
00065                 Write files directly.  Alternatively use its login(3)/logout(3).
00066         SunOS 4:
00067                 Not tested.  Resembles FreeBSD, but no login()/logout().
00068 
00069 lastlog:
00070         Should "lastlog" files, if any, be updated?
00071         BSD systems (SunOS 4, FreeBSD):
00072                 o  Prominent mention on man pages.
00073         System-V (e.g. Solaris 2):
00074                 o  No mention on man pages, even under "man -k".
00075                 o  Has a "/var/adm/lastlog" file, but pututxline() etc. seem
00076                    not to touch it.
00077                 o  Despite downplaying (above), nevertheless has <lastlog.h>.
00078         So perhaps UN*X "lastlog" facility is intended for tty/terminal only?
00079 
00080 Notes:
00081         Each connection requires a small number (starting at 0, working up)
00082         to represent the line.  This must be unique within and across all
00083         smbd processes.  It is the 'id_num' from Samba's session.c code.
00084 
00085         The 4 byte 'ut_id' component is vital to distinguish connections,
00086         of which there could be several hundred or even thousand.
00087         Entries seem to be printable characters, with optional NULL pads.
00088 
00089         We need to be distinct from other entries in utmp/wtmp.
00090 
00091         Observed things: therefore avoid them.  Add to this list please.
00092         From Solaris 2.x (because that's what I have):
00093                 'sN'    : run-levels; N: [0-9]
00094                 'co'    : console
00095                 'CC'    : arbitrary things;  C: [a-z]
00096                 'rXNN'  : rlogin;  N: [0-9]; X: [0-9a-z]
00097                 'tXNN'  : rlogin;  N: [0-9]; X: [0-9a-z]
00098                 '/NNN'  : Solaris CDE
00099                 'ftpZ'  : ftp (Z is the number 255, aka 0377, aka 0xff)
00100         Mostly a record uses the same 'ut_id' in both "utmp" and "wtmp",
00101         but differences have been seen.
00102 
00103         Arbitrarily I have chosen to use a distinctive 'SM' for the
00104         first two bytes.
00105 
00106         The remaining two bytes encode the session 'id_num' (see above).
00107         Our caller (session.c) should note our 16-bit limitation.
00108 
00109 ****************************************************************************/
00110 
00111 #ifndef WITH_UTMP
00112 /*
00113  * Not WITH_UTMP?  Simply supply dummy routines.
00114  */
00115 
00116 void sys_utmp_claim(const char *username, const char *hostname, 
00117                     struct in_addr *ipaddr,
00118                     const char *id_str, int id_num)
00119 {}
00120 
00121 void sys_utmp_yield(const char *username, const char *hostname, 
00122                     struct in_addr *ipaddr,
00123                     const char *id_str, int id_num)
00124 {}
00125 
00126 #else /* WITH_UTMP */
00127 
00128 #include <utmp.h>
00129 
00130 #ifdef HAVE_UTMPX_H
00131 #include <utmpx.h>
00132 #endif
00133 
00134 /* BSD systems: some may need lastlog.h (SunOS 4), some may not (FreeBSD) */
00135 /* Some System-V systems (e.g. Solaris 2) declare this too. */
00136 #ifdef HAVE_LASTLOG_H
00137 #include <lastlog.h>
00138 #endif
00139 
00140 /****************************************************************************
00141  Default paths to various {u,w}tmp{,x} files.
00142 ****************************************************************************/
00143 
00144 #ifdef  HAVE_UTMPX_H
00145 
00146 static const char *ux_pathname =
00147 # if defined (UTMPX_FILE)
00148         UTMPX_FILE ;
00149 # elif defined (_UTMPX_FILE)
00150         _UTMPX_FILE ;
00151 # elif defined (_PATH_UTMPX)
00152         _PATH_UTMPX ;
00153 # else
00154         "" ;
00155 # endif
00156 
00157 static const char *wx_pathname =
00158 # if defined (WTMPX_FILE)
00159         WTMPX_FILE ;
00160 # elif defined (_WTMPX_FILE)
00161         _WTMPX_FILE ;
00162 # elif defined (_PATH_WTMPX)
00163         _PATH_WTMPX ;
00164 # else
00165         "" ;
00166 # endif
00167 
00168 #endif  /* HAVE_UTMPX_H */
00169 
00170 static const char *ut_pathname =
00171 # if defined (UTMP_FILE)
00172         UTMP_FILE ;
00173 # elif defined (_UTMP_FILE)
00174         _UTMP_FILE ;
00175 # elif defined (_PATH_UTMP)
00176         _PATH_UTMP ;
00177 # else
00178         "" ;
00179 # endif
00180 
00181 static const char *wt_pathname =
00182 # if defined (WTMP_FILE)
00183         WTMP_FILE ;
00184 # elif defined (_WTMP_FILE)
00185         _WTMP_FILE ;
00186 # elif defined (_PATH_WTMP)
00187         _PATH_WTMP ;
00188 # else
00189         "" ;
00190 # endif
00191 
00192 /* BSD-like systems might want "lastlog" support. */
00193 /* *** Not yet implemented */
00194 #ifndef HAVE_PUTUTLINE          /* see "pututline_my()" */
00195 static const char *ll_pathname =
00196 # if defined (_PATH_LASTLOG)    /* what other names (if any?) */
00197         _PATH_LASTLOG ;
00198 # else
00199         "" ;
00200 # endif /* _PATH_LASTLOG */
00201 #endif  /* HAVE_PUTUTLINE */
00202 
00203 /*
00204  * Get name of {u,w}tmp{,x} file.
00205  *      return: fname contains filename
00206  *              Possibly empty if this code not yet ported to this system.
00207  *
00208  * utmp{,x}:  try "utmp dir", then default (a define)
00209  * wtmp{,x}:  try "wtmp dir", then "utmp dir", then default (a define)
00210  */
00211 static void uw_pathname(pstring fname, const char *uw_name, const char *uw_default)
00212 {
00213         pstring dirname;
00214 
00215         pstrcpy(dirname, "");
00216 
00217         /* For w-files, first look for explicit "wtmp dir" */
00218         if (uw_name[0] == 'w') {
00219                 pstrcpy(dirname,lp_wtmpdir());
00220                 trim_char(dirname,'\0','/');
00221         }
00222 
00223         /* For u-files and non-explicit w-dir, look for "utmp dir" */
00224         if (strlen(dirname) == 0) {
00225                 pstrcpy(dirname,lp_utmpdir());
00226                 trim_char(dirname,'\0','/');
00227         }
00228 
00229         /* If explicit directory above, use it */
00230         if (strlen(dirname) != 0) {
00231                 pstrcpy(fname, dirname);
00232                 pstrcat(fname, "/");
00233                 pstrcat(fname, uw_name);
00234                 return;
00235         }
00236 
00237         /* No explicit directory: attempt to use default paths */
00238         if (strlen(uw_default) == 0) {
00239                 /* No explicit setting, no known default.
00240                  * Has it yet been ported to this OS?
00241                  */
00242                 DEBUG(2,("uw_pathname: unable to determine pathname\n"));
00243         }
00244         pstrcpy(fname, uw_default);
00245 }
00246 
00247 #ifndef HAVE_PUTUTLINE
00248 
00249 /****************************************************************************
00250  Update utmp file directly.  No subroutine interface: probably a BSD system.
00251 ****************************************************************************/
00252 
00253 static void pututline_my(pstring uname, struct utmp *u, BOOL claim)
00254 {
00255         DEBUG(1,("pututline_my: not yet implemented\n"));
00256         /* BSD implementor: may want to consider (or not) adjusting "lastlog" */
00257 }
00258 #endif /* HAVE_PUTUTLINE */
00259 
00260 #ifndef HAVE_UPDWTMP
00261 
00262 /****************************************************************************
00263  Update wtmp file directly.  No subroutine interface: probably a BSD system.
00264  Credit: Michail Vidiassov <master@iaas.msu.ru>
00265 ****************************************************************************/
00266 
00267 static void updwtmp_my(pstring wname, struct utmp *u, BOOL claim)
00268 {
00269         int fd;
00270         struct stat buf;
00271 
00272         if (! claim) {
00273                 /*
00274                  * BSD-like systems:
00275                  *      may use empty ut_name to distinguish a logout record.
00276                  *
00277                  * May need "if defined(SUNOS4)" etc. around some of these,
00278                  * but try to avoid if possible.
00279                  *
00280                  * SunOS 4:
00281                  *      man page indicates ut_name and ut_host both NULL
00282                  * FreeBSD 4.0:
00283                  *      man page appears not to specify (hints non-NULL)
00284                  *      A correspondent suggest at least ut_name should be NULL
00285                  */
00286 #if defined(HAVE_UT_UT_NAME)
00287                 memset((char *)&u->ut_name, '\0', sizeof(u->ut_name));
00288 #endif
00289 #if defined(HAVE_UT_UT_HOST)
00290                 memset((char *)&u->ut_host, '\0', sizeof(u->ut_host));
00291 #endif
00292         }
00293         /* Stolen from logwtmp function in libutil.
00294          * May be more locking/blocking is needed?
00295          */
00296         if ((fd = open(wname, O_WRONLY|O_APPEND, 0)) < 0)
00297                 return;
00298         if (fstat(fd, &buf) == 0) {
00299                 if (write(fd, (char *)u, sizeof(struct utmp)) != sizeof(struct utmp))
00300                 (void) ftruncate(fd, buf.st_size);
00301         }
00302         (void) close(fd);
00303 }
00304 #endif /* HAVE_UPDWTMP */
00305 
00306 /****************************************************************************
00307  Update via utmp/wtmp (not utmpx/wtmpx).
00308 ****************************************************************************/
00309 
00310 static void utmp_nox_update(struct utmp *u, BOOL claim)
00311 {
00312         pstring uname, wname;
00313 #if defined(PUTUTLINE_RETURNS_UTMP)
00314         struct utmp *urc;
00315 #endif /* PUTUTLINE_RETURNS_UTMP */
00316 
00317         uw_pathname(uname, "utmp", ut_pathname);
00318         DEBUG(2,("utmp_nox_update: uname:%s\n", uname));
00319 
00320 #ifdef HAVE_PUTUTLINE
00321         if (strlen(uname) != 0) {
00322                 utmpname(uname);
00323         }
00324 
00325 # if defined(PUTUTLINE_RETURNS_UTMP)
00326         setutent();
00327         urc = pututline(u);
00328         endutent();
00329         if (urc == NULL) {
00330                 DEBUG(2,("utmp_nox_update: pututline() failed\n"));
00331                 return;
00332         }
00333 # else  /* PUTUTLINE_RETURNS_UTMP */
00334         setutent();
00335         pututline(u);
00336         endutent();
00337 # endif /* PUTUTLINE_RETURNS_UTMP */
00338 
00339 #else   /* HAVE_PUTUTLINE */
00340         if (strlen(uname) != 0) {
00341                 pututline_my(uname, u, claim);
00342         }
00343 #endif /* HAVE_PUTUTLINE */
00344 
00345         uw_pathname(wname, "wtmp", wt_pathname);
00346         DEBUG(2,("utmp_nox_update: wname:%s\n", wname));
00347         if (strlen(wname) != 0) {
00348 #ifdef HAVE_UPDWTMP
00349                 updwtmp(wname, u);
00350                 /*
00351                  * updwtmp() and the newer updwtmpx() may be unsymmetrical.
00352                  * At least one OS, Solaris 2.x declares the former in the
00353                  * "utmpx" (latter) file and context.
00354                  * In the Solaris case this is irrelevant: it has both and
00355                  * we always prefer the "x" case, so doesn't come here.
00356                  * But are there other systems, with no "x", which lack
00357                  * updwtmp() perhaps?
00358                  */
00359 #else
00360                 updwtmp_my(wname, u, claim);
00361 #endif /* HAVE_UPDWTMP */
00362         }
00363 }
00364 
00365 /****************************************************************************
00366  Copy a string in the utmp structure.
00367 ****************************************************************************/
00368 
00369 static void utmp_strcpy(char *dest, const char *src, size_t n)
00370 {
00371         size_t len = 0;
00372 
00373         memset(dest, '\0', n);
00374         if (src)
00375                 len = strlen(src);
00376         if (len >= n) {
00377                 memcpy(dest, src, n);
00378         } else {
00379                 if (len)
00380                         memcpy(dest, src, len);
00381         }
00382 }
00383 
00384 /****************************************************************************
00385  Update via utmpx/wtmpx (preferred) or via utmp/wtmp.
00386 ****************************************************************************/
00387 
00388 static void sys_utmp_update(struct utmp *u, const char *hostname, BOOL claim)
00389 {
00390 #if !defined(HAVE_UTMPX_H)
00391         /* No utmpx stuff.  Drop to non-x stuff */
00392         utmp_nox_update(u, claim);
00393 #elif !defined(HAVE_PUTUTXLINE)
00394         /* Odd.  Have utmpx.h but no "pututxline()".  Drop to non-x stuff */
00395         DEBUG(1,("utmp_update: have utmpx.h but no pututxline() function\n"));
00396         utmp_nox_update(u, claim);
00397 #elif !defined(HAVE_GETUTMPX)
00398         /* Odd.  Have utmpx.h but no "getutmpx()".  Drop to non-x stuff */
00399         DEBUG(1,("utmp_update: have utmpx.h but no getutmpx() function\n"));
00400         utmp_nox_update(u, claim);
00401 #else
00402         pstring uname, wname;
00403         struct utmpx ux, *uxrc;
00404 
00405         getutmpx(u, &ux);
00406 
00407 #if defined(HAVE_UX_UT_SYSLEN)
00408         if (hostname)
00409                 ux.ut_syslen = strlen(hostname) + 1;    /* include end NULL */
00410         else
00411                 ux.ut_syslen = 0;
00412 #endif
00413 #if defined(HAVE_UT_UT_HOST)
00414         utmp_strcpy(ux.ut_host, hostname, sizeof(ux.ut_host));
00415 #endif
00416 
00417         uw_pathname(uname, "utmpx", ux_pathname);
00418         uw_pathname(wname, "wtmpx", wx_pathname);
00419         DEBUG(2,("utmp_update: uname:%s wname:%s\n", uname, wname));
00420         /*
00421          * Check for either uname or wname being empty.
00422          * Some systems, such as Redhat 6, have a "utmpx.h" which doesn't
00423          * define default filenames.
00424          * Also, our local installation has not provided an override.
00425          * Drop to non-x method.  (E.g. RH6 has good defaults in "utmp.h".)
00426          */
00427         if ((strlen(uname) == 0) || (strlen(wname) == 0)) {
00428                 utmp_nox_update(u, claim);
00429         } else {
00430                 utmpxname(uname);
00431                 setutxent();
00432                 uxrc = pututxline(&ux);
00433                 endutxent();
00434                 if (uxrc == NULL) {
00435                         DEBUG(2,("utmp_update: pututxline() failed\n"));
00436                         return;
00437                 }
00438                 updwtmpx(wname, &ux);
00439         }
00440 #endif /* HAVE_UTMPX_H */
00441 }
00442 
00443 #if defined(HAVE_UT_UT_ID)
00444 /****************************************************************************
00445  Encode the unique connection number into "ut_id".
00446 ****************************************************************************/
00447 
00448 static int ut_id_encode(int i, char *fourbyte)
00449 {
00450         int nbase;
00451         const char *ut_id_encstr = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
00452         
00453         fourbyte[0] = 'S';
00454         fourbyte[1] = 'M';
00455 
00456 /*
00457  * Encode remaining 2 bytes from 'i'.
00458  * 'ut_id_encstr' is the character set on which modulo arithmetic is done.
00459  * Example: digits would produce the base-10 numbers from '001'.
00460  */
00461         nbase = strlen(ut_id_encstr);
00462 
00463         fourbyte[3] = ut_id_encstr[i % nbase];
00464         i /= nbase;
00465         fourbyte[2] = ut_id_encstr[i % nbase];
00466         i /= nbase;
00467 
00468         return(i);      /* 0: good; else overflow */
00469 }
00470 #endif /* defined(HAVE_UT_UT_ID) */
00471 
00472 
00473 /*
00474   fill a system utmp structure given all the info we can gather 
00475 */
00476 static BOOL sys_utmp_fill(struct utmp *u,
00477                           const char *username, const char *hostname,
00478                           struct in_addr *ipaddr,
00479                           const char *id_str, int id_num)
00480 {                         
00481         struct timeval timeval;
00482 
00483         /*
00484          * ut_name, ut_user:
00485          *      Several (all?) systems seems to define one as the other.
00486          *      It is easier and clearer simply to let the following take its course,
00487          *      rather than to try to detect and optimise.
00488          */
00489 #if defined(HAVE_UT_UT_USER)
00490         utmp_strcpy(u->ut_user, username, sizeof(u->ut_user));
00491 #elif defined(HAVE_UT_UT_NAME)
00492         utmp_strcpy(u->ut_name, username, sizeof(u->ut_name));
00493 #endif
00494 
00495         /*
00496          * ut_line:
00497          *      If size limit proves troublesome, then perhaps use "ut_id_encode()".
00498          */
00499         if (strlen(id_str) > sizeof(u->ut_line)) {
00500                 DEBUG(1,("id_str [%s] is too long for %lu char utmp field\n",
00501                          id_str, (unsigned long)sizeof(u->ut_line)));
00502                 return False;
00503         }
00504         utmp_strcpy(u->ut_line, id_str, sizeof(u->ut_line));
00505 
00506 #if defined(HAVE_UT_UT_PID)
00507         u->ut_pid = sys_getpid();
00508 #endif
00509 
00510 /*
00511  * ut_time, ut_tv: 
00512  *      Some have one, some the other.  Many have both, but defined (aliased).
00513  *      It is easier and clearer simply to let the following take its course.
00514  *      But note that we do the more precise ut_tv as the final assignment.
00515  */
00516 #if defined(HAVE_UT_UT_TIME)
00517         GetTimeOfDay(&timeval);
00518         u->ut_time = timeval.tv_sec;
00519 #elif defined(HAVE_UT_UT_TV)
00520         GetTimeOfDay(&timeval);
00521         u->ut_tv = timeval;
00522 #else
00523 #error "with-utmp must have UT_TIME or UT_TV"
00524 #endif
00525 
00526 #if defined(HAVE_UT_UT_HOST)
00527         utmp_strcpy(u->ut_host, hostname, sizeof(u->ut_host));
00528 #endif
00529 #if defined(HAVE_UT_UT_ADDR)
00530         if (ipaddr)
00531                 u->ut_addr = ipaddr->s_addr;
00532         /*
00533          * "(unsigned long) ut_addr" apparently exists on at least HP-UX 10.20.
00534          * Volunteer to implement, please ...
00535          */
00536 #endif
00537 
00538 #if defined(HAVE_UT_UT_ID)
00539         if (ut_id_encode(id_num, u->ut_id) != 0) {
00540                 DEBUG(1,("utmp_fill: cannot encode id %d\n", id_num));
00541                 return False;
00542         }
00543 #endif
00544 
00545         return True;
00546 }
00547 
00548 /****************************************************************************
00549  Close a connection.
00550 ****************************************************************************/
00551 
00552 void sys_utmp_yield(const char *username, const char *hostname, 
00553                     struct in_addr *ipaddr,
00554                     const char *id_str, int id_num)
00555 {
00556         struct utmp u;
00557 
00558         ZERO_STRUCT(u);
00559 
00560 #if defined(HAVE_UT_UT_EXIT)
00561         u.ut_exit.e_termination = 0;
00562         u.ut_exit.e_exit = 0;
00563 #endif
00564 
00565 #if defined(HAVE_UT_UT_TYPE)
00566         u.ut_type = DEAD_PROCESS;
00567 #endif
00568 
00569         if (!sys_utmp_fill(&u, username, hostname, ipaddr, id_str, id_num)) return;
00570 
00571         sys_utmp_update(&u, NULL, False);
00572 }
00573 
00574 /****************************************************************************
00575  Claim a entry in whatever utmp system the OS uses.
00576 ****************************************************************************/
00577 
00578 void sys_utmp_claim(const char *username, const char *hostname, 
00579                     struct in_addr *ipaddr,
00580                     const char *id_str, int id_num)
00581 {
00582         struct utmp u;
00583 
00584         ZERO_STRUCT(u);
00585 
00586 #if defined(HAVE_UT_UT_TYPE)
00587         u.ut_type = USER_PROCESS;
00588 #endif
00589 
00590         if (!sys_utmp_fill(&u, username, hostname, ipaddr, id_str, id_num)) return;
00591 
00592         sys_utmp_update(&u, hostname, True);
00593 }
00594 
00595 #endif /* WITH_UTMP */

Sambaに対してSat Aug 29 21:23:26 2009に生成されました。  doxygen 1.4.7