pam_encfs-0.1.4.4/000075500000000000000000000000001215326310300136035ustar00rootroot00000000000000pam_encfs-0.1.4.4/Makefile000064400000000000000000000010531215326310300152420ustar00rootroot00000000000000PAM_LIB_DIR = $(DESTDIR)/lib/security CC = gcc LD = ld INSTALL = /usr/bin/install CFLAGS = -fPIC -O2 -c -g -Wall -Wformat-security -fno-strict-aliasing LDFLAGS = --shared PAMLIB = -lpam CPPFLAGS = all: pam_encfs.so pam_encfs.so: pam_encfs.o $(LD) $(LDFLAGS) -o pam_encfs.so pam_encfs.o $(PAMLIB) pam_encfs.o: pam_encfs.c $(CC) $(CFLAGS) pam_encfs.c install: pam_encfs.so $(INSTALL) -m 0755 -d $(PAM_LIB_DIR) $(INSTALL) -m 0644 pam_encfs.so $(PAM_LIB_DIR) clean: rm -f pam_encfs.o pam_encfs.so spotless: rm -f pam_encfs.so pam_encfs.o *~ core pam_encfs-0.1.4.4/README000064400000000000000000000125201215326310300144630ustar00rootroot00000000000000pam_encfs by Anders Aagaard *Documentation is written quick and dirty, if something is wrong/you can't get it working, PLEASE mail me :)* Put pam_encfs.conf in /etc/security and modify your pam to load (for example): auth required pam_encfs.so and if you want to auto umount on logout: session required pam_encfs.so (note that setting "encfs_default --idle=1", means it'll auto umount after 1 minute idletime, so you can ignore this if you want to) If you want gdm working you'll have to do this: (to allow use of --public / allow_root / allow_other) #echo "user_allow_other" >> /etc/fuse.conf #adduser testuser (put him in the fuse group if you have one) #mkdir -p /mnt/storage/enc/testuser Setup your /etc/pam_encfs.conf (default should work) #chown testuser:testuser /mnt/storage/enc/testuser #su testuser #encfs /mnt/storage/enc/testuser /home/testuser *use same password as your login atm* #fusermount -u /home/testuser when you login, the directory should be mounted. example to enable encryption for existing user: *logout of any important things, turn off your apps, preferably do this in terminal login/as root* sudo mkdir -p /mnt/storage/enc/anders /mnt/storage/enc/tmp *use your main password on next part* encfs /mnt/storage/enc/anders /mnt/storage/enc/tmp -- -o allow_root cd /home/anders find . -print -xdev | cpio -pamd /mnt/storage/enc/tmp fusermount -u /mnt/storage/enc/tmp cd / sudo mv /home/anders /home/anders.BAK sudo mkdir /home/anders sudo chown anders:anders /home/anders sudo rmdir /mnt/storage/enc/tmp *logout* on next login (in theory) your homedir should be mounted ;) FAQ : Q: Is there an example configuration file? A: Yes, both in svn (link at http://pam-encfs.googlecode.com/svn/trunk/pam_encfs.conf ), and in the downloaded archive from my release. Some distributions have chosen an extreamly simple example configuration file, mine is a bit more explained. Q: What command will pam_encfs run to mount a directory? A: It depends on your options, but something like: encfs -S --idle=1 -v /mnt/storage/enc/test /home/test -- -o allow_other,allow_root,nonempty Q : My KDE doesn't work. A : Login through KDE sometimes fails because KDE tries to store files to the home directory before mounting, and expect them to be there afterwards. To work around this you'll need to set 3 things in /etc/kde3/kdm/kdmrc, "DmrcDir=/tmp" (in the general section). And "UserAuthDir,ClientLogFile", both can be set to /tmp, these are in the [X-*-Core] section. There might be security related issues with this solution, I haven't looked into that. If your paranoid about it you could make a temp directory /tmp/user that only you have access to. Q: Can I mount multiple under one login directories with pam_encfs? A: No, there is however an unofficial patch here : http://bugs.gentoo.org/show_bug.cgi?id=102112 ( https://joshua.haninge.kth.se/~sachankara/pam_encfs-0.1.3-multiple-mount-points.patch ). This has not been applied to the main tree, as it segfaults when I test it with a very basic encfs configuration file (but might work with more advanced ones). Q: pam_encfs does not find my encfs executable A: pam_encfs uses execvp, that means that in some systems it wont find it if it's in /usr/local/bin, make a symlink to /usr/bin. Q: It works on normal login, but not in gdm. A: Problem1, you have your encfs settings too paranoid, gdm requires some things (hard links?) that the paranoid encfs settings do not support. A: Problem2, /etc/pam.d/gdm has a different system than /etc/pam.d/login, fix it ;). A: Problem3, You dont have the fuse option user_allow_root(or other) set, Make sure /etc/fuse.conf has user_allow_other (or user_allow_root). Make sure /etc/pam_encfs.conf has fuse_default allow_root, or the fuse option allow_root set. Q: It asks me for my password twice. A: Try adding use_first_pass after pam_unix (or any other module that supports it). Q: I've tried to use pam_encfs as my main authentication scheme, it doesn't work! A: I return PAM_IGNORE on errors, this can't work reliably as a main system, because of for example logging in twice (in which case the directory would already be mounted, and we therefor can't check password ok). Q: I can't login to X because the filesystem doesn't support locks. A: This could be a problem if your not using drop_permission, use it. And if you REALLY want to mount as root, put: export XAUTHORITY=/tmp/.Xauthority-$USER export ICEAUTHORITY=/tmp/.ICEauthority-$USER in your ~/.bashrc My system-auth file on gentoo: auth required pam_env.so auth sufficient /lib/security/pam_encfs.so auth sufficient /lib/security/pam_sha512.so pwdfile /etc/security/pam.sha auth sufficient pam_unix.so likeauth nullok auth required pam_deny.so account required pam_unix.so password required pam_cracklib.so retry=3 password sufficient pam_unix.so nullok md5 shadow use_authtok password required pam_deny.so session required pam_limits.so session required pam_unix.so Here it'll ask for the password twice, my modules (pam_encfs/pam_sha512) will try to use any previous password if it finds one. So if you move pam_unix.so in auth to under pam_env.so, it'll ask for the password once. Note that if pam_unix gets a password it finds ok, pam_encfs/pam_sha512 wont be used at all. pam_encfs-0.1.4.4/pam_encfs.c000075500000000000000000000475031215326310300157160ustar00rootroot00000000000000/* pam_encfs by Anders Aagaard ############################################################################ # Copyright (C) 2004 by Anders Aagaard # aagaande@gmail.com # # This program is free software; you can redistribute it and#or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the # Free Software Foundation, Inc., # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################ */ /* Todo: - Cleanup - check if paths from pam_encfs.conf has trailing slash and remove it. - support changing passwords (hm.. do we want to do that? Screwing up mount/umount is ok, screwing up changing password is not.) - option to let this count as "main" authentication, ie send OK back instead of ignore. - atm I send ignore to avoid any potentional security problems. - also not sure if I wanna support this, as it wouldn't work every time the directory is already mounted. */ #ifdef HAVE_CONFIG_H #include #endif /* for solaris compatibility */ #define _POSIX_PTHREAD_SEMANTICS #ifdef HAVE_SECURITY_PAM_APPL_H #include #elif defined(HAVE_PAM_PAM_APPL_H) #include #endif #ifdef HAVE_SECURITY_PAM_MISC_H #include #elif defined(HAVE_PAM_PAM_MISC_H) #include #endif #ifndef HAVE_PAM_PAM_MODULES_H #include #else #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define READ_END 0 #define WRITE_END 1 #define USERNAME_MAX 127 #define PATH_MAX 256 #define BUFSIZE ((USERNAME_MAX +1) + ((PATH_MAX+1) * 2)) #define CONFIGFILE "/etc/security/pam_encfs.conf" static void _pam_log(int err, const char *format, ...); static char default_encfs_options[USERNAME_MAX]; static char default_fuse_options[USERNAME_MAX]; static int drop_permissions = 0; /* --------------------------- PAM functions -------------------------------- */ /* this function ripped from pam_unix/support.c */ int converse(pam_handle_t * pamh, int nargs, struct pam_message **message, struct pam_response **response) { int retval; struct pam_conv *conv; retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv); if (retval == PAM_SUCCESS) { retval = conv->conv(nargs, (const struct pam_message **) message, response, conv->appdata_ptr); } return retval; } /* this function ripped from pam_unix/support.c */ int _set_auth_tok(pam_handle_t * pamh, int flags, int argc, const char **argv) { int retval; char *p; struct pam_message msg[1], *pmsg[1]; struct pam_response *resp; /* set up conversation call */ pmsg[0] = &msg[0]; msg[0].msg_style = PAM_PROMPT_ECHO_OFF; msg[0].msg = "Password: "; resp = NULL; if ((retval = converse(pamh, 1, pmsg, &resp)) != PAM_SUCCESS) return retval; if (resp) { if ((flags & PAM_DISALLOW_NULL_AUTHTOK) && resp[0].resp == NULL) { free(resp); return PAM_AUTH_ERR; } p = resp[0].resp; /* This could be a memory leak. If resp[0].resp is malloc()ed, then it has to be free()ed! -- alex */ resp[0].resp = NULL; } else return PAM_CONV_ERR; free(resp); pam_set_item(pamh, PAM_AUTHTOK, p); return PAM_SUCCESS; } int waitpid_timeout(pid_t pid, int *status, int options) { pid_t retval; int i = 0; do { retval = waitpid(pid, status, options); if (i++ > 10) { return 1; } } while (retval == 0 || (retval == -1 && errno == EINTR)); return 0; } int is_dir(const char *path) { struct stat statbuf; if ((stat(path,&statbuf) == 0) && (S_ISDIR(statbuf.st_mode) == 1)) { return 1; } return 0; } int checkmnt(const char *targetpath) { FILE *f = setmntent("/etc/mtab", "r"); struct mntent *m; while ((m = getmntent(f))) { if (strcmp(m->mnt_fsname, "encfs") == 0) { // DEBUG _pam_log(LOG_ERR, "Found mounted fuse system : %s",m->mnt_dir); if (strcmp(targetpath, m->mnt_dir) == 0) { // DEBUG _pam_log(LOG_ERR, "ENCFS already mounted on : %s",m->mnt_dir); return 1; } } } return 0; } void searchAndReplace(char *line) { char *str; do { str = strchr(line, ','); if (str != NULL) { *str = ' '; } } while (str != NULL); } int buildCmd(char *arg[], int pos, char *line) { int orig_pos = pos; if (strlen(line) == 0) return 0; while (line) { arg[pos++] = line; if ((line = strchr(line, ' '))) { *line++ = '\0'; } } return pos - orig_pos; } const char *getHome(struct passwd *pwd, pam_handle_t * pamh) { const char *tmp = NULL; tmp = pam_getenv(pamh, "HOME"); if (!tmp || *tmp == '\0') { if (pwd->pw_dir && pwd->pw_dir != '\0') return pwd->pw_dir; else return NULL; } else return tmp; } int readconfig(struct passwd *pwd, pam_handle_t * pamh, const char *user, char *path, char *targetpath, char *encfs_options, char *fuse_options) { FILE *conffile; char line[BUFSIZE]; char username[USERNAME_MAX]; int parsed; const char *tmp; // Return 1 = error, 2 = silent error (ie already mounted) if ((conffile = fopen(CONFIGFILE, "r")) == NULL) { _pam_log(LOG_ERR, "Failed to open conffile %s", CONFIGFILE); return 0; } while (fgets(line, BUFSIZE, conffile) != NULL) { if (line[0] == '#') continue; parsed = sscanf(line, "%s%s%s%s%s", username, path, targetpath, encfs_options, fuse_options); if (parsed == -1) continue; if (strcmp("drop_permissions", username) == 0) { drop_permissions = 1; continue; } if (strcmp("encfs_default", username) == 0) { if (parsed == 2 && !strcmp("-",path) == 0) strcpy(default_encfs_options, path); continue; } if (strcmp("fuse_default", username) == 0) { if (parsed == 2 && !strcmp("-",path) == 0) strcpy(default_fuse_options, path); continue; } if (parsed == 5) { // Parsing user: if (strcmp("-", encfs_options) == 0) strcpy(encfs_options, ""); if (strcmp("-", fuse_options) == 0) strcpy(fuse_options, ""); searchAndReplace(default_encfs_options); searchAndReplace(encfs_options); // Check if this is the right user / default user. if ((strcmp("-",username) != 0) && (strcmp(user,username) != 0) && (strcmp("*",username) !=0)) continue; if (strcmp("-",username) == 0) { strcat(path, "/"); strcat(path, user); // Todo check if dir exists and give better error msg. } // If username is '*', paths are relative to $HOME if (strcmp("*", username) == 0 && strcmp("-", targetpath) != 0) { if ((tmp = getHome(pwd, pamh))) { char home[PATH_MAX]; strcpy(home, tmp); strcat(home, "/"); strcat(home, path); strcpy(path, home); strcpy(home, tmp); strcat(home, "/"); strcat(home, targetpath); strcpy(targetpath, home); } } if (strcmp("-", targetpath) == 0) { // We do not have targetpath, construct one. strcpy(targetpath, ""); if ((tmp = getHome(pwd, pamh))) { strcpy(targetpath, tmp); } } // Done, check targetpath and return. if (!targetpath || *targetpath == '\0') { _pam_log(LOG_ERR, "Can't get to HOME dir for user %s", user); fclose(conffile); return 0; } // Check if path exists, if we're "-" then we dont care, if not we give error. if (is_dir(path)) { // We may fail to stat this directory (EPERM) if it's mounted because of fuse's funky permission system. if (!is_dir(targetpath)) { if (checkmnt(targetpath)) { // Doublecheck if we're mounted, for some reason we can't stat the dir even when root if it's mounted. // we are mounted, but we return 1 anyway so we can store targetpath fclose(conffile); return 1; } _pam_log(LOG_ERR, "TargetPath for %s does not exist (%s)", user, targetpath); fclose(conffile); return 0; } fclose(conffile); return 1; } // Path does not exist, if we're a specified user give error, if not keep looking. if ((strcmp("-", username) != 0) && (strcmp("*", username) != 0)) { _pam_log(LOG_ERR, "Path for %s does not exist (%s)", user, path); fclose(conffile); return 0; } continue; } continue; } fclose(conffile); return 0; } static void targetpath_cleanup(pam_handle_t * pamh, void *ptr, int err) { if (ptr) free(ptr); } PAM_EXTERN int pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc, const char **argv) { const char *user = NULL, *passwd = NULL; struct passwd *pwd; int rval, status; pid_t pid; // For checking mount paths: (mount from + target) char path[PATH_MAX]; char targetpath[PATH_MAX]; char encfs_options[USERNAME_MAX]; char fuse_options[USERNAME_MAX]; char *targetpath_store; strcpy(default_encfs_options, ""); strcpy(default_fuse_options, ""); // For execing: char *arg[USERNAME_MAX]; int arg_pos = 0; int i; int inpipe[2], outpipe[2]; rval = pam_get_user(pamh, &user, NULL); if ((rval != PAM_SUCCESS) || (!user)) { _pam_log(LOG_ERR, "can't get username: %s", pam_strerror(pamh, rval)); return PAM_AUTH_ERR; } rval = pam_get_item(pamh, PAM_AUTHTOK, (const void **) (void *) &passwd); if (rval != PAM_SUCCESS) { _pam_log(LOG_ERR, "Could not retrieve user's password"); return PAM_AUTH_ERR; } if (!passwd) { rval = _set_auth_tok(pamh, flags, argc, argv); if (rval != PAM_SUCCESS) { return rval; } rval = pam_get_item(pamh, PAM_AUTHTOK, (const void **) (void *) &passwd); if (rval != PAM_SUCCESS || passwd == NULL) { _pam_log(LOG_ERR, "Could not retrieve user's password"); return PAM_AUTH_ERR; } } if ((pwd = getpwnam(user)) == NULL) { _pam_log(LOG_ERR, "Could not getpwnam"); return PAM_AUTH_ERR; } // Read configfile if (!readconfig (pwd, pamh, pwd->pw_name, path, targetpath, encfs_options, fuse_options)) { // DEBUG _pam_log(LOG_ERR,"No entry for user found in log"); return PAM_IGNORE; } //DEBUG _pam_log(LOG_ERR,"Username : %s, Encpath : %s, Targetmount : %s",pwd->pw_name,path,targetpath); //Store targetpath targetpath_store = strdup(targetpath); if ((i = pam_set_data(pamh, "encfs_targetpath", targetpath_store, targetpath_cleanup)) != PAM_SUCCESS) { _pam_log(LOG_ERR, "Storing targetpath FAIL"); free(targetpath_store); return i; } // Check if we're mounted already. if (checkmnt(targetpath)) { //DEBUG _pam_log(LOG_ERR,"Already mounted"); return PAM_IGNORE; } /* _pam_log(LOG_ERR,"Config output for %s:",user); _pam_log(LOG_ERR," path : %s",path); _pam_log(LOG_ERR," targetpath : %s",targetpath); _pam_log(LOG_ERR," encfs : %s %s",default_encfs_options,encfs_options); _pam_log(LOG_ERR," fuse : %s %s",default_fuse_options,fuse_options); */ arg_pos += buildCmd(arg, arg_pos, "encfs"); arg_pos += buildCmd(arg, arg_pos, "-S"); arg_pos += buildCmd(arg, arg_pos, default_encfs_options); arg_pos += buildCmd(arg, arg_pos, encfs_options); arg_pos += buildCmd(arg, arg_pos, path); arg_pos += buildCmd(arg, arg_pos, targetpath); if (strlen(default_fuse_options) > 0 && strlen(fuse_options) > 0) strcat(fuse_options, ","); strcat(fuse_options,default_fuse_options); if (strlen(fuse_options) > 0) { arg_pos += buildCmd(arg, arg_pos, "--"); arg_pos += buildCmd(arg, arg_pos, "-o"); arg_pos += buildCmd(arg, arg_pos, fuse_options); } arg[arg_pos] = NULL; /* printf("Arguments : "); for (i = 0; i < arg_pos+1;i++) { _pam_log(LOG_ERR,"Data : %s",arg[i]); } _pam_log(LOG_ERR,"Number of arguments : %d",arg_pos); */ /* arg[0] = cmd; arg[1] = params; // arg[2] = params2; arg[2] = params3; arg[3] = path; arg[4] = targetpath; arg[5] = fuseparams; arg[6] = fuseparams2; arg[7] = NULL; */ if (pipe(inpipe) || pipe(outpipe)) { _pam_log(LOG_ERR, "Failed to create pipe"); return PAM_IGNORE; } // Execute switch (pid = fork()) { case -1: _pam_log(LOG_ERR, "Fork failed"); return PAM_SERVICE_ERR; case 0: if (drop_permissions == 1) if ((initgroups(pwd->pw_name, pwd->pw_gid) == -1) || (setgid(pwd->pw_gid) == -1) || (setuid(pwd->pw_uid) == -1)) { _pam_log(LOG_ERR, "Dropping permissions failed"); return PAM_SERVICE_ERR; } close(outpipe[WRITE_END]); dup2(outpipe[READ_END], fileno(stdin)); close(outpipe[READ_END]); close(inpipe[READ_END]); dup2(inpipe[WRITE_END], fileno(stdout)); close(inpipe[WRITE_END]); // For some reason the current directory has to be set to targetpath (or path?) before exec'ing encfs through gdm chdir(targetpath); execvp("encfs", arg); char errstr[128]; snprintf(errstr, 127, "%d - %s", errno, strerror(errno)); _pam_log(LOG_ERR, "Exec failed - %s", errstr); exit(127); } int len; close(inpipe[WRITE_END]); close(outpipe[READ_END]); if (waitpid(pid, &status, WNOHANG) == 0) { len = write(outpipe[WRITE_END], passwd, (size_t) strlen(passwd)); if ((len != (size_t) strlen(passwd)) || (write(outpipe[WRITE_END], "\n", 1) != 1)) _pam_log(LOG_ERR, "Did not send password to pipe (%d sent)", len); close(outpipe[WRITE_END]); } if (waitpid_timeout(pid, &status, 0)) { _pam_log(LOG_ERR, "Timed out waiting for encfs, killing\n"); kill(pid, SIGKILL); } int exitstatus = WEXITSTATUS(status); char buff[512]; len = read(inpipe[READ_END], &buff, 511); close(inpipe[READ_END]); buff[len] = 0; if (!checkmnt(targetpath) && (len > 0 || exitstatus > 0)) { _pam_log(LOG_ERR, "exitcode : %d, errorstring : %s", exitstatus, buff); return PAM_AUTH_ERR; } else { return PAM_IGNORE; } return PAM_AUTH_ERR; } PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t * pamh, int flags, int argc, const char *argv[]) { return PAM_IGNORE; } PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags, int argc, const char **argv) { return PAM_IGNORE; } PAM_EXTERN int pam_sm_open_session(pam_handle_t * pamh, int flags, int argc, const char **argv) { return PAM_IGNORE; } PAM_EXTERN int pam_sm_close_session(pam_handle_t * pamh, int flags, int argc, const char **argv) { int retval; pid_t pid; char *targetpath; char *args[4]; // _pam_log(LOG_ERR,"Geteuid : %d",geteuid()); if ((retval = pam_get_data(pamh, "encfs_targetpath", (const void **) &targetpath)) != PAM_SUCCESS) return retval; if (!checkmnt(targetpath)) { _pam_log(LOG_ERR, "Targetpath is not mounted!: %s", targetpath); return PAM_SERVICE_ERR; } args[0] = "fusermount"; args[1] = "-uz"; args[2] = targetpath; args[3] = NULL; switch (pid = fork()) { case -1: _pam_log(LOG_ERR, "Fork failed"); return PAM_SERVICE_ERR; case 0: execvp("fusermount", args); char errstr[128]; snprintf(errstr, 127, "%d - %s", errno, strerror(errno)); _pam_log(LOG_ERR, "Exec failed - %s", errstr); exit(127); } if (waitpid(pid, NULL, 0) == -1) _pam_log(LOG_ERR, "Waitpid failed - %s", strerror(errno)); /*We'll get this error every single time we have more than one session active, todo fix this with some better checks + support fuser -km if no more session connected. if (checkmnt(targetpath)) { _pam_log(LOG_ERR,"Failed to unmount %s",targetpath); return PAM_SERVICE_ERR; } */ return PAM_IGNORE; } PAM_EXTERN int pam_sm_setcred(pam_handle_t * pamh, int flags, int argc, const char **argv) { return PAM_IGNORE; } /* function for correct syslog logging provided by Scipio */ static void _pam_log(int err, const char *format, ...) { va_list args; va_start(args, format); openlog("pam_encfs", LOG_CONS | LOG_PID, LOG_AUTH); vsyslog(err, format, args); va_end(args); closelog(); } pam_encfs-0.1.4.4/pam_encfs.conf000064400000000000000000000034301215326310300164050ustar00rootroot00000000000000#This file is parsed top to bottom, until the first mount line that matches is found, then it stops. #Note that I dont support spaces in params #So if your for example gonna specify idle time use --idle=X not -i X. #If this is specified program will attempt to drop permissions before running encfs. #(will not work with --public for example, as that requires encfs to run as root) drop_permissions #This specifies which options to pass to encfs for every user. #You can find encfs options by running encfs without any arguments encfs_default --idle=1 #Same for fuse, note that allow_root (or allow_other, or --public in encfs) is needed to run gdm/X. #you can find fuse options with encfs -H fuse_default allow_root,nonempty #For a mount line, - = generic, we try to fill in what we need. #A Mount line is constructed like this: #USERNAME if "-" or "*" gets replaced with $USER #SOURCE if USERNAME is -, replace with path + /$USER # if USERNAME is *, replace with $HOME/ + sourcepath #TARGET PATH if - replace with $HOME # if USERNAME is *, replace with $HOME/ + targetpath #ENCFS OPTIONS encfs options here is encfs_default + encfs_options #FUSE OPTIONS encfs options here is fuse_default + fuse_options #Keep in mind that the configuration file is parsed top to bottom, so if you put your generic line on top, #that will always match before any custom lines under it. #In this example, with example_user uncommented, the "-" line will never be parsed if you login as example_user. #In the lines with the USERNAME "*", all paths are relative to $HOME #USERNAME SOURCE TARGET PATH ENCFS Options FUSE Options #example_user /mnt/enc/example_user /home/example_user -v,--idle=1 allow_root #* .private private -v allow_other - /mnt/enc - -v allow_other