📄 loginrec.c
字号:
* leave all fields set, but change the ut_type field to DEAD_PROCESS. * * Since we're only looking for logins here, we know that the username * must be set correctly. On systems that leave it in, we check for * ut_type==USER_PROCESS (indicating a login.) * * Portability: Some systems may set something other than USER_PROCESS * to indicate a login process. I don't know of any as I write. Also, * it's possible that some systems may both leave the username in * place and not have ut_type. *//* return true if this wtmp entry indicates a login */static intwtmp_islogin(struct logininfo *li, struct utmp *ut){ if (strncmp(li->username, ut->ut_name, MIN_SIZEOF(li->username, ut->ut_name)) == 0) {# ifdef HAVE_TYPE_IN_UTMP if (ut->ut_type & USER_PROCESS) return (1);# else return (1);# endif } return (0);}intwtmp_get_entry(struct logininfo *li){ struct stat st; struct utmp ut; int fd, found = 0; /* Clear the time entries in our logininfo */ li->tv_sec = li->tv_usec = 0; if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) { logit("%s: problem opening %s: %s", __func__, WTMP_FILE, strerror(errno)); return (0); } if (fstat(fd, &st) != 0) { logit("%s: couldn't stat %s: %s", __func__, WTMP_FILE, strerror(errno)); close(fd); return (0); } /* Seek to the start of the last struct utmp */ if (lseek(fd, -(off_t)sizeof(struct utmp), SEEK_END) == -1) { /* Looks like we've got a fresh wtmp file */ close(fd); return (0); } while (!found) { if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) { logit("%s: read of %s failed: %s", __func__, WTMP_FILE, strerror(errno)); close (fd); return (0); } if ( wtmp_islogin(li, &ut) ) { found = 1; /* * We've already checked for a time in struct * utmp, in login_getlast() */# ifdef HAVE_TIME_IN_UTMP li->tv_sec = ut.ut_time;# else# if HAVE_TV_IN_UTMP li->tv_sec = ut.ut_tv.tv_sec;# endif# endif line_fullname(li->line, ut.ut_line, MIN_SIZEOF(li->line, ut.ut_line));# ifdef HAVE_HOST_IN_UTMP strlcpy(li->hostname, ut.ut_host, MIN_SIZEOF(li->hostname, ut.ut_host));# endif continue; } /* Seek back 2 x struct utmp */ if (lseek(fd, -(off_t)(2 * sizeof(struct utmp)), SEEK_CUR) == -1) { /* We've found the start of the file, so quit */ close(fd); return (0); } } /* We found an entry. Tidy up and return */ close(fd); return (1);}# endif /* USE_WTMP *//** ** Low-level wtmpx functions **/#ifdef USE_WTMPX/* * Write a wtmpx entry direct to the end of the file * This is a slight modification of code in OpenBSD's logwtmp.c */static intwtmpx_write(struct logininfo *li, struct utmpx *utx){#ifndef HAVE_UPDWTMPX struct stat buf; int fd, ret = 1; if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) { logit("%s: problem opening %s: %s", __func__, WTMPX_FILE, strerror(errno)); return (0); } if (fstat(fd, &buf) == 0) if (atomicio(vwrite, fd, utx, sizeof(*utx)) != sizeof(*utx)) { ftruncate(fd, buf.st_size); logit("%s: problem writing %s: %s", __func__, WTMPX_FILE, strerror(errno)); ret = 0; } close(fd); return (ret);#else updwtmpx(WTMPX_FILE, utx); return (1);#endif}static intwtmpx_perform_login(struct logininfo *li){ struct utmpx utx; construct_utmpx(li, &utx); return (wtmpx_write(li, &utx));}static intwtmpx_perform_logout(struct logininfo *li){ struct utmpx utx; construct_utmpx(li, &utx); return (wtmpx_write(li, &utx));}intwtmpx_write_entry(struct logininfo *li){ switch(li->type) { case LTYPE_LOGIN: return (wtmpx_perform_login(li)); case LTYPE_LOGOUT: return (wtmpx_perform_logout(li)); default: logit("%s: invalid type field", __func__); return (0); }}/* Please see the notes above wtmp_islogin() for information about the next two functions *//* Return true if this wtmpx entry indicates a login */static intwtmpx_islogin(struct logininfo *li, struct utmpx *utx){ if (strncmp(li->username, utx->ut_name, MIN_SIZEOF(li->username, utx->ut_name)) == 0 ) {# ifdef HAVE_TYPE_IN_UTMPX if (utx->ut_type == USER_PROCESS) return (1);# else return (1);# endif } return (0);}intwtmpx_get_entry(struct logininfo *li){ struct stat st; struct utmpx utx; int fd, found=0; /* Clear the time entries */ li->tv_sec = li->tv_usec = 0; if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) { logit("%s: problem opening %s: %s", __func__, WTMPX_FILE, strerror(errno)); return (0); } if (fstat(fd, &st) != 0) { logit("%s: couldn't stat %s: %s", __func__, WTMPX_FILE, strerror(errno)); close(fd); return (0); } /* Seek to the start of the last struct utmpx */ if (lseek(fd, -(off_t)sizeof(struct utmpx), SEEK_END) == -1 ) { /* probably a newly rotated wtmpx file */ close(fd); return (0); } while (!found) { if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) { logit("%s: read of %s failed: %s", __func__, WTMPX_FILE, strerror(errno)); close (fd); return (0); } /* * Logouts are recorded as a blank username on a particular * line. So, we just need to find the username in struct utmpx */ if (wtmpx_islogin(li, &utx)) { found = 1;# if defined(HAVE_TV_IN_UTMPX) li->tv_sec = utx.ut_tv.tv_sec;# elif defined(HAVE_TIME_IN_UTMPX) li->tv_sec = utx.ut_time;# endif line_fullname(li->line, utx.ut_line, sizeof(li->line));# if defined(HAVE_HOST_IN_UTMPX) strlcpy(li->hostname, utx.ut_host, MIN_SIZEOF(li->hostname, utx.ut_host));# endif continue; } if (lseek(fd, -(off_t)(2 * sizeof(struct utmpx)), SEEK_CUR) == -1) { close(fd); return (0); } } close(fd); return (1);}#endif /* USE_WTMPX *//** ** Low-level libutil login() functions **/#ifdef USE_LOGINstatic intsyslogin_perform_login(struct logininfo *li){ struct utmp *ut; ut = xmalloc(sizeof(*ut)); construct_utmp(li, ut); login(ut); free(ut); return (1);}static intsyslogin_perform_logout(struct logininfo *li){# ifdef HAVE_LOGOUT char line[UT_LINESIZE]; (void)line_stripname(line, li->line, sizeof(line)); if (!logout(line)) logit("%s: logout() returned an error", __func__);# ifdef HAVE_LOGWTMP else logwtmp(line, "", "");# endif /* FIXME: (ATL - if the need arises) What to do if we have * login, but no logout? what if logout but no logwtmp? All * routines are in libutil so they should all be there, * but... */# endif return (1);}intsyslogin_write_entry(struct logininfo *li){ switch (li->type) { case LTYPE_LOGIN: return (syslogin_perform_login(li)); case LTYPE_LOGOUT: return (syslogin_perform_logout(li)); default: logit("%s: Invalid type field", __func__); return (0); }}#endif /* USE_LOGIN *//* end of file log-syslogin.c *//** ** Low-level lastlog functions **/#ifdef USE_LASTLOG#define LL_FILE 1#define LL_DIR 2#define LL_OTHER 3static voidlastlog_construct(struct logininfo *li, struct lastlog *last){ /* clear the structure */ memset(last, '\0', sizeof(*last)); line_stripname(last->ll_line, li->line, sizeof(last->ll_line)); strlcpy(last->ll_host, li->hostname, MIN_SIZEOF(last->ll_host, li->hostname)); last->ll_time = li->tv_sec;}static intlastlog_filetype(char *filename){ struct stat st; if (stat(LASTLOG_FILE, &st) != 0) { logit("%s: Couldn't stat %s: %s", __func__, LASTLOG_FILE, strerror(errno)); return (0); } if (S_ISDIR(st.st_mode)) return (LL_DIR); else if (S_ISREG(st.st_mode)) return (LL_FILE); else return (LL_OTHER);}/* open the file (using filemode) and seek to the login entry */static intlastlog_openseek(struct logininfo *li, int *fd, int filemode){ off_t offset; int type; char lastlog_file[1024]; type = lastlog_filetype(LASTLOG_FILE); switch (type) { case LL_FILE: strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file)); break; case LL_DIR: snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s", LASTLOG_FILE, li->username); break; default: logit("%s: %.100s is not a file or directory!", __func__, LASTLOG_FILE); return (0); } *fd = open(lastlog_file, filemode, 0600); if (*fd < 0) { debug("%s: Couldn't open %s: %s", __func__, lastlog_file, strerror(errno)); return (0); } if (type == LL_FILE) { /* find this uid's offset in the lastlog file */ offset = (off_t) ((long)li->uid * sizeof(struct lastlog)); if (lseek(*fd, offset, SEEK_SET) != offset) { logit("%s: %s->lseek(): %s", __func__, lastlog_file, strerror(errno)); return (0); } } return (1);}static intlastlog_perform_login(struct logininfo *li){ struct lastlog last; int fd; /* create our struct lastlog */ lastlog_construct(li, &last); if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT)) return (0); /* write the entry */ if (atomicio(vwrite, fd, &last, sizeof(last)) != sizeof(last)) { close(fd); logit("%s: Error writing to %s: %s", __func__, LASTLOG_FILE, strerror(errno)); return (0); } close(fd); return (1);}intlastlog_write_entry(struct logininfo *li){ switch(li->type) { case LTYPE_LOGIN: return (lastlog_perform_login(li)); default: logit("%s: Invalid type field", __func__); return (0); }}static voidlastlog_populate_entry(struct logininfo *li, struct lastlog *last){ line_fullname(li->line, last->ll_line, sizeof(li->line)); strlcpy(li->hostname, last->ll_host, MIN_SIZEOF(li->hostname, last->ll_host)); li->tv_sec = last->ll_time;}intlastlog_get_entry(struct logininfo *li){ struct lastlog last; int fd, ret; if (!lastlog_openseek(li, &fd, O_RDONLY)) return (0); ret = atomicio(read, fd, &last, sizeof(last)); close(fd); switch (ret) { case 0: memset(&last, '\0', sizeof(last)); /* FALLTHRU */ case sizeof(last): lastlog_populate_entry(li, &last); return (1); case -1: error("%s: Error reading from %s: %s", __func__, LASTLOG_FILE, strerror(errno)); return (0); default: error("%s: Error reading from %s: Expecting %d, got %d", __func__, LASTLOG_FILE, sizeof(last), ret); return (0); } /* NOTREACHED */ return (0);}#endif /* USE_LASTLOG */#ifdef USE_BTMP /* * Logs failed login attempts in _PATH_BTMP if that exists. * The most common login failure is to give password instead of username. * So the _PATH_BTMP file checked for the correct permission, so that * only root can read it. */voidrecord_failed_login(const char *username, const char *hostname, const char *ttyn){ int fd; struct utmp ut; struct sockaddr_storage from; size_t fromlen = sizeof(from); struct sockaddr_in *a4; struct sockaddr_in6 *a6; time_t t; struct stat fst; if (geteuid() != 0) return; if ((fd = open(_PATH_BTMP, O_WRONLY | O_APPEND)) < 0) { debug("Unable to open the btmp file %s: %s", _PATH_BTMP, strerror(errno)); return; } if (fstat(fd, &fst) < 0) { logit("%s: fstat of %s failed: %s", __func__, _PATH_BTMP, strerror(errno)); goto out; } if((fst.st_mode & (S_IRWXG | S_IRWXO)) || (fst.st_uid != 0)){ logit("Excess permission or bad ownership on file %s", _PATH_BTMP); goto out; } memset(&ut, 0, sizeof(ut)); /* strncpy because we don't necessarily want nul termination */ strncpy(ut.ut_user, username, sizeof(ut.ut_user)); strlcpy(ut.ut_line, "ssh:notty", sizeof(ut.ut_line)); time(&t); ut.ut_time = t; /* ut_time is not always a time_t */ ut.ut_type = LOGIN_PROCESS; ut.ut_pid = getpid(); /* strncpy because we don't necessarily want nul termination */ strncpy(ut.ut_host, hostname, sizeof(ut.ut_host)); if (packet_connection_is_on_socket() && getpeername(packet_get_connection_in(), (struct sockaddr *)&from, &fromlen) == 0) { ipv64_normalise_mapped(&from, &fromlen); if (from.ss_family == AF_INET) { a4 = (struct sockaddr_in *)&from; memcpy(&ut.ut_addr, &(a4->sin_addr), MIN_SIZEOF(ut.ut_addr, a4->sin_addr)); }#ifdef HAVE_ADDR_V6_IN_UTMP if (from.ss_family == AF_INET6) { a6 = (struct sockaddr_in6 *)&from; memcpy(&ut.ut_addr_v6, &(a6->sin6_addr), MIN_SIZEOF(ut.ut_addr_v6, a6->sin6_addr)); }#endif } if (atomicio(vwrite, fd, &ut, sizeof(ut)) != sizeof(ut)) error("Failed to write to %s: %s", _PATH_BTMP, strerror(errno));out: close(fd);}#endif /* USE_BTMP */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -