]> arthur.barton.de Git - netatalk.git/blobdiff - libatalk/util/netatalk_conf.c
fix: basedir regex symlink problem
[netatalk.git] / libatalk / util / netatalk_conf.c
index 4479116991d27a422638fb33700b7e64d5e86f2b..6d252e83db772af8dde924f1d1cad49d2fb4671b 100644 (file)
@@ -67,6 +67,42 @@ static int have_uservol = 0; /* whether there's generic user home share in confi
 static struct vol *Volumes = NULL;
 static uint16_t    lastvid = 0;
 
+
+/*
+ * Normalize volume path
+ */
+static 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
+}
+
+
 /* 
  * Get a volumes UUID from the config file.
  * If there is none, it is generated and stored there.
@@ -562,8 +598,8 @@ static struct vol *creatvol(AFPObj *obj,
 {
     EC_INIT;
     struct vol  *volume = NULL;
-    int         suffixlen, vlen, tmpvlen, u8mvlen, macvlen;
-    char        tmpname[AFPVOL_U8MNAMELEN+1];
+    int         i, suffixlen, vlen, tmpvlen, u8mvlen, macvlen;
+    char        *tmpname;
     ucs2_t      u8mtmpname[(AFPVOL_U8MNAMELEN+1)*2], mactmpname[(AFPVOL_MACNAMELEN+1)*2];
     char        suffix[6]; /* max is #FFFF */
     uint16_t    flags;
@@ -642,9 +678,14 @@ static struct vol *creatvol(AFPObj *obj,
     else
     EC_NULL( volume->v_maccodepage = strdup(obj->options.maccodepage) );
 
+    vlen = strlen(name);
+    tmpname = strdup(name);
+    for(i = 0; i < vlen; i++)
+        if(tmpname[i] == '/') tmpname[i] = ':';
+
     bstring dbpath;
     EC_NULL_LOG( val = iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "vol dbpath", _PATH_STATEDIR "CNID/") );
-    EC_NULL_LOG( dbpath = bformat("%s/%s/", val, name) );
+    EC_NULL_LOG( dbpath = bformat("%s/%s/", val, tmpname) );
     EC_NULL_LOG( volume->v_dbpath = strdup(bdata(dbpath)) );
     bdestroy(dbpath);
 
