]> 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 cd2246ed54bf5156f08589f2921dfcbae7581941..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.
@@ -170,26 +206,28 @@ static char *get_vol_uuid(const AFPObj *obj, const char *volname)
   As we can't check (requires write access) on ro-volumes, we switch ea:auto
   volumes that are options:ro to ea:none.
 */
+#define EABUFSZ 4
 static int do_check_ea_support(const struct vol *vol)
 {
     int haseas;
-    char eaname[] = {"org.netatalk.supports-eas.XXXXXX"};
+    const char *eaname = "org.netatalk.has-Extended-Attributes";
     const char *eacontent = "yes";
+    char buf[EABUFSZ];
 
-    if ((vol->v_flags & AFPVOL_RO) == AFPVOL_RO) {
-        LOG(log_note, logtype_afpd, "read-only volume '%s', can't test for EA support, assuming yes", vol->v_localname);
+    if (sys_lgetxattr(vol->v_path, eaname, buf, EABUFSZ) != -1)
         return 1;
-    }
 
-    mktemp(eaname);
+    if (vol->v_flags & AFPVOL_RO) {
+        LOG(log_debug, logtype_afpd, "read-only volume '%s', can't test for EA support, assuming yes", vol->v_localname);
+        return 1;
+    }
 
     become_root();
 
-    if ((sys_setxattr(vol->v_path, eaname, eacontent, 4, 0)) == 0) {
-        sys_removexattr(vol->v_path, eaname);
+    if ((sys_setxattr(vol->v_path, eaname, eacontent, strlen(eacontent) + 1, 0)) == 0) {
         haseas = 1;
     } else {
-        LOG(log_warning, logtype_afpd, "volume \"%s\" does not support Extended Attributes or read-only volume root",
+        LOG(log_warning, logtype_afpd, "volume \"%s\" does not support Extended Attributes or read-only volume",
             vol->v_localname);
         haseas = 0;
     }
@@ -202,25 +240,14 @@ static int do_check_ea_support(const struct vol *vol)
 static void check_ea_support(struct vol *vol)
 {
     int haseas;
-    char eaname[] = {"org.netatalk.supports-eas.XXXXXX"};
-    const char *eacontent = "yes";
 
     haseas = do_check_ea_support(vol);
 
     if (vol->v_vfs_ea == AFPVOL_EA_AUTO) {
-        if ((vol->v_flags & AFPVOL_RO) == AFPVOL_RO) {
-            LOG(log_info, logtype_afpd, "read-only volume '%s', can't test for EA support, disabling EAs", vol->v_localname);
-            vol->v_vfs_ea = AFPVOL_EA_NONE;
-            return;
-        }
-
-        if (haseas) {
+        if (haseas)
             vol->v_vfs_ea = AFPVOL_EA_SYS;
-        } else {
-            LOG(log_warning, logtype_afpd, "volume \"%s\" does not support Extended Attributes, using ea:ad instead",
-                vol->v_localname);
-            vol->v_vfs_ea = AFPVOL_EA_AD;
-        }
+        else
+            vol->v_vfs_ea = AFPVOL_EA_NONE;
     }
 
     if (vol->v_adouble == AD_VERSION_EA) {
@@ -571,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;
@@ -651,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);
 
@@ -752,7 +784,7 @@ static struct vol *creatvol(AFPObj *obj,
     if (getoption_bool(obj->iniconfig, section, "acls", preset, 1))
         volume->v_flags |= AFPVOL_ACLS;
 #endif
-    if (!getoption_bool(obj->iniconfig, section, "convert adouble", preset, 1))
+    if (!getoption_bool(obj->iniconfig, section, "convert appledouble", preset, 1))
         volume->v_flags |= AFPVOL_NOV2TOEACONV;
 
     if (getoption_bool(obj->iniconfig, section, "preexec close", preset, 0))
@@ -765,12 +797,15 @@ static struct vol *creatvol(AFPObj *obj,
      * 1) neither the rolist nor the rwlist exist -> rw
      * 2) rolist exists -> ro if user is in it.
      * 3) rwlist exists -> ro unless user is in it.
+     * 4) cnid scheme = last -> ro forcibly.
      */
     if (pwd) {
         if (accessvol(obj, getoption(obj->iniconfig, section, "rolist", preset, NULL), pwd->pw_name) == 1
             || accessvol(obj, getoption(obj->iniconfig, section, "rwlist", preset, NULL), pwd->pw_name) == 0)
             volume->v_flags |= AFPVOL_RO;
     }
+    if (0 == strcmp(volume->v_cnidscheme, "last"))
+        volume->v_flags |= AFPVOL_RO;
 
     if ((volume->v_flags & AFPVOL_NODEV))
         volume->v_ad_options |= ADVOL_NODEV;
@@ -801,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, "???");
@@ -816,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;
         }
