#include <sys/time.h>
#include <time.h>
#include <sys/wait.h>
+#include <libgen.h>
#include <atalk/adouble.h>
#include <atalk/ea.h>
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()
+ */
+int ochmod(char *path, mode_t mode, const struct stat *st, int options)
+{
+ struct stat sb;
+
+ 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 /
}
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_error, 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_error, 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;
+}