@@ -795,12 +836,9 @@ static struct vol *creatvol(AFPObj *obj,
     /* because v_vid has not been decided yet. */
     suffixlen = sprintf(suffix, "#%X", lastvid + 1 );
 
-
-    vlen = strlen( name );
-
     /* Unicode Volume Name */
     /* Firstly convert name from unixcharset to UTF8-MAC */
-    flags = CONV_IGNORE;
+    flags = CONV_IGNORE | CONV_ALLOW_SLASH;
     tmpvlen = convert_charset(obj->options.unixcharset, CH_UTF8_MAC, 0, name, vlen, tmpname, AFPVOL_U8MNAMELEN, &flags);
     if (tmpvlen <= 0) {
         strcpy(tmpname, "???");
@@ -810,7 +848,7 @@ static struct vol *creatvol(AFPObj *obj,
     /* Do we have to mangle ? */
     if ( (flags & CONV_REQMANGLE) || (tmpvlen > obj->options.volnamelen)) {
         if (tmpvlen + suffixlen > obj->options.volnamelen) {
-            flags = CONV_FORCE;
+            flags = CONV_FORCE | CONV_ALLOW_SLASH;
             tmpvlen = convert_charset(obj->options.unixcharset, CH_UTF8_MAC, 0, name, vlen, tmpname, obj->options.volnamelen - suffixlen, &flags);
             tmpname[tmpvlen >= 0 ? tmpvlen : 0] = 0;
         }
@@ -826,7 +864,7 @@ static struct vol *creatvol(AFPObj *obj,
 
     /* Maccharset Volume Name */
     /* Firsty convert name from unixcharset to maccharset */
-    flags = CONV_IGNORE;
+    flags = CONV_IGNORE | CONV_ALLOW_SLASH;
     tmpvlen = convert_charset(obj->options.unixcharset, obj->options.maccharset, 0, name, vlen, tmpname, AFPVOL_U8MNAMELEN, &flags);
     if (tmpvlen <= 0) {
         strcpy(tmpname, "???");
@@ -836,7 +874,7 @@ static struct vol *creatvol(AFPObj *obj,
     /* Do we have to mangle ? */
     if ( (flags & CONV_REQMANGLE) || (tmpvlen > AFPVOL_MACNAMELEN)) {
         if (tmpvlen + suffixlen > AFPVOL_MACNAMELEN) {
-            flags = CONV_FORCE;
+            flags = CONV_FORCE | CONV_ALLOW_SLASH;
             tmpvlen = convert_charset(obj->options.unixcharset,
                                       obj->options.maccharset,
                                       0,
@@ -951,9 +989,10 @@ static int readvolfile(AFPObj *obj, const struct passwd *pwent)
     EC_INIT;
     static int regexerr = -1;
     static regex_t reg;
-    char        path[MAXPATHLEN + 1];
+    char        *realpw_dir;
+    char        *realvolpath;
     char        volname[AFPVOL_U8MNAMELEN + 1];
-    char        tmp[MAXPATHLEN + 1];
+    char        tmp[MAXPATHLEN + 1], tmp2[MAXPATHLEN + 1];
     const char  *preset, *default_preset, *p, *basedir;
     char        *q, *u;
     int         i;
@@ -984,6 +1023,8 @@ static int readvolfile(AFPObj *obj, const struct passwd *pwent)
             if (pwent->pw_dir == NULL || STRCMP("", ==, pwent->pw_dir))
                 /* no user home */
                 continue;
+            if ((realpw_dir = realpath_safe(pwent->pw_dir)) == NULL)
+                continue;
 
             /* check if user home matches our "basedir regex" */
             if ((basedir = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "basedir regex", NULL)) == NULL) {
@@ -998,13 +1039,13 @@ static int readvolfile(AFPObj *obj, const struct passwd *pwent)
                 LOG(log_debug, logtype_default, "readvolfile: bad basedir regex: %s", errbuf);
             }
 
-            if (regexec(&reg, pwent->pw_dir, 1, match, 0) == REG_NOMATCH) {
+            if (regexec(&reg, realpw_dir, 1, match, 0) == REG_NOMATCH) {
                 LOG(log_debug, logtype_default, "readvolfile: user home \"%s\" doesn't match basedir regex \"%s\"",
-                    pwent->pw_dir, basedir);
+                    realpw_dir, basedir);
                 continue;
             }
 
-            strlcpy(tmp, pwent->pw_dir, MAXPATHLEN);
+            strlcpy(tmp, realpw_dir, MAXPATHLEN);
             strlcat(tmp, "/", MAXPATHLEN);
             if (p = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "path", NULL))
                 strlcat(tmp, p, MAXPATHLEN);
@@ -1015,7 +1056,7 @@ static int readvolfile(AFPObj *obj, const struct passwd *pwent)
             strlcpy(tmp, p, MAXPATHLEN);
         }
 
-        if (volxlate(obj, path, sizeof(path) - 1, tmp, pwent, NULL, NULL) == NULL)
+        if (volxlate(obj, tmp2, sizeof(tmp2) - 1, tmp, pwent, NULL, NULL) == NULL)
             continue;
 
         /* do variable substitution for volume name */
@@ -1025,16 +1066,23 @@ static int readvolfile(AFPObj *obj, const struct passwd *pwent)
                 LOG(log_warning, logtype_afpd, "home name must contain $u.");
                 p = "$u's home";
             }
+            if (strchr(p, ':') != NULL) {
+                LOG(log_warning, logtype_afpd, "home name must not contain \":\".");
+                p = "$u's home";
+            }
             strlcpy(tmp, p, MAXPATHLEN);
         } else {
             strlcpy(tmp, secname, AFPVOL_U8MNAMELEN);
         }
-        if (volxlate(obj, volname, sizeof(volname) - 1, tmp, pwent, path, NULL) == NULL)
+        if (volxlate(obj, volname, sizeof(volname) - 1, tmp, pwent, tmp2, NULL) == NULL)
             continue;
 
         preset = iniparser_getstring(obj->iniconfig, secname, "vol preset", NULL);
 
-        creatvol(obj, pwent, secname, volname, path, preset ? preset : default_preset ? default_preset : NULL);
+        if ((realvolpath = realpath_safe(tmp2)) == NULL)
+            continue;
+
+        creatvol(obj, pwent, secname, volname, realvolpath, preset ? preset : default_preset ? default_preset : NULL);
     }
 
 EC_CLEANUP:
@@ -1257,7 +1305,7 @@ struct vol *getvolbypath(AFPObj *obj, const char *path)
     const struct passwd *pw;
     char        volname[AFPVOL_U8MNAMELEN + 1];
     char        abspath[MAXPATHLEN + 1];
-    char        volpath[MAXPATHLEN + 1];
+    char        volpath[MAXPATHLEN + 1], *realvolpath;
     char        tmpbuf[MAXPATHLEN + 1];
     const char *secname, *basedir, *p = NULL, *subpath = NULL, *subpathconfig;
     char *user = NULL, *prw;
@@ -1333,14 +1381,16 @@ struct vol *getvolbypath(AFPObj *obj, const char *path)
         subpath = prw;
 
     strlcat(tmpbuf, user, MAXPATHLEN);
-    strlcat(obj->username, user, MAXUSERLEN);
+    strlcpy(obj->username, user, MAXUSERLEN);
     strlcat(tmpbuf, "/", MAXPATHLEN);
 
     /* (6) */
     if (subpathconfig = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "path", NULL)) {
+        /*
         if (!subpath || strncmp(subpathconfig, subpath, strlen(subpathconfig)) != 0) {
             EC_FAIL;
         }
+        */
         strlcat(tmpbuf, subpathconfig, MAXPATHLEN);
         strlcat(tmpbuf, "/", MAXPATHLEN);
     }
@@ -1350,23 +1400,26 @@ struct vol *getvolbypath(AFPObj *obj, const char *path)
     if (volxlate(obj, volpath, sizeof(volpath) - 1, tmpbuf, pw, NULL, NULL) == NULL)
         return NULL;
 
+    if ((realvolpath = realpath_safe(volpath)) == NULL)
+        return NULL;
+
     EC_NULL( pw = getpwnam(user) );
 
-    LOG(log_debug, logtype_afpd, "getvolbypath(\"%s\"): user: %s, homedir: %s => volpath: \"%s\"",
-        path, user, pw->pw_dir, volpath);
+    LOG(log_debug, logtype_afpd, "getvolbypath(\"%s\"): user: %s, homedir: %s => realvolpath: \"%s\"",
+        path, user, pw->pw_dir, realvolpath);
 
     /* do variable substitution for volume name */
     p = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "home name", "$u's home");
     if (strstr(p, "$u") == NULL)
         p = "$u's home";
     strlcpy(tmpbuf, p, AFPVOL_U8MNAMELEN);
-    EC_NULL_LOG( volxlate(obj, volname, sizeof(volname) - 1, tmpbuf, pw, volpath, NULL) );
+    EC_NULL_LOG( volxlate(obj, volname, sizeof(volname) - 1, tmpbuf, pw, realvolpath, NULL) );
 
     const char  *preset, *default_preset;
     default_preset = iniparser_getstring(obj->iniconfig, INISEC_GLOBAL, "vol preset", NULL);
     preset = iniparser_getstring(obj->iniconfig, INISEC_HOMES, "vol preset", NULL);
 
-    vol = creatvol(obj, pw, INISEC_HOMES, volname, volpath, preset ? preset : default_preset ? default_preset : NULL);
+    vol = creatvol(obj, pw, INISEC_HOMES, volname, realvolpath, preset ? preset : default_preset ? default_preset : NULL);
 
 EC_CLEANUP:
     if (user)