Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : utmp routines
4 : Copyright (C) T.D.Lee@durham.ac.uk 1999
5 : Heavily modified by Andrew Bartlett and Tridge, April 2001
6 :
7 : This program is free software; you can redistribute it and/or modify
8 : it under the terms of the GNU General Public License as published by
9 : the Free Software Foundation; either version 3 of the License, or
10 : (at your option) any later version.
11 :
12 : This program is distributed in the hope that it will be useful,
13 : but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : GNU General Public License for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with this program. If not, see <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #include "includes.h"
22 : #include "system/filesys.h"
23 : #include "smbd/smbd.h"
24 :
25 : /****************************************************************************
26 : Reflect connection status in utmp/wtmp files.
27 : T.D.Lee@durham.ac.uk September 1999
28 :
29 : With grateful thanks since then to many who have helped port it to
30 : different operating systems. The variety of OS quirks thereby
31 : uncovered is amazing...
32 :
33 : Hints for porting:
34 : o Always attempt to use programmatic interface (pututline() etc.)
35 : Indeed, at present only programmatic use is supported.
36 : o The only currently supported programmatic interface to "wtmp{,x}"
37 : is through "updwtmp*()" routines.
38 : o The "x" (utmpx/wtmpx; HAVE_UTMPX_H) seems preferable.
39 : o The HAVE_* items should identify supported features.
40 : o If at all possible, avoid "if defined(MY-OS)" constructions.
41 :
42 : OS observations and status:
43 : Almost every OS seems to have its own quirks.
44 :
45 : Solaris 2.x:
46 : Tested on 2.6 and 2.7; should be OK on other flavours.
47 : AIX:
48 : Apparently has utmpx.h but doesn't implement.
49 : OSF:
50 : Has utmpx.h, but (e.g.) no "getutmpx()". (Is this like AIX ?)
51 : Redhat 6:
52 : utmpx.h seems not to set default filenames. non-x better.
53 : IRIX 6.5:
54 : Not tested. Appears to have "x".
55 : HP-UX 9.x:
56 : Not tested. Appears to lack "x".
57 : HP-UX 10.x:
58 : Not tested.
59 : "updwtmp*()" routines seem absent, so no current wtmp* support.
60 : Has "ut_addr": probably trivial to implement (although remember
61 : that IPv6 is coming...).
62 :
63 : FreeBSD:
64 : No "putut*()" type of interface.
65 : No "ut_type" and associated defines.
66 : Write files directly. Alternatively use its login(3)/logout(3).
67 : SunOS 4:
68 : Not tested. Resembles FreeBSD, but no login()/logout().
69 :
70 : lastlog:
71 : Should "lastlog" files, if any, be updated?
72 : BSD systems (SunOS 4, FreeBSD):
73 : o Prominent mention on man pages.
74 : System-V (e.g. Solaris 2):
75 : o No mention on man pages, even under "man -k".
76 : o Has a "/var/adm/lastlog" file, but pututxline() etc. seem
77 : not to touch it.
78 : o Despite downplaying (above), nevertheless has <lastlog.h>.
79 : So perhaps UN*X "lastlog" facility is intended for tty/terminal only?
80 :
81 : Notes:
82 : Each connection requires a small number (starting at 0, working up)
83 : to represent the line. This must be unique within and across all
84 : smbd processes. It is the 'id_num' from Samba's session.c code.
85 :
86 : The 4 byte 'ut_id' component is vital to distinguish connections,
87 : of which there could be several hundred or even thousand.
88 : Entries seem to be printable characters, with optional NULL pads.
89 :
90 : We need to be distinct from other entries in utmp/wtmp.
91 :
92 : Observed things: therefore avoid them. Add to this list please.
93 : From Solaris 2.x (because that's what I have):
94 : 'sN' : run-levels; N: [0-9]
95 : 'co' : console
96 : 'CC' : arbitrary things; C: [a-z]
97 : 'rXNN' : rlogin; N: [0-9]; X: [0-9a-z]
98 : 'tXNN' : rlogin; N: [0-9]; X: [0-9a-z]
99 : '/NNN' : Solaris CDE
100 : 'ftpZ' : ftp (Z is the number 255, aka 0377, aka 0xff)
101 : Mostly a record uses the same 'ut_id' in both "utmp" and "wtmp",
102 : but differences have been seen.
103 :
104 : Arbitrarily I have chosen to use a distinctive 'SM' for the
105 : first two bytes.
106 :
107 : The remaining two bytes encode the session 'id_num' (see above).
108 : Our caller (session.c) should note our 16-bit limitation.
109 :
110 : ****************************************************************************/
111 :
112 : #ifndef WITH_UTMP
113 : /*
114 : * Not WITH_UTMP? Simply supply dummy routines.
115 : */
116 :
117 : void sys_utmp_claim(const char *username, const char *hostname,
118 : const char *id_str, int id_num)
119 : {}
120 :
121 : void sys_utmp_yield(const char *username, const char *hostname,
122 : const char *id_str, int id_num)
123 : {}
124 :
125 : #else /* WITH_UTMP */
126 :
127 : #ifdef HAVE_UTMP_H
128 : #include <utmp.h>
129 : #endif
130 :
131 : #ifdef HAVE_UTMPX_H
132 : #include <utmpx.h>
133 : #endif
134 :
135 : /* BSD systems: some may need lastlog.h (SunOS 4), some may not (FreeBSD) */
136 : /* Some System-V systems (e.g. Solaris 2) declare this too. */
137 : #ifdef HAVE_LASTLOG_H
138 : #include <lastlog.h>
139 : #endif
140 :
141 : /****************************************************************************
142 : Default paths to various {u,w}tmp{,x} files.
143 : ****************************************************************************/
144 :
145 : #ifdef HAVE_UTMPX_H
146 :
147 : static const char * const ux_pathname =
148 : # if defined (UTMPX_FILE)
149 : UTMPX_FILE ;
150 : # elif defined (_UTMPX_FILE)
151 : _UTMPX_FILE ;
152 : # elif defined (_PATH_UTMPX)
153 : _PATH_UTMPX ;
154 : # else
155 : "" ;
156 : # endif
157 :
158 : static const char * const wx_pathname =
159 : # if defined (WTMPX_FILE)
160 : WTMPX_FILE ;
161 : # elif defined (_WTMPX_FILE)
162 : _WTMPX_FILE ;
163 : # elif defined (_PATH_WTMPX)
164 : _PATH_WTMPX ;
165 : # else
166 : "" ;
167 : # endif
168 :
169 : #endif /* HAVE_UTMPX_H */
170 :
171 : static const char * const ut_pathname =
172 : # if defined (UTMP_FILE)
173 : UTMP_FILE ;
174 : # elif defined (_UTMP_FILE)
175 : _UTMP_FILE ;
176 : # elif defined (_PATH_UTMP)
177 : _PATH_UTMP ;
178 : # else
179 : "" ;
180 : # endif
181 :
182 : static const char * const wt_pathname =
183 : # if defined (WTMP_FILE)
184 : WTMP_FILE ;
185 : # elif defined (_WTMP_FILE)
186 : _WTMP_FILE ;
187 : # elif defined (_PATH_WTMP)
188 : _PATH_WTMP ;
189 : # else
190 : "" ;
191 : # endif
192 :
193 : /* BSD-like systems might want "lastlog" support. */
194 : #if 0 /* *** Not yet implemented */
195 : #ifndef HAVE_PUTUTLINE /* see "pututline_my()" */
196 : static const char *ll_pathname =
197 : # if defined (_PATH_LASTLOG) /* what other names (if any?) */
198 : _PATH_LASTLOG ;
199 : # else
200 : "" ;
201 : # endif /* _PATH_LASTLOG */
202 : #endif /* HAVE_PUTUTLINE */
203 : #endif
204 :
205 : /*
206 : * Get name of {u,w}tmp{,x} file.
207 : * return: fname contains filename
208 : * Possibly empty if this code not yet ported to this system.
209 : *
210 : * utmp{,x}: try "utmp dir", then default (a define)
211 : * wtmp{,x}: try "wtmp dir", then "utmp dir", then default (a define)
212 : */
213 0 : static char *uw_pathname(TALLOC_CTX *ctx,
214 : const char *uw_name,
215 : const char *uw_default)
216 : {
217 0 : char *dirname = NULL;
218 :
219 : /* For w-files, first look for explicit "wtmp dir" */
220 0 : if (uw_name[0] == 'w') {
221 0 : dirname = talloc_strdup(ctx, lp_wtmp_directory());
222 0 : if (!dirname) {
223 0 : return NULL;
224 : }
225 0 : trim_char(dirname,'\0','/');
226 : }
227 :
228 : /* For u-files and non-explicit w-dir, look for "utmp dir" */
229 0 : if ((dirname == NULL) || (strlen(dirname) == 0)) {
230 0 : dirname = talloc_strdup(ctx, lp_utmp_directory());
231 0 : if (!dirname) {
232 0 : return NULL;
233 : }
234 0 : trim_char(dirname,'\0','/');
235 : }
236 :
237 : /* If explicit directory above, use it */
238 0 : if (dirname && strlen(dirname) != 0) {
239 0 : return talloc_asprintf(ctx,
240 : "%s/%s",
241 : dirname,
242 : uw_name);
243 : }
244 :
245 : /* No explicit directory: attempt to use default paths */
246 0 : if (strlen(uw_default) == 0) {
247 : /* No explicit setting, no known default.
248 : * Has it yet been ported to this OS?
249 : */
250 0 : DEBUG(2,("uw_pathname: unable to determine pathname\n"));
251 : }
252 0 : return talloc_strdup(ctx, uw_default);
253 : }
254 :
255 : #ifndef HAVE_PUTUTLINE
256 : /****************************************************************************
257 : Update utmp file directly. No subroutine interface: probably a BSD system.
258 : ****************************************************************************/
259 :
260 : static void pututline_my(const char *uname, struct utmp *u, bool claim)
261 : {
262 : DEBUG(1,("pututline_my: not yet implemented\n"));
263 : /* BSD implementor: may want to consider (or not) adjusting "lastlog" */
264 : }
265 : #endif /* HAVE_PUTUTLINE */
266 :
267 : #ifndef HAVE_UPDWTMP
268 :
269 : /****************************************************************************
270 : Update wtmp file directly. No subroutine interface: probably a BSD system.
271 : Credit: Michail Vidiassov <master@iaas.msu.ru>
272 : ****************************************************************************/
273 :
274 : static void updwtmp_my(const char *wname, struct utmp *u, bool claim)
275 : {
276 : int fd;
277 : struct stat buf;
278 :
279 : if (! claim) {
280 : /*
281 : * BSD-like systems:
282 : * may use empty ut_name to distinguish a logout record.
283 : *
284 : * May need "if defined(SUNOS4)" etc. around some of these,
285 : * but try to avoid if possible.
286 : *
287 : * SunOS 4:
288 : * man page indicates ut_name and ut_host both NULL
289 : * FreeBSD 4.0:
290 : * man page appears not to specify (hints non-NULL)
291 : * A correspondent suggest at least ut_name should be NULL
292 : */
293 : #if defined(HAVE_UT_UT_NAME)
294 : memset((char *)&u->ut_name, '\0', sizeof(u->ut_name));
295 : #endif
296 : #if defined(HAVE_UT_UT_HOST)
297 : memset((char *)&u->ut_host, '\0', sizeof(u->ut_host));
298 : #endif
299 : }
300 : /* Stolen from logwtmp function in libutil.
301 : * May be more locking/blocking is needed?
302 : */
303 : if ((fd = open(wname, O_WRONLY|O_APPEND, 0)) < 0)
304 : return;
305 : if (fstat(fd, &buf) == 0) {
306 : if (write(fd, (char *)u, sizeof(struct utmp)) != sizeof(struct utmp))
307 : (void) ftruncate(fd, buf.st_size);
308 : }
309 : (void) close(fd);
310 : }
311 : #endif /* HAVE_UPDWTMP */
312 :
313 : /****************************************************************************
314 : Update via utmp/wtmp (not utmpx/wtmpx).
315 : ****************************************************************************/
316 :
317 0 : static void utmp_nox_update(struct utmp *u, bool claim)
318 : {
319 0 : char *uname = NULL;
320 0 : char *wname = NULL;
321 : #if defined(PUTUTLINE_RETURNS_UTMP)
322 0 : struct utmp *urc;
323 : #endif /* PUTUTLINE_RETURNS_UTMP */
324 :
325 0 : uname = uw_pathname(talloc_tos(), "utmp", ut_pathname);
326 0 : if (!uname) {
327 0 : return;
328 : }
329 0 : DEBUG(2,("utmp_nox_update: uname:%s\n", uname));
330 :
331 : #ifdef HAVE_PUTUTLINE
332 0 : if (strlen(uname) != 0) {
333 0 : utmpname(uname);
334 : }
335 :
336 : # if defined(PUTUTLINE_RETURNS_UTMP)
337 0 : setutent();
338 0 : urc = pututline(u);
339 0 : endutent();
340 0 : if (urc == NULL) {
341 0 : DEBUG(2,("utmp_nox_update: pututline() failed\n"));
342 0 : return;
343 : }
344 : # else /* PUTUTLINE_RETURNS_UTMP */
345 : setutent();
346 : pututline(u);
347 : endutent();
348 : # endif /* PUTUTLINE_RETURNS_UTMP */
349 :
350 : #else /* HAVE_PUTUTLINE */
351 : if (strlen(uname) != 0) {
352 : pututline_my(uname, u, claim);
353 : }
354 : #endif /* HAVE_PUTUTLINE */
355 :
356 0 : wname = uw_pathname(talloc_tos(), "wtmp", wt_pathname);
357 0 : if (!wname) {
358 0 : return;
359 : }
360 0 : DEBUG(2,("utmp_nox_update: wname:%s\n", wname));
361 0 : if (strlen(wname) != 0) {
362 : #ifdef HAVE_UPDWTMP
363 0 : updwtmp(wname, u);
364 : /*
365 : * updwtmp() and the newer updwtmpx() may be unsymmetrical.
366 : * At least one OS, Solaris 2.x declares the former in the
367 : * "utmpx" (latter) file and context.
368 : * In the Solaris case this is irrelevant: it has both and
369 : * we always prefer the "x" case, so doesn't come here.
370 : * But are there other systems, with no "x", which lack
371 : * updwtmp() perhaps?
372 : */
373 : #else
374 : updwtmp_my(wname, u, claim);
375 : #endif /* HAVE_UPDWTMP */
376 : }
377 : }
378 :
379 : /****************************************************************************
380 : Copy a string in the utmp structure.
381 : ****************************************************************************/
382 :
383 0 : static void utmp_strcpy(char *dest, const char *src, size_t n)
384 : {
385 0 : size_t len = 0;
386 :
387 0 : memset(dest, '\0', n);
388 0 : if (src)
389 0 : len = strlen(src);
390 0 : if (len >= n) {
391 0 : memcpy(dest, src, n);
392 : } else {
393 0 : if (len)
394 0 : memcpy(dest, src, len);
395 : }
396 0 : }
397 :
398 : /****************************************************************************
399 : Update via utmpx/wtmpx (preferred) or via utmp/wtmp.
400 : ****************************************************************************/
401 :
402 0 : static void sys_utmp_update(struct utmp *u, const char *hostname, bool claim)
403 : {
404 : #if !defined(HAVE_UTMPX_H)
405 : /* No utmpx stuff. Drop to non-x stuff */
406 : utmp_nox_update(u, claim);
407 : #elif !defined(HAVE_PUTUTXLINE)
408 : /* Odd. Have utmpx.h but no "pututxline()". Drop to non-x stuff */
409 : DEBUG(1,("utmp_update: have utmpx.h but no pututxline() function\n"));
410 : utmp_nox_update(u, claim);
411 : #elif !defined(HAVE_GETUTMPX)
412 : /* Odd. Have utmpx.h but no "getutmpx()". Drop to non-x stuff */
413 : DEBUG(1,("utmp_update: have utmpx.h but no getutmpx() function\n"));
414 : utmp_nox_update(u, claim);
415 : #elif !defined(HAVE_UPDWTMPX)
416 : /* Have utmpx.h but no "updwtmpx()". Drop to non-x stuff */
417 : DEBUG(1,("utmp_update: have utmpx.h but no updwtmpx() function\n"));
418 : utmp_nox_update(u, claim);
419 : #else
420 0 : char *uname = NULL;
421 0 : char *wname = NULL;
422 0 : struct utmpx ux, *uxrc;
423 :
424 0 : getutmpx(u, &ux);
425 :
426 : #if defined(HAVE_UX_UT_SYSLEN)
427 : if (hostname)
428 : ux.ut_syslen = strlen(hostname) + 1; /* include end NULL */
429 : else
430 : ux.ut_syslen = 0;
431 : #endif
432 : #if defined(HAVE_UX_UT_HOST)
433 0 : utmp_strcpy(ux.ut_host, hostname, sizeof(ux.ut_host));
434 : #endif
435 :
436 0 : uname = uw_pathname(talloc_tos(), "utmpx", ux_pathname);
437 0 : wname = uw_pathname(talloc_tos(), "wtmpx", wx_pathname);
438 0 : if (uname && wname) {
439 0 : DEBUG(2,("utmp_update: uname:%s wname:%s\n", uname, wname));
440 : }
441 :
442 : /*
443 : * Check for either uname or wname being empty.
444 : * Some systems, such as Redhat 6, have a "utmpx.h" which doesn't
445 : * define default filenames.
446 : * Also, our local installation has not provided an override.
447 : * Drop to non-x method. (E.g. RH6 has good defaults in "utmp.h".)
448 : */
449 0 : if (!uname || !wname || (strlen(uname) == 0) || (strlen(wname) == 0)) {
450 0 : utmp_nox_update(u, claim);
451 : } else {
452 0 : utmpxname(uname);
453 0 : setutxent();
454 0 : uxrc = pututxline(&ux);
455 0 : endutxent();
456 0 : if (uxrc == NULL) {
457 0 : DEBUG(2,("utmp_update: pututxline() failed\n"));
458 0 : return;
459 : }
460 0 : updwtmpx(wname, &ux);
461 : }
462 : #endif /* HAVE_UTMPX_H */
463 : }
464 :
465 : #if defined(HAVE_UT_UT_ID)
466 : /****************************************************************************
467 : Encode the unique connection number into "ut_id".
468 : ****************************************************************************/
469 :
470 0 : static int ut_id_encode(int i, char *fourbyte)
471 : {
472 0 : int nbase;
473 0 : const char *ut_id_encstr = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
474 :
475 : /*
476 : * 'ut_id_encstr' is the character set on which modulo arithmetic is done.
477 : * Example: digits would produce the base-10 numbers from '001'.
478 : */
479 0 : nbase = strlen(ut_id_encstr);
480 :
481 0 : fourbyte[0] = ut_id_encstr[i % nbase];
482 0 : i /= nbase;
483 0 : fourbyte[1] = ut_id_encstr[i % nbase];
484 0 : i /= nbase;
485 0 : fourbyte[3] = ut_id_encstr[i % nbase];
486 0 : i /= nbase;
487 0 : fourbyte[2] = ut_id_encstr[i % nbase];
488 0 : i /= nbase;
489 :
490 : /* we do not care about overflows as i is a random number */
491 0 : return 0;
492 : }
493 : #endif /* defined(HAVE_UT_UT_ID) */
494 :
495 :
496 : /*
497 : fill a system utmp structure given all the info we can gather
498 : */
499 0 : static bool sys_utmp_fill(struct utmp *u,
500 : const char *username, const char *hostname,
501 : const char *id_str, int id_num)
502 : {
503 0 : struct timeval timeval;
504 :
505 : /*
506 : * ut_name, ut_user:
507 : * Several (all?) systems seems to define one as the other.
508 : * It is easier and clearer simply to let the following take its course,
509 : * rather than to try to detect and optimise.
510 : */
511 : #if defined(HAVE_UT_UT_USER)
512 0 : utmp_strcpy(u->ut_user, username, sizeof(u->ut_user));
513 : #elif defined(HAVE_UT_UT_NAME)
514 : utmp_strcpy(u->ut_name, username, sizeof(u->ut_name));
515 : #endif
516 :
517 : /*
518 : * ut_line:
519 : * If size limit proves troublesome, then perhaps use "ut_id_encode()".
520 : */
521 0 : utmp_strcpy(u->ut_line, id_str, sizeof(u->ut_line));
522 :
523 : #if defined(HAVE_UT_UT_PID)
524 0 : u->ut_pid = getpid();
525 : #endif
526 :
527 : /*
528 : * ut_time, ut_tv:
529 : * Some have one, some the other. Many have both, but defined (aliased).
530 : * It is easier and clearer simply to let the following take its course.
531 : * But note that we do the more precise ut_tv as the final assignment.
532 : */
533 : #if defined(HAVE_UT_UT_TIME)
534 0 : GetTimeOfDay(&timeval);
535 0 : u->ut_time = timeval.tv_sec;
536 : #elif defined(HAVE_UT_UT_TV)
537 : GetTimeOfDay(&timeval);
538 : u->ut_tv = timeval;
539 : #else
540 : #error "with-utmp must have UT_TIME or UT_TV"
541 : #endif
542 :
543 : #if defined(HAVE_UT_UT_HOST)
544 0 : utmp_strcpy(u->ut_host, hostname, sizeof(u->ut_host));
545 : #endif
546 :
547 : #if defined(HAVE_UT_UT_ID)
548 0 : if (ut_id_encode(id_num, u->ut_id) != 0) {
549 0 : DEBUG(1,("utmp_fill: cannot encode id %d\n", id_num));
550 0 : return False;
551 : }
552 : #endif
553 :
554 0 : return True;
555 : }
556 :
557 : /****************************************************************************
558 : Close a connection.
559 : ****************************************************************************/
560 :
561 0 : void sys_utmp_yield(const char *username, const char *hostname,
562 : const char *id_str, int id_num)
563 : {
564 0 : struct utmp u;
565 :
566 0 : ZERO_STRUCT(u);
567 :
568 : #if defined(HAVE_UT_UT_EXIT)
569 0 : u.ut_exit.e_termination = 0;
570 0 : u.ut_exit.e_exit = 0;
571 : #endif
572 :
573 : #if defined(HAVE_UT_UT_TYPE)
574 0 : u.ut_type = DEAD_PROCESS;
575 : #endif
576 :
577 0 : if (!sys_utmp_fill(&u, username, hostname, id_str, id_num))
578 0 : return;
579 :
580 0 : sys_utmp_update(&u, NULL, False);
581 : }
582 :
583 : /****************************************************************************
584 : Claim a entry in whatever utmp system the OS uses.
585 : ****************************************************************************/
586 :
587 0 : void sys_utmp_claim(const char *username, const char *hostname,
588 : const char *id_str, int id_num)
589 : {
590 0 : struct utmp u;
591 :
592 0 : ZERO_STRUCT(u);
593 :
594 : #if defined(HAVE_UT_UT_TYPE)
595 0 : u.ut_type = USER_PROCESS;
596 : #endif
597 :
598 0 : if (!sys_utmp_fill(&u, username, hostname, id_str, id_num))
599 0 : return;
600 :
601 0 : sys_utmp_update(&u, hostname, True);
602 : }
603 :
604 : #endif /* WITH_UTMP */
|