Annotation of embedaddon/sudo/src/utmp.c, revision 1.1.1.4

1.1       misho       1: /*
1.1.1.4 ! misho       2:  * Copyright (c) 2011-2013 Todd C. Miller <Todd.Miller@courtesan.com>
1.1       misho       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: #if 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>
1.1.1.3   misho      55: #include <signal.h>
1.1       misho      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)
1.1.1.2   misho      66: # define setutxent()   setutent()
                     67: # define endutxent()   endutent()
1.1       misho      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;
1.1.1.2   misho      97:     debug_decl(utmp_setid, SUDO_DEBUG_UTMP)
1.1       misho      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);
1.1.1.2   misho     115: 
                    116:     debug_return;
1.1       misho     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;
1.1.1.2   misho     127:     debug_decl(utmp_settime, SUDO_DEBUG_UTMP)
1.1       misho     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
1.1.1.2   misho     137: 
                    138:     debug_return;
1.1       misho     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: {
1.1.1.2   misho     148:     debug_decl(utmp_file, SUDO_DEBUG_UTMP)
                    149: 
1.1       misho     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
1.1.1.2   misho     172:     debug_return;
1.1       misho     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)
1.1.1.2   misho     184: bool
1.1       misho     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;
1.1.1.2   misho     189:     bool rval = false;
                    190:     debug_decl(utmp_login, SUDO_DEBUG_UTMP)
1.1       misho     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)
1.1.1.2   misho     207:        rval = true;
1.1       misho     208:     endutxent();
                    209: 
1.1.1.2   misho     210:     debug_return_bool(rval);
1.1       misho     211: }
                    212: 
1.1.1.2   misho     213: bool
1.1       misho     214: utmp_logout(const char *line, int status)
                    215: {
1.1.1.2   misho     216:     bool rval = false;
1.1       misho     217:     sudo_utmp_t *ut, utbuf;
1.1.1.2   misho     218:     debug_decl(utmp_logout, SUDO_DEBUG_UTMP)
1.1       misho     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)
1.1.1.2   misho     237:            rval = true;
1.1       misho     238:     }
1.1.1.2   misho     239:     debug_return_bool(rval);
1.1       misho     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;
1.1.1.2   misho     255:     debug_decl(utmp_slot, SUDO_DEBUG_UTMP)
1.1       misho     256: 
                    257:     setttyent();
                    258:     while ((tty = getttyent()) != NULL) {
                    259:        if (strcmp(line, tty->ty_name) == 0)
                    260:            break;
                    261:        slot++;
                    262:     }
                    263:     endttyent();
1.1.1.2   misho     264:     debug_return_int(tty ? slot : 0);
1.1       misho     265: }
                    266: # else
                    267: static int
                    268: utmp_slot(const char *line, int ttyfd)
                    269: {
                    270:     int sfd, slot;
1.1.1.2   misho     271:     debug_decl(utmp_slot, SUDO_DEBUG_UTMP)
1.1       misho     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)
1.1.1.4 ! misho     278:        fatal(_("unable to save stdin"));
1.1       misho     279:     if (dup2(ttyfd, STDIN_FILENO) == -1)
1.1.1.4 ! misho     280:        fatal(_("unable to dup2 stdin"));
1.1       misho     281:     slot = ttyslot();
                    282:     if (dup2(sfd, STDIN_FILENO) == -1)
1.1.1.4 ! misho     283:        fatal(_("unable to restore stdin"));
1.1       misho     284:     close(sfd);
                    285: 
1.1.1.2   misho     286:     debug_return_int(slot);
1.1       misho     287: }
                    288: # endif /* HAVE_GETTTYENT */
                    289: 
1.1.1.2   misho     290: bool
1.1       misho     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;
1.1.1.2   misho     295:     bool rval = false;
                    296:     int slot;
1.1       misho     297:     FILE *fp;
1.1.1.2   misho     298:     debug_decl(utmp_login, SUDO_DEBUG_UTMP)
1.1       misho     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);
1.1.1.3   misho     330: #ifdef HAVE_FSEEKO
                    331:     if (fseeko(fp, slot * (off_t)sizeof(utbuf), SEEK_SET) == 0) {
                    332: #else
1.1       misho     333:     if (fseek(fp, slot * (long)sizeof(utbuf), SEEK_SET) == 0) {
1.1.1.3   misho     334: #endif
1.1       misho     335:        if (fwrite(&utbuf, sizeof(utbuf), 1, fp) == 1)
1.1.1.2   misho     336:            rval = true;
1.1       misho     337:     }
                    338:     fclose(fp);
                    339: 
                    340: done:
1.1.1.2   misho     341:     debug_return_bool(rval);
1.1       misho     342: }
                    343: 
1.1.1.2   misho     344: bool
1.1       misho     345: utmp_logout(const char *line, int status)
                    346: {
                    347:     sudo_utmp_t utbuf;
1.1.1.2   misho     348:     bool rval = false;
1.1       misho     349:     FILE *fp;
1.1.1.2   misho     350:     debug_decl(utmp_logout, SUDO_DEBUG_UTMP)
1.1       misho     351: 
                    352:     if ((fp = fopen(_PATH_UTMP, "r+")) == NULL)
1.1.1.2   misho     353:        debug_return_int(rval);
1.1       misho     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. */
1.1.1.3   misho     367: #ifdef HAVE_FSEEKO
                    368:            if (fseeko(fp, (off_t)0 - (off_t)sizeof(utbuf), SEEK_CUR) == 0) {
                    369: #else
1.1       misho     370:            if (fseek(fp, 0L - (long)sizeof(utbuf), SEEK_CUR) == 0) {
1.1.1.3   misho     371: #endif
1.1       misho     372:                if (fwrite(&utbuf, sizeof(utbuf), 1, fp) == 1)
1.1.1.2   misho     373:                    rval = true;
1.1       misho     374:            }
                    375:            break;
                    376:        }
                    377:     }
                    378:     fclose(fp);
                    379: 
1.1.1.2   misho     380:     debug_return_bool(rval);
1.1       misho     381: }
                    382: #endif /* HAVE_GETUTXID || HAVE_GETUTID */

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