@@ -832,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, "???");
@@ -842,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,
@@ -957,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;
@@ -990,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) {
@@ -1004,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);
@@ -1021,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 */
@@ -1031,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:
@@ -1263,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;
@@ -1339,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);
     }
@@ -1356,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)
@@ -1432,8 +1479,6 @@ int afp_config_parse(AFPObj *AFPObj, char *processname)
     /* "server options" boolean options */
     if (!iniparser_getboolean(config, INISEC_GLOBAL, "zeroconf", 1))
         options->flags |= OPTION_NOZEROCONF;
-    if (iniparser_getboolean(config, INISEC_GLOBAL, "icon", 0))
-        options->flags |= OPTION_CUSTOMICON;
     if (iniparser_getboolean(config, INISEC_GLOBAL, "advertise ssh", 0))
         options->flags |= OPTION_ANNOUNCESSH;
     if (iniparser_getboolean(config, INISEC_GLOBAL, "map acls", 1))
@@ -1448,6 +1493,8 @@ int afp_config_parse(AFPObj *AFPObj, char *processname)
         options->flags |= OPTION_NOSENDFILE;
     if (iniparser_getboolean(config, INISEC_GLOBAL, "solaris share reservations", 1))
         options->flags |= OPTION_SHARE_RESERV;
+    if (iniparser_getboolean(config, INISEC_GLOBAL, "afp read locks", 0))
+        options->flags |= OPTION_AFP_READ_LOCK;
     if (!iniparser_getboolean(config, INISEC_GLOBAL, "save password", 1))
         options->passwdbits |= PASSWD_NOSAVE;
     if (iniparser_getboolean(config, INISEC_GLOBAL, "set password", 0))
@@ -1541,7 +1588,7 @@ int afp_config_parse(AFPObj *AFPObj, char *processname)
     /* unix charset is in [G] only */
     if (!(p = iniparser_getstring(config, INISEC_GLOBAL, "unix charset", NULL))) {
         options->unixcodepage = strdup("UTF8");
-        charset_names[CH_UNIX] = strdup("UTF8");
+        set_charset_name(CH_UNIX, "UTF8");
     } else {
         if (strcasecmp(p, "LOCALE") == 0) {
 #if defined(CODESET)
@@ -1557,7 +1604,7 @@ int afp_config_parse(AFPObj *AFPObj, char *processname)
             p = strdup("UTF8");
         }
         options->unixcodepage = strdup(p);
-        charset_names[CH_UNIX] = strdup(p);
+        set_charset_name(CH_UNIX, p);
     }
     options->unixcharset = CH_UNIX;
     LOG(log_debug, logtype_afpd, "Global unix charset is %s", options->unixcodepage);
@@ -1576,13 +1623,13 @@ int afp_config_parse(AFPObj *AFPObj, char *processname)
     /* mac charset is in [G] and [V] */
     if (!(p = iniparser_getstring(config, INISEC_GLOBAL, "mac charset", NULL))) {
         options->maccodepage = strdup("MAC_ROMAN");
-        charset_names[CH_MAC] = strdup("MAC_ROMAN");
+        set_charset_name(CH_MAC, "MAC_ROMAN");
     } else {
         if (strncasecmp(p, "MAC", 3) != 0) {
             LOG(log_warning, logtype_afpd, "Is '%s' really mac charset? ", p);
         }
         options->maccodepage = strdup(p);
-        charset_names[CH_MAC] = strdup(p);
+        set_charset_name(CH_MAC, p);
     }
     options->maccharset = CH_MAC;
     LOG(log_debug, logtype_afpd, "Global mac charset is %s", options->maccodepage);