diff --git a/CVE-2018-7169.patch b/CVE-2018-7169.patch deleted file mode 100644 index 9ba4e4c..0000000 --- a/CVE-2018-7169.patch +++ /dev/null @@ -1,180 +0,0 @@ -From fb28c99b8a66ff2605c5cb96abc0a4d975f92de0 Mon Sep 17 00:00:00 2001 -From: Aleksa Sarai -Date: Thu, 15 Feb 2018 23:49:40 +1100 -Subject: [PATCH] newgidmap: enforce setgroups=deny if self-mapping a group - -This is necessary to match the kernel-side policy of "self-mapping in a -user namespace is fine, but you cannot drop groups" -- a policy that was -created in order to stop user namespaces from allowing trivial privilege -escalation by dropping supplementary groups that were "blacklisted" from -certain paths. - -This is the simplest fix for the underlying issue, and effectively makes -it so that unless a user has a valid mapping set in /etc/subgid (which -only administrators can modify) -- and they are currently trying to use -that mapping -- then /proc/$pid/setgroups will be set to deny. This -workaround is only partial, because ideally it should be possible to set -an "allow_setgroups" or "deny_setgroups" flag in /etc/subgid to allow -administrators to further restrict newgidmap(1). - -We also don't write anything in the "allow" case because "allow" is the -default, and users may have already written "deny" even if they -technically are allowed to use setgroups. And we don't write anything if -the setgroups policy is already "deny". - -Ref: https://bugs.launchpad.net/ubuntu/+source/shadow/+bug/1729357 -Fixes: CVE-2018-7169 -Reported-by: Craig Furman -Signed-off-by: Aleksa Sarai ---- - src/newgidmap.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++------ - 1 file changed, 80 insertions(+), 9 deletions(-) - -diff --git a/src/newgidmap.c b/src/newgidmap.c -index b1e33513..59a2e75c 100644 ---- a/src/newgidmap.c -+++ b/src/newgidmap.c -@@ -46,32 +46,37 @@ - */ - const char *Prog; - --static bool verify_range(struct passwd *pw, struct map_range *range) -+ -+static bool verify_range(struct passwd *pw, struct map_range *range, bool *allow_setgroups) - { - /* An empty range is invalid */ - if (range->count == 0) - return false; - -- /* Test /etc/subgid */ -- if (have_sub_gids(pw->pw_name, range->lower, range->count)) -+ /* Test /etc/subgid. If the mapping is valid then we allow setgroups. */ -+ if (have_sub_gids(pw->pw_name, range->lower, range->count)) { -+ *allow_setgroups = true; - return true; -+ } - -- /* Allow a process to map its own gid */ -- if ((range->count == 1) && (pw->pw_gid == range->lower)) -+ /* Allow a process to map its own gid. */ -+ if ((range->count == 1) && (pw->pw_gid == range->lower)) { -+ /* noop -- if setgroups is enabled already we won't disable it. */ - return true; -+ } - - return false; - } - - static void verify_ranges(struct passwd *pw, int ranges, -- struct map_range *mappings) -+ struct map_range *mappings, bool *allow_setgroups) - { - struct map_range *mapping; - int idx; - - mapping = mappings; - for (idx = 0; idx < ranges; idx++, mapping++) { -- if (!verify_range(pw, mapping)) { -+ if (!verify_range(pw, mapping, allow_setgroups)) { - fprintf(stderr, _( "%s: gid range [%lu-%lu) -> [%lu-%lu) not allowed\n"), - Prog, - mapping->upper, -@@ -89,6 +94,70 @@ static void usage(void) - exit(EXIT_FAILURE); - } - -+void write_setgroups(int proc_dir_fd, bool allow_setgroups) -+{ -+ int setgroups_fd; -+ char *policy, policy_buffer[4096]; -+ -+ /* -+ * Default is "deny", and any "allow" will out-rank a "deny". We don't -+ * forcefully write an "allow" here because the process we are writing -+ * mappings for may have already set themselves to "deny" (and "allow" -+ * is the default anyway). So allow_setgroups == true is a noop. -+ */ -+ policy = "deny\n"; -+ if (allow_setgroups) -+ return; -+ -+ setgroups_fd = openat(proc_dir_fd, "setgroups", O_RDWR|O_CLOEXEC); -+ if (setgroups_fd < 0) { -+ /* -+ * If it's an ENOENT then we are on too old a kernel for the setgroups -+ * code to exist. Emit a warning and bail on this. -+ */ -+ if (ENOENT == errno) { -+ fprintf(stderr, _("%s: kernel doesn't support setgroups restrictions\n"), Prog); -+ goto out; -+ } -+ fprintf(stderr, _("%s: couldn't open process setgroups: %s\n"), -+ Prog, -+ strerror(errno)); -+ exit(EXIT_FAILURE); -+ } -+ -+ /* -+ * Check whether the policy is already what we want. /proc/self/setgroups -+ * is write-once, so attempting to write after it's already written to will -+ * fail. -+ */ -+ if (read(setgroups_fd, policy_buffer, sizeof(policy_buffer)) < 0) { -+ fprintf(stderr, _("%s: failed to read setgroups: %s\n"), -+ Prog, -+ strerror(errno)); -+ exit(EXIT_FAILURE); -+ } -+ if (!strncmp(policy_buffer, policy, strlen(policy))) -+ goto out; -+ -+ /* Write the policy. */ -+ if (lseek(setgroups_fd, 0, SEEK_SET) < 0) { -+ fprintf(stderr, _("%s: failed to seek setgroups: %s\n"), -+ Prog, -+ strerror(errno)); -+ exit(EXIT_FAILURE); -+ } -+ if (dprintf(setgroups_fd, "%s", policy) < 0) { -+ fprintf(stderr, _("%s: failed to setgroups %s policy: %s\n"), -+ Prog, -+ policy, -+ strerror(errno)); -+ exit(EXIT_FAILURE); -+ } -+ -+out: -+ close(setgroups_fd); -+} -+ - /* - * newgidmap - Set the gid_map for the specified process - */ -@@ -103,6 +172,7 @@ int main(int argc, char **argv) - struct stat st; - struct passwd *pw; - int written; -+ bool allow_setgroups = false; - - Prog = Basename (argv[0]); - -@@ -145,7 +215,7 @@ int main(int argc, char **argv) - (unsigned long) getuid ())); - return EXIT_FAILURE; - } -- -+ - /* Get the effective uid and effective gid of the target process */ - if (fstat(proc_dir_fd, &st) < 0) { - fprintf(stderr, _("%s: Could not stat directory for target %u\n"), -@@ -177,8 +247,9 @@ int main(int argc, char **argv) - if (!mappings) - usage(); - -- verify_ranges(pw, ranges, mappings); -+ verify_ranges(pw, ranges, mappings, &allow_setgroups); - -+ write_setgroups(proc_dir_fd, allow_setgroups); - write_mapping(proc_dir_fd, ranges, mappings, "gid_map"); - sub_gid_close(); - diff --git a/shadow-4.1.5.1-pam_group.patch b/shadow-4.1.5.1-pam_group.patch deleted file mode 100644 index 3cc8da7..0000000 --- a/shadow-4.1.5.1-pam_group.patch +++ /dev/null @@ -1,50 +0,0 @@ -Date: Thu Apr 6 16:04:17 CEST 2017 -Bug: bnc#1031643 -Upstream: https://github.com/shadow-maint/shadow/pull/74 - -dynamically added users via pam_group are not listed in groups -databases but are still valid. - - -Index: shadow-4.1.5.1/src/newgrp.c -=================================================================== ---- shadow-4.1.5.1.orig/src/newgrp.c -+++ shadow-4.1.5.1/src/newgrp.c -@@ -372,6 +372,7 @@ int main (int argc, char **argv) - { - bool initflag = false; - int i; -+ bool is_member = false; - bool cflag = false; - int err = 0; - gid_t gid; -@@ -610,6 +611,18 @@ int main (int argc, char **argv) - goto failure; - } - -+#ifdef HAVE_SETGROUPS -+ /* when using pam_group, she will not be listed in the groups -+ * database. However getgroups() will return the group. So -+ * if she is listed there already it is ok to grant membership. -+ */ -+ for (i = 0; i < ngroups; i++) { -+ if (grp->gr_gid == grouplist[i]) { -+ is_member = true; -+ break; -+ } -+ } -+#endif /* HAVE_SETGROUPS */ - /* - * For splitted groups (due to limitations of NIS), check all - * groups of the same GID like the requested group for -@@ -638,7 +651,9 @@ int main (int argc, char **argv) - /* - * Check if the user is allowed to access this group. - */ -- check_perms (grp, pwd, group); -+ if (!is_member) { -+ check_perms (grp, pwd, group); -+ } - - /* - * all successful validations pass through this point. The group id diff --git a/shadow-4.5.tar.xz b/shadow-4.5.tar.xz deleted file mode 100644 index a4b1f2a..0000000 --- a/shadow-4.5.tar.xz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fc8c858381ad577a5c25ff5beb6ee60a34f8719c73e4e7c61e74188b4e54b741 -size 1626704 diff --git a/shadow-4.5.tar.xz.asc b/shadow-4.5.tar.xz.asc deleted file mode 100644 index a570bf1..0000000 --- a/shadow-4.5.tar.xz.asc +++ /dev/null @@ -1,11 +0,0 @@ ------BEGIN PGP SIGNATURE----- -Version: GnuPG v1 - -iQEcBAABCgAGBQJZHKsTAAoJEOn+6gaoXj+dIT8H/2UNtVDHrtXk4Z6d5RGZPKvs -eBOiK03Mk89jtD1YFBLu4kk2tkFP86lylfRdnQisqAovLwsOF36gAoZUK/0X71x3 -AcyYUEno2Go8Sf+Ol432+Tj6FdBG3n/yXzroh7PmxG58PBMV1PBm95RLZ/uVp7hz -WLtrd8FL4QC4fe0rt00BOb2bW6Bbcx+XiXI6QCvLwtQi2bnJt2DcIYxiqZPni4YT -FUAClxYB/hHbjChg3EMFn8JZkaf8SLMuiyLGYKNbqLaXu88/El80QYATSuCNH14y -ywMYEXxKHIiaJ+KnNfjUb3fXHlWyaeWDltguIr0EMfIjF5VrtN4fKxsJy8ZJtMc= -=G9lG ------END PGP SIGNATURE----- diff --git a/shadow-4.6.0-fix-usermod-prefix-crash.patch b/shadow-4.6.0-fix-usermod-prefix-crash.patch new file mode 100644 index 0000000..c67ae64 --- /dev/null +++ b/shadow-4.6.0-fix-usermod-prefix-crash.patch @@ -0,0 +1,86 @@ +Bug: https://github.com/shadow-maint/shadow/issues/110 +Containing following two fixes. + +From 73a876a05612c278da747faeaeea40c3b8d34a53 Mon Sep 17 00:00:00 2001 +From: fariouche +Date: Tue, 8 May 2018 21:17:46 -0500 +Subject: [PATCH 1/2] Fix usermod crash + +Return newly allocated pointers when the caller will free them. + +Closes #110 +--- + libmisc/prefix_flag.c | 2 +- + src/usermod.c | 10 ++++++---- + 2 files changed, 7 insertions(+), 5 deletions(-) + +diff --git a/libmisc/prefix_flag.c b/libmisc/prefix_flag.c +index 6581235e..8ceffd26 100644 +--- a/libmisc/prefix_flag.c ++++ b/libmisc/prefix_flag.c +@@ -333,7 +333,7 @@ extern struct group *prefix_getgr_nam_gid(const char *grname) + && (gid == (gid_t)gid)) { + return prefix_getgrgid ((gid_t) gid); + } +- return prefix_getgrnam (grname); ++ return __gr_dup(prefix_getgrnam (grname)); + } + else + return getgr_nam_gid(grname); +diff --git a/src/usermod.c b/src/usermod.c +index e571426f..7355ad31 100644 +--- a/src/usermod.c ++++ b/src/usermod.c +@@ -1251,11 +1251,13 @@ static void process_flags (int argc, char **argv) + prefix_user_home = xmalloc(len); + wlen = snprintf(prefix_user_home, len, "%s/%s", prefix, user_home); + assert (wlen == (int) len -1); ++ if (user_newhome) { ++ len = strlen(prefix) + strlen(user_newhome) + 2; ++ prefix_user_newhome = xmalloc(len); ++ wlen = snprintf(prefix_user_newhome, len, "%s/%s", prefix, user_newhome); ++ assert (wlen == (int) len -1); ++ } + +- len = strlen(prefix) + strlen(user_newhome) + 2; +- prefix_user_newhome = xmalloc(len); +- wlen = snprintf(prefix_user_newhome, len, "%s/%s", prefix, user_newhome); +- assert (wlen == (int) len -1); + } + else { + prefix_user_home = user_home; + +From 48dcf7852e51b9d8e7926737cc7f7823978b7d7d Mon Sep 17 00:00:00 2001 +From: Serge Hallyn +Date: Tue, 8 May 2018 21:37:55 -0500 +Subject: [PATCH 2/2] usermod: prevent a segv + +in the case where prefix does not exist. + +Signed-off-by: Serge Hallyn +--- + libmisc/prefix_flag.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/libmisc/prefix_flag.c b/libmisc/prefix_flag.c +index 8ceffd26..96b11faa 100644 +--- a/libmisc/prefix_flag.c ++++ b/libmisc/prefix_flag.c +@@ -319,6 +319,7 @@ extern struct group *prefix_getgr_nam_gid(const char *grname) + { + long long int gid; + char *endptr; ++ struct group *g; + + if (NULL == grname) { + return NULL; +@@ -333,7 +334,8 @@ extern struct group *prefix_getgr_nam_gid(const char *grname) + && (gid == (gid_t)gid)) { + return prefix_getgrgid ((gid_t) gid); + } +- return __gr_dup(prefix_getgrnam (grname)); ++ g = prefix_getgrnam (grname); ++ return g ? __gr_dup(g) : NULL; + } + else + return getgr_nam_gid(grname); diff --git a/shadow-4.6.tar.xz b/shadow-4.6.tar.xz new file mode 100644 index 0000000..4785e9d --- /dev/null +++ b/shadow-4.6.tar.xz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0998c8d84242a231ab0acb7f8613927ff5bcff095f8aa6b79478893a03f05583 +size 1678100 diff --git a/shadow-4.6.tar.xz.asc b/shadow-4.6.tar.xz.asc new file mode 100644 index 0000000..5321e1d --- /dev/null +++ b/shadow-4.6.tar.xz.asc @@ -0,0 +1,11 @@ +-----BEGIN PGP SIGNATURE----- + +iQEzBAABCgAdFiEE8dCNt3gYW/eEAC3/6f7qBqheP50FAlrncOkACgkQ6f7qBqhe +P52UGAf/eOnoIYIZ52y72iMxeNfQMTMjYTZd1YrtjlK0RQKquK7FrCOg91MvOF2B +hLVKu2OU7mzuPTMSAraAxjXLkrM0E3vFjMtu1fHBGlGTMspAfik/9Gu9qoevAKXy +BRqgN5m5HMfoGPeEjzILzaGq8bnPKIOfJ0iAYVkjjIa73Vn20uTmNgNZIRqHqwfw +5GUFHn6cjQXFcQ3ngywgwQD7/h/65w8dBbGysF551sAqzPJRbneQL9Wtklcqi1ub +55NyF0ifT67RqMh+EyxhuhXP1Hi57PTEAeqaFMFxnPlQPb+8pQ8nszWBmI+vUN8D +FmhwCtSTnmKlj0jeAqevmkijJhGPQQ== +=fk/F +-----END PGP SIGNATURE----- diff --git a/shadow.changes b/shadow.changes index d9a31b3..d984ef1 100644 --- a/shadow.changes +++ b/shadow.changes @@ -1,3 +1,30 @@ +------------------------------------------------------------------- +Mon May 14 12:45:42 UTC 2018 - mvetter@suse.com + +- Update to 4.6: + * Newgrp: avoid unnecessary lookups + * Make language less binary + * Add error when turning off man switch + * Spelling fixes + * Make userdel work with -R + * newgidmap: enforce setgroups=deny if self-mapping a group + * Norwegian bokmål translation + * pwck: prevent crash by not passing O_CREAT + * WITH_TCB fixes from Mandriva + * Fix pwconv and grpconv entry skips + * Fix -- slurping in su + * add --prefix option +- Remove CVE-2018-7169.patch: upstreamed +- Remove shadow-4.1.5.1-pam_group.patch: upstreamed +- Update userdel-script.patch: change due to prefix +- Update useradd-mkdirs.patch: change due to prefix + Additionally changed in that patch: + * Test for strdup() failure + * Directory to 0755 instead 0777 +- Add shadow-4.6.0-fix-usermod-prefix-crash.patch: + Fixes crash in usermod when called with --prefix. + See https://github.com/shadow-maint/shadow/issues/110 + ------------------------------------------------------------------- Thu Feb 22 15:10:45 UTC 2018 - fvogt@suse.com diff --git a/shadow.spec b/shadow.spec index 5e91df8..2d20fa8 100644 --- a/shadow.spec +++ b/shadow.spec @@ -17,7 +17,7 @@ Name: shadow -Version: 4.5 +Version: 4.6 Release: 0 Summary: Utilities to Manage User and Group Accounts License: BSD-3-Clause AND GPL-2.0+ @@ -43,9 +43,8 @@ Patch6: shadow-4.1.5.1-userdel-helpfix.patch Patch7: shadow-4.1.5.1-logmsg.patch Patch10: encryption_method_nis.patch Patch11: useradd-mkdirs.patch -Patch18: shadow-4.1.5.1-pam_group.patch +Patch12: shadow-4.6.0-fix-usermod-prefix-crash.patch Patch20: disable_new_audit_function.patch -Patch21: CVE-2018-7169.patch BuildRequires: audit-devel > 2.3 BuildRequires: libacl-devel BuildRequires: libattr-devel @@ -68,7 +67,7 @@ group accounts. %prep %setup -q -a 1 %patch0 -%patch1 +%patch1 -p1 %patch2 %patch3 %patch4 @@ -76,11 +75,10 @@ group accounts. %patch6 %patch7 %patch10 -%patch11 -%patch18 -p1 +%patch11 -p1 +%patch12 -p1 %if 0%{?suse_version} < 1330 %patch20 -p1 -%patch21 -p1 %endif iconv -f ISO88591 -t utf-8 doc/HOWTO > doc/HOWTO.utf8 diff --git a/useradd-mkdirs.patch b/useradd-mkdirs.patch index 25d0c9f..54df69f 100644 --- a/useradd-mkdirs.patch +++ b/useradd-mkdirs.patch @@ -1,30 +1,37 @@ -Index: src/useradd.c -=================================================================== ---- src/useradd.c.orig -+++ src/useradd.c -@@ -1943,6 +1943,13 @@ static void usr_update (void) +https://github.com/shadow-maint/shadow/pull/112 + +useradd-mkdirs.patch adapted to two comments in https://github.com/shadow-maint/shadow/pull/2 +* check for stdup failure +* create dirs with 0755 instead of 0777 +diff -urEbwB shadow-4.6/src/useradd.c shadow-4.6.new-useradd-mkdirs/src/useradd.c +--- shadow-4.6/src/useradd.c 2018-04-29 18:42:37.000000000 +0200 ++++ shadow-4.6.new-useradd-mkdirs/src/useradd.c 2018-05-15 17:11:03.706371270 +0200 +@@ -2018,6 +2018,19 @@ static void create_home (void) { - if (access (user_home, F_OK) != 0) { -+ char path[strlen (user_home) + 2]; + if (access (prefix_user_home, F_OK) != 0) { ++ char path[strlen (prefix_user_home) + 2]; + char *bhome, *cp; + + path[0] = '\0'; -+ bhome = strdup (user_home); ++ bhome = strdup (prefix_user_home); ++ if (!bhome) { ++ fprintf (stderr, ++ _("%s: error while duplicating string %s\n"), ++ Prog, user_home); ++ fail_exit (E_HOMEDIR); ++ } + ++bhome; + #ifdef WITH_SELINUX - if (set_selinux_file_context (user_home) != 0) { + if (set_selinux_file_context (prefix_user_home) != 0) { fprintf (stderr, -@@ -1951,19 +1958,42 @@ static void create_home (void) +@@ -2026,11 +2039,20 @@ fail_exit (E_HOMEDIR); } #endif - /* XXX - create missing parent directories. --marekm */ -- if (mkdir (user_home, 0) != 0) { -- fprintf (stderr, -- _("%s: cannot create directory %s\n"), -- Prog, user_home); +- if (mkdir (prefix_user_home, 0) != 0) { + + /* Check for every part of the path, if the directory + exists. If not, create it with permissions 755 and @@ -36,36 +43,31 @@ Index: src/useradd.c + strcat (path, cp); + if (access (path, F_OK) != 0) { + if (mkdir (path, 0) != 0) { -+ fprintf (stderr, -+ _("%s: cannot create directory %s\n"), + fprintf (stderr, + _("%s: cannot create directory %s\n"), +- Prog, prefix_user_home); + Prog, path); #ifdef WITH_AUDIT -- audit_logger (AUDIT_ADD_USER, Prog, -- "adding home directory", -- user_name, (unsigned int) user_id, -- SHADOW_AUDIT_FAILURE); -+ audit_logger (AUDIT_ADD_USER, Prog, -+ "adding home directory", -+ user_name, (unsigned int) user_id, -+ SHADOW_AUDIT_FAILURE); + audit_logger (AUDIT_ADD_USER, Prog, + "adding home directory", +@@ -2039,6 +2061,20 @@ #endif -- fail_exit (E_HOMEDIR); -+ fail_exit (E_HOMEDIR); -+ } + fail_exit (E_HOMEDIR); + } + if (chown (path, 0, 0) < 0) { + fprintf (stderr, + _("%s: warning: chown on `%s' failed: %m\n"), + Prog, path); + } -+ if (chmod (path, 0777) < 0) { ++ if (chmod (path, 0755) < 0) { + fprintf (stderr, + _("%s: warning: chmod on `%s' failed: %m\n"), + Prog, path); + } + } + cp = strtok (NULL, "/"); - } ++ } + - chown (user_home, user_id, user_gid); - chmod (user_home, + (void) chown (prefix_user_home, user_id, user_gid); + chmod (prefix_user_home, 0777 & ~getdef_num ("UMASK", GETDEF_DEFAULT_UMASK)); diff --git a/userdel-script.patch b/userdel-script.patch index f06f2fa..1aae489 100644 --- a/userdel-script.patch +++ b/userdel-script.patch @@ -1,6 +1,16 @@ ---- src/userdel.c -+++ src/userdel.c -@@ -762,13 +762,13 @@ static void update_user (void) +diff -urEbwB shadow-4.6/src/userdel.c shadow-4.6.new/src/userdel.c +--- shadow-4.6/src/userdel.c 2018-04-29 18:42:37.000000000 +0200 ++++ shadow-4.6.new/src/userdel.c 2018-05-14 16:13:43.996280216 +0200 +@@ -125,7 +125,7 @@ + static void fail_exit (int); + static void open_files (void); + static void update_user (void); +-static void user_cancel (const char *); ++static void call_script (const char *, const char *); + + #ifdef EXTRA_CHECK_HOME_DIR + static bool path_prefix (const char *, const char *); +@@ -767,13 +767,13 @@ * cron, at, or print jobs. */ @@ -16,7 +26,7 @@ if (NULL == cmd) { return; } -@@ -1163,9 +1163,10 @@ int main (int argc, char **argv) +@@ -1213,9 +1213,10 @@ } /* @@ -25,20 +35,20 @@ + * Do the hard stuff - open the files, remove the user entries, + * remove the home directory, then close and update the files. */ -+ call_script ("USERDEL_PRECMD", user_name); ++ call_script ("USERDEL_PRECMD", user_name); open_files (); update_user (); update_groups (); -@@ -1268,7 +1269,7 @@ int main (int argc, char **argv) - * Cancel any crontabs or at jobs. Have to do this before we remove +@@ -1319,7 +1320,7 @@ * the entry from /etc/passwd. */ -- user_cancel (user_name); -+ call_script ("USERDEL_CMD", user_name); + if(prefix[0] == '\0') +- user_cancel (user_name); ++ call_script ("USERDEL_CMD", user_name); close_files (); #ifdef WITH_TCB -@@ -1278,6 +1279,8 @@ int main (int argc, char **argv) +@@ -1329,6 +1330,9 @@ nscd_flush_cache ("passwd"); nscd_flush_cache ("group"); @@ -47,4 +57,4 @@ + return ((0 != errors) ? E_HOMEDIR : E_SUCCESS); } -- +