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
166void set_utmp_time(struct logininfo *li, struct utmp *ut);
167void construct_utmp(struct logininfo *li, struct utmp *ut);
168#endif
169
170#ifdef HAVE_UTMPX_H
171void set_utmpx_time(struct logininfo *li, struct utmpx *ut);
172void construct_utmpx(struct logininfo *li, struct utmpx *ut);
173#endif
174
175int utmp_write_entry(struct logininfo *li);
176int utmpx_write_entry(struct logininfo *li);
177int wtmp_write_entry(struct logininfo *li);
178int wtmpx_write_entry(struct logininfo *li);
179int lastlog_write_entry(struct logininfo *li);
180int syslogin_write_entry(struct logininfo *li);
181
182int wtmp_get_entry(struct logininfo *li);
183int 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 */
201int
202login_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 */
217int
218login_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 */
234struct
235logininfo *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 */
247void
248login_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 */
262int
263login_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 */
297void
298login_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 */
309void
310login_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 **/
327int
328login_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
364int
365login_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) */
406char *
407line_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 */
420char *
421line_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 */
437char *
438line_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 */
476void
477set_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
489void
490construct_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 */
573void
574set_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
586void
587construct_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
664static int
665utmp_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 */
679static int
680utmp_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
738static int
739utmp_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
759static int
760utmp_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
780int
781utmp_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
814static int
815utmpx_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 */
829static int
830utmpx_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
837static int
838utmpx_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
858static int
859utmpx_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
879int
880utmpx_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 */
903static int
904wtmp_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
925static int
926wtmp_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
935static int
936wtmp_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
945int
946wtmp_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 */
977static int
978wtmp_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
992int
993wtmp_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 */
1069static int
1070wtmpx_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
1094static int
1095wtmpx_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
1104static int
1105wtmpx_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
1114int
1115wtmpx_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 */
1132static int
1133wtmpx_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
1148int
1149wtmpx_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
1218static int
1219syslogin_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
1234static int
1235syslogin_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
1257int
1258syslogin_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
1283static void
1284lastlog_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
1295static int
1296lastlog_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 */
1315static int
1316lastlog_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
1358static int
1359lastlog_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
1382int
1383lastlog_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