From 7363ad7b3230b7b03a83f68a0ea33b4144c78a79 Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Mon, 11 Sep 2023 10:27:35 -0600 Subject: [PATCH] Use the user-ID instead of user-name for the timestamp and lecture file. This avoids problems if the user name itself contains a path separator. --- docs/sudoers.man.in | 6 ++-- docs/sudoers.mdoc.in | 6 ++-- docs/sudoers_timestamp.man.in | 12 +++++-- docs/sudoers_timestamp.mdoc.in | 11 ++++-- plugins/sudoers/check.c | 4 +-- plugins/sudoers/timestamp.c | 63 +++++++++++++++++++++++++--------- plugins/sudoers/timestamp.h | 4 +-- 7 files changed, 73 insertions(+), 33 deletions(-) diff --git a/docs/sudoers.man.in b/docs/sudoers.man.in index bea31ea93..33d676f48 100644 --- a/docs/sudoers.man.in +++ b/docs/sudoers.man.in @@ -25,7 +25,7 @@ .nr BA @BAMAN@ .nr LC @LCMAN@ .nr PS @PSMAN@ -.TH "SUDOERS" "@mansectform@" "August 28, 2023" "Sudo @PACKAGE_VERSION@" "File Formats Manual" +.TH "SUDOERS" "@mansectform@" "September 20, 2023" "Sudo @PACKAGE_VERSION@" "File Formats Manual" .nh .if n .ad l .SH "NAME" @@ -6199,7 +6199,7 @@ line in the sudo.conf(@mansectform@) file. .TP 3n -unable to open @rundir@/ts/username +unable to open @rundir@/ts/user-ID \fBsudoers\fR was unable to read or create the user's time stamp file. This can happen when @@ -6213,7 +6213,7 @@ The default mode for \fI@rundir@\fR is 0711. .TP 3n -unable to write to @rundir@/ts/username +unable to write to @rundir@/ts/user-ID \fBsudoers\fR was unable to write to the user's time stamp file. .TP 3n diff --git a/docs/sudoers.mdoc.in b/docs/sudoers.mdoc.in index b861d8f7b..9c7bade91 100644 --- a/docs/sudoers.mdoc.in +++ b/docs/sudoers.mdoc.in @@ -25,7 +25,7 @@ .nr BA @BAMAN@ .nr LC @LCMAN@ .nr PS @PSMAN@ -.Dd August 28, 2023 +.Dd September 20, 2023 .Dt SUDOERS @mansectform@ .Os Sudo @PACKAGE_VERSION@ .Sh NAME @@ -5797,7 +5797,7 @@ file) to the line in the .Xr sudo.conf @mansectform@ file. -.It unable to open @rundir@/ts/username +.It unable to open @rundir@/ts/user-ID .Nm was unable to read or create the user's time stamp file. This can happen when @@ -5810,7 +5810,7 @@ is not searchable by group or other. The default mode for .Pa @rundir@ is 0711. -.It unable to write to @rundir@/ts/username +.It unable to write to @rundir@/ts/user-ID .Nm was unable to write to the user's time stamp file. .It @rundir@/ts is owned by uid X, should be Y diff --git a/docs/sudoers_timestamp.man.in b/docs/sudoers_timestamp.man.in index e824029a2..92564025b 100644 --- a/docs/sudoers_timestamp.man.in +++ b/docs/sudoers_timestamp.man.in @@ -2,7 +2,7 @@ .\" .\" SPDX-License-Identifier: ISC .\" -.\" Copyright (c) 2017-2020, 2022 Todd C. Miller +.\" Copyright (c) 2017-2020, 2022-2023 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 @@ -16,7 +16,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.TH "SUDOERS_TIMESTAMP" "@mansectform@" "September 13, 2022" "Sudo @PACKAGE_VERSION@" "File Formats Manual" +.TH "SUDOERS_TIMESTAMP" "@mansectform@" "September 20, 2023" "Sudo @PACKAGE_VERSION@" "File Formats Manual" .nh .if n .ad l .SH "NAME" @@ -25,7 +25,7 @@ .SH "DESCRIPTION" The \fBsudoers\fR -plugin uses per-user time stamp files for credential caching. +plugin uses per-user-ID time stamp files for credential caching. Once a user has been authenticated, they may use \fBsudo\fR without a password for a short period of time @@ -278,6 +278,12 @@ This prevents re-use of the time stamp file after logout in most cases. Support was added for the kernel-based tty time stamps available in OpenBSD which do not use an on-disk time stamp file. +.TP 6n +1.9.15 +Time stamp file path names are now based on the invoking user-ID +instead of the user name. +This avoids problems with user names that include a path separator +character. .SH "AUTHORS" Many people have worked on \fBsudo\fR diff --git a/docs/sudoers_timestamp.mdoc.in b/docs/sudoers_timestamp.mdoc.in index 633cc7555..cc0ea9b23 100644 --- a/docs/sudoers_timestamp.mdoc.in +++ b/docs/sudoers_timestamp.mdoc.in @@ -1,7 +1,7 @@ .\" .\" SPDX-License-Identifier: ISC .\" -.\" Copyright (c) 2017-2020, 2022 Todd C. Miller +.\" Copyright (c) 2017-2020, 2022-2023 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 @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd September 13, 2022 +.Dd September 20, 2023 .Dt SUDOERS_TIMESTAMP @mansectform@ .Os Sudo @PACKAGE_VERSION@ .Sh NAME @@ -24,7 +24,7 @@ .Sh DESCRIPTION The .Nm sudoers -plugin uses per-user time stamp files for credential caching. +plugin uses per-user-ID time stamp files for credential caching. Once a user has been authenticated, they may use .Nm sudo without a password for a short period of time @@ -256,6 +256,11 @@ This prevents re-use of the time stamp file after logout in most cases. Support was added for the kernel-based tty time stamps available in .Ox which do not use an on-disk time stamp file. +.It 1.9.15 +Time stamp file path names are now based on the invoking user-ID +instead of the user name. +This avoids problems with user names that include a path separator +character. .El .Sh AUTHORS Many people have worked on diff --git a/plugins/sudoers/check.c b/plugins/sudoers/check.c index 8880de5d8..434e3ca14 100644 --- a/plugins/sudoers/check.c +++ b/plugins/sudoers/check.c @@ -201,7 +201,7 @@ check_user(struct sudoers_context *ctx, unsigned int validated, ret = verify_user(ctx, closure.auth_pw, prompt, validated, &callback); if (ret == AUTH_SUCCESS && closure.lectured) - (void)set_lectured(ctx->user.name); /* lecture error not fatal */ + (void)set_lectured(ctx); /* lecture error not fatal */ free(prompt); break; } @@ -251,7 +251,7 @@ display_lecture(struct sudo_conv_callback *callback) debug_return; if (def_lecture == never || - (def_lecture == once && already_lectured(closure->ctx->user.name))) + (def_lecture == once && already_lectured(closure->ctx))) debug_return; memset(&msg, 0, sizeof(msg)); diff --git a/plugins/sudoers/timestamp.c b/plugins/sudoers/timestamp.c index 04e8e03c7..0529ae73a 100644 --- a/plugins/sudoers/timestamp.c +++ b/plugins/sudoers/timestamp.c @@ -442,9 +442,10 @@ ts_init_key_nonglobal(const struct sudoers_context *ctx, void * timestamp_open(const struct sudoers_context *ctx) { + int tries, len, dfd = -1, fd = -1; + char uidstr[STRLEN_MAX_UNSIGNED(uid_t) + 1]; struct ts_cookie *cookie; char *fname = NULL; - int tries, dfd = -1, fd = -1; debug_decl(timestamp_open, SUDOERS_DEBUG_AUTH); /* Zero timeout means don't use the time stamp file. */ @@ -459,14 +460,19 @@ timestamp_open(const struct sudoers_context *ctx) goto bad; /* Open time stamp file. */ - if (asprintf(&fname, "%s/%s", def_timestampdir, ctx->user.name) == -1) { + len = snprintf(uidstr, sizeof(uidstr), "%u", (unsigned int)ctx->user.uid); + if (len < 0 || len >= ssizeof(uidstr)) { + errno = EINVAL; + goto bad; + } + if (asprintf(&fname, "%s/%s", def_timestampdir, uidstr) == -1) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); goto bad; } for (tries = 1; ; tries++) { struct stat sb; - fd = ts_openat(dfd, ctx->user.name, O_RDWR|O_CREAT); + fd = ts_openat(dfd, uidstr, O_RDWR|O_CREAT); switch (fd) { case TIMESTAMP_OPEN_ERROR: log_warning(ctx, SLOG_SEND_MAIL, N_("unable to open %s"), fname); @@ -492,7 +498,7 @@ timestamp_open(const struct sudoers_context *ctx) sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO, "removing time stamp file that predates boot time"); close(fd); - unlinkat(dfd, ctx->user.name, 0); + unlinkat(dfd, uidstr, 0); continue; } } @@ -1004,7 +1010,8 @@ int timestamp_remove(const struct sudoers_context *ctx, bool unlink_it) { struct timestamp_entry key, entry; - int dfd = -1, fd = -1, ret = true; + int len, dfd = -1, fd = -1, ret = true; + char uidstr[STRLEN_MAX_UNSIGNED(uid_t) + 1]; char *fname = NULL; debug_decl(timestamp_remove, SUDOERS_DEBUG_AUTH); @@ -1025,7 +1032,13 @@ timestamp_remove(const struct sudoers_context *ctx, bool unlink_it) goto done; } - if (asprintf(&fname, "%s/%s", def_timestampdir, ctx->user.name) == -1) { + len = snprintf(uidstr, sizeof(uidstr), "%u", (unsigned int)ctx->user.uid); + if (len < 0 || len >= ssizeof(uidstr)) { + errno = EINVAL; + ret = -1; + goto done; + } + if (asprintf(&fname, "%s/%s", def_timestampdir, uidstr) == -1) { sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); ret = -1; goto done; @@ -1033,12 +1046,12 @@ timestamp_remove(const struct sudoers_context *ctx, bool unlink_it) /* For "sudo -K" simply unlink the time stamp file. */ if (unlink_it) { - ret = unlinkat(dfd, ctx->user.name, 0) ? -1 : true; + ret = unlinkat(dfd, uidstr, 0) ? -1 : true; goto done; } /* Open time stamp file and lock it for exclusive access. */ - fd = ts_openat(dfd, ctx->user.name, O_RDWR); + fd = ts_openat(dfd, uidstr, O_RDWR); switch (fd) { case TIMESTAMP_OPEN_ERROR: if (errno != ENOENT) @@ -1113,18 +1126,28 @@ cb_timestampowner(struct sudoers_context *ctx, const char *file, * Returns true if the user has already been lectured. */ bool -already_lectured(const char *user) +already_lectured(const struct sudoers_context *ctx) { + char uidstr[STRLEN_MAX_UNSIGNED(uid_t) + 1]; bool ret = false; struct stat sb; - int dfd; + int dfd, len; debug_decl(already_lectured, SUDOERS_DEBUG_AUTH); + /* Check the existence and validity of timestamp dir. */ dfd = ts_secure_opendir(def_lecture_status_dir, false, true); - if (dfd != -1) { - ret = fstatat(dfd, user, &sb, AT_SYMLINK_NOFOLLOW) == 0; + if (dfd == -1) + goto done; + + len = snprintf(uidstr, sizeof(uidstr), "%u", (unsigned int)ctx->user.uid); + if (len < 0 || len >= ssizeof(uidstr)) + goto done; + + ret = fstatat(dfd, uidstr, &sb, AT_SYMLINK_NOFOLLOW) == 0; + +done: + if (dfd != -1) close(dfd); - } debug_return_bool(ret); } @@ -1133,9 +1156,10 @@ already_lectured(const char *user) * Returns true on success, false on failure or -1 on setuid failure. */ int -set_lectured(const char *user) +set_lectured(const struct sudoers_context *ctx) { - int dfd, fd, ret = false; + char uidstr[STRLEN_MAX_UNSIGNED(uid_t) + 1]; + int dfd, fd, len, ret = false; debug_decl(set_lectured, SUDOERS_DEBUG_AUTH); /* Check the validity of timestamp dir and create if missing. */ @@ -1143,8 +1167,12 @@ set_lectured(const char *user) if (dfd == -1) goto done; + len = snprintf(uidstr, sizeof(uidstr), "%u", (unsigned int)ctx->user.uid); + if (len < 0 || len >= ssizeof(uidstr)) + goto done; + /* Create lecture file. */ - fd = ts_openat(dfd, user, O_WRONLY|O_CREAT|O_EXCL); + fd = ts_openat(dfd, uidstr, O_WRONLY|O_CREAT|O_EXCL); switch (fd) { case TIMESTAMP_OPEN_ERROR: /* Failed to open, not a fatal error. */ @@ -1159,9 +1187,10 @@ set_lectured(const char *user) ret = true; break; } - close(dfd); done: + if (dfd != -1) + close(dfd); debug_return_int(ret); } diff --git a/plugins/sudoers/timestamp.h b/plugins/sudoers/timestamp.h index 4ce5ad1d5..924d84cb1 100644 --- a/plugins/sudoers/timestamp.h +++ b/plugins/sudoers/timestamp.h @@ -96,8 +96,8 @@ int timestamp_status(void *vcookie, struct passwd *pw); uid_t timestamp_get_uid(void); bool cb_timestampowner(struct sudoers_context *ctx, const char *file, int line, int column, const union sudo_defs_val *sd_un, int op); int get_starttime(pid_t pid, struct timespec *starttime); -bool already_lectured(const char *user); -int set_lectured(const char *user); +bool already_lectured(const struct sudoers_context *ctx); +int set_lectured(const struct sudoers_context *ctx); void display_lecture(struct sudo_conv_callback *callback); int create_admin_success_flag(const struct sudoers_context *ctx);