Annotation of embedaddon/sudo/src/utmp.c, revision 1.1
1.1 ! misho 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>