winsecur.c 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. /*
  2. * winsecur.c: implementation of winsecur.h.
  3. */
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include "putty.h"
  7. #if !defined NO_SECURITY
  8. #include "winsecur.h"
  9. /* Initialised once, then kept around to reuse forever */
  10. static PSID worldsid, networksid, usersid;
  11. DEF_WINDOWS_FUNCTION(OpenProcessToken);
  12. DEF_WINDOWS_FUNCTION(GetTokenInformation);
  13. DEF_WINDOWS_FUNCTION(InitializeSecurityDescriptor);
  14. DEF_WINDOWS_FUNCTION(SetSecurityDescriptorOwner);
  15. DEF_WINDOWS_FUNCTION(GetSecurityInfo);
  16. DEF_WINDOWS_FUNCTION(SetSecurityInfo);
  17. DEF_WINDOWS_FUNCTION(SetEntriesInAclA);
  18. bool got_advapi(void)
  19. {
  20. static bool attempted = false;
  21. static bool successful;
  22. static HMODULE advapi;
  23. if (!attempted) {
  24. attempted = true;
  25. advapi = load_system32_dll("advapi32.dll");
  26. successful = advapi &&
  27. GET_WINDOWS_FUNCTION(advapi, GetSecurityInfo) &&
  28. GET_WINDOWS_FUNCTION(advapi, SetSecurityInfo) &&
  29. GET_WINDOWS_FUNCTION(advapi, OpenProcessToken) &&
  30. GET_WINDOWS_FUNCTION(advapi, GetTokenInformation) &&
  31. GET_WINDOWS_FUNCTION(advapi, InitializeSecurityDescriptor) &&
  32. GET_WINDOWS_FUNCTION(advapi, SetSecurityDescriptorOwner) &&
  33. GET_WINDOWS_FUNCTION(advapi, SetEntriesInAclA);
  34. }
  35. return successful;
  36. }
  37. PSID get_user_sid(void)
  38. {
  39. HANDLE proc = NULL, tok = NULL;
  40. TOKEN_USER *user = NULL;
  41. DWORD toklen, sidlen;
  42. PSID sid = NULL, ret = NULL;
  43. if (usersid)
  44. return usersid;
  45. if (!got_advapi())
  46. goto cleanup;
  47. if ((proc = OpenProcess(MAXIMUM_ALLOWED, false,
  48. GetCurrentProcessId())) == NULL)
  49. goto cleanup;
  50. if (!p_OpenProcessToken(proc, TOKEN_QUERY, &tok))
  51. goto cleanup;
  52. if (!p_GetTokenInformation(tok, TokenUser, NULL, 0, &toklen) &&
  53. GetLastError() != ERROR_INSUFFICIENT_BUFFER)
  54. goto cleanup;
  55. if ((user = (TOKEN_USER *)LocalAlloc(LPTR, toklen)) == NULL)
  56. goto cleanup;
  57. if (!p_GetTokenInformation(tok, TokenUser, user, toklen, &toklen))
  58. goto cleanup;
  59. sidlen = GetLengthSid(user->User.Sid);
  60. sid = (PSID)smalloc(sidlen);
  61. if (!CopySid(sidlen, sid, user->User.Sid))
  62. goto cleanup;
  63. /* Success. Move sid into the return value slot, and null it out
  64. * to stop the cleanup code freeing it. */
  65. ret = usersid = sid;
  66. sid = NULL;
  67. cleanup:
  68. if (proc != NULL)
  69. CloseHandle(proc);
  70. if (tok != NULL)
  71. CloseHandle(tok);
  72. if (user != NULL)
  73. LocalFree(user);
  74. if (sid != NULL)
  75. sfree(sid);
  76. return ret;
  77. }
  78. static bool getsids(char **error)
  79. {
  80. #ifdef __clang__
  81. #pragma clang diagnostic push
  82. #pragma clang diagnostic ignored "-Wmissing-braces"
  83. #endif
  84. SID_IDENTIFIER_AUTHORITY world_auth = SECURITY_WORLD_SID_AUTHORITY;
  85. SID_IDENTIFIER_AUTHORITY nt_auth = SECURITY_NT_AUTHORITY;
  86. #ifdef __clang__
  87. #pragma clang diagnostic pop
  88. #endif
  89. bool ret = false;
  90. *error = NULL;
  91. if (!usersid) {
  92. if ((usersid = get_user_sid()) == NULL) {
  93. *error = dupprintf("unable to construct SID for current user: %s",
  94. win_strerror(GetLastError()));
  95. goto cleanup;
  96. }
  97. }
  98. if (!worldsid) {
  99. if (!AllocateAndInitializeSid(&world_auth, 1, SECURITY_WORLD_RID,
  100. 0, 0, 0, 0, 0, 0, 0, &worldsid)) {
  101. *error = dupprintf("unable to construct SID for world: %s",
  102. win_strerror(GetLastError()));
  103. goto cleanup;
  104. }
  105. }
  106. if (!networksid) {
  107. if (!AllocateAndInitializeSid(&nt_auth, 1, SECURITY_NETWORK_RID,
  108. 0, 0, 0, 0, 0, 0, 0, &networksid)) {
  109. *error = dupprintf("unable to construct SID for "
  110. "local same-user access only: %s",
  111. win_strerror(GetLastError()));
  112. goto cleanup;
  113. }
  114. }
  115. ret = true;
  116. cleanup:
  117. return ret;
  118. }
  119. bool make_private_security_descriptor(DWORD permissions,
  120. PSECURITY_DESCRIPTOR *psd,
  121. PACL *acl,
  122. char **error)
  123. {
  124. EXPLICIT_ACCESS ea[3];
  125. int acl_err;
  126. bool ret = false;
  127. *psd = NULL;
  128. *acl = NULL;
  129. *error = NULL;
  130. if (!getsids(error))
  131. goto cleanup;
  132. memset(ea, 0, sizeof(ea));
  133. ea[0].grfAccessPermissions = permissions;
  134. ea[0].grfAccessMode = REVOKE_ACCESS;
  135. ea[0].grfInheritance = NO_INHERITANCE;
  136. ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
  137. ea[0].Trustee.ptstrName = (LPTSTR)worldsid;
  138. ea[1].grfAccessPermissions = permissions;
  139. ea[1].grfAccessMode = GRANT_ACCESS;
  140. ea[1].grfInheritance = NO_INHERITANCE;
  141. ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
  142. ea[1].Trustee.ptstrName = (LPTSTR)usersid;
  143. ea[2].grfAccessPermissions = permissions;
  144. ea[2].grfAccessMode = REVOKE_ACCESS;
  145. ea[2].grfInheritance = NO_INHERITANCE;
  146. ea[2].Trustee.TrusteeForm = TRUSTEE_IS_SID;
  147. ea[2].Trustee.ptstrName = (LPTSTR)networksid;
  148. acl_err = p_SetEntriesInAclA(3, ea, NULL, acl);
  149. if (acl_err != ERROR_SUCCESS || *acl == NULL) {
  150. *error = dupprintf("unable to construct ACL: %s",
  151. win_strerror(acl_err));
  152. goto cleanup;
  153. }
  154. *psd = (PSECURITY_DESCRIPTOR)
  155. LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
  156. if (!*psd) {
  157. *error = dupprintf("unable to allocate security descriptor: %s",
  158. win_strerror(GetLastError()));
  159. goto cleanup;
  160. }
  161. if (!InitializeSecurityDescriptor(*psd, SECURITY_DESCRIPTOR_REVISION)) {
  162. *error = dupprintf("unable to initialise security descriptor: %s",
  163. win_strerror(GetLastError()));
  164. goto cleanup;
  165. }
  166. if (!SetSecurityDescriptorOwner(*psd, usersid, false)) {
  167. *error = dupprintf("unable to set owner in security descriptor: %s",
  168. win_strerror(GetLastError()));
  169. goto cleanup;
  170. }
  171. if (!SetSecurityDescriptorDacl(*psd, true, *acl, false)) {
  172. *error = dupprintf("unable to set DACL in security descriptor: %s",
  173. win_strerror(GetLastError()));
  174. goto cleanup;
  175. }
  176. ret = true;
  177. cleanup:
  178. if (!ret) {
  179. if (*psd) {
  180. LocalFree(*psd);
  181. *psd = NULL;
  182. }
  183. if (*acl) {
  184. LocalFree(*acl);
  185. *acl = NULL;
  186. }
  187. } else {
  188. sfree(*error);
  189. *error = NULL;
  190. }
  191. return ret;
  192. }
  193. static bool acl_restricted = false;
  194. bool restricted_acl(void) { return acl_restricted; }
  195. static bool really_restrict_process_acl(char **error)
  196. {
  197. EXPLICIT_ACCESS ea[2];
  198. int acl_err;
  199. bool ret = false;
  200. PACL acl = NULL;
  201. static const DWORD nastyace=WRITE_DAC | WRITE_OWNER |
  202. PROCESS_CREATE_PROCESS | PROCESS_CREATE_THREAD |
  203. PROCESS_DUP_HANDLE |
  204. PROCESS_SET_QUOTA | PROCESS_SET_INFORMATION |
  205. PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE |
  206. PROCESS_SUSPEND_RESUME;
  207. if (!getsids(error))
  208. goto cleanup;
  209. memset(ea, 0, sizeof(ea));
  210. /* Everyone: deny */
  211. ea[0].grfAccessPermissions = nastyace;
  212. ea[0].grfAccessMode = DENY_ACCESS;
  213. ea[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
  214. ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
  215. ea[0].Trustee.ptstrName = (LPTSTR)worldsid;
  216. /* User: user ace */
  217. ea[1].grfAccessPermissions = ~nastyace & 0x1fff;
  218. ea[1].grfAccessMode = GRANT_ACCESS;
  219. ea[1].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
  220. ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
  221. ea[1].Trustee.ptstrName = (LPTSTR)usersid;
  222. acl_err = p_SetEntriesInAclA(2, ea, NULL, &acl);
  223. if (acl_err != ERROR_SUCCESS || acl == NULL) {
  224. *error = dupprintf("unable to construct ACL: %s",
  225. win_strerror(acl_err));
  226. goto cleanup;
  227. }
  228. if (ERROR_SUCCESS != p_SetSecurityInfo
  229. (GetCurrentProcess(), SE_KERNEL_OBJECT,
  230. OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
  231. usersid, NULL, acl, NULL)) {
  232. *error = dupprintf("Unable to set process ACL: %s",
  233. win_strerror(GetLastError()));
  234. goto cleanup;
  235. }
  236. acl_restricted = true;
  237. ret=true;
  238. cleanup:
  239. if (!ret) {
  240. if (acl) {
  241. LocalFree(acl);
  242. acl = NULL;
  243. }
  244. }
  245. return ret;
  246. }
  247. #endif /* !defined NO_SECURITY */
  248. /*
  249. * Lock down our process's ACL, to present an obstacle to malware
  250. * trying to write into its memory. This can't be a full defence,
  251. * because well timed malware could attack us before this code runs -
  252. * even if it was unconditionally run at the very start of main(),
  253. * which we wouldn't want to do anyway because it turns out in practie
  254. * that interfering with other processes in this way has significant
  255. * non-infringing uses on Windows (e.g. screen reader software).
  256. *
  257. * If we've been requested to do this and are unsuccessful, bomb out
  258. * via modalfatalbox rather than continue in a less protected mode.
  259. *
  260. * This function is intentionally outside the #ifndef NO_SECURITY that
  261. * covers the rest of this file, because when PuTTY is compiled
  262. * without the ability to restrict its ACL, we don't want it to
  263. * silently pretend to honour the instruction to do so.
  264. */
  265. void restrict_process_acl(void)
  266. {
  267. char *error = NULL;
  268. bool ret;
  269. #if !defined NO_SECURITY
  270. ret = really_restrict_process_acl(&error);
  271. #else
  272. ret = false;
  273. error = dupstr("ACL restrictions not compiled into this binary");
  274. #endif
  275. if (!ret)
  276. modalfatalbox("Could not restrict process ACL: %s", error);
  277. }