private static unsafe bool TryGetUserNameFromPasswd(byte *buf, int bufLen, out string path) { // Call getpwuid_r to get the passwd struct Interop.Sys.Passwd passwd; int error = Interop.Sys.GetPwUidR(Interop.Sys.GetEUid(), out passwd, buf, bufLen); // If the call succeeds, give back the user name retrieved if (error == 0) { Debug.Assert(passwd.Name != null); path = Marshal.PtrToStringAnsi((IntPtr)passwd.Name); return(true); } // If the current user's entry could not be found, give back null, // but still return true as false indicates the buffer was too small. if (error == -1) { path = null; return(true); } var errorInfo = new Interop.ErrorInfo(error); // If the call failed because the buffer was too small, return false to // indicate the caller should try again with a larger buffer. if (errorInfo.Error == Interop.Error.ERANGE) { path = null; return(false); } // Otherwise, fail. throw new IOException(errorInfo.GetErrorMessage(), errorInfo.RawErrno); }
internal static Exception GetIOException(Interop.ErrorInfo errorInfo, string?path = null) { string msg = errorInfo.GetErrorMessage(); return(new IOException( string.IsNullOrEmpty(path) ? msg : $"{msg} : '{path}'", errorInfo.RawErrno)); }
private static unsafe bool TryGetPasswd(string name, byte *buf, int bufLen, out Interop.Sys.Passwd?passwd) { // Call getpwnam_r to get the passwd struct Interop.Sys.Passwd tempPasswd; int error = Interop.Sys.GetPwNamR(name, out tempPasswd, buf, bufLen); // If the call succeeds, give back the passwd retrieved if (error == 0) { passwd = tempPasswd; return(true); } // If the current user's entry could not be found, give back null, // but still return true as false indicates the buffer was too small. if (error == -1) { passwd = null; return(true); } var errorInfo = new Interop.ErrorInfo(error); // If the call failed because the buffer was too small, return false to // indicate the caller should try again with a larger buffer. if (errorInfo.Error == Interop.Error.ERANGE) { passwd = null; return(false); } // Otherwise, fail. throw new Win32Exception(errorInfo.RawErrno, errorInfo.GetErrorMessage()); }
private static Exception CreateExceptionFromErrno() { Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo(); return((errorInfo.Error == Interop.Error.ENOMEM || errorInfo.Error == Interop.Error.EPERM) ? (Exception) new OutOfMemoryException(SR.OutOfMemory_MemoryResourceLimits) : (Exception) new InvalidOperationException(errorInfo.GetErrorMessage())); }
/// <summary>Invoke <see cref="Interop.Sys.SysConf"/>, throwing if it fails.</summary> private static int CheckedSysConf(Interop.Sys.SysConfName name) { long result = Interop.Sys.SysConf(name); if (result == -1) { Interop.ErrorInfo errno = Interop.Sys.GetLastErrorInfo(); throw errno.Error == Interop.Error.EINVAL ? new ArgumentOutOfRangeException(nameof(name), name, errno.GetErrorMessage()) : Interop.GetIOException(errno); } return((int)result); }
/// <summary> /// Checks the file has the correct permissions and attempts to modify them if they're inappropriate. /// </summary> /// <param name="stream"> /// The file stream to check. /// </param> /// <param name="userId"> /// The current userId from GetEUid(). /// </param> private static void EnsureFilePermissions(FileStream stream, uint userId) { // Verify that we're creating files with u+rw and g-rw, o-rw. const Interop.Sys.Permissions requiredPermissions = Interop.Sys.Permissions.S_IRUSR | Interop.Sys.Permissions.S_IWUSR; const Interop.Sys.Permissions forbiddenPermissions = Interop.Sys.Permissions.S_IRGRP | Interop.Sys.Permissions.S_IWGRP | Interop.Sys.Permissions.S_IROTH | Interop.Sys.Permissions.S_IWOTH; Interop.Sys.FileStatus stat; if (Interop.Sys.FStat(stream.SafeFileHandle, out stat) != 0) { Interop.ErrorInfo error = Interop.Sys.GetLastErrorInfo(); throw new CryptographicException( SR.Cryptography_FileStatusError, new IOException(error.GetErrorMessage(), error.RawErrno)); } if (stat.Uid != userId) { throw new CryptographicException(SR.Format(SR.Cryptography_OwnerNotCurrentUser, stream.Name)); } if ((stat.Mode & (int)requiredPermissions) != (int)requiredPermissions || (stat.Mode & (int)forbiddenPermissions) != 0) { if (Interop.Sys.FChMod(stream.SafeFileHandle, (int)requiredPermissions) < 0) { Interop.ErrorInfo error = Interop.Sys.GetLastErrorInfo(); throw new CryptographicException( SR.Format(SR.Cryptography_InvalidFilePermissions, stream.Name), new IOException(error.GetErrorMessage(), error.RawErrno)); } // Verify the chmod applied. if (Interop.Sys.FStat(stream.SafeFileHandle, out stat) != 0) { Interop.ErrorInfo error = Interop.Sys.GetLastErrorInfo(); throw new CryptographicException( SR.Cryptography_FileStatusError, new IOException(error.GetErrorMessage(), error.RawErrno)); } if ((stat.Mode & (int)requiredPermissions) != (int)requiredPermissions || (stat.Mode & (int)forbiddenPermissions) != 0) { throw new CryptographicException(SR.Format(SR.Cryptography_InvalidFilePermissions, stream.Name)); } } }
/// <summary>Wrapper for getpwuid_r.</summary> /// <param name="bufLen">The length of the buffer to use when storing the password result.</param> /// <param name="path">The resulting path; null if the user didn't have an entry.</param> /// <returns>true if the call was successful (path may still be null); false is a larger buffer is needed.</returns> private static bool TryGetHomeDirectoryFromPasswd(int bufLen, out string path) { // Call getpwuid_r to get the passwd struct Interop.Sys.Passwd passwd; IntPtr buffer = Marshal.AllocHGlobal(bufLen); try { int error = Interop.Sys.GetPwUidR(Interop.Sys.GetEUid(), out passwd, buffer, bufLen); // If the call succeeds, give back the home directory path retrieved if (error == 0) { Debug.Assert(passwd.HomeDirectory != IntPtr.Zero); path = Marshal.PtrToStringAnsi(passwd.HomeDirectory); return(true); } // If the current user's entry could not be found, give back null // path, but still return true as false indicates the buffer was // too small. if (error == -1) { path = null; return(true); } var errorInfo = new Interop.ErrorInfo(error); // If the call failed because the buffer was too small, return false to // indicate the caller should try again with a larger buffer. if (errorInfo.Error == Interop.Error.ERANGE) { path = null; return(false); } // Otherwise, fail. throw new IOException(errorInfo.GetErrorMessage(), errorInfo.RawErrno); } finally { // Deallocate the buffer we created Marshal.FreeHGlobal(buffer); } }
/// <summary>Wrapper for getpwuid_r.</summary> /// <param name="buf">The scratch buffer to pass into getpwuid_r.</param> /// <param name="bufLen">The length of <paramref name="buf"/>.</param> /// <param name="path">The resulting path; null if the user didn't have an entry.</param> /// <returns>true if the call was successful (path may still be null); false is a larger buffer is needed.</returns> private static unsafe bool TryGetHomeDirectoryFromPasswd(byte *buf, int bufLen, out string path) { while (true) { // Call getpwuid_r to get the passwd struct Interop.Sys.Passwd passwd; IntPtr result; int rv = Interop.Sys.GetPwUid(Interop.Sys.GetEUid(), out passwd, buf, bufLen, out result); // If the call succeeds, give back the home directory path retrieved if (rv == 0) { if (result == IntPtr.Zero) { // Current user's entry could not be found path = null; // we'll still return true, as false indicates the buffer was too small } else { Debug.Assert(result == (IntPtr)(&passwd)); Debug.Assert(passwd.HomeDirectory != null); path = Marshal.PtrToStringAnsi((IntPtr)passwd.HomeDirectory); } return(true); } // If the call failed because it was interrupted, try again. Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo(); if (errorInfo.Error == Interop.Error.EINTR) { continue; } // If the call failed because the buffer was too small, return false to // indicate the caller should try again with a larger buffer. if (errorInfo.Error == Interop.Error.ERANGE) { path = null; return(false); } // Otherwise, fail. throw new IOException(errorInfo.GetErrorMessage(), errorInfo.RawErrno); } }
/// <summary> /// Checks the file has the correct permissions. /// </summary> /// <param name="stream"> /// The file stream to check. /// </param> /// <param name="userId"> /// The current userId from GetEUid(). /// </param> private static void EnsureFilePermissions(FileStream stream, uint userId) { // Verify that we're creating files with u+rw and o-rw, g-rw. const Interop.Sys.Permissions requiredPermissions = Interop.Sys.Permissions.S_IRUSR | Interop.Sys.Permissions.S_IWUSR; const Interop.Sys.Permissions forbiddenPermissions = Interop.Sys.Permissions.S_IROTH | Interop.Sys.Permissions.S_IWOTH | Interop.Sys.Permissions.S_IRGRP | Interop.Sys.Permissions.S_IWGRP; // NOTE: no need to call DangerousAddRef here, since the FileStream is // held open outside of this method int fileDescriptor = (int)stream.SafeFileHandle.DangerousGetHandle(); Interop.Sys.FileStatus stat; if (Interop.Sys.FStat(fileDescriptor, out stat) != 0) { Interop.ErrorInfo error = Interop.Sys.GetLastErrorInfo(); throw new CryptographicException( SR.Cryptography_FileStatusError, new IOException(error.GetErrorMessage(), error.RawErrno)); } if (stat.Uid != userId) { throw new CryptographicException(SR.Format(SR.Cryptography_OwnerNotCurrentUser, stream.Name)); } if ((stat.Mode & (int)requiredPermissions) != (int)requiredPermissions) { throw new CryptographicException(SR.Format(SR.Cryptography_InsufficientFilePermissions, stream.Name)); } if ((stat.Mode & (int)forbiddenPermissions) != 0) { throw new CryptographicException(SR.Format(SR.Cryptography_TooBroadFilePermissions, stream.Name)); } }
/// <summary> /// Checks the store directory has the correct permissions. /// </summary> /// <param name="path"> /// The path of the directory to check. /// </param> /// <param name="userId"> /// The current userId from GetEUid(). /// </param> private static void EnsureDirectoryPermissions(string path, uint userId) { Interop.Sys.FileStatus dirStat; if (Interop.Sys.Stat(path, out dirStat) != 0) { Interop.ErrorInfo error = Interop.Sys.GetLastErrorInfo(); throw new CryptographicException( SR.Cryptography_FileStatusError, new IOException(error.GetErrorMessage(), error.RawErrno)); } if (dirStat.Uid != userId) { throw new CryptographicException(SR.Format(SR.Cryptography_OwnerNotCurrentUser, path)); } if ((dirStat.Mode & (int)Interop.Sys.Permissions.S_IRWXU) != (int)Interop.Sys.Permissions.S_IRWXU) { throw new CryptographicException(SR.Format(SR.Cryptography_InvalidDirectoryPermissions, path)); } }
/// <summary> /// Checks the store directory has the correct permissions. /// </summary> /// <param name="path"> /// The path of the directory to check. /// </param> /// <param name="userId"> /// The current userId from GetEUid(). /// </param> private static void EnsureDirectoryPermissions(string path, uint userId) { Interop.Sys.FileStatus dirStat; if (Interop.Sys.Stat(path, out dirStat) != 0) { Interop.ErrorInfo error = Interop.Sys.GetLastErrorInfo(); throw new CryptographicException( SR.Cryptography_FileStatusError, new IOException(error.GetErrorMessage(), error.RawErrno)); } if (dirStat.Uid != userId) { throw new CryptographicException(SR.Format(SR.Cryptography_OwnerNotCurrentUser, path)); } const UnixFileMode UserReadWriteExecute = UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute; UnixFileMode permissions = File.GetUnixFileMode(path); if ((permissions & UserReadWriteExecute) != UserReadWriteExecute) { throw new CryptographicException(SR.Format(SR.Cryptography_InvalidDirectoryPermissions, path)); } }
/// <summary>Wrapper for getpwuid_r.</summary> /// <param name="buf">The scratch buffer to pass into getpwuid_r.</param> /// <param name="bufLen">The length of <paramref name="buf"/>.</param> /// <param name="path">The resulting path; null if the user didn't have an entry.</param> /// <returns>true if the call was successful (path may still be null); false is a larger buffer is needed.</returns> private static unsafe bool TryGetHomeDirectoryFromPasswd(byte* buf, int bufLen, out string path) { // Call getpwuid_r to get the passwd struct Interop.Sys.Passwd passwd; int error = Interop.Sys.GetPwUidR(Interop.Sys.GetEUid(), out passwd, buf, bufLen); // If the call succeeds, give back the home directory path retrieved if (error == 0) { Debug.Assert(passwd.HomeDirectory != null); path = Marshal.PtrToStringAnsi((IntPtr)passwd.HomeDirectory); return true; } // If the current user's entry could not be found, give back null // path, but still return true as false indicates the buffer was // too small. if (error == -1) { path = null; return true; } var errorInfo = new Interop.ErrorInfo(error); // If the call failed because the buffer was too small, return false to // indicate the caller should try again with a larger buffer. if (errorInfo.Error == Interop.Error.ERANGE) { path = null; return false; } // Otherwise, fail. throw new IOException(errorInfo.GetErrorMessage(), errorInfo.RawErrno); }
private bool ForkAndExecProcess( ProcessStartInfo startInfo, string?resolvedFilename, string[] argv, string[] envp, string?cwd, bool setCredentials, uint userId, uint groupId, uint[]?groups, out int stdinFd, out int stdoutFd, out int stderrFd, bool usesTerminal, bool throwOnNoExec = true) { if (string.IsNullOrEmpty(resolvedFilename)) { Interop.ErrorInfo errno = Interop.Error.ENOENT.Info(); throw CreateExceptionForErrorStartingProcess(errno.GetErrorMessage(), errno.RawErrno, startInfo.FileName, cwd); } // Lock to avoid races with OnSigChild // By using a ReaderWriterLock we allow multiple processes to start concurrently. s_processStartLock.EnterReadLock(); try { if (usesTerminal) { ConfigureTerminalForChildProcesses(1); } int childPid; // Invoke the shim fork/execve routine. It will create pipes for all requested // redirects, fork a child process, map the pipe ends onto the appropriate stdin/stdout/stderr // descriptors, and execve to execute the requested process. The shim implementation // is used to fork/execve as executing managed code in a forked process is not safe (only // the calling thread will transfer, thread IDs aren't stable across the fork, etc.) int errno = Interop.Sys.ForkAndExecProcess( resolvedFilename, argv, envp, cwd, startInfo.RedirectStandardInput, startInfo.RedirectStandardOutput, startInfo.RedirectStandardError, setCredentials, userId, groupId, groups, out childPid, out stdinFd, out stdoutFd, out stderrFd); if (errno == 0) { // Ensure we'll reap this process. // note: SetProcessId will set this if we don't set it first. _waitStateHolder = new ProcessWaitState.Holder(childPid, isNewChild: true, usesTerminal); // Store the child's information into this Process object. Debug.Assert(childPid >= 0); SetProcessId(childPid); SetProcessHandle(new SafeProcessHandle(_processId, GetSafeWaitHandle())); return(true); } else { if (!throwOnNoExec && new Interop.ErrorInfo(errno).Error == Interop.Error.ENOEXEC) { return(false); } throw CreateExceptionForErrorStartingProcess(new Interop.ErrorInfo(errno).GetErrorMessage(), errno, resolvedFilename, cwd); } } finally { s_processStartLock.ExitReadLock(); if (_waitStateHolder == null && usesTerminal) { // We failed to launch a child that could use the terminal. s_processStartLock.EnterWriteLock(); ConfigureTerminalForChildProcesses(-1); s_processStartLock.ExitWriteLock(); } } }
internal static Exception GetIOException(Interop.ErrorInfo errorInfo) { return(new IOException(errorInfo.GetErrorMessage(), errorInfo.RawErrno)); }