/* * SPDX-License-Identifier: ISC * * Copyright (c) 2009-2021 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * This is an open source non-commercial project. Dear PVS-Studio, please check it. * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com */ #include #include #include #include #ifdef HAVE_STDBOOL_H # include #else # include "compat/stdbool.h" #endif #include #include #include #include #include #include "sudo_compat.h" #include "sudo_debug.h" #include "sudo_fatal.h" #include "sudo_gettext.h" #include "sudo_iolog.h" #include "sudo_util.h" /* * Read the on-disk sequence number, set sessid to the next * number, and update the on-disk copy. * Uses file locking to avoid sequence number collisions. */ bool iolog_nextid(const char *iolog_dir, char sessid[7]) { char buf[32], *ep; int i, fd = -1; unsigned long id = 0; size_t len; ssize_t nread; bool ret = false; char pathbuf[PATH_MAX]; static const char b36char[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; const uid_t iolog_uid = iolog_get_uid(); const gid_t iolog_gid = iolog_get_gid(); debug_decl(iolog_nextid, SUDO_DEBUG_UTIL); /* * Create I/O log directory if it doesn't already exist. */ len = strlcpy(pathbuf, iolog_dir, sizeof(pathbuf)); if (len >= sizeof(pathbuf)) { errno = ENAMETOOLONG; sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, "%s: %s", __func__, iolog_dir); goto done; } if (!iolog_mkdirs(pathbuf)) goto done; /* * Open sequence file */ len = strlcat(pathbuf, "/seq", sizeof(pathbuf)); if (len >= sizeof(pathbuf)) { errno = ENAMETOOLONG; sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, "%s: %s/seq", __func__, iolog_dir); goto done; } fd = iolog_openat(AT_FDCWD, pathbuf, O_RDWR|O_CREAT); if (fd == -1) { sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, "%s: unable to open %s", __func__, pathbuf); goto done; } if (!sudo_lock_file(fd, SUDO_LOCK)) { sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, "unable to lock %s", pathbuf); goto done; } if (fchown(fd, iolog_uid, iolog_gid) != 0) { sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, "%s: unable to fchown %d:%d %s", __func__, (int)iolog_uid, (int)iolog_gid, pathbuf); } /* Read current seq number (base 36). */ nread = read(fd, buf, sizeof(buf) - 1); if (nread != 0) { if (nread == -1) { goto done; } if (buf[nread - 1] == '\n') nread--; buf[nread] = '\0'; id = strtoul(buf, &ep, 36); if (ep == buf || *ep != '\0' || id >= iolog_get_maxseq()) { sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, "%s: bad sequence number: %s", pathbuf, buf); id = 0; } } id++; /* * Convert id to a string and stash in sessid. * Note that that least significant digits go at the end of the string. */ for (i = 5; i >= 0; i--) { buf[i] = b36char[id % 36]; id /= 36; } buf[6] = '\n'; /* Stash id for logging purposes. */ memcpy(sessid, buf, 6); sessid[6] = '\0'; /* Rewind and overwrite old seq file, including the NUL byte. */ if (pwrite(fd, buf, 7, 0) != 7) { sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO, "%s: unable to write %s", __func__, pathbuf); goto done; } ret = true; done: if (fd != -1) close(fd); debug_return_bool(ret); }