File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / sudo / src / utmp.c
Revision 1.1: download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 16:23:02 2012 UTC (12 years, 4 months ago) by misho
CVS tags: MAIN, HEAD
Initial revision

    1: /*
    2:  * Copyright (c) 2011 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/param.h>
   21: #include <sys/time.h>
   22: #include <sys/wait.h>
   23: #include <stdio.h>
   24: #ifdef STDC_HEADERS
   25: # include <stdlib.h>
   26: # include <stddef.h>
   27: #else
   28: # ifdef HAVE_STDLIB_H
   29: #  include <stdlib.h>
   30: # endif
   31: #endif /* STDC_HEADERS */
   32: #ifdef HAVE_STRING_H
   33: # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
   34: #  include <memory.h>
   35: # endif
   36: # include <string.h>
   37: #endif /* HAVE_STRING_H */
   38: #ifdef HAVE_STRINGS_H
   39: # include <strings.h>
   40: #endif /* HAVE_STRINGS_H */
   41: #ifdef HAVE_UNISTD_H
   42: # include <unistd.h>
   43: #endif /* HAVE_UNISTD_H */
   44: #if TIME_WITH_SYS_TIME
   45: # include <time.h>
   46: #endif
   47: #ifdef HAVE_UTMPX_H
   48: # include <utmpx.h>
   49: #else
   50: # include <utmp.h>
   51: #endif /* HAVE_UTMPX_H */
   52: #ifdef HAVE_GETTTYENT
   53: # include <ttyent.h>
   54: #endif
   55: #include <fcntl.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(u)
   67: # define endutxent	endutent(u)
   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: 
   98:     /* Skip over "tty" in the id if old entry did too. */
   99:     if (old != NULL) {
  100: 	if (strncmp(line, "tty", 3) == 0) {
  101: 	    idlen = MIN(sizeof(old->ut_id), 3);
  102: 	    if (strncmp(old->ut_id, "tty", idlen) != 0)
  103: 		line += 3;
  104: 	}
  105:     }
  106:     
  107:     /* Store as much as will fit, skipping parts of the beginning as needed. */
  108:     idlen = strlen(line);
  109:     if (idlen > sizeof(new->ut_id)) {
  110: 	line += idlen - sizeof(new->ut_id);
  111: 	idlen = sizeof(new->ut_id);
  112:     }
  113:     strncpy(new->ut_id, line, idlen);
  114: }
  115: #endif /* HAVE_GETUTXID || HAVE_GETUTID */
  116: 
  117: /*
  118:  * Store time in utmp structure.
  119:  */
  120: static void
  121: utmp_settime(sudo_utmp_t *ut)
  122: {
  123:     struct timeval tv;
  124: 
  125:     gettimeofday(&tv, NULL);
  126: 
  127: #if defined(HAVE_STRUCT_UTMP_UT_TV) || defined(HAVE_STRUCT_UTMPX_UT_TV)
  128:     ut->ut_tv.tv_sec = tv.tv_sec;
  129:     ut->ut_tv.tv_usec = tv.tv_usec;
  130: #else
  131:     ut->ut_time = tv.tv_sec;
  132: #endif
  133: }
  134: 
  135: /*
  136:  * Fill in a utmp entry, using an old entry as a template if there is one.
  137:  */
  138: static void
  139: utmp_fill(const char *line, const char *user, sudo_utmp_t *ut_old,
  140:     sudo_utmp_t *ut_new)
  141: {
  142:     if (ut_old == NULL) {
  143: 	memset(ut_new, 0, sizeof(*ut_new));
  144: 	if (user == NULL) {
  145: 	    strncpy(ut_new->ut_user, user_details.username,
  146: 		sizeof(ut_new->ut_user));
  147: 	}
  148:     } else if (ut_old != ut_new) {
  149: 	memcpy(ut_new, ut_old, sizeof(*ut_new));
  150:     }
  151:     if (user != NULL)
  152: 	strncpy(ut_new->ut_user, user, sizeof(ut_new->ut_user));
  153:     strncpy(ut_new->ut_line, line, sizeof(ut_new->ut_line));
  154: #if defined(HAVE_STRUCT_UTMPX_UT_ID) || defined(HAVE_STRUCT_UTMP_UT_ID)
  155:     utmp_setid(ut_old, ut_new);
  156: #endif
  157: #if defined(HAVE_STRUCT_UTMPX_UT_PID) || defined(HAVE_STRUCT_UTMP_UT_PID)
  158:     ut_new->ut_pid = getpid();
  159: #endif
  160:     utmp_settime(ut_new);
  161: #if defined(HAVE_STRUCT_UTMPX_UT_TYPE) || defined(HAVE_STRUCT_UTMP_UT_TYPE)
  162:     ut_new->ut_type = USER_PROCESS;
  163: #endif
  164: }
  165: 
  166: /*
  167:  * There are two basic utmp file types:
  168:  *
  169:  *  POSIX:  sequential access with new entries appended to the end.
  170:  *	    Manipulated via {get,put}utent()/{get,put}getutxent().
  171:  *
  172:  *  Legacy: sparse file indexed by ttyslot() * sizeof(struct utmp)
  173:  */
  174: #if defined(HAVE_GETUTXID) || defined(HAVE_GETUTID)
  175: int
  176: utmp_login(const char *from_line, const char *to_line, int ttyfd,
  177:     const char *user)
  178: {
  179:     sudo_utmp_t utbuf, *ut_old = NULL;
  180:     int rval = FALSE;
  181: 
  182:     /* Strip off /dev/ prefix from line as needed. */
  183:     if (strncmp(to_line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
  184: 	to_line += sizeof(_PATH_DEV) - 1;
  185:     setutxent();
  186:     if (from_line != NULL) {
  187: 	if (strncmp(from_line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
  188: 	    from_line += sizeof(_PATH_DEV) - 1;
  189: 
  190: 	/* Lookup old line. */
  191: 	memset(&utbuf, 0, sizeof(utbuf));
  192: 	strncpy(utbuf.ut_line, from_line, sizeof(utbuf.ut_line));
  193: 	ut_old = getutxline(&utbuf);
  194:     }
  195:     utmp_fill(to_line, user, ut_old, &utbuf);
  196:     if (pututxline(&utbuf) != NULL)
  197: 	rval = TRUE;
  198:     endutxent();
  199: 
  200:     return rval;
  201: }
  202: 
  203: int
  204: utmp_logout(const char *line, int status)
  205: {
  206:     int rval = FALSE;
  207:     sudo_utmp_t *ut, utbuf;
  208: 
  209:     /* Strip off /dev/ prefix from line as needed. */
  210:     if (strncmp(line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
  211: 	line += sizeof(_PATH_DEV) - 1;
  212:    
  213:     memset(&utbuf, 0, sizeof(utbuf));
  214:     strncpy(utbuf.ut_line, line, sizeof(utbuf.ut_line));
  215:     if ((ut = getutxline(&utbuf)) != NULL) {
  216: 	memset(ut->ut_user, 0, sizeof(ut->ut_user));
  217: # if defined(HAVE_STRUCT_UTMPX_UT_TYPE) || defined(HAVE_STRUCT_UTMP_UT_TYPE)
  218: 	ut->ut_type = DEAD_PROCESS;
  219: # endif
  220: # if defined(HAVE_STRUCT_UTMPX_UT_EXIT) || defined(HAVE_STRUCT_UTMP_UT_EXIT)
  221: 	ut->ut_exit.__e_exit = WEXITSTATUS(status);
  222: 	ut->ut_exit.__e_termination = WIFEXITED(status) ? WEXITSTATUS(status) : 0;
  223: # endif
  224: 	utmp_settime(ut);
  225: 	if (pututxline(ut) != NULL)
  226: 	    rval = TRUE;
  227:     }
  228:     return rval;
  229: }
  230: 
  231: #else /* !HAVE_GETUTXID && !HAVE_GETUTID */
  232: 
  233: /*
  234:  * Find the slot for the specified line (tty name and file descriptor).
  235:  * Returns a slot suitable for seeking into utmp on success or <= 0 on error.
  236:  * If getttyent() is available we can use that to compute the slot.
  237:  */
  238: # ifdef HAVE_GETTTYENT
  239: static int
  240: utmp_slot(const char *line, int ttyfd)
  241: {
  242:     int slot = 1;
  243:     struct ttyent *tty;
  244: 
  245:     setttyent();
  246:     while ((tty = getttyent()) != NULL) {
  247: 	if (strcmp(line, tty->ty_name) == 0)
  248: 	    break;
  249: 	slot++;
  250:     }
  251:     endttyent();
  252:     return tty ? slot : 0;
  253: }
  254: # else
  255: static int
  256: utmp_slot(const char *line, int ttyfd)
  257: {
  258:     int sfd, slot;
  259: 
  260:     /*
  261:      * Temporarily point stdin to the tty since ttyslot()
  262:      * doesn't take an argument.
  263:      */
  264:     if ((sfd = dup(STDIN_FILENO)) == -1)
  265: 	error(1, _("unable to save stdin"));
  266:     if (dup2(ttyfd, STDIN_FILENO) == -1)
  267: 	error(1, _("unable to dup2 stdin"));
  268:     slot = ttyslot();
  269:     if (dup2(sfd, STDIN_FILENO) == -1)
  270: 	error(1, _("unable to restore stdin"));
  271:     close(sfd);
  272: 
  273:     return slot;
  274: }
  275: # endif /* HAVE_GETTTYENT */
  276: 
  277: int
  278: utmp_login(const char *from_line, const char *to_line, int ttyfd,
  279:     const char *user)
  280: {
  281:     sudo_utmp_t utbuf, *ut_old = NULL;
  282:     int slot, rval = FALSE;
  283:     FILE *fp;
  284: 
  285:     /* Strip off /dev/ prefix from line as needed. */
  286:     if (strncmp(to_line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
  287: 	to_line += sizeof(_PATH_DEV) - 1;
  288: 
  289:     /* Find slot for new entry. */
  290:     slot = utmp_slot(to_line, ttyfd);
  291:     if (slot <= 0)
  292: 	goto done;
  293: 
  294:     if ((fp = fopen(_PATH_UTMP, "r+")) == NULL)
  295: 	goto done;
  296: 
  297:     if (from_line != NULL) {
  298: 	if (strncmp(from_line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
  299: 	    from_line += sizeof(_PATH_DEV) - 1;
  300: 
  301: 	/* Lookup old line. */
  302: 	while (fread(&utbuf, sizeof(utbuf), 1, fp) == 1) {
  303: # ifdef HAVE_STRUCT_UTMP_UT_ID
  304: 	    if (utbuf.ut_type != LOGIN_PROCESS && utbuf.ut_type != USER_PROCESS)
  305: 		continue;
  306: # endif
  307: 	    if (utbuf.ut_user[0] &&
  308: 		!strncmp(utbuf.ut_line, from_line, sizeof(utbuf.ut_line))) {
  309: 		ut_old = &utbuf;
  310: 		break;
  311: 	    }
  312: 	}
  313:     }
  314:     utmp_fill(to_line, user, ut_old, &utbuf);
  315:     if (fseek(fp, slot * (long)sizeof(utbuf), SEEK_SET) == 0) {
  316: 	if (fwrite(&utbuf, sizeof(utbuf), 1, fp) == 1)
  317: 	    rval = TRUE;
  318:     }
  319:     fclose(fp);
  320: 
  321: done:
  322:     return rval;
  323: }
  324: 
  325: int
  326: utmp_logout(const char *line, int status)
  327: {
  328:     sudo_utmp_t utbuf;
  329:     int rval = FALSE;
  330:     FILE *fp;
  331: 
  332:     if ((fp = fopen(_PATH_UTMP, "r+")) == NULL)
  333: 	return rval;
  334: 
  335:     /* Strip off /dev/ prefix from line as needed. */
  336:     if (strncmp(line, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
  337: 	line += sizeof(_PATH_DEV) - 1;
  338:    
  339:     while (fread(&utbuf, sizeof(utbuf), 1, fp) == 1) {
  340: 	if (!strncmp(utbuf.ut_line, line, sizeof(utbuf.ut_line))) {
  341: 	    memset(utbuf.ut_user, 0, sizeof(utbuf.ut_user));
  342: # if defined(HAVE_STRUCT_UTMP_UT_TYPE)
  343: 	    utbuf.ut_type = DEAD_PROCESS;
  344: # endif
  345: 	    utmp_settime(&utbuf);
  346: 	    /* Back up and overwrite record. */
  347: 	    if (fseek(fp, 0L - (long)sizeof(utbuf), SEEK_CUR) == 0) {
  348: 		if (fwrite(&utbuf, sizeof(utbuf), 1, fp) == 1)
  349: 		    rval = TRUE;
  350: 	    }
  351: 	    break;
  352: 	}
  353:     }
  354:     fclose(fp);
  355: 
  356:     return rval;
  357: }
  358: #endif /* HAVE_GETUTXID || HAVE_GETUTID */

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