|
28 | 28 | using WixSharp; |
29 | 29 | using static DevolutionsGateway.Actions.WinAPI; |
30 | 30 | using File = System.IO.File; |
| 31 | +using StoreLocation = System.Security.Cryptography.X509Certificates.StoreLocation; |
| 32 | +using StoreName = System.Security.Cryptography.X509Certificates.StoreName; |
31 | 33 |
|
32 | 34 | namespace DevolutionsGateway.Actions |
33 | 35 | { |
@@ -1069,6 +1071,80 @@ public static ActionResult SetProgramDataDirectoryPermissions(Session session) |
1069 | 1071 | } |
1070 | 1072 | } |
1071 | 1073 |
|
| 1074 | + [CustomAction] |
| 1075 | + public static ActionResult SetCertificatePrivateKeyPermissions(Session session) |
| 1076 | + { |
| 1077 | + try |
| 1078 | + { |
| 1079 | + // Skip when the gateway will auto-generate a certificate — the selected system-store |
| 1080 | + // cert (if any) isn't actually being used in that case. |
| 1081 | + if (session.Get(GatewayProperties.configureWebApp) && session.Get(GatewayProperties.generateCertificate)) |
| 1082 | + { |
| 1083 | + session.Log("certificate is being auto-generated; skipping private key permission grant"); |
| 1084 | + return ActionResult.Success; |
| 1085 | + } |
| 1086 | + |
| 1087 | + StoreLocation location = session.Get(GatewayProperties.certificateLocation); |
| 1088 | + StoreName storeName = session.Get(GatewayProperties.certificateStore); |
| 1089 | + string subjectName = session.Get(GatewayProperties.certificateName); |
| 1090 | + |
| 1091 | + if (string.IsNullOrWhiteSpace(subjectName)) |
| 1092 | + { |
| 1093 | + session.Log("certificateName is empty; skipping private key permission grant"); |
| 1094 | + return ActionResult.Success; |
| 1095 | + } |
| 1096 | + |
| 1097 | + // Use the same selection logic the Gateway service uses at startup. Fresh installs |
| 1098 | + // initialize gateway.json with tls_verify_strict=true (devolutions-gateway/src/config.rs |
| 1099 | + // generate_new), so we apply the strict filter here too. |
| 1100 | + CertificateSelection.Result selection = CertificateSelection.Select( |
| 1101 | + location, storeName, subjectName, strictMode: true); |
| 1102 | + |
| 1103 | + if (selection.Selected == null) |
| 1104 | + { |
| 1105 | + if (selection.AllFiltered) |
| 1106 | + { |
| 1107 | + session.Log($"found {selection.MatchCount} certificate(s) matching subject {subjectName} in {location}\\{storeName}, but all were filtered out by strict-mode prerequisites (issues: {selection.FilteredReasons})"); |
| 1108 | + } |
| 1109 | + else |
| 1110 | + { |
| 1111 | + session.Log($"no certificate matching subject {subjectName} found in {location}\\{storeName}"); |
| 1112 | + } |
| 1113 | + return ActionResult.Success; |
| 1114 | + } |
| 1115 | + |
| 1116 | + try |
| 1117 | + { |
| 1118 | + X509Certificate2 certificate = selection.Selected; |
| 1119 | + session.Log($"selected certificate {certificate.Thumbprint} (NotAfter={certificate.NotAfter:o}) for subject {subjectName}"); |
| 1120 | + |
| 1121 | + if (PrivateKeyPermissions.HasNetworkServiceReadPermission(certificate)) |
| 1122 | + { |
| 1123 | + session.Log("NETWORK SERVICE already has Read access to the certificate's private key"); |
| 1124 | + return ActionResult.Success; |
| 1125 | + } |
| 1126 | + |
| 1127 | + if (PrivateKeyPermissions.TryGrantNetworkServiceReadPermission(certificate, out Exception grantError)) |
| 1128 | + { |
| 1129 | + session.Log("granted NETWORK SERVICE Read access to the certificate's private key"); |
| 1130 | + return ActionResult.Success; |
| 1131 | + } |
| 1132 | + |
| 1133 | + session.Log($"failed to grant NETWORK SERVICE Read access to the certificate's private key: {grantError}"); |
| 1134 | + } |
| 1135 | + finally |
| 1136 | + { |
| 1137 | + selection.Selected.Dispose(); |
| 1138 | + } |
| 1139 | + } |
| 1140 | + catch (Exception e) |
| 1141 | + { |
| 1142 | + session.Log($"unexpected error setting certificate private key permissions: {e}"); |
| 1143 | + } |
| 1144 | + |
| 1145 | + return ActionResult.Success; |
| 1146 | + } |
| 1147 | + |
1072 | 1148 | [CustomAction] |
1073 | 1149 | public static ActionResult SetUsersDatabaseFilePermissions(Session session) |
1074 | 1150 | { |
|
0 commit comments