X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=netatalk.git;a=blobdiff_plain;f=libatalk%2Futil%2Funix.c;h=4572caddd454d36c1b0cd3e45b6b258b7aedadf3;hp=56c3e168eaea05c05705464a0f21830aedf60c03;hb=c72d10d6f92fe81d040ab983768d7fdccea7fb2e;hpb=a0e6244c0526e79d3455ffee71bb9d36b49df4a6 diff --git a/libatalk/util/unix.c b/libatalk/util/unix.c index 56c3e168..4572cadd 100644 --- a/libatalk/util/unix.c +++ b/libatalk/util/unix.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -43,6 +44,7 @@ #include #include #include +#include /* close all FDs >= a specified value */ static void closeall(int fd) @@ -227,20 +229,107 @@ char *stripped_slashes_basename(char *p) return (strrchr(p, '/') ? strrchr(p, '/') + 1 : p); } +/********************************************************************************* + * chdir(), chmod(), chown(), stat() wrappers taking an additional option. + * Currently the only used options are O_NOFOLLOW, used to switch between symlink + * behaviour, and O_NETATALK_ACL for ochmod() indicating chmod_acl() shall be + * called which does special ACL handling depending on the filesytem + *********************************************************************************/ + +int ostat(const char *path, struct stat *buf, int options) +{ + if (options & O_NOFOLLOW) + return lstat(path, buf); + else + return stat(path, buf); +} + +int ochown(const char *path, uid_t owner, gid_t group, int options) +{ + if (options & O_NOFOLLOW) + return lchown(path, owner, group); + else + return chown(path, owner, group); +} + +/*! + * chmod() wrapper for symlink and ACL handling + * + * @param path (r) path + * @param mode (r) requested mode + * @param sb (r) stat() of path or NULL + * @param option (r) O_NOFOLLOW | O_NETATALK_ACL + * + * Options description: + * O_NOFOLLOW: don't chmod() symlinks, do nothing, return 0 + * O_NETATALK_ACL: call chmod_acl() instead of chmod() + * O_IGNORE: ignore chmod() request, directly return 0 + */ +int ochmod(char *path, mode_t mode, const struct stat *st, int options) +{ + struct stat sb; + + if (options & O_IGNORE) + return 0; + + if (!st) { + if (lstat(path, &sb) != 0) + return -1; + st = &sb; + } + + if (options & O_NOFOLLOW) + if (S_ISLNK(st->st_mode)) + return 0; + + if (options & O_NETATALK_ACL) { + return chmod_acl(path, mode); + } else { + return chmod(path, mode); + } +} + +/* + * @brief ostat/fsstatat multiplexer + * + * ostatat mulitplexes ostat and fstatat. If we dont HAVE_ATFUNCS, dirfd is ignored. + * + * @param dirfd (r) Only used if HAVE_ATFUNCS, ignored else, -1 gives AT_FDCWD + * @param path (r) pathname + * @param st (rw) pointer to struct stat + */ +int ostatat(int dirfd, const char *path, struct stat *st, int options) +{ +#ifdef HAVE_ATFUNCS + if (dirfd == -1) + dirfd = AT_FDCWD; + return fstatat(dirfd, path, st, (options & O_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0); +#else + return ostat(path, st, options); +#endif + + /* DEADC0DE */ + return -1; +} + /*! * @brief symlink safe chdir replacement * - * Only chdirs to dir if it doesn't contain symlinks. + * Only chdirs to dir if it doesn't contain symlinks or if symlink checking + * is disabled * * @returns 1 if a path element is a symlink, 0 otherwise, -1 on syserror */ -int lchdir(const char *dir) +int ochdir(const char *dir, int options) { char buf[MAXPATHLEN+1]; char cwd[MAXPATHLEN+1]; char *test; int i; + if (!(options & O_NOFOLLOW)) + return chdir(dir); + /* dir is a canonical path (without "../" "./" "//" ) but may end with a / @@ -342,3 +431,126 @@ int gmem(gid_t gid, int ngroups, gid_t *groups) } return( 0 ); } + +/* + * realpath() replacement that always allocates storage for returned path + */ +char *realpath_safe(const char *path) +{ + char *resolved_path; + +#ifdef REALPATH_TAKES_NULL + if ((resolved_path = realpath(path, NULL)) == NULL) { + LOG(log_debug, logtype_afpd, "realpath() cannot resolve path \"%s\"", path); + return NULL; + } + return resolved_path; +#else + if ((resolved_path = malloc(MAXPATHLEN+1)) == NULL) + return NULL; + if (realpath(path, resolved_path) == NULL) { + free(resolved_path); + LOG(log_debug, logtype_afpd, "realpath() cannot resolve path \"%s\"", path); + return NULL; + } + /* Safe some memory */ + char *tmp; + if ((tmp = strdup(resolved_path)) == NULL) { + free(resolved_path); + return NULL; + } + free(resolved_path); + resolved_path = tmp; + return resolved_path; +#endif +} + +/** + * Returns pointer to static buffer with basename of path + **/ +const char *basename_safe(const char *path) +{ + static char buf[MAXPATHLEN+1]; + strlcpy(buf, path, MAXPATHLEN); + return basename(buf); +} + +/** + * extended strtok allows the quoted strings + * modified strtok.c in glibc 2.0.6 + **/ +char *strtok_quote(char *s, const char *delim) +{ + static char *olds = NULL; + char *token; + + if (s == NULL) + s = olds; + + /* Scan leading delimiters. */ + s += strspn (s, delim); + if (*s == '\0') + return NULL; + + /* Find the end of the token. */ + token = s; + + if (token[0] == '\"') { + token++; + s = strpbrk (token, "\""); + } else { + s = strpbrk (token, delim); + } + + if (s == NULL) { + /* This token finishes the string. */ + olds = strchr (token, '\0'); + } else { + /* Terminate the token and make OLDS point past it. */ + *s = '\0'; + olds = s + 1; + } + return token; +} + +int set_groups(AFPObj *obj, struct passwd *pwd) +{ + if (initgroups(pwd->pw_name, pwd->pw_gid) < 0) + LOG(log_error, logtype_afpd, "initgroups(%s, %d): %s", pwd->pw_name, pwd->pw_gid, strerror(errno)); + + if ((obj->ngroups = getgroups(0, NULL)) < 0) { + LOG(log_error, logtype_afpd, "login: %s getgroups: %s", pwd->pw_name, strerror(errno)); + return -1; + } + + if (obj->groups) + free(obj->groups); + if (NULL == (obj->groups = calloc(obj->ngroups, sizeof(gid_t))) ) { + LOG(log_error, logtype_afpd, "login: %s calloc: %d", obj->ngroups); + return -1; + } + + if ((obj->ngroups = getgroups(obj->ngroups, obj->groups)) < 0 ) { + LOG(log_error, logtype_afpd, "login: %s getgroups: %s", pwd->pw_name, strerror(errno)); + return -1; + } + + return 0; +} + +#define GROUPSTR_BUFSIZE 1024 +const char *print_groups(int ngroups, gid_t *groups) +{ + static char groupsstr[GROUPSTR_BUFSIZE]; + int i; + char *s = groupsstr; + + if (ngroups == 0) + return "-"; + + for (i = 0; (i < ngroups) && (s < &groupsstr[GROUPSTR_BUFSIZE]); i++) { + s += snprintf(s, &groupsstr[GROUPSTR_BUFSIZE] - s, " %u", groups[i]); + } + + return groupsstr; +}