comparison loginrec.c @ 4:fe6bca95afa7

Makefile.in contains updated files required
author Matt Johnston <matt@ucc.asn.au>
date Tue, 01 Jun 2004 02:46:09 +0000
parents
children 5c6f9d27ea1c
comparison
equal deleted inserted replaced
-1:000000000000 4:fe6bca95afa7
1 /*
2 * Copyright (c) 2000 Andre Lucas. All rights reserved.
3 * Portions copyright (c) 1998 Todd C. Miller
4 * Portions copyright (c) 1996 Jason Downs
5 * Portions copyright (c) 1996 Theo de Raadt
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 /**
29 ** loginrec.c: platform-independent login recording and lastlog retrieval
30 **/
31
32 /*
33 The new login code explained
34 ============================
35
36 This code attempts to provide a common interface to login recording
37 (utmp and friends) and last login time retrieval.
38
39 Its primary means of achieving this is to use 'struct logininfo', a
40 union of all the useful fields in the various different types of
41 system login record structures one finds on UNIX variants.
42
43 We depend on autoconf to define which recording methods are to be
44 used, and which fields are contained in the relevant data structures
45 on the local system. Many C preprocessor symbols affect which code
46 gets compiled here.
47
48 The code is designed to make it easy to modify a particular
49 recording method, without affecting other methods nor requiring so
50 many nested conditional compilation blocks as were commonplace in
51 the old code.
52
53 For login recording, we try to use the local system's libraries as
54 these are clearly most likely to work correctly. For utmp systems
55 this usually means login() and logout() or setutent() etc., probably
56 in libutil, along with logwtmp() etc. On these systems, we fall back
57 to writing the files directly if we have to, though this method
58 requires very thorough testing so we do not corrupt local auditing
59 information. These files and their access methods are very system
60 specific indeed.
61
62 For utmpx systems, the corresponding library functions are
63 setutxent() etc. To the author's knowledge, all utmpx systems have
64 these library functions and so no direct write is attempted. If such
65 a system exists and needs support, direct analogues of the [uw]tmp
66 code should suffice.
67
68 Retrieving the time of last login ('lastlog') is in some ways even
69 more problemmatic than login recording. Some systems provide a
70 simple table of all users which we seek based on uid and retrieve a
71 relatively standard structure. Others record the same information in
72 a directory with a separate file, and others don't record the
73 information separately at all. For systems in the latter category,
74 we look backwards in the wtmp or wtmpx file for the last login entry
75 for our user. Naturally this is slower and on busy systems could
76 incur a significant performance penalty.
77
78 Calling the new code
79 --------------------
80
81 In OpenSSH all login recording and retrieval is performed in
82 login.c. Here you'll find working examples. Also, in the logintest.c
83 program there are more examples.
84
85 Internal handler calling method
86 -------------------------------
87
88 When a call is made to login_login() or login_logout(), both
89 routines set a struct logininfo flag defining which action (log in,
90 or log out) is to be taken. They both then call login_write(), which
91 calls whichever of the many structure-specific handlers autoconf
92 selects for the local system.
93
94 The handlers themselves handle system data structure specifics. Both
95 struct utmp and struct utmpx have utility functions (see
96 construct_utmp*()) to try to make it simpler to add extra systems
97 that introduce new features to either structure.
98
99 While it may seem terribly wasteful to replicate so much similar
100 code for each method, experience has shown that maintaining code to
101 write both struct utmp and utmpx in one function, whilst maintaining
102 support for all systems whether they have library support or not, is
103 a difficult and time-consuming task.
104
105 Lastlog support proceeds similarly. Functions login_get_lastlog()
106 (and its OpenSSH-tuned friend login_get_lastlog_time()) call
107 getlast_entry(), which tries one of three methods to find the last
108 login time. It uses local system lastlog support if it can,
109 otherwise it tries wtmp or wtmpx before giving up and returning 0,
110 meaning "tilt".
111
112 Maintenance
113 -----------
114
115 In many cases it's possible to tweak autoconf to select the correct
116 methods for a particular platform, either by improving the detection
117 code (best), or by presetting DISABLE_<method> or CONF_<method>_FILE
118 symbols for the platform.
119
120 Use logintest to check which symbols are defined before modifying
121 configure.ac and loginrec.c. (You have to build logintest yourself
122 with 'make logintest' as it's not built by default.)
123
124 Otherwise, patches to the specific method(s) are very helpful!
125
126 */
127
128 /**
129 ** TODO:
130 ** homegrown ttyslot()
131 ** test, test, test
132 **
133 ** Platform status:
134 ** ----------------
135 **
136 ** Known good:
137 ** Linux (Redhat 6.2, Debian)
138 ** Solaris
139 ** HP-UX 10.20 (gcc only)
140 ** IRIX
141 ** NeXT - M68k/HPPA/Sparc (4.2/3.3)
142 **
143 ** Testing required: Please send reports!
144 ** NetBSD
145 ** HP-UX 11
146 ** AIX
147 **
148 ** Platforms with known problems:
149 ** Some variants of Slackware Linux
150 **
151 **/
152
153 /*RCSID("$Id: loginrec.c,v 1.2 2004/05/04 10:17:43 matt Exp $");*/
154
155 #include "includes.h"
156 #include "loginrec.h"
157 #include "dbutil.h"
158 #include "atomicio.h"
159
160 /**
161 ** prototypes for helper functions in this file
162 **/
163
164 #if HAVE_UTMP_H
165 void set_utmp_time(struct logininfo *li, struct utmp *ut);
166 void construct_utmp(struct logininfo *li, struct utmp *ut);
167 #endif
168
169 #ifdef HAVE_UTMPX_H
170 void set_utmpx_time(struct logininfo *li, struct utmpx *ut);
171 void construct_utmpx(struct logininfo *li, struct utmpx *ut);
172 #endif
173
174 int utmp_write_entry(struct logininfo *li);
175 int utmpx_write_entry(struct logininfo *li);
176 int wtmp_write_entry(struct logininfo *li);
177 int wtmpx_write_entry(struct logininfo *li);
178 int lastlog_write_entry(struct logininfo *li);
179 int syslogin_write_entry(struct logininfo *li);
180
181 int getlast_entry(struct logininfo *li);
182 int lastlog_get_entry(struct logininfo *li);
183 int wtmp_get_entry(struct logininfo *li);
184 int wtmpx_get_entry(struct logininfo *li);
185
186 /* pick the shortest string */
187 #define MIN_SIZEOF(s1,s2) ( sizeof(s1) < sizeof(s2) ? sizeof(s1) : sizeof(s2) )
188
189 /**
190 ** platform-independent login functions
191 **/
192
193 /* login_login(struct logininfo *) -Record a login
194 *
195 * Call with a pointer to a struct logininfo initialised with
196 * login_init_entry() or login_alloc_entry()
197 *
198 * Returns:
199 * >0 if successful
200 * 0 on failure (will use OpenSSH's logging facilities for diagnostics)
201 */
202 int
203 login_login (struct logininfo *li)
204 {
205 li->type = LTYPE_LOGIN;
206 return login_write(li);
207 }
208
209
210 /* login_logout(struct logininfo *) - Record a logout
211 *
212 * Call as with login_login()
213 *
214 * Returns:
215 * >0 if successful
216 * 0 on failure (will use OpenSSH's logging facilities for diagnostics)
217 */
218 int
219 login_logout(struct logininfo *li)
220 {
221 li->type = LTYPE_LOGOUT;
222 return login_write(li);
223 }
224
225 /* login_get_lastlog_time(int) - Retrieve the last login time
226 *
227 * Retrieve the last login time for the given uid. Will try to use the
228 * system lastlog facilities if they are available, but will fall back
229 * to looking in wtmp/wtmpx if necessary
230 *
231 * Returns:
232 * 0 on failure, or if user has never logged in
233 * Time in seconds from the epoch if successful
234 *
235 * Useful preprocessor symbols:
236 * DISABLE_LASTLOG: If set, *never* even try to retrieve lastlog
237 * info
238 * USE_LASTLOG: If set, indicates the presence of system lastlog
239 * facilities. If this and DISABLE_LASTLOG are not set,
240 * try to retrieve lastlog information from wtmp/wtmpx.
241 */
242 unsigned int
243 login_get_lastlog_time(const int uid)
244 {
245 struct logininfo li;
246
247 if (login_get_lastlog(&li, uid))
248 return li.tv_sec;
249 else
250 return 0;
251 }
252
253 /* login_get_lastlog(struct logininfo *, int) - Retrieve a lastlog entry
254 *
255 * Retrieve a logininfo structure populated (only partially) with
256 * information from the system lastlog data, or from wtmp/wtmpx if no
257 * system lastlog information exists.
258 *
259 * Note this routine must be given a pre-allocated logininfo.
260 *
261 * Returns:
262 * >0: A pointer to your struct logininfo if successful
263 * 0 on failure (will use OpenSSH's logging facilities for diagnostics)
264 *
265 */
266 struct logininfo *
267 login_get_lastlog(struct logininfo *li, const int uid)
268 {
269 struct passwd *pw;
270
271 memset(li, '\0', sizeof(*li));
272 li->uid = uid;
273
274 /*
275 * If we don't have a 'real' lastlog, we need the username to
276 * reliably search wtmp(x) for the last login (see
277 * wtmp_get_entry().)
278 */
279 pw = getpwuid(uid);
280 if (pw == NULL)
281 dropbear_exit("login_get_lastlog: Cannot find account for uid %i", uid);
282
283 /* No MIN_SIZEOF here - we absolutely *must not* truncate the
284 * username */
285 strlcpy(li->username, pw->pw_name, sizeof(li->username));
286
287 if (getlast_entry(li))
288 return li;
289 else
290 return NULL;
291 }
292
293
294 /* login_alloc_entry(int, char*, char*, char*) - Allocate and initialise
295 * a logininfo structure
296 *
297 * This function creates a new struct logininfo, a data structure
298 * meant to carry the information required to portably record login info.
299 *
300 * Returns a pointer to a newly created struct logininfo. If memory
301 * allocation fails, the program halts.
302 */
303 struct
304 logininfo *login_alloc_entry(int pid, const char *username,
305 const char *hostname, const char *line)
306 {
307 struct logininfo *newli;
308
309 newli = (struct logininfo *) m_malloc (sizeof(*newli));
310 (void)login_init_entry(newli, pid, username, hostname, line);
311 return newli;
312 }
313
314
315 /* login_free_entry(struct logininfo *) - free struct memory */
316 void
317 login_free_entry(struct logininfo *li)
318 {
319 m_free(li);
320 }
321
322
323 /* login_init_entry(struct logininfo *, int, char*, char*, char*)
324 * - initialise a struct logininfo
325 *
326 * Populates a new struct logininfo, a data structure meant to carry
327 * the information required to portably record login info.
328 *
329 * Returns: 1
330 */
331 int
332 login_init_entry(struct logininfo *li, int pid, const char *username,
333 const char *hostname, const char *line)
334 {
335 struct passwd *pw;
336
337 memset(li, 0, sizeof(*li));
338
339 li->pid = pid;
340
341 /* set the line information */
342 if (line)
343 line_fullname(li->line, line, sizeof(li->line));
344
345 if (username) {
346 strlcpy(li->username, username, sizeof(li->username));
347 pw = getpwnam(li->username);
348 if (pw == NULL)
349 dropbear_exit("login_init_entry: Cannot find user \"%s\"",
350 li->username);
351 li->uid = pw->pw_uid;
352 }
353
354 if (hostname)
355 strlcpy(li->hostname, hostname, sizeof(li->hostname));
356
357 return 1;
358 }
359
360 /* login_set_current_time(struct logininfo *) - set the current time
361 *
362 * Set the current time in a logininfo structure. This function is
363 * meant to eliminate the need to deal with system dependencies for
364 * time handling.
365 */
366 void
367 login_set_current_time(struct logininfo *li)
368 {
369 struct timeval tv;
370
371 gettimeofday(&tv, NULL);
372
373 li->tv_sec = tv.tv_sec;
374 li->tv_usec = tv.tv_usec;
375 }
376
377 /* copy a sockaddr_* into our logininfo */
378 void
379 login_set_addr(struct logininfo *li, const struct sockaddr *sa,
380 const unsigned int sa_size)
381 {
382 unsigned int bufsize = sa_size;
383
384 /* make sure we don't overrun our union */
385 if (sizeof(li->hostaddr) < sa_size)
386 bufsize = sizeof(li->hostaddr);
387
388 memcpy((void *)&(li->hostaddr.sa), (const void *)sa, bufsize);
389 }
390
391
392 /**
393 ** login_write: Call low-level recording functions based on autoconf
394 ** results
395 **/
396 int
397 login_write (struct logininfo *li)
398 {
399 #ifndef HAVE_CYGWIN
400 if ((int)geteuid() != 0) {
401 dropbear_log(LOG_WARNING,
402 "Attempt to write login records by non-root user (aborting)");
403 return 1;
404 }
405 #endif
406
407 /* set the timestamp */
408 login_set_current_time(li);
409 #ifdef USE_LOGIN
410 syslogin_write_entry(li);
411 #endif
412 #ifdef USE_LASTLOG
413 if (li->type == LTYPE_LOGIN) {
414 lastlog_write_entry(li);
415 }
416 #endif
417 #ifdef USE_UTMP
418 utmp_write_entry(li);
419 #endif
420 #ifdef USE_WTMP
421 wtmp_write_entry(li);
422 #endif
423 #ifdef USE_UTMPX
424 utmpx_write_entry(li);
425 #endif
426 #ifdef USE_WTMPX
427 wtmpx_write_entry(li);
428 #endif
429 return 0;
430 }
431
432 #ifdef LOGIN_NEEDS_UTMPX
433 int
434 login_utmp_only(struct logininfo *li)
435 {
436 li->type = LTYPE_LOGIN;
437 login_set_current_time(li);
438 # ifdef USE_UTMP
439 utmp_write_entry(li);
440 # endif
441 # ifdef USE_WTMP
442 wtmp_write_entry(li);
443 # endif
444 # ifdef USE_UTMPX
445 utmpx_write_entry(li);
446 # endif
447 # ifdef USE_WTMPX
448 wtmpx_write_entry(li);
449 # endif
450 return 0;
451 }
452 #endif
453
454 /**
455 ** getlast_entry: Call low-level functions to retrieve the last login
456 ** time.
457 **/
458
459 /* take the uid in li and return the last login time */
460 int
461 getlast_entry(struct logininfo *li)
462 {
463 #ifdef USE_LASTLOG
464 return(lastlog_get_entry(li));
465 #else /* !USE_LASTLOG */
466
467 #ifdef DISABLE_LASTLOG
468 /* On some systems we shouldn't even try to obtain last login
469 * time, e.g. AIX */
470 return 0;
471 # else /* DISABLE_LASTLOG */
472 /* Try to retrieve the last login time from wtmp */
473 # if defined(USE_WTMP) && (defined(HAVE_STRUCT_UTMP_UT_TIME) || defined(HAVE_STRUCT_UTMP_UT_TV))
474 /* retrieve last login time from utmp */
475 return (wtmp_get_entry(li));
476 # else /* defined(USE_WTMP) && (defined(HAVE_STRUCT_UTMP_UT_TIME) || defined(HAVE_STRUCT_UTMP_UT_TV)) */
477 /* If wtmp isn't available, try wtmpx */
478 # if defined(USE_WTMPX) && (defined(HAVE_STRUCT_UTMPX_UT_TIME) || defined(HAVE_STRUCT_UTMPX_UT_TV))
479 /* retrieve last login time from utmpx */
480 return (wtmpx_get_entry(li));
481 # else
482 /* Give up: No means of retrieving last login time */
483 return 0;
484 # endif /* USE_WTMPX && (HAVE_STRUCT_UTMPX_UT_TIME || HAVE_STRUCT_UTMPX_UT_TV) */
485 # endif /* USE_WTMP && (HAVE_STRUCT_UTMP_UT_TIME || HAVE_STRUCT_UTMP_UT_TV) */
486 # endif /* DISABLE_LASTLOG */
487 #endif /* USE_LASTLOG */
488 }
489
490
491
492 /*
493 * 'line' string utility functions
494 *
495 * These functions process the 'line' string into one of three forms:
496 *
497 * 1. The full filename (including '/dev')
498 * 2. The stripped name (excluding '/dev')
499 * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00
500 * /dev/pts/1 -> ts/1 )
501 *
502 * Form 3 is used on some systems to identify a .tmp.? entry when
503 * attempting to remove it. Typically both addition and removal is
504 * performed by one application - say, sshd - so as long as the choice
505 * uniquely identifies a terminal it's ok.
506 */
507
508
509 /* line_fullname(): add the leading '/dev/' if it doesn't exist make
510 * sure dst has enough space, if not just copy src (ugh) */
511 char *
512 line_fullname(char *dst, const char *src, size_t dstsize)
513 {
514 memset(dst, '\0', dstsize);
515 if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5))) {
516 strlcpy(dst, src, dstsize);
517 } else {
518 strlcpy(dst, "/dev/", dstsize);
519 strlcat(dst, src, dstsize);
520 }
521 return dst;
522 }
523
524 /* line_stripname(): strip the leading '/dev' if it exists, return dst */
525 char *
526 line_stripname(char *dst, const char *src, size_t dstsize)
527 {
528 memset(dst, '\0', dstsize);
529 if (strncmp(src, "/dev/", 5) == 0)
530 strlcpy(dst, src + 5, dstsize);
531 else
532 strlcpy(dst, src, dstsize);
533 return dst;
534 }
535
536 /* line_abbrevname(): Return the abbreviated (usually four-character)
537 * form of the line (Just use the last <dstsize> characters of the
538 * full name.)
539 *
540 * NOTE: use strncpy because we do NOT necessarily want zero
541 * termination */
542 char *
543 line_abbrevname(char *dst, const char *src, size_t dstsize)
544 {
545 size_t len;
546
547 memset(dst, '\0', dstsize);
548
549 /* Always skip prefix if present */
550 if (strncmp(src, "/dev/", 5) == 0)
551 src += 5;
552
553 #ifdef WITH_ABBREV_NO_TTY
554 if (strncmp(src, "tty", 3) == 0)
555 src += 3;
556 #endif
557
558 len = strlen(src);
559
560 if (len > 0) {
561 if (((int)len - dstsize) > 0)
562 src += ((int)len - dstsize);
563
564 /* note: _don't_ change this to strlcpy */
565 strncpy(dst, src, (size_t)dstsize);
566 }
567
568 return dst;
569 }
570
571 /**
572 ** utmp utility functions
573 **
574 ** These functions manipulate struct utmp, taking system differences
575 ** into account.
576 **/
577
578 #if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN)
579
580 /* build the utmp structure */
581 void
582 set_utmp_time(struct logininfo *li, struct utmp *ut)
583 {
584 # ifdef HAVE_STRUCT_UTMP_UT_TV
585 ut->ut_tv.tv_sec = li->tv_sec;
586 ut->ut_tv.tv_usec = li->tv_usec;
587 # else
588 # ifdef HAVE_STRUCT_UTMP_UT_TIME
589 ut->ut_time = li->tv_sec;
590 # endif
591 # endif
592 }
593
594 void
595 construct_utmp(struct logininfo *li,
596 struct utmp *ut)
597 {
598 # ifdef HAVE_ADDR_V6_IN_UTMP
599 struct sockaddr_in6 *sa6;
600 # endif
601 memset(ut, '\0', sizeof(*ut));
602
603 /* First fill out fields used for both logins and logouts */
604
605 # ifdef HAVE_STRUCT_UTMP_UT_ID
606 line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id));
607 # endif
608
609 # ifdef HAVE_STRUCT_UTMP_UT_TYPE
610 /* This is done here to keep utmp constants out of struct logininfo */
611 switch (li->type) {
612 case LTYPE_LOGIN:
613 ut->ut_type = USER_PROCESS;
614 #ifdef _UNICOS
615 cray_set_tmpdir(ut);
616 #endif
617 break;
618 case LTYPE_LOGOUT:
619 ut->ut_type = DEAD_PROCESS;
620 #ifdef _UNICOS
621 cray_retain_utmp(ut, li->pid);
622 #endif
623 break;
624 }
625 # endif
626 set_utmp_time(li, ut);
627
628 line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line));
629
630 # ifdef HAVE_STRUCT_UTMP_UT_PID
631 ut->ut_pid = li->pid;
632 # endif
633
634 /* If we're logging out, leave all other fields blank */
635 if (li->type == LTYPE_LOGOUT)
636 return;
637
638 /*
639 * These fields are only used when logging in, and are blank
640 * for logouts.
641 */
642
643 /* Use strncpy because we don't necessarily want null termination */
644 strncpy(ut->ut_name, li->username, MIN_SIZEOF(ut->ut_name, li->username));
645 # ifdef HAVE_STRUCT_UTMP_UT_HOST
646 strncpy(ut->ut_host, li->hostname, MIN_SIZEOF(ut->ut_host, li->hostname));
647 # endif
648 # ifdef HAVE_STRUCT_UTMP_UT_ADDR
649 /* this is just a 32-bit IP address */
650 if (li->hostaddr.sa.sa_family == AF_INET)
651 ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
652 # endif
653 # ifdef HAVE_ADDR_V6_IN_UTMP
654 /* this is just a 128-bit IPv6 address */
655 if (li->hostaddr.sa.sa_family == AF_INET6) {
656 sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa);
657 memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16);
658 if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
659 ut->ut_addr_v6[0] = ut->ut_addr_v6[3];
660 ut->ut_addr_v6[1] = 0;
661 ut->ut_addr_v6[2] = 0;
662 ut->ut_addr_v6[3] = 0;
663 }
664 }
665 # endif
666 }
667 #endif /* USE_UTMP || USE_WTMP || USE_LOGIN */
668
669 /**
670 ** utmpx utility functions
671 **
672 ** These functions manipulate struct utmpx, accounting for system
673 ** variations.
674 **/
675
676 #if defined(USE_UTMPX) || defined (USE_WTMPX)
677 /* build the utmpx structure */
678 void
679 set_utmpx_time(struct logininfo *li, struct utmpx *utx)
680 {
681 # ifdef HAVE_STRUCT_UTMPX_UT_TV
682 utx->ut_tv.tv_sec = li->tv_sec;
683 utx->ut_tv.tv_usec = li->tv_usec;
684 # else /* HAVE_STRUCT_UTMPX_UT_TV */
685 # ifdef HAVE_STRUCT_UTMPX_UT_TIME
686 utx->ut_time = li->tv_sec;
687 # endif /* HAVE_STRUCT_UTMPX_UT_TIME */
688 # endif /* HAVE_STRUCT_UTMPX_UT_TV */
689 }
690
691 void
692 construct_utmpx(struct logininfo *li, struct utmpx *utx)
693 {
694 # ifdef HAVE_ADDR_V6_IN_UTMP
695 struct sockaddr_in6 *sa6;
696 # endif
697 memset(utx, '\0', sizeof(*utx));
698 # ifdef HAVE_STRUCT_UTMPX_UT_ID
699 line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id));
700 # endif
701
702 /* this is done here to keep utmp constants out of loginrec.h */
703 switch (li->type) {
704 case LTYPE_LOGIN:
705 utx->ut_type = USER_PROCESS;
706 break;
707 case LTYPE_LOGOUT:
708 utx->ut_type = DEAD_PROCESS;
709 break;
710 }
711 line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line));
712 set_utmpx_time(li, utx);
713 utx->ut_pid = li->pid;
714 /* strncpy(): Don't necessarily want null termination */
715 strncpy(utx->ut_name, li->username, MIN_SIZEOF(utx->ut_name, li->username));
716
717 if (li->type == LTYPE_LOGOUT)
718 return;
719
720 /*
721 * These fields are only used when logging in, and are blank
722 * for logouts.
723 */
724
725 # ifdef HAVE_STRUCT_UTMPX_UT_HOST
726 strncpy(utx->ut_host, li->hostname, MIN_SIZEOF(utx->ut_host, li->hostname));
727 # endif
728 # ifdef HAVE_STRUCT_UTMPX_UT_ADDR
729 /* this is just a 32-bit IP address */
730 if (li->hostaddr.sa.sa_family == AF_INET)
731 utx->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
732 # endif
733 # ifdef HAVE_ADDR_V6_IN_UTMP
734 /* this is just a 128-bit IPv6 address */
735 if (li->hostaddr.sa.sa_family == AF_INET6) {
736 sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa);
737 memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16);
738 if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
739 ut->ut_addr_v6[0] = ut->ut_addr_v6[3];
740 ut->ut_addr_v6[1] = 0;
741 ut->ut_addr_v6[2] = 0;
742 ut->ut_addr_v6[3] = 0;
743 }
744 }
745 # endif
746 # ifdef HAVE_STRUCT_UTMPX_UT_SYSLEN
747 /* ut_syslen is the length of the utx_host string */
748 utx->ut_syslen = MIN(strlen(li->hostname), sizeof(utx->ut_host));
749 # endif
750 }
751 #endif /* USE_UTMPX || USE_WTMPX */
752
753 /**
754 ** Low-level utmp functions
755 **/
756
757 /* FIXME: (ATL) utmp_write_direct needs testing */
758 #ifdef USE_UTMP
759
760 /* if we can, use pututline() etc. */
761 # if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \
762 defined(HAVE_PUTUTLINE)
763 # define UTMP_USE_LIBRARY
764 # endif
765
766
767 /* write a utmp entry with the system's help (pututline() and pals) */
768 # ifdef UTMP_USE_LIBRARY
769 static int
770 utmp_write_library(struct logininfo *li, struct utmp *ut)
771 {
772 setutent();
773 pututline(ut);
774
775 # ifdef HAVE_ENDUTENT
776 endutent();
777 # endif
778 return 1;
779 }
780 # else /* UTMP_USE_LIBRARY */
781
782 /* write a utmp entry direct to the file */
783 /* This is a slightly modification of code in OpenBSD's login.c */
784 static int
785 utmp_write_direct(struct logininfo *li, struct utmp *ut)
786 {
787 struct utmp old_ut;
788 register int fd;
789 int tty;
790
791 /* FIXME: (ATL) ttyslot() needs local implementation */
792
793 #if defined(HAVE_GETTTYENT)
794 register struct ttyent *ty;
795
796 tty=0;
797
798 setttyent();
799 while ((struct ttyent *)0 != (ty = getttyent())) {
800 tty++;
801 if (!strncmp(ty->ty_name, ut->ut_line, sizeof(ut->ut_line)))
802 break;
803 }
804 endttyent();
805
806 if((struct ttyent *)0 == ty) {
807 dropbear_log(LOG_WARNING, "utmp_write_entry: tty not found");
808 return(1);
809 }
810 #else /* FIXME */
811
812 tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */
813
814 #endif /* HAVE_GETTTYENT */
815
816 if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) {
817 (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
818 /*
819 * Prevent luser from zero'ing out ut_host.
820 * If the new ut_line is empty but the old one is not
821 * and ut_line and ut_name match, preserve the old ut_line.
822 */
823 if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) &&
824 (ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') &&
825 (strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) &&
826 (strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) {
827 (void)memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host));
828 }
829
830 (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
831 if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut))
832 dropbear_log(LOG_WARNING, "utmp_write_direct: error writing %s: %s",
833 UTMP_FILE, strerror(errno));
834
835 (void)close(fd);
836 return 1;
837 } else {
838 return 0;
839 }
840 }
841 # endif /* UTMP_USE_LIBRARY */
842
843 static int
844 utmp_perform_login(struct logininfo *li)
845 {
846 struct utmp ut;
847
848 construct_utmp(li, &ut);
849 # ifdef UTMP_USE_LIBRARY
850 if (!utmp_write_library(li, &ut)) {
851 dropbear_log(LOG_WARNING, "utmp_perform_login: utmp_write_library() failed");
852 return 0;
853 }
854 # else
855 if (!utmp_write_direct(li, &ut)) {
856 dropbear_log(LOG_WARNING, "utmp_perform_login: utmp_write_direct() failed");
857 return 0;
858 }
859 # endif
860 return 1;
861 }
862
863
864 static int
865 utmp_perform_logout(struct logininfo *li)
866 {
867 struct utmp ut;
868
869 construct_utmp(li, &ut);
870 # ifdef UTMP_USE_LIBRARY
871 if (!utmp_write_library(li, &ut)) {
872 dropbear_log(LOG_WARNING, "utmp_perform_logout: utmp_write_library() failed");
873 return 0;
874 }
875 # else
876 if (!utmp_write_direct(li, &ut)) {
877 dropbear_log(LOG_WARNING, "utmp_perform_logout: utmp_write_direct() failed");
878 return 0;
879 }
880 # endif
881 return 1;
882 }
883
884
885 int
886 utmp_write_entry(struct logininfo *li)
887 {
888 switch(li->type) {
889 case LTYPE_LOGIN:
890 return utmp_perform_login(li);
891
892 case LTYPE_LOGOUT:
893 return utmp_perform_logout(li);
894
895 default:
896 dropbear_log(LOG_WARNING, "utmp_write_entry: invalid type field");
897 return 0;
898 }
899 }
900 #endif /* USE_UTMP */
901
902
903 /**
904 ** Low-level utmpx functions
905 **/
906
907 /* not much point if we don't want utmpx entries */
908 #ifdef USE_UTMPX
909
910 /* if we have the wherewithall, use pututxline etc. */
911 # if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \
912 defined(HAVE_PUTUTXLINE)
913 # define UTMPX_USE_LIBRARY
914 # endif
915
916
917 /* write a utmpx entry with the system's help (pututxline() and pals) */
918 # ifdef UTMPX_USE_LIBRARY
919 static int
920 utmpx_write_library(struct logininfo *li, struct utmpx *utx)
921 {
922 setutxent();
923 pututxline(utx);
924
925 # ifdef HAVE_ENDUTXENT
926 endutxent();
927 # endif
928 return 1;
929 }
930
931 # else /* UTMPX_USE_LIBRARY */
932
933 /* write a utmp entry direct to the file */
934 static int
935 utmpx_write_direct(struct logininfo *li, struct utmpx *utx)
936 {
937 dropbear_log(LOG_WARNING, "utmpx_write_direct: not implemented!");
938 return 0;
939 }
940 # endif /* UTMPX_USE_LIBRARY */
941
942 static int
943 utmpx_perform_login(struct logininfo *li)
944 {
945 struct utmpx utx;
946
947 construct_utmpx(li, &utx);
948 # ifdef UTMPX_USE_LIBRARY
949 if (!utmpx_write_library(li, &utx)) {
950 dropbear_log(LOG_WARNING, "utmpx_perform_login: utmp_write_library() failed");
951 return 0;
952 }
953 # else
954 if (!utmpx_write_direct(li, &ut)) {
955 dropbear_log(LOG_WARNING, "utmpx_perform_login: utmp_write_direct() failed");
956 return 0;
957 }
958 # endif
959 return 1;
960 }
961
962
963 static int
964 utmpx_perform_logout(struct logininfo *li)
965 {
966 struct utmpx utx;
967
968 construct_utmpx(li, &utx);
969 # ifdef HAVE_STRUCT_UTMPX_UT_ID
970 line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id));
971 # endif
972 # ifdef HAVE_STRUCT_UTMPX_UT_TYPE
973 utx.ut_type = DEAD_PROCESS;
974 # endif
975
976 # ifdef UTMPX_USE_LIBRARY
977 utmpx_write_library(li, &utx);
978 # else
979 utmpx_write_direct(li, &utx);
980 # endif
981 return 1;
982 }
983
984 int
985 utmpx_write_entry(struct logininfo *li)
986 {
987 switch(li->type) {
988 case LTYPE_LOGIN:
989 return utmpx_perform_login(li);
990 case LTYPE_LOGOUT:
991 return utmpx_perform_logout(li);
992 default:
993 dropbear_log(LOG_WARNING, "utmpx_write_entry: invalid type field");
994 return 0;
995 }
996 }
997 #endif /* USE_UTMPX */
998
999
1000 /**
1001 ** Low-level wtmp functions
1002 **/
1003
1004 #ifdef USE_WTMP
1005
1006 /* write a wtmp entry direct to the end of the file */
1007 /* This is a slight modification of code in OpenBSD's logwtmp.c */
1008 static int
1009 wtmp_write(struct logininfo *li, struct utmp *ut)
1010 {
1011 struct stat buf;
1012 int fd, ret = 1;
1013
1014 if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
1015 dropbear_log(LOG_WARNING, "wtmp_write: problem writing %s: %s",
1016 WTMP_FILE, strerror(errno));
1017 return 0;
1018 }
1019 if (fstat(fd, &buf) == 0)
1020 if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut)) {
1021 ftruncate(fd, buf.st_size);
1022 dropbear_log(LOG_WARNING, "wtmp_write: problem writing %s: %s",
1023 WTMP_FILE, strerror(errno));
1024 ret = 0;
1025 }
1026 (void)close(fd);
1027 return ret;
1028 }
1029
1030 static int
1031 wtmp_perform_login(struct logininfo *li)
1032 {
1033 struct utmp ut;
1034
1035 construct_utmp(li, &ut);
1036 return wtmp_write(li, &ut);
1037 }
1038
1039
1040 static int
1041 wtmp_perform_logout(struct logininfo *li)
1042 {
1043 struct utmp ut;
1044
1045 construct_utmp(li, &ut);
1046 return wtmp_write(li, &ut);
1047 }
1048
1049
1050 int
1051 wtmp_write_entry(struct logininfo *li)
1052 {
1053 switch(li->type) {
1054 case LTYPE_LOGIN:
1055 return wtmp_perform_login(li);
1056 case LTYPE_LOGOUT:
1057 return wtmp_perform_logout(li);
1058 default:
1059 dropbear_log(LOG_WARNING, "wtmp_write_entry: invalid type field");
1060 return 0;
1061 }
1062 }
1063
1064
1065 /* Notes on fetching login data from wtmp/wtmpx
1066 *
1067 * Logouts are usually recorded with (amongst other things) a blank
1068 * username on a given tty line. However, some systems (HP-UX is one)
1069 * leave all fields set, but change the ut_type field to DEAD_PROCESS.
1070 *
1071 * Since we're only looking for logins here, we know that the username
1072 * must be set correctly. On systems that leave it in, we check for
1073 * ut_type==USER_PROCESS (indicating a login.)
1074 *
1075 * Portability: Some systems may set something other than USER_PROCESS
1076 * to indicate a login process. I don't know of any as I write. Also,
1077 * it's possible that some systems may both leave the username in
1078 * place and not have ut_type.
1079 */
1080
1081 /* return true if this wtmp entry indicates a login */
1082 static int
1083 wtmp_islogin(struct logininfo *li, struct utmp *ut)
1084 {
1085 if (strncmp(li->username, ut->ut_name,
1086 MIN_SIZEOF(li->username, ut->ut_name)) == 0) {
1087 # ifdef HAVE_STRUCT_UTMP_UT_TYPE
1088 if (ut->ut_type & USER_PROCESS)
1089 return 1;
1090 # else
1091 return 1;
1092 # endif
1093 }
1094 return 0;
1095 }
1096
1097 int
1098 wtmp_get_entry(struct logininfo *li)
1099 {
1100 struct stat st;
1101 struct utmp ut;
1102 int fd, found=0;
1103
1104 /* Clear the time entries in our logininfo */
1105 li->tv_sec = li->tv_usec = 0;
1106
1107 if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) {
1108 dropbear_log(LOG_WARNING, "wtmp_get_entry: problem opening %s: %s",
1109 WTMP_FILE, strerror(errno));
1110 return 0;
1111 }
1112 if (fstat(fd, &st) != 0) {
1113 dropbear_log(LOG_WARNING, "wtmp_get_entry: couldn't stat %s: %s",
1114 WTMP_FILE, strerror(errno));
1115 close(fd);
1116 return 0;
1117 }
1118
1119 /* Seek to the start of the last struct utmp */
1120 if (lseek(fd, -(off_t)sizeof(struct utmp), SEEK_END) == -1) {
1121 /* Looks like we've got a fresh wtmp file */
1122 close(fd);
1123 return 0;
1124 }
1125
1126 while (!found) {
1127 if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) {
1128 dropbear_log(LOG_WARNING, "wtmp_get_entry: read of %s failed: %s",
1129 WTMP_FILE, strerror(errno));
1130 close (fd);
1131 return 0;
1132 }
1133 if ( wtmp_islogin(li, &ut) ) {
1134 found = 1;
1135 /* We've already checked for a time in struct
1136 * utmp, in login_getlast(). */
1137 # ifdef HAVE_STRUCT_UTMP_UT_TIME
1138 li->tv_sec = ut.ut_time;
1139 # else
1140 # if HAVE_STRUCT_UTMP_UT_TV
1141 li->tv_sec = ut.ut_tv.tv_sec;
1142 # endif
1143 # endif
1144 line_fullname(li->line, ut.ut_line,
1145 MIN_SIZEOF(li->line, ut.ut_line));
1146 # ifdef HAVE_STRUCT_UTMP_UT_HOST
1147 strlcpy(li->hostname, ut.ut_host,
1148 MIN_SIZEOF(li->hostname, ut.ut_host));
1149 # endif
1150 continue;
1151 }
1152 /* Seek back 2 x struct utmp */
1153 if (lseek(fd, -(off_t)(2 * sizeof(struct utmp)), SEEK_CUR) == -1) {
1154 /* We've found the start of the file, so quit */
1155 close (fd);
1156 return 0;
1157 }
1158 }
1159
1160 /* We found an entry. Tidy up and return */
1161 close(fd);
1162 return 1;
1163 }
1164 # endif /* USE_WTMP */
1165
1166
1167 /**
1168 ** Low-level wtmpx functions
1169 **/
1170
1171 #ifdef USE_WTMPX
1172 /* write a wtmpx entry direct to the end of the file */
1173 /* This is a slight modification of code in OpenBSD's logwtmp.c */
1174 static int
1175 wtmpx_write(struct logininfo *li, struct utmpx *utx)
1176 {
1177 struct stat buf;
1178 int fd, ret = 1;
1179
1180 if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
1181 dropbear_log(LOG_WARNING, "wtmpx_write: problem opening %s: %s",
1182 WTMPX_FILE, strerror(errno));
1183 return 0;
1184 }
1185
1186 if (fstat(fd, &buf) == 0)
1187 if (atomicio(write, fd, utx, sizeof(*utx)) != sizeof(*utx)) {
1188 ftruncate(fd, buf.st_size);
1189 dropbear_log(LOG_WARNING, "wtmpx_write: problem writing %s: %s",
1190 WTMPX_FILE, strerror(errno));
1191 ret = 0;
1192 }
1193 (void)close(fd);
1194
1195 return ret;
1196 }
1197
1198
1199 static int
1200 wtmpx_perform_login(struct logininfo *li)
1201 {
1202 struct utmpx utx;
1203
1204 construct_utmpx(li, &utx);
1205 return wtmpx_write(li, &utx);
1206 }
1207
1208
1209 static int
1210 wtmpx_perform_logout(struct logininfo *li)
1211 {
1212 struct utmpx utx;
1213
1214 construct_utmpx(li, &utx);
1215 return wtmpx_write(li, &utx);
1216 }
1217
1218
1219 int
1220 wtmpx_write_entry(struct logininfo *li)
1221 {
1222 switch(li->type) {
1223 case LTYPE_LOGIN:
1224 return wtmpx_perform_login(li);
1225 case LTYPE_LOGOUT:
1226 return wtmpx_perform_logout(li);
1227 default:
1228 dropbear_log(LOG_WARNING, "wtmpx_write_entry: invalid type field");
1229 return 0;
1230 }
1231 }
1232
1233 /* Please see the notes above wtmp_islogin() for information about the
1234 next two functions */
1235
1236 /* Return true if this wtmpx entry indicates a login */
1237 static int
1238 wtmpx_islogin(struct logininfo *li, struct utmpx *utx)
1239 {
1240 if ( strncmp(li->username, utx->ut_name,
1241 MIN_SIZEOF(li->username, utx->ut_name)) == 0 ) {
1242 # ifdef HAVE_STRUCT_UTMPX_UT_TYPE
1243 if (utx->ut_type == USER_PROCESS)
1244 return 1;
1245 # else
1246 return 1;
1247 # endif
1248 }
1249 return 0;
1250 }
1251
1252
1253 int
1254 wtmpx_get_entry(struct logininfo *li)
1255 {
1256 struct stat st;
1257 struct utmpx utx;
1258 int fd, found=0;
1259
1260 /* Clear the time entries */
1261 li->tv_sec = li->tv_usec = 0;
1262
1263 if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) {
1264 dropbear_log(LOG_WARNING, "wtmpx_get_entry: problem opening %s: %s",
1265 WTMPX_FILE, strerror(errno));
1266 return 0;
1267 }
1268 if (fstat(fd, &st) != 0) {
1269 dropbear_log(LOG_WARNING, "wtmpx_get_entry: couldn't stat %s: %s",
1270 WTMPX_FILE, strerror(errno));
1271 close(fd);
1272 return 0;
1273 }
1274
1275 /* Seek to the start of the last struct utmpx */
1276 if (lseek(fd, -(off_t)sizeof(struct utmpx), SEEK_END) == -1 ) {
1277 /* probably a newly rotated wtmpx file */
1278 close(fd);
1279 return 0;
1280 }
1281
1282 while (!found) {
1283 if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) {
1284 dropbear_log(LOG_WARNING, "wtmpx_get_entry: read of %s failed: %s",
1285 WTMPX_FILE, strerror(errno));
1286 close (fd);
1287 return 0;
1288 }
1289 /* Logouts are recorded as a blank username on a particular line.
1290 * So, we just need to find the username in struct utmpx */
1291 if ( wtmpx_islogin(li, &utx) ) {
1292 found = 1;
1293 # ifdef HAVE_STRUCT_UTMPX_UT_TV
1294 li->tv_sec = utx.ut_tv.tv_sec;
1295 # else
1296 # ifdef HAVE_STRUCT_UTMPX_UT_TIME
1297 li->tv_sec = utx.ut_time;
1298 # endif
1299 # endif
1300 line_fullname(li->line, utx.ut_line, sizeof(li->line));
1301 # ifdef HAVE_STRUCT_UTMPX_UT_HOST
1302 strlcpy(li->hostname, utx.ut_host,
1303 MIN_SIZEOF(li->hostname, utx.ut_host));
1304 # endif
1305 continue;
1306 }
1307 if (lseek(fd, -(off_t)(2 * sizeof(struct utmpx)), SEEK_CUR) == -1) {
1308 close (fd);
1309 return 0;
1310 }
1311 }
1312
1313 close(fd);
1314 return 1;
1315 }
1316 #endif /* USE_WTMPX */
1317
1318 /**
1319 ** Low-level libutil login() functions
1320 **/
1321
1322 #ifdef USE_LOGIN
1323 static int
1324 syslogin_perform_login(struct logininfo *li)
1325 {
1326 struct utmp *ut;
1327
1328 if (! (ut = (struct utmp *)malloc(sizeof(*ut)))) {
1329 dropbear_log(LOG_WARNING, "syslogin_perform_login: couldn't malloc()");
1330 return 0;
1331 }
1332 construct_utmp(li, ut);
1333 login(ut);
1334 free(ut);
1335
1336 return 1;
1337 }
1338
1339 static int
1340 syslogin_perform_logout(struct logininfo *li)
1341 {
1342 # ifdef HAVE_LOGOUT
1343 char line[8];
1344
1345 (void)line_stripname(line, li->line, sizeof(line));
1346
1347 if (!logout(line)) {
1348 dropbear_log(LOG_WARNING, "syslogin_perform_logout: logout(%s) returned an error: %s", line, strerror(errno));
1349 # ifdef HAVE_LOGWTMP
1350 } else {
1351 logwtmp(line, "", "");
1352 # endif
1353 }
1354 /* FIXME: (ATL - if the need arises) What to do if we have
1355 * login, but no logout? what if logout but no logwtmp? All
1356 * routines are in libutil so they should all be there,
1357 * but... */
1358 # endif
1359 return 1;
1360 }
1361
1362 int
1363 syslogin_write_entry(struct logininfo *li)
1364 {
1365 switch (li->type) {
1366 case LTYPE_LOGIN:
1367 return syslogin_perform_login(li);
1368 case LTYPE_LOGOUT:
1369 return syslogin_perform_logout(li);
1370 default:
1371 dropbear_log(LOG_WARNING, "syslogin_write_entry: Invalid type field");
1372 return 0;
1373 }
1374 }
1375 #endif /* USE_LOGIN */
1376
1377 /* end of file log-syslogin.c */
1378
1379 /**
1380 ** Low-level lastlog functions
1381 **/
1382
1383 #ifdef USE_LASTLOG
1384 #define LL_FILE 1
1385 #define LL_DIR 2
1386 #define LL_OTHER 3
1387
1388 static void
1389 lastlog_construct(struct logininfo *li, struct lastlog *last)
1390 {
1391 /* clear the structure */
1392 memset(last, '\0', sizeof(*last));
1393
1394 (void)line_stripname(last->ll_line, li->line, sizeof(last->ll_line));
1395 strlcpy(last->ll_host, li->hostname,
1396 MIN_SIZEOF(last->ll_host, li->hostname));
1397 last->ll_time = li->tv_sec;
1398 }
1399
1400 static int
1401 lastlog_filetype(char *filename)
1402 {
1403 struct stat st;
1404
1405 if (stat(LASTLOG_FILE, &st) != 0) {
1406 dropbear_log(LOG_WARNING, "lastlog_perform_login: Couldn't stat %s: %s", LASTLOG_FILE,
1407 strerror(errno));
1408 return 0;
1409 }
1410 if (S_ISDIR(st.st_mode))
1411 return LL_DIR;
1412 else if (S_ISREG(st.st_mode))
1413 return LL_FILE;
1414 else
1415 return LL_OTHER;
1416 }
1417
1418
1419 /* open the file (using filemode) and seek to the login entry */
1420 static int
1421 lastlog_openseek(struct logininfo *li, int *fd, int filemode)
1422 {
1423 off_t offset;
1424 int type;
1425 char lastlog_file[1024];
1426
1427 type = lastlog_filetype(LASTLOG_FILE);
1428 switch (type) {
1429 case LL_FILE:
1430 strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file));
1431 break;
1432 case LL_DIR:
1433 snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s",
1434 LASTLOG_FILE, li->username);
1435 break;
1436 default:
1437 dropbear_log(LOG_WARNING, "lastlog_openseek: %.100s is not a file or directory!",
1438 LASTLOG_FILE);
1439 return 0;
1440 }
1441
1442 *fd = open(lastlog_file, filemode);
1443 if ( *fd < 0) {
1444 dropbear_log(LOG_INFO, "lastlog_openseek: Couldn't open %s: %s",
1445 lastlog_file, strerror(errno));
1446 return 0;
1447 }
1448
1449 if (type == LL_FILE) {
1450 /* find this uid's offset in the lastlog file */
1451 offset = (off_t) ((long)li->uid * sizeof(struct lastlog));
1452
1453 if ( lseek(*fd, offset, SEEK_SET) != offset ) {
1454 dropbear_log(LOG_WARNING, "lastlog_openseek: %s->lseek(): %s",
1455 lastlog_file, strerror(errno));
1456 return 0;
1457 }
1458 }
1459
1460 return 1;
1461 }
1462
1463 static int
1464 lastlog_perform_login(struct logininfo *li)
1465 {
1466 struct lastlog last;
1467 int fd;
1468
1469 /* create our struct lastlog */
1470 lastlog_construct(li, &last);
1471
1472 if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT))
1473 return(0);
1474
1475 /* write the entry */
1476 if (atomicio(write, fd, &last, sizeof(last)) != sizeof(last)) {
1477 close(fd);
1478 dropbear_log(LOG_WARNING, "lastlog_write_filemode: Error writing to %s: %s",
1479 LASTLOG_FILE, strerror(errno));
1480 return 0;
1481 }
1482
1483 close(fd);
1484 return 1;
1485 }
1486
1487 int
1488 lastlog_write_entry(struct logininfo *li)
1489 {
1490 switch(li->type) {
1491 case LTYPE_LOGIN:
1492 return lastlog_perform_login(li);
1493 default:
1494 dropbear_log(LOG_WARNING, "lastlog_write_entry: Invalid type field");
1495 return 0;
1496 }
1497 }
1498
1499 static void
1500 lastlog_populate_entry(struct logininfo *li, struct lastlog *last)
1501 {
1502 line_fullname(li->line, last->ll_line, sizeof(li->line));
1503 strlcpy(li->hostname, last->ll_host,
1504 MIN_SIZEOF(li->hostname, last->ll_host));
1505 li->tv_sec = last->ll_time;
1506 }
1507
1508 int
1509 lastlog_get_entry(struct logininfo *li)
1510 {
1511 struct lastlog last;
1512 int fd, ret;
1513
1514 if (!lastlog_openseek(li, &fd, O_RDONLY))
1515 return (0);
1516
1517 ret = atomicio(read, fd, &last, sizeof(last));
1518 close(fd);
1519
1520 switch (ret) {
1521 case 0:
1522 memset(&last, '\0', sizeof(last));
1523 /* FALLTHRU */
1524 case sizeof(last):
1525 lastlog_populate_entry(li, &last);
1526 return (1);
1527 case -1:
1528 dropbear_log(LOG_ERR, "Error reading from %s: %s",
1529 LASTLOG_FILE, strerror(errno));
1530 return (0);
1531 default:
1532 dropbear_log(LOG_ERR, "Error reading from %s: Expecting %d, got %d",
1533 LASTLOG_FILE, sizeof(last), ret);
1534 return (0);
1535 }
1536
1537 /* NOTREACHED */
1538 return (0);
1539 }
1540 #endif /* USE_LASTLOG */