diff --git a/misc.c b/misc.c index f541055c1..c22d7c68c 100644 --- a/misc.c +++ b/misc.c @@ -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 *