Index: /trunk/src/VBox/Runtime/r3/posix/process-creation-posix.cpp
===================================================================
--- /trunk/src/VBox/Runtime/r3/posix/process-creation-posix.cpp	(revision 57868)
+++ /trunk/src/VBox/Runtime/r3/posix/process-creation-posix.cpp	(revision 57869)
@@ -41,7 +41,8 @@
 #include <signal.h>
 #include <grp.h>
+#include <paths.h>
+#include <pwd.h>
 #if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
 # include <crypt.h>
-# include <pwd.h>
 # include <shadow.h>
 #endif
@@ -103,5 +104,5 @@
     pw = getpwnam(pszUser);
     if (!pw)
-        return VERR_PERMISSION_DENIED;
+        return VERR_AUTHENTICATION_FAILURE;
 
     if (!pszPasswd)
@@ -127,5 +128,5 @@
     }
     if (!fCorrect)
-        return VERR_PERMISSION_DENIED;
+        return VERR_AUTHENTICATION_FAILURE;
 
     *pGid = pw->pw_gid;
@@ -138,5 +139,5 @@
 
     if (getpwnam_r(pszUser, &pw, szBuf, sizeof(szBuf), &ppw) != 0 || ppw == NULL)
-        return VERR_PERMISSION_DENIED;
+        return VERR_AUTHENTICATION_FAILURE;
 
     if (!pszPasswd)
@@ -151,5 +152,5 @@
     char *pszEncPasswd = crypt(pszPasswd, ppw->pw_passwd);
     if (strcmp(pszEncPasswd, ppw->pw_passwd))
-        return VERR_PERMISSION_DENIED;
+        return VERR_AUTHENTICATION_FAILURE;
 
     *pGid = ppw->pw_gid;
@@ -159,5 +160,5 @@
 #else
     NOREF(pszUser); NOREF(pszPasswd); NOREF(pGid); NOREF(pUid);
-    return VERR_PERMISSION_DENIED;
+    return VERR_AUTHENTICATION_FAILURE;
 #endif
 }
@@ -271,4 +272,136 @@
                           NULL /*pszAsUser*/, NULL /* pszPassword*/,
                           pProcess);
