upstream: refactor tilde_expand_filename() and make it handle ~user

paths with no trailing slash; feedback/ok markus and jsg

OpenBSD-Commit-ID: a2ab365598a902f0f14ba6a4f8fb2d07a9b5d51d
This commit is contained in:
djm@openbsd.org 2022-01-08 07:32:45 +00:00 committed by Damien Miller
parent dc38236ab6
commit 9614113377
1 changed files with 47 additions and 31 deletions

78
misc.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: misc.c,v 1.171 2021/11/13 21:14:13 deraadt Exp $ */
/* $OpenBSD: misc.c,v 1.172 2022/01/08 07:32:45 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
* Copyright (c) 2005-2020 Damien Miller. All rights reserved.
@ -1119,53 +1119,69 @@ freeargs(arglist *args)
int
tilde_expand(const char *filename, uid_t uid, char **retp)
{
const char *path, *sep;
char user[128], *ret;
char *ocopy = NULL, *copy, *s = NULL;
const char *path = NULL, *user = NULL;
struct passwd *pw;
u_int len, slash;
size_t len;
int ret = -1, r, slash;
*retp = NULL;
if (*filename != '~') {
*retp = xstrdup(filename);
return 0;
}
filename++;
ocopy = copy = xstrdup(filename + 1);
path = strchr(filename, '/');
if (path != NULL && path > filename) { /* ~user/path */
slash = path - filename;
if (slash > sizeof(user) - 1) {
error_f("~username too long");
return -1;
if (*copy == '\0') /* ~ */
path = NULL;
else if (*copy == '/') {
copy += strspn(copy, "/");
if (*copy == '\0')
path = NULL; /* ~/ */
else
path = copy; /* ~/path */
} else {
user = copy;
if ((path = strchr(copy, '/')) != NULL) {
copy[path - copy] = '\0';
path++;
path += strspn(path, "/");
if (*path == '\0') /* ~user/ */
path = NULL;
/* else ~user/path */
}
memcpy(user, filename, slash);
user[slash] = '\0';
/* else ~user */
}
if (user != NULL) {
if ((pw = getpwnam(user)) == NULL) {
error_f("No such user %s", user);
return -1;
goto out;
}
} else if ((pw = getpwuid(uid)) == NULL) { /* ~/path */
} else if ((pw = getpwuid(uid)) == NULL) {
error_f("No such uid %ld", (long)uid);
return -1;
goto out;
}
/* Make sure directory has a trailing '/' */
len = strlen(pw->pw_dir);
if (len == 0 || pw->pw_dir[len - 1] != '/')
sep = "/";
else
sep = "";
slash = (len = strlen(pw->pw_dir)) == 0 || pw->pw_dir[len - 1] != '/';
/* Skip leading '/' from specified path */
if (path != NULL)
filename = path + 1;
if (xasprintf(&ret, "%s%s%s", pw->pw_dir, sep, filename) >= PATH_MAX) {
error_f("Path too long");
return -1;
if ((r = xasprintf(&s, "%s%s%s", pw->pw_dir,
slash ? "/" : "", path != NULL ? path : "")) <= 0) {
error_f("xasprintf failed");
goto out;
}
*retp = ret;
return 0;
if (r >= PATH_MAX) {
error_f("Path too long");
goto out;
}
/* success */
ret = 0;
*retp = s;
s = NULL;
out:
free(s);
free(ocopy);
return ret;
}
char *