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