+}
+
+
+/**
+ * Adjust the profile environment after forking the child process and changing
+ * the UID.
+ *
+ * @returns IRPT status code.
+ * @param   hEnvToUse       The environment we're going to use with execve.
+ * @param   fFlags          The process creation flags.
+ * @param   hEnv            The environment passed in by the user.
+ */
+static int rtProcPosixAdjustProfileEnvFromChild(RTENV hEnvToUse, uint32_t fFlags, RTENV hEnv)
+{
+    int rc = VINF_SUCCESS;
+#ifdef RT_OS_DARWIN
+    if (   RT_SUCCESS(rc)
+        && (!(fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) || RTEnvExistEx(hEnv, "TMPDIR")) )
+    {
+        char szValue[_4K];
+        rc = confstr(_SC_DARWIN_USER_TEMP_DIR, szValue, sizeof(szValue));
+        if (rc > 0 && rc < sizeof(szValue))
+        {
+            char *pszTmp
+            rc = RTStrCurrentCPToUtf8(&pszTmp, achBuf);
+            if (RT_SUCCESS(rc))
+            {
+                rc = RTEnvSetEx(hEnvToUse, "TMPDIR", pszTmp);
+                RTStrFree(pszTmp);
+            }
+        }
+        else
+            rc = VERR_BUFFER_OVERFLOW;
+    }
+#endif
+    return rc;
+}
+
+
+/**
+ * Create a very very basic environment for a user.
+ *
+ * @returns IPRT status code.
+ * @param   phEnvToUse  Where to return the created environment.
+ * @param   pszUser     The user name for the profile.
+ */
+static int rtProcPosixCreateProfileEnv(PRTENV phEnvToUse, const char *pszUser)
+{
+    struct passwd   Pwd;
+    struct passwd  *pPwd = NULL;
+    char            achBuf[_4K];
+    int             rc;
+    errno = 0;
+    if (pszUser)
+        rc = getpwnam_r(pszUser, &Pwd, achBuf, sizeof(achBuf), &pPwd);
+    else
+        rc = getpwuid_r(getuid(), &Pwd, achBuf, sizeof(achBuf), &pPwd);
+    if (rc == 0 && pPwd)
+    {
+        char *pszDir;
+        rc = RTStrCurrentCPToUtf8(&pszDir, pPwd->pw_dir);
+        if (RT_SUCCESS(rc))
+        {
+            char *pszShell;
+            rc = RTStrCurrentCPToUtf8(&pszShell, pPwd->pw_shell);
+            if (RT_SUCCESS(rc))
+            {
+                char *pszUserFree = NULL;
+                if (!pszUser)
+                {
+                    rc = RTStrCurrentCPToUtf8(&pszUserFree, pPwd->pw_name);
+                    if (RT_SUCCESS(rc))
+                        pszUser = pszUserFree;
+                }
+                if (RT_SUCCESS(rc))
+                {
+                    rc = RTEnvCreate(phEnvToUse);
+                    if (RT_SUCCESS(rc))
+                    {
+                        RTENV hEnvToUse = *phEnvToUse;
+
+                        rc = RTEnvSetEx(hEnvToUse, "HOME", pszDir);
+                        if (RT_SUCCESS(rc))
+                            rc = RTEnvSetEx(hEnvToUse, "SHELL", pszShell);
+                        if (RT_SUCCESS(rc))
+                            rc = RTEnvSetEx(hEnvToUse, "USER", pszUser);
+                        if (RT_SUCCESS(rc))
+                            rc = RTEnvSetEx(hEnvToUse, "LOGNAME", pszUser);
+
+                        if (RT_SUCCESS(rc))
+                            rc = RTEnvSetEx(hEnvToUse, "PATH", pPwd->pw_uid == 0 ? _PATH_STDPATH : _PATH_DEFPATH);
+
+                        if (RT_SUCCESS(rc))
+                        {
+                            RTStrPrintf(achBuf, sizeof(achBuf), "%s/%s", _PATH_MAILDIR, pszUser);
+                            rc = RTEnvSetEx(hEnvToUse, "MAIL", achBuf);
+                        }
+
+#ifdef RT_OS_DARWIN
+                        if (RT_SUCCESS(rc) && !pszUserFree)
+                        {
+                            rc = confstr(_SC_DARWIN_USER_TEMP_DIR, achBuf, sizeof(achBuf));
+                            if (rc > 0 && rc < sizeof(achBuf))
+                            {
+                                char *pszTmp
+                                rc = RTStrCurrentCPToUtf8(&pszTmp, achBuf);
+                                if (RT_SUCCESS(rc))
+                                {
+                                    rc = RTEnvSetEx(hEnvToUse, "TMPDIR", pszTmp);
+                                    RTStrFree(pszTmp);
+                                }
+                            }
+                            else
+                                rc = VERR_BUFFER_OVERFLOW;
+                        }
+#endif
+
+                        /** @todo load /etc/environment, /etc/profile.env and ~/.pam_environment? */
+
+                        if (RT_FAILURE(rc))
+                            RTEnvDestroy(hEnvToUse);
+                    }
+                    RTStrFree(pszUserFree);
+                }
+                RTStrFree(pszShell);
+            }
+            RTStrFree(pszDir);
+        }
+    }
+    else
+        rc = errno ? RTErrConvertFromErrno(errno) : VERR_ACCESS_DENIED;
+    return rc;
 }
 
@@ -292,4 +425,14 @@
 }
 
+/**
+ * Cleans up the environment on the way out.
+ */
+static int rtProcPosixCreateReturn(int rc, RTENV hEnvToUse, RTENV hEnv)
+{
+    if (hEnvToUse != hEnv)
+        RTEnvDestroy(hEnvToUse);
+    return rc;
+}
+
 
 RTR3DECL(int)   RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
@@ -307,8 +450,5 @@
     AssertReturn(!(fFlags & RTPROC_FLAGS_DETACHED) || !phProcess, VERR_INVALID_PARAMETER);
     AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER);
