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