MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4#pragma warning disable 1634, 16915 6using System.Diagnostics.CodeAnalysis;7using System.Net;8using System.Security;9using System.Runtime.Serialization;10using System.Security.Cryptography;11using Microsoft.PowerShell;12 13namespace System.Management.Automation14{15 /// <summary>16 /// Defines the valid types of PSCredentials. Used by PromptForCredential calls.17 /// </summary>18 [Flags]19 public enum PSCredentialTypes20 {21 /// <summary>22 /// Generic credentials.23 /// </summary>24 Generic = 1,25 26 /// <summary>27 /// Credentials valid for a domain.28 /// </summary>29 Domain = 2,30 31 /// <summary>32 /// Default credentials.33 /// </summary>34 Default = Generic | Domain35 }36 37 /// <summary>38 /// Defines the options available when prompting for credentials. Used39 /// by PromptForCredential calls.40 /// </summary>41 [Flags]42 public enum PSCredentialUIOptions43 {44 /// <summary>45 /// Validates the username, but not its existence46 /// or correctness.47 /// </summary>48 Default = ValidateUserNameSyntax,49 50 /// <summary>51 /// Performs no validation.52 /// </summary>53 None = 0,54 55 /// <summary>56 /// Validates the username, but not its existence.57 /// or correctness.58 /// </summary>59 ValidateUserNameSyntax,60 61 /// <summary>62 /// Always prompt, even if a persisted credential was available.63 /// </summary>64 AlwaysPrompt,65 66 /// <summary>67 /// Username is read-only, and the user may not modify it.68 /// </summary>69 ReadOnlyUserName70 }71 72 /// <summary>73 /// Declare a delegate which returns the encryption key and initialization vector for symmetric encryption algorithm.74 /// </summary>75 /// <param name="context">The streaming context, which contains the serialization context.</param>76 /// <param name="key">Symmetric encryption key.</param>77 /// <param name="iv">Symmetric encryption initialization vector.</param>78 /// <returns></returns>79 public delegate bool GetSymmetricEncryptionKey(StreamingContext context, out byte[] key, out byte[] iv);80 81 /// <summary>82 /// Offers a centralized way to manage usernames, passwords, and83 /// credentials.84 /// </summary>85 [Serializable]86 public sealed class PSCredential : ISerializable87 {88 /// <summary>89 /// Gets or sets a delegate which returns the encryption key and initialization vector for symmetric encryption algorithm.90 /// </summary>91 public static GetSymmetricEncryptionKey GetSymmetricEncryptionKeyDelegate92 {93 get94 {95 return s_delegate;96 }97 98 set99 {100 s_delegate = value;101 }102 }103 104 private static GetSymmetricEncryptionKey s_delegate = null;105 106 /// <summary>107 /// GetObjectData.108 /// </summary>109 /// <param name="info"></param>110 /// <param name="context"></param>111 public void GetObjectData(SerializationInfo info, StreamingContext context)112 {113 if (info == null)114 return;115 116 // serialize the secure string117 string safePassword = string.Empty;118 119 if (_password != null && _password.Length > 0)120 {121 byte[] key;122 byte[] iv;123 if (s_delegate != null && s_delegate(context, out key, out iv))124 {125 safePassword = SecureStringHelper.Encrypt(_password, key, iv).EncryptedData;126 }127 else128 {129 try130 {131 safePassword = SecureStringHelper.Protect(_password);132 }133 catch (CryptographicException cryptographicException)134 {135 throw PSTraceSource.NewInvalidOperationException(cryptographicException, Credential.CredentialDisallowed);136 }137 }138 }139 140 info.AddValue("UserName", _userName);141 info.AddValue("Password", safePassword);142 }143 144 /// <summary>145 /// PSCredential.146 /// </summary>147 /// <param name="info"></param>148 /// <param name="context"></param>149 private PSCredential(SerializationInfo info, StreamingContext context)150 {151 if (info == null)152 return;153 154 _userName = (string)info.GetValue("UserName", typeof(string));155 156 // deserialize to secure string157 string safePassword = (string)info.GetValue("Password", typeof(string));158 if (safePassword == string.Empty)159 {160 _password = new SecureString();161 }162 else163 {164 byte[] key;165 byte[] iv;166 if (s_delegate != null && s_delegate(context, out key, out iv))167 {168 _password = SecureStringHelper.Decrypt(safePassword, key, iv);169 }170 else171 {172 _password = SecureStringHelper.Unprotect(safePassword);173 }174 }175 }176 177 private readonly string _userName;178 private readonly SecureString _password;179 180 /// <summary>181 /// User's name.182 /// </summary>183 public string UserName184 {185 get { return _userName; }186 }187 188 /// <summary>189 /// User's password.190 /// </summary>191 public SecureString Password192 {193 get { return _password; }194 }195 196 /// <summary>197 /// Initializes a new instance of the PSCredential class with a198 /// username and password.199 /// </summary>200 /// <param name="userName">User's name.</param>201 /// <param name="password">User's password.</param>202 public PSCredential(string userName, SecureString password)203 {204 Utils.CheckArgForNullOrEmpty(userName, "userName");205 Utils.CheckArgForNull(password, "password");206 207 _userName = userName;208 _password = password;209 }210 211 /// <summary>212 /// Initializes a new instance of the PSCredential class with a213 /// username and password from PSObject.214 /// </summary>215 /// <param name="pso"></param>216 public PSCredential(PSObject pso)217 {218 if (pso == null)219 throw PSTraceSource.NewArgumentNullException(nameof(pso));220 221 if (pso.Properties["UserName"] != null)222 {223 _userName = (string)pso.Properties["UserName"].Value;224 225 if (pso.Properties["Password"] != null)226 _password = (SecureString)pso.Properties["Password"].Value;227 }228 }229 230 /// <summary>231 /// Initializes a new instance of the PSCredential class.232 /// </summary>233 private PSCredential()234 {235 }236 237 private NetworkCredential _netCred;238 239 /// <summary>240 /// Returns an equivalent NetworkCredential object for this241 /// PSCredential.242 ///243 /// A null is returned if244 /// -- current object has not been initialized245 /// -- current creds are not compatible with NetworkCredential246 /// (such as smart card creds or cert creds)247 /// </summary>248 /// <returns>249 /// null if the current object has not been initialized.250 /// null if the current credentials are incompatible with251 /// a NetworkCredential -- such as smart card credentials.252 /// the appropriate network credential for this PSCredential otherwise.253 /// </returns>254 public NetworkCredential GetNetworkCredential()255 {256 if (_netCred == null)257 {258 string user = null;259 string domain = null;260 261 if (IsValidUserName(_userName, out user, out domain))262 {263 _netCred = new NetworkCredential(user, _password, domain);264 }265 }266 267 return _netCred;268 }269 270 /// <summary>271 /// Provides an explicit cast to get a NetworkCredential272 /// from this PSCredential.273 /// </summary>274 /// <param name="credential">PSCredential to convert.</param>275 /// <returns>276 /// null if the current object has not been initialized.277 /// null if the current credentials are incompatible with278 /// a NetworkCredential -- such as smart card credentials.279 /// the appropriate network credential for this PSCredential otherwise.280 /// </returns>281 public static explicit operator NetworkCredential(PSCredential credential)282 {283#pragma warning disable 56506284 285 if (credential == null)286 {287 throw PSTraceSource.NewArgumentNullException("credential");288 }289 290 return credential.GetNetworkCredential();291 292#pragma warning restore 56506293 }294 295 /// <summary>296 /// Gets an empty PSCredential. This is an PSCredential with both UserName297 /// and Password initialized to null.298 /// </summary>299 public static PSCredential Empty300 {301 get302 {303 return s_empty;304 }305 }306 307 private static readonly PSCredential s_empty = new PSCredential();308 309 /// <summary>310 /// Parse a string that represents a fully qualified username311 /// to verify that it is syntactically valid. We only support312 /// two formats:313 /// -- domain\user314 /// -- user@domain315 ///316 /// for any other format, we simply treat the entire string317 /// as user name and set domain name to "".318 /// </summary>319 private static bool IsValidUserName(string input,320 out string user,321 out string domain)322 {323 if (string.IsNullOrEmpty(input))324 {325 user = domain = null;326 return false;327 }328 329 SplitUserDomain(input, out user, out domain);330 331 if ((user == null) ||332 (domain == null) ||333 (user.Length == 0))334 {335 // UserName is the public property of Credential object. Use this as336 // parameter name in error337 // See bug NTRAID#Windows OS Bugs-1106386-2005/03/25-hiteshr338 throw PSTraceSource.NewArgumentException("UserName", Credential.InvalidUserNameFormat);339 }340 341 return true;342 }343 344 /// <summary>345 /// Split a given string into its user and domain346 /// components. Supported formats are:347 /// -- domain\user348 /// -- user@domain349 ///350 /// With any other format, the entire input is treated as user351 /// name and domain is set to "".352 ///353 /// In any case, the function does not check if the split string354 /// are really valid as user or domain names.355 /// </summary>356 private static void SplitUserDomain(string input,357 out string user,358 out string domain)359 {360 int i = 0;361 user = null;362 domain = null;363 364 if ((i = input.IndexOf('\\')) >= 0)365 {366 user = input.Substring(i + 1);367 domain = input.Substring(0, i);368 return;369 }370 371 // In V1 and V2, we had a bug where email addresses (i.e. foo@bar.com)372 // were being split into Username=Foo, Domain=bar.com.373 //374 // This was breaking apps (i.e.: Exchange), so we need to make375 // Username = foo@bar.com if the domain has a dot in it (since376 // domains can't have dots).377 //378 // HOWEVER, there was a workaround for this bug in v1 and v2, where the379 // cred could be entered as "foo@bar.com@bar.com" - making:380 // Username = foo@bar.com, Domain = bar.com381 //382 // We need to keep the behaviour in this case.383 384 i = input.LastIndexOf('@');385 386 if (387 (i >= 0) &&388 (389 (input.LastIndexOf('.') < i) ||390 (input.IndexOf('@') != i)391 )392 )393 {394 domain = input.Substring(i + 1);395 user = input.Substring(0, i);396 }397 else398 {399 user = input;400 domain = string.Empty;401 }402 }403 }404}405 