X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=windows%2Fwinsecur.c;h=76dcae915d86e9398f1e06a7840b9604522869c3;hb=510f49e405e71ba5c97875e7a019364e1ef5fac9;hp=9271af5f91ada069b5afcf17e56056a749b27d78;hpb=f6f78f835543099d4b5e342ba34f91104862803b;p=PuTTY.git diff --git a/windows/winsecur.c b/windows/winsecur.c index 9271af5f..76dcae91 100644 --- a/windows/winsecur.c +++ b/windows/winsecur.c @@ -12,6 +12,10 @@ #define WINSECUR_GLOBAL #include "winsecur.h" +/* Initialised once, then kept around to reuse forever */ +static PSID worldsid, networksid, usersid; + + int got_advapi(void) { static int attempted = FALSE; @@ -23,10 +27,12 @@ int got_advapi(void) advapi = load_system32_dll("advapi32.dll"); successful = advapi && GET_WINDOWS_FUNCTION(advapi, GetSecurityInfo) && + GET_WINDOWS_FUNCTION(advapi, SetSecurityInfo) && GET_WINDOWS_FUNCTION(advapi, OpenProcessToken) && GET_WINDOWS_FUNCTION(advapi, GetTokenInformation) && GET_WINDOWS_FUNCTION(advapi, InitializeSecurityDescriptor) && - GET_WINDOWS_FUNCTION(advapi, SetSecurityDescriptorOwner); + GET_WINDOWS_FUNCTION(advapi, SetSecurityDescriptorOwner) && + GET_WINDOWS_FUNCTION(advapi, SetEntriesInAclA); } return successful; } @@ -38,6 +44,9 @@ PSID get_user_sid(void) DWORD toklen, sidlen; PSID sid = NULL, ret = NULL; + if (usersid) + return usersid; + if (!got_advapi()) goto cleanup; @@ -67,7 +76,7 @@ PSID get_user_sid(void) /* Success. Move sid into the return value slot, and null it out * to stop the cleanup code freeing it. */ - ret = sid; + ret = usersid = sid; sid = NULL; cleanup: @@ -83,4 +92,234 @@ PSID get_user_sid(void) return ret; } +int getsids(char **error) +{ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-braces" +#endif + SID_IDENTIFIER_AUTHORITY world_auth = SECURITY_WORLD_SID_AUTHORITY; + SID_IDENTIFIER_AUTHORITY nt_auth = SECURITY_NT_AUTHORITY; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + + int ret = FALSE; + + *error = NULL; + + if (!usersid) { + if ((usersid = get_user_sid()) == NULL) { + *error = dupprintf("unable to construct SID for current user: %s", + win_strerror(GetLastError())); + goto cleanup; + } + } + + if (!worldsid) { + if (!AllocateAndInitializeSid(&world_auth, 1, SECURITY_WORLD_RID, + 0, 0, 0, 0, 0, 0, 0, &worldsid)) { + *error = dupprintf("unable to construct SID for world: %s", + win_strerror(GetLastError())); + goto cleanup; + } + } + + if (!networksid) { + if (!AllocateAndInitializeSid(&nt_auth, 1, SECURITY_NETWORK_RID, + 0, 0, 0, 0, 0, 0, 0, &networksid)) { + *error = dupprintf("unable to construct SID for " + "local same-user access only: %s", + win_strerror(GetLastError())); + goto cleanup; + } + } + + ret = TRUE; + + cleanup: + return ret; +} + + +int make_private_security_descriptor(DWORD permissions, + PSECURITY_DESCRIPTOR *psd, + PACL *acl, + char **error) +{ + EXPLICIT_ACCESS ea[3]; + int acl_err; + int ret = FALSE; + + + *psd = NULL; + *acl = NULL; + *error = NULL; + + if (!getsids(error)) + goto cleanup; + + memset(ea, 0, sizeof(ea)); + ea[0].grfAccessPermissions = permissions; + ea[0].grfAccessMode = REVOKE_ACCESS; + ea[0].grfInheritance = NO_INHERITANCE; + ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea[0].Trustee.ptstrName = (LPTSTR)worldsid; + ea[1].grfAccessPermissions = permissions; + ea[1].grfAccessMode = GRANT_ACCESS; + ea[1].grfInheritance = NO_INHERITANCE; + ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea[1].Trustee.ptstrName = (LPTSTR)usersid; + ea[2].grfAccessPermissions = permissions; + ea[2].grfAccessMode = REVOKE_ACCESS; + ea[2].grfInheritance = NO_INHERITANCE; + ea[2].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea[2].Trustee.ptstrName = (LPTSTR)networksid; + + acl_err = p_SetEntriesInAclA(3, ea, NULL, acl); + if (acl_err != ERROR_SUCCESS || *acl == NULL) { + *error = dupprintf("unable to construct ACL: %s", + win_strerror(acl_err)); + goto cleanup; + } + + *psd = (PSECURITY_DESCRIPTOR) + LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); + if (!*psd) { + *error = dupprintf("unable to allocate security descriptor: %s", + win_strerror(GetLastError())); + goto cleanup; + } + + if (!InitializeSecurityDescriptor(*psd, SECURITY_DESCRIPTOR_REVISION)) { + *error = dupprintf("unable to initialise security descriptor: %s", + win_strerror(GetLastError())); + goto cleanup; + } + + if (!SetSecurityDescriptorOwner(*psd, usersid, FALSE)) { + *error = dupprintf("unable to set owner in security descriptor: %s", + win_strerror(GetLastError())); + goto cleanup; + } + + if (!SetSecurityDescriptorDacl(*psd, TRUE, *acl, FALSE)) { + *error = dupprintf("unable to set DACL in security descriptor: %s", + win_strerror(GetLastError())); + goto cleanup; + } + + ret = TRUE; + + cleanup: + if (!ret) { + if (*psd) { + LocalFree(*psd); + *psd = NULL; + } + if (*acl) { + LocalFree(*acl); + *acl = NULL; + } + } else { + sfree(*error); + *error = NULL; + } + return ret; +} + +static int really_restrict_process_acl(char **error) +{ + EXPLICIT_ACCESS ea[2]; + int acl_err; + int ret=FALSE; + PACL acl = NULL; + + static const DWORD nastyace=WRITE_DAC | WRITE_OWNER | + PROCESS_CREATE_PROCESS | PROCESS_CREATE_THREAD | + PROCESS_DUP_HANDLE | + PROCESS_SET_QUOTA | PROCESS_SET_INFORMATION | + PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE | + PROCESS_SUSPEND_RESUME; + + if (!getsids(error)) + goto cleanup; + + memset(ea, 0, sizeof(ea)); + + /* Everyone: deny */ + ea[0].grfAccessPermissions = nastyace; + ea[0].grfAccessMode = DENY_ACCESS; + ea[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; + ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea[0].Trustee.ptstrName = (LPTSTR)worldsid; + + /* User: user ace */ + ea[1].grfAccessPermissions = ~nastyace & 0x1fff; + ea[1].grfAccessMode = GRANT_ACCESS; + ea[1].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; + ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea[1].Trustee.ptstrName = (LPTSTR)usersid; + + acl_err = p_SetEntriesInAclA(2, ea, NULL, &acl); + + if (acl_err != ERROR_SUCCESS || acl == NULL) { + *error = dupprintf("unable to construct ACL: %s", + win_strerror(acl_err)); + goto cleanup; + } + + if (ERROR_SUCCESS != p_SetSecurityInfo + (GetCurrentProcess(), SE_KERNEL_OBJECT, + OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, + usersid, NULL, acl, NULL)) { + *error = dupprintf("Unable to set process ACL: %s", + win_strerror(GetLastError())); + goto cleanup; + } + + + ret=TRUE; + + cleanup: + if (!ret) { + if (acl) { + LocalFree(acl); + acl = NULL; + } + } + return ret; +} #endif /* !defined NO_SECURITY */ + +/* + * Lock down our process's ACL, to present an obstacle to malware + * trying to write into its memory. This can't be a full defence, + * because well timed malware could attack us before this code runs - + * even if it was unconditionally run at the very start of main(), + * which we wouldn't want to do anyway because it turns out in practie + * that interfering with other processes in this way has significant + * non-infringing uses on Windows (e.g. screen reader software). + * + * If we've been requested to do this and are unsuccessful, bomb out + * via modalfatalbox rather than continue in a less protected mode. + * + * This function is intentionally outside the #ifndef NO_SECURITY that + * covers the rest of this file, because when PuTTY is compiled + * without the ability to restrict its ACL, we don't want it to + * silently pretend to honour the instruction to do so. + */ +void restrict_process_acl(void) +{ + char *error = NULL; + int ret; + +#if !defined NO_SECURITY + ret = really_restrict_process_acl(&error); +#else + ret = FALSE; + error = dupstr("ACL restrictions not compiled into this binary"); +#endif + if (!ret) + modalfatalbox("Could not restrict process ACL: %s", error); +}