-    const char * const *papszEnv = RTEnvGetExecEnvP(hEnv);
-    AssertPtrReturn(papszEnv, VERR_INVALID_HANDLE);
     AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
-    /** @todo search the PATH (add flag for this). */
     AssertPtrNullReturn(pszAsUser, VERR_INVALID_POINTER);
     AssertReturn(!pszAsUser || *pszAsUser, VERR_INVALID_PARAMETER);
@@ -379,4 +519,28 @@
 
     /*
+     * Create the child environment if either RTPROC_FLAGS_PROFILE or
+     * RTPROC_FLAGS_ENV_CHANGE_RECORD are in effect.
+     */
+    RTENV hEnvToUse = hEnv;
+    if (   (fFlags & (RTPROC_FLAGS_ENV_CHANGE_RECORD | RTPROC_FLAGS_PROFILE))
+        && (   (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD)
+            || hEnv == RTENV_DEFAULT) )
+    {
+        if (fFlags & RTPROC_FLAGS_PROFILE)
+            rc = rtProcPosixCreateProfileEnv(&hEnvToUse, pszAsUser);
+        else
+            rc = RTEnvClone(&hEnvToUse, RTENV_DEFAULT);
+        if (RT_SUCCESS(rc))
+        {
+            if ((fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) && hEnv != RTENV_DEFAULT)
+                rc = RTEnvApplyChanges(hEnvToUse, hEnv);
+            if (RT_FAILURE(rc))
+                RTEnvDestroy(hEnvToUse);
+        }
+        if (RT_FAILURE(rc))
+            return rc;
+    }
+
+    /*
      * Check for execute access to the file.
      */
@@ -384,19 +548,29 @@
     if (access(pszExec, X_OK))
     {
+        rc = errno;
         if (   !(fFlags & RTPROC_FLAGS_SEARCH_PATH)
-            || errno != ENOENT
+            || rc != ENOENT
             || RTPathHavePath(pszExec) )
-            return RTErrConvertFromErrno(errno);
-
-        /* search */
-        char *pszPath = RTEnvDupEx(hEnv, "PATH");
-        rc = RTPathTraverseList(pszPath, ':', rtPathFindExec, (void *)pszExec, &szRealExec[0]);
-        RTStrFree(pszPath);
+            rc = RTErrConvertFromErrno(rc);
+        else
+        {
+            /* search */
+            char *pszPath = RTEnvDupEx(hEnvToUse, "PATH");
+            rc = RTPathTraverseList(pszPath, ':', rtPathFindExec, (void *)pszExec, &szRealExec[0]);
+            RTStrFree(pszPath);
+            if (RT_SUCCESS(rc))
+                pszExec = szRealExec;
+            else
+                rc = rc == VERR_END_OF_STRING ? VERR_FILE_NOT_FOUND : rc;
+        }
+
         if (RT_FAILURE(rc))
-            return rc == VERR_END_OF_STRING ? VERR_FILE_NOT_FOUND : rc;
-        pszExec = szRealExec;
+            return rtProcPosixCreateReturn(rc, hEnvToUse, hEnv);
     }
 
     pid_t pid = -1;
+    const char * const *papszEnv = RTEnvGetExecEnvP(hEnvToUse);
+    AssertPtrReturn(papszEnv, rtProcPosixCreateReturn(VERR_INVALID_HANDLE, hEnvToUse, hEnv));
+
 
     /*
@@ -418,5 +592,5 @@
             templateFd = rtSolarisContractPreFork();
             if (templateFd == -1)
-                return VERR_OPEN_FAILED;
+                return rtProcPosixCreateReturn(VERR_OPEN_FAILED, hEnvToUse, hEnv);
         }
 # endif /* RT_OS_SOLARIS */
@@ -427,5 +601,5 @@
             if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
                 rtSolarisContractPostForkChild(templateFd);
-# endif /* RT_OS_SOLARIS */
+# endif
             setsid(); /* see comment above */
 
