| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351 | /* * windows/utils/security.c: implementation of security-api.h. */#include <stdio.h>#include <stdlib.h>#include "putty.h"#include "security-api.h"/* Initialised once, then kept around to reuse forever */static PSID worldsid, networksid, usersid;DEF_WINDOWS_FUNCTION(OpenProcessToken);DEF_WINDOWS_FUNCTION(GetTokenInformation);DEF_WINDOWS_FUNCTION(InitializeSecurityDescriptor);DEF_WINDOWS_FUNCTION(SetSecurityDescriptorOwner);DEF_WINDOWS_FUNCTION(GetSecurityInfo);DEF_WINDOWS_FUNCTION(SetSecurityInfo);DEF_WINDOWS_FUNCTION(SetEntriesInAclA);#ifdef MPEXTvoid win_secur_cleanup(void){    if (usersid)    {        sfree(usersid);        usersid = NULL;    }}#endifbool should_have_security(void){#ifdef LEGACY_WINDOWS    /* Legacy pre-NT platforms are not expected to have any of these APIs */    init_winver();    return (osPlatformId == VER_PLATFORM_WIN32_NT);#else    /* In the up-to-date PuTTY builds which do not support those     * platforms, unconditionally return true, to minimise the risk of     * compiling out security checks. */    return true;#endif}bool got_advapi(void){    static bool attempted = false;    static bool successful;    static HMODULE advapi;    if (!attempted) {        attempted = true;        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, SetEntriesInAclA);    }    return successful;}PSID get_user_sid(void){    HANDLE proc = NULL, tok = NULL;    TOKEN_USER *user = NULL;    DWORD toklen, sidlen;    PSID sid = NULL, ret = NULL;    if (usersid)        return usersid;    if (!got_advapi())        goto cleanup;    if ((proc = OpenProcess(MAXIMUM_ALLOWED, false,                            GetCurrentProcessId())) == NULL)        goto cleanup;    if (!p_OpenProcessToken(proc, TOKEN_QUERY, &tok))        goto cleanup;    if (!p_GetTokenInformation(tok, TokenUser, NULL, 0, &toklen) &&        GetLastError() != ERROR_INSUFFICIENT_BUFFER)        goto cleanup;    if ((user = (TOKEN_USER *)LocalAlloc(LPTR, toklen)) == NULL)        goto cleanup;    if (!p_GetTokenInformation(tok, TokenUser, user, toklen, &toklen))        goto cleanup;    sidlen = GetLengthSid(user->User.Sid);    sid = (PSID)smalloc(sidlen);    if (!CopySid(sidlen, sid, user->User.Sid))        goto cleanup;    /* Success. Move sid into the return value slot, and null it out     * to stop the cleanup code freeing it. */    ret = usersid = sid;    sid = NULL;  cleanup:    if (proc != NULL)        CloseHandle(proc);    if (tok != NULL)        CloseHandle(tok);    if (user != NULL)        LocalFree(user);    if (sid != NULL)        sfree(sid);    return ret;}static bool 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    bool 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;}bool make_private_security_descriptor(DWORD permissions,                                      PSECURITY_DESCRIPTOR *psd,                                      PACL *acl,                                      char **error){    EXPLICIT_ACCESS ea[3];    int acl_err;    bool 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 bool acl_restricted = false;bool restricted_acl(void) { return acl_restricted; }static bool really_restrict_process_acl(char **error){    EXPLICIT_ACCESS ea[2];    int acl_err;    bool 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;    }    acl_restricted = true;    ret=true;  cleanup:    if (!ret) {        if (acl) {            LocalFree(acl);            acl = NULL;        }    }    return ret;}/* * 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;    bool ret;    ret = really_restrict_process_acl(&error);    if (!ret)        modalfatalbox("Could not restrict process ACL: %s", error);}
 |