File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / sudo / src / utmp.c
Revision 1.1.1.5 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 16:12:55 2014 UTC (10 years ago) by misho
Branches: sudo, MAIN
CVS tags: v1_8_10p3_0, v1_8_10p3, HEAD
sudo v 1.8.10p3

    1: /*
    2:  * Copyright (c) 2011-2013 Todd C. Miller <Todd.Miller@courtesan.com>
    3:  *
    4:  * Permission to use, copy, modify, and distribute this software for any
    5:  * purpose with or without fee is hereby granted, provided that the above
    6:  * copyright notice and this permission notice appear in all copies.
    7:  *
    8:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    9:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   10:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   11:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   12:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   13:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   14:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   15:  */
   16: 
   17: #include <config.h>
   18: 
   19: #include <sys/types.h>
   20: #include <sys/time.h>
   21: #include <sys/wait.h>
   22: #include <stdio.h>
   23: #ifdef STDC_HEADERS
   24: # include <stdlib.h>
   25: # include <stddef.h>
   26: #else
   27: # ifdef HAVE_STDLIB_H
   28: #  include <stdlib.h>
   29: # endif
   30: #endif /* STDC_HEADERS */
   31: #ifdef HAVE_STRING_H
   32: # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
   33: #  include <memory.h>
   34: # endif
   35: # include <string.h>
   36: #endif /* HAVE_STRING_H */
   37: #ifdef HAVE_STRINGS_H
   38: # include <strings.h>
   39: #endif /* HAVE_STRINGS_H */
   40: #ifdef HAVE_UNISTD_H
   41: # include <unistd.h>
   42: #endif /* HAVE_UNISTD_H */
   43: #ifdef TIME_WITH_SYS_TIME
   44: # include <time.h>
   45: #endif
   46: #ifdef HAVE_UTMPX_H
   47: # include <utmpx.h>
   48: #else
   49: # include <utmp.h>
   50: #endif /* HAVE_UTMPX_H */
   51: #ifdef HAVE_GETTTYENT
   52: # include <ttyent.h>
   53: #endif
   54: #include <fcntl.h>
   55: #include <signal.h>
   56: 
   57: #include "sudo.h"
   58: #include "sudo_exec.h"
   59: 
   60: /*
   61:  * Simplify handling of utmp vs. utmpx
   62:  */
   63: #if !defined(HAVE_GETUTXID) && defined(HAVE_GETUTID)
   64: # define getutxline(u)	getutline(u)
   65: # define pututxline(u)	pututline(u)
   66: # define setutxent()	setutent()
   67: # define endutxent()	endutent()
   68: #endif /* !HAVE_GETUTXID && HAVE_GETUTID */
   69: 
   70: #ifdef HAVE_GETUTXID
   71: typedef struct utmpx sudo_utmp_t;
   72: #else
   73: typedef struct utmp sudo_utmp_t;
   74: /* Older systems have ut_name, not us_user */
   75: # if !defined(HAVE_STRUCT_UTMP_UT_USER) && !defined(ut_user)
   76: #  define ut_user ut_name
   77: # endif
   78: #endif
   79: 
   80: /* HP-UX has __e_termination and __e_exit, others lack the __ */
   81: #if defined(HAVE_STRUCT_UTMPX_UT_EXIT_E_TERMINATION) || defined(HAVE_STRUCT_UTMP_UT_EXIT_E_TERMINATION)
   82: # undef  __e_termination
   83: # define __e_termination	e_termination
   84: # undef  __e_exit
   85: # define __e_exit		e_exit
   86: #endif
   87: 
   88: #if defined(HAVE_GETUTXID) || defined(HAVE_GETUTID)
   89: /*
   90:  * Create ut_id from the new ut_line and the old ut_id.
   91:  */
   92: static void
   93: utmp_setid(sudo_utmp_t *old, sudo_utmp_t *new)
   94: {
   95:     const char *line = new->ut_line;
   96:     size_t idlen;
   97:     debug_decl(utmp_setid, SUDO_DEBUG_UTMP)
   98: 
   99:     /* Skip over "tty" in the id if old entry did too. */
  100:     if (old != NULL) {
  101: 	if (strncmp(line, "tty", 3) == 0) {
  102: 	    idlen = MIN(sizeof(old->ut_id), 3);
  103: 	    if (strncmp(old->ut_id, "tty", idlen) != 0)
  104: 		line += 3;
  105: 	}
  106:     }
  107:     
  108:     /* Store as much as will fit, skipping parts of the beginning as needed. */
  109:     idlen = strlen(line);
  110:     if (idlen > sizeof(new->ut_id)) {
  111: 	line += idlen - sizeof(new->ut_id);
  112: 	idlen = sizeof(new->ut_id);
  113:     }
  114:     strncpy(new->ut_id, line, idlen);
  115: 
  116:     debug_return;
  117: }
  118: #endif /* HAVE_GETUTXID || HAVE_GETUTID */
  119: 
  120: /*
  121:  * Store time in utmp structure.
  122:  */
  123: static void
  124: utmp_settime(sudo_utmp_t *ut)
  125: {
  126:     struct timeval tv;
  127:     debug_decl(utmp_settime, SUDO_DEBUG_UTMP)
  128: 
  129:     gettimeofday(&tv, NULL);
  130: 
  131: #if defined(HAVE_STRUCT_UTMP_UT_TV) || defined(HAVE_STRUCT_UTMPX_UT_TV)
  132:     ut->ut_tv.tv_sec = tv.tv_sec;
  133:     ut->ut_tv.tv_usec = tv.tv_usec;
  134: #else
  135:     ut->ut_time = tv.tv_sec;
  136: #endif
  137: 
  138:     debug_return;
  139: }
  140: 
  141: /*
  142:  * Fill in a utmp entry, using an old entry as a template if there is one.
  143:  */
  144: static void
  145: utmp_fill(const char *line, const char *user, sudo_utmp_t *ut_old,
  146:     sudo_utmp_t *ut_new)
  147: {
  148:     debug_decl(utmp_file, SUDO_DEBUG_UTMP)
  149: 
  150:     if (ut_old == NULL) {
  151: 	memset(ut_new, 0, sizeof(*ut_new));
  152: 	if (user == NULL) {
  153: 	    strncpy(ut_new->ut_user, user_details.username,
  154: 		sizeof(ut_new->ut_user));
  155: 	}
  156:     } else if (ut_old != ut_new) {
  157: 	memcpy(ut_new, ut_old, sizeof(*ut_new));
  158:     }
  159:     if (user != NULL)
  160: 	strncpy(ut_new->ut_user, user, sizeof(ut_new->ut_user));
  161:     strncpy(ut_new->ut_line, line, sizeof(ut_new->ut_line));
  162: #if defined(HAVE_STRUCT_UTMPX_UT_ID) || defined(HAVE_STRUCT_UTMP_UT_ID)
  163:     utmp_setid(ut_old, ut_new);
  164: #endif
  165: #if defined(HAVE_STRUCT_UTMPX_UT_PID) || defined(HAVE_STRUCT_UTMP_UT_PID)
  166:     ut_new->ut_pid = getpid();
  167: #endif
  168:     utmp_settime(ut_new);
  169: #if defined(HAVE_STRUCT_UTMPX_UT_TYPE) || defined(HAVE_STRUCT_UTMP_UT_TYPE)
  170:     ut_new->ut_type = USER_PROCESS;
  171: #endif
  172:     debug_return;
  173: }
  174: 
  175: /*
  176:  * There are two basic utmp file types:
  177:  *
  178:  *  POSIX:  sequential access with new entries appended to the end.
  179:  *	    Manipulated via {get,put}utent()/{get,put}getutxent().
  180:  *
  181:  *  Legacy: sparse file indexed by ttyslot() * sizeof(struct utmp)
  182:  */
  183: #if defined(HAVE_GETUTXID) || defined(HAVE_GETUTID)
  184: bool
  185: utmp_login(const char *from_line, const char *to_line, int ttyfd,
  186:     const char *user)
  187: {
  188:     sudo_utmp_t utbuf, *ut_old = NULL;
  189:     bool rval = false;
  190:     debug_decl(utmp_login, SUDO_DEBUG_UTMP)
  191: 
  192:     /* Strip off /dev/ prefix from line as needed. */
  193:     if (strncmp(to_line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
  194: 	to_line += sizeof(_PATH_DEV) - 1;
  195:     setutxent();
  196:     if (from_line != NULL) {
  197: 	if (strncmp(from_line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
  198: 	    from_line += sizeof(_PATH_DEV) - 1;
  199: 
  200: 	/* Lookup old line. */
  201: 	memset(&utbuf, 0, sizeof(utbuf));
  202: 	strncpy(utbuf.ut_line, from_line, sizeof(utbuf.ut_line));
  203: 	ut_old = getutxline(&utbuf);
  204:     }
  205:     utmp_fill(to_line, user, ut_old, &utbuf);
  206:     if (pututxline(&utbuf) != NULL)
  207: 	rval = true;
  208:     endutxent();
  209: 
  210:     debug_return_bool(rval);
  211: }
  212: 
  213: bool
  214: utmp_logout(const char *line, int status)
  215: {
  216:     bool rval = false;
  217:     sudo_utmp_t *ut, utbuf;
  218:     debug_decl(utmp_logout, SUDO_DEBUG_UTMP)
  219: 
  220:     /* Strip off /dev/ prefix from line as needed. */
  221:     if (strncmp(line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
  222: 	line += sizeof(_PATH_DEV) - 1;
  223:    
  224:     memset(&utbuf, 0, sizeof(utbuf));
  225:     strncpy(utbuf.ut_line, line, sizeof(utbuf.ut_line));
  226:     if ((ut = getutxline(&utbuf)) != NULL) {
  227: 	memset(ut->ut_user, 0, sizeof(ut->ut_user));
  228: # if defined(HAVE_STRUCT_UTMPX_UT_TYPE) || defined(HAVE_STRUCT_UTMP_UT_TYPE)
  229: 	ut->ut_type = DEAD_PROCESS;
  230: # endif
  231: # if defined(HAVE_STRUCT_UTMPX_UT_EXIT) || defined(HAVE_STRUCT_UTMP_UT_EXIT)
  232: 	ut->ut_exit.__e_exit = WEXITSTATUS(status);
  233: 	ut->ut_exit.__e_termination = WIFEXITED(status) ? WEXITSTATUS(status) : 0;
  234: # endif
  235: 	utmp_settime(ut);
  236: 	if (pututxline(ut) != NULL)
  237: 	    rval = true;
  238:     }
  239:     debug_return_bool(rval);
  240: }
  241: 
  242: #else /* !HAVE_GETUTXID && !HAVE_GETUTID */
  243: 
  244: /*
  245:  * Find the slot for the specified line (tty name and file descriptor).
  246:  * Returns a slot suitable for seeking into utmp on success or <= 0 on error.
  247:  * If getttyent() is available we can use that to compute the slot.
  248:  */
  249: # ifdef HAVE_GETTTYENT
  250: static int
  251: utmp_slot(const char *line, int ttyfd)
  252: {
  253:     int slot = 1;
  254:     struct ttyent *tty;
  255:     debug_decl(utmp_slot, SUDO_DEBUG_UTMP)
  256: 
  257:     setttyent();
  258:     while ((tty = getttyent()) != NULL) {
  259: 	if (strcmp(line, tty->ty_name) == 0)
  260: 	    break;
  261: 	slot++;
  262:     }
  263:     endttyent();
  264:     debug_return_int(tty ? slot : 0);
  265: }
  266: # else
  267: static int
  268: utmp_slot(const char *line, int ttyfd)
  269: {
  270:     int sfd, slot;
  271:     debug_decl(utmp_slot, SUDO_DEBUG_UTMP)
  272: 
  273:     /*
  274:      * Temporarily point stdin to the tty since ttyslot()
  275:      * doesn't take an argument.
  276:      */
  277:     if ((sfd = dup(STDIN_FILENO)) == -1)
  278: 	fatal(U_("unable to save stdin"));
  279:     if (dup2(ttyfd, STDIN_FILENO) == -1)
  280: 	fatal(U_("unable to dup2 stdin"));
  281:     slot = ttyslot();
  282:     if (dup2(sfd, STDIN_FILENO) == -1)
  283: 	fatal(U_("unable to restore stdin"));
  284:     close(sfd);
  285: 
  286:     debug_return_int(slot);
  287: }
  288: # endif /* HAVE_GETTTYENT */
  289: 
  290: bool
  291: utmp_login(const char *from_line, const char *to_line, int ttyfd,
  292:     const char *user)
  293: {
  294:     sudo_utmp_t utbuf, *ut_old = NULL;
  295:     bool rval = false;
  296:     int slot;
  297:     FILE *fp;
  298:     debug_decl(utmp_login, SUDO_DEBUG_UTMP)
  299: 
  300:     /* Strip off /dev/ prefix from line as needed. */
  301:     if (strncmp(to_line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
  302: 	to_line += sizeof(_PATH_DEV) - 1;
  303: 
  304:     /* Find slot for new entry. */
  305:     slot = utmp_slot(to_line, ttyfd);
  306:     if (slot <= 0)
  307: 	goto done;
  308: 
  309:     if ((fp = fopen(_PATH_UTMP, "r+")) == NULL)
  310: 	goto done;
  311: 
  312:     if (from_line != NULL) {
  313: 	if (strncmp(from_line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
  314: 	    from_line += sizeof(_PATH_DEV) - 1;
  315: 
  316: 	/* Lookup old line. */
  317: 	while (fread(&utbuf, sizeof(utbuf), 1, fp) == 1) {
  318: # ifdef HAVE_STRUCT_UTMP_UT_ID
  319: 	    if (utbuf.ut_type != LOGIN_PROCESS && utbuf.ut_type != USER_PROCESS)
  320: 		continue;
  321: # endif
  322: 	    if (utbuf.ut_user[0] &&
  323: 		!strncmp(utbuf.ut_line, from_line, sizeof(utbuf.ut_line))) {
  324: 		ut_old = &utbuf;
  325: 		break;
  326: 	    }
  327: 	}
  328:     }
  329:     utmp_fill(to_line, user, ut_old, &utbuf);
  330: #ifdef HAVE_FSEEKO
  331:     if (fseeko(fp, slot * (off_t)sizeof(utbuf), SEEK_SET) == 0) {
  332: #else
  333:     if (fseek(fp, slot * (long)sizeof(utbuf), SEEK_SET) == 0) {
  334: #endif
  335: 	if (fwrite(&utbuf, sizeof(utbuf), 1, fp) == 1)
  336: 	    rval = true;
  337:     }
  338:     fclose(fp);
  339: 
  340: done:
  341:     debug_return_bool(rval);
  342: }
  343: 
  344: bool
  345: utmp_logout(const char *line, int status)
  346: {
  347:     sudo_utmp_t utbuf;
  348:     bool rval = false;
  349:     FILE *fp;
  350:     debug_decl(utmp_logout, SUDO_DEBUG_UTMP)
  351: 
  352:     if ((fp = fopen(_PATH_UTMP, "r+")) == NULL)
  353: 	debug_return_int(rval);
  354: 
  355:     /* Strip off /dev/ prefix from line as needed. */
  356:     if (strncmp(line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
  357: 	line += sizeof(_PATH_DEV) - 1;
  358:    
  359:     while (fread(&utbuf, sizeof(utbuf), 1, fp) == 1) {
  360: 	if (!strncmp(utbuf.ut_line, line, sizeof(utbuf.ut_line))) {
  361: 	    memset(utbuf.ut_user, 0, sizeof(utbuf.ut_user));
  362: # if defined(HAVE_STRUCT_UTMP_UT_TYPE)
  363: 	    utbuf.ut_type = DEAD_PROCESS;
  364: # endif
  365: 	    utmp_settime(&utbuf);
  366: 	    /* Back up and overwrite record. */
  367: #ifdef HAVE_FSEEKO
  368: 	    if (fseeko(fp, (off_t)0 - (off_t)sizeof(utbuf), SEEK_CUR) == 0) {
  369: #else
  370: 	    if (fseek(fp, 0L - (long)sizeof(utbuf), SEEK_CUR) == 0) {
  371: #endif
  372: 		if (fwrite(&utbuf, sizeof(utbuf), 1, fp) == 1)
  373: 		    rval = true;
  374: 	    }
  375: 	    break;
  376: 	}
  377:     }
  378:     fclose(fp);
  379: 
  380:     debug_return_bool(rval);
  381: }
  382: #endif /* HAVE_GETUTXID || HAVE_GETUTID */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>