@@ -435,8 +609,8 @@
         else
         {
-#ifdef RT_OS_SOLARIS
+# ifdef RT_OS_SOLARIS
             if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
                 rtSolarisContractPostForkParent(templateFd, pid);
-#endif /* RT_OS_SOLARIS */
+# endif
             if (pid > 0)
             {
@@ -454,10 +628,10 @@
                 /* Assume that something wasn't found. No detailed info. */
                 if (status)
-                    return VERR_PROCESS_NOT_FOUND;
+                    return rtProcPosixCreateReturn(VERR_PROCESS_NOT_FOUND, hEnvToUse, hEnv);
                 if (phProcess)
                     *phProcess = 0;
-                return VINF_SUCCESS;
-            }
-            return RTErrConvertFromErrno(errno);
+                return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv);
+            }
+            return rtProcPosixCreateReturn(RTErrConvertFromErrno(errno), hEnvToUse, hEnv);
         }
     }
@@ -560,5 +734,5 @@
                 if (phProcess)
                     *phProcess = pid;
-                return VINF_SUCCESS;
+                return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv);
             }
         }
@@ -577,5 +751,5 @@
             templateFd = rtSolarisContractPreFork();
             if (templateFd == -1)
-                return VERR_OPEN_FAILED;
+                return rtProcPosixCreateReturn(VERR_OPEN_FAILED, hEnvToUse, hEnv);
         }
 #endif /* RT_OS_SOLARIS */
@@ -627,4 +801,23 @@
             }
 #endif
+
+            /*
+             * Some final profile environment tweaks, if running as user.
+             */
+            if (   (fFlags & RTPROC_FLAGS_PROFILE)
+                && pszAsUser
+                && (   (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD)
+                    || hEnv == RTENV_DEFAULT) )
+            {
+                rc = rtProcPosixAdjustProfileEnvFromChild(hEnvToUse, fFlags, hEnv);
+                papszEnv = RTEnvGetExecEnvP(hEnvToUse);
+                if (RT_FAILURE(rc) || !papszEnv)
+                {
+                    if (fFlags & RTPROC_FLAGS_DETACHED)
+                        _Exit(126);
+                    else
+                        exit(126);
+                }
+            }
 
             /*
@@ -694,5 +887,5 @@
             if (phProcess)
                 *phProcess = pid;
-            return VINF_SUCCESS;
+            return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv);
         }
         /* For a detached process this happens in the temp process, so
@@ -700,8 +893,8 @@
         if (fFlags & RTPROC_FLAGS_DETACHED)
             _Exit(124);
-        return RTErrConvertFromErrno(errno);
-    }
-
-    return VERR_NOT_IMPLEMENTED;
+        return rtProcPosixCreateReturn(RTErrConvertFromErrno(errno), hEnvToUse, hEnv);
+    }
+
+    return rtProcPosixCreateReturn(VERR_NOT_IMPLEMENTED, hEnvToUse, hEnv);
 }
 
Index: /trunk/src/VBox/Runtime/testcase/tstRTProcCreateEx.cpp
===================================================================
--- /trunk/src/VBox/Runtime/testcase/tstRTProcCreateEx.cpp	(revision 57868)
+++ /trunk/src/VBox/Runtime/testcase/tstRTProcCreateEx.cpp	(revision 57869)
@@ -90,5 +90,5 @@
 
     int cErrors = 0;
-    char szValue[_1K];
+    char szValue[_16K];
 
     /*
@@ -166,5 +166,5 @@
     }
 
-#if 0
+#if 1
     /* For manual testing. */
     if (strcmp(argv[2],"noinherit-change-record") == 0)
@@ -178,5 +178,4 @@
             {
                 char szVarNm[_1K];
-                char szValue[_16K];
                 rc = RTEnvGetByIndexEx(hEnv, i, szVarNm, sizeof(szVarNm), szValue, sizeof(szValue));
                 if (RT_SUCCESS(rc))
@@ -707,7 +706,5 @@
     if (pszAsUser)
         tstRTCreateProcEx5(pszAsUser, pszPassword);
-#ifdef RT_OS_WINDOWS
     tstRTCreateProcEx6(pszAsUser, pszPassword);
-#endif
 
     /** @todo Cover files, ++ */
