Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

321 lines
12 KiB

  1. //#pragma title( "PwGen.cpp - PasswordGenerate implementation" )
  2. /*
  3. Copyright (c) 1995-1998, Mission Critical Software, Inc. All rights reserved.
  4. ===============================================================================
  5. Module - PwGen.cpp
  6. System - EnterpriseAdministrator
  7. Author - Steven Bailey, Marcus Erickson
  8. Created - 1997-05-30
  9. Description - PasswordGenerate implementation
  10. Updates -
  11. ===============================================================================
  12. */
  13. #include <windows.h>
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <time.h>
  17. #include <WinCrypt.h>
  18. #include "Common.hpp"
  19. #include "Err.hpp"
  20. #include "UString.hpp"
  21. #include "pwgen.hpp"
  22. DWORD __stdcall GenerateRandom(DWORD dwCount, BYTE* pbRandomType, BYTE* pbRandomChar);
  23. /////////////////////////////////////////////////////////////////////////////////////////////////////////
  24. // Generate a password from the rules provided.
  25. // Returns ERROR_SUCCESS if successful, else ERROR_INVALID_PARAMETER.
  26. // If successful, the new password is returned in the supplied buffer.
  27. // The buffer must be long enough to hold the minimum length password
  28. // that is required by the rules, plus a terminating NULL.
  29. DWORD __stdcall // ret-EA/OS return code
  30. EaPasswordGenerate(
  31. DWORD dwMinUC, // in -minimum upper case chars
  32. DWORD dwMinLC, // in -minimum lower case chars
  33. DWORD dwMinDigits, // in -minimum numeric digits
  34. DWORD dwMinSpecial, // in -minimum special chars
  35. DWORD dwMaxConsecutiveAlpha,// in -maximum consecutive alpha chars
  36. DWORD dwMinLength, // in -minimum length
  37. WCHAR * newPassword, // out-returned password
  38. DWORD dwBufferLength // in -returned area buffer length
  39. )
  40. {
  41. DWORD dwMaxLength = PWGEN_MAX_LENGTH;
  42. DWORD dwNewLength; // actual length of new password
  43. DWORD dwUC = dwMinUC; // actual numbers of these characters
  44. DWORD dwLC = dwMinLC;
  45. DWORD dwDigits = dwMinDigits;
  46. DWORD dwSpecial = dwMinSpecial;
  47. DWORD dwActualLength = dwUC + dwLC + dwDigits + dwSpecial; // total length specified by the minima
  48. TCHAR pszNewPassword[PWGEN_MAX_LENGTH+1]; // out-returned password
  49. BYTE bRandomType[PWGEN_MAX_LENGTH]; // cryptographically generated random bytes for type
  50. BYTE bRandomChar[PWGEN_MAX_LENGTH]; // cryptographically generated random bytes for character
  51. const TCHAR *szSourceString[4] = { // the lists of characters by type
  52. { TEXT("ABDEFGHJKLMNQRTY") },
  53. { TEXT("abcdefghkmnpqrstuvwxyz") },
  54. { TEXT("23456789") },
  55. { TEXT("~!@#$%^+=") }
  56. };
  57. DWORD dwToPlace[4]; // number of characters of a type to place
  58. int iType[4]; // type of each character class
  59. int iTypes; // total number of types
  60. enum { // types of chars
  61. eUC = 0,
  62. eLC,
  63. eDigit,
  64. eSpecial
  65. };
  66. DWORD err;
  67. // Sanity checking
  68. // Does the minimum passed to us exceed the maximum?
  69. if (dwMinLength > dwMaxLength)
  70. return ERROR_INVALID_PARAMETER;
  71. // Adjust the minimum length
  72. dwMinLength = max(dwMinLength, dwMinUC + dwMinLC + dwMinDigits + dwMinSpecial);
  73. dwMinLength = max(dwMinLength, PWGEN_MIN_LENGTH);
  74. // Do the minimum requirements make the password too long?
  75. if ((dwMinUC + dwMinLC + dwMinDigits + dwMinSpecial) > dwMaxLength)
  76. return ERROR_INVALID_PARAMETER;
  77. // Adjust maximum length to size of buffer.
  78. dwMaxLength = min(dwMaxLength, dwBufferLength - 1);
  79. // Do the min LC and UC characters make it impossible to satisfy the maximum consecutive alpha characters?
  80. if (dwMaxConsecutiveAlpha) {
  81. if (dwMaxLength - dwMaxLength / (dwMaxConsecutiveAlpha + 1) < (dwMinUC + dwMinLC))
  82. return ERROR_INVALID_PARAMETER;
  83. }
  84. // Adjust the minimum length to accomodate the rules about max consecutive alphas.
  85. if (dwMaxConsecutiveAlpha) {
  86. DWORD dwTotalAlpha = dwUC + dwLC;
  87. if (dwTotalAlpha) {
  88. DWORD dwMinGroups = dwTotalAlpha / dwMaxConsecutiveAlpha; // we need at least this minus one separators
  89. if (dwTotalAlpha % dwMaxConsecutiveAlpha)
  90. ++dwMinGroups;
  91. dwMinLength = max(dwMinLength, dwTotalAlpha + dwMinGroups - 1);
  92. }
  93. }
  94. // Check confirmed min length against maximum length.
  95. if (dwMinLength > dwMaxLength)
  96. return ERROR_INVALID_PARAMETER;
  97. // Seed the random-number generator with current time so that
  98. // the numbers will be different every time we run.
  99. #ifndef _DEBUG
  100. // Note for debugging: If this is run in a tight loop, the tick count
  101. // won't be incrementing between calls, so the same password will generate
  102. // repeatedly. That doesn't help you test anything.
  103. srand( (int)GetTickCount() );
  104. #endif
  105. // Determine the actual length of new password.
  106. dwNewLength = dwMinLength;
  107. // Adjust max consecutive alpha
  108. if (dwMaxConsecutiveAlpha == 0)
  109. dwMaxConsecutiveAlpha = dwNewLength;
  110. // Determine the actual numbers of each type of character.
  111. if (dwActualLength < dwNewLength) {
  112. // Try to pad with alphabetic characters.
  113. // Determine the maximum number of alpha characters that could be added.
  114. int iAddAlpha = (int)(dwNewLength - dwNewLength / (dwMaxConsecutiveAlpha + 1) - (dwUC + dwLC));
  115. // It cannot exceed the number of characters we need.
  116. if ((DWORD)iAddAlpha > (dwNewLength - dwActualLength))
  117. iAddAlpha = (int)(dwNewLength - dwActualLength);
  118. dwLC += (DWORD)iAddAlpha;
  119. dwActualLength += (DWORD)iAddAlpha;
  120. }
  121. // Make certain there are enough groups.
  122. if (dwActualLength < dwNewLength)
  123. // The padding is separators.
  124. dwDigits += dwNewLength - dwActualLength;
  125. // Prepare to generate the characters.
  126. dwToPlace[0] = dwUC;
  127. dwToPlace[1] = dwLC;
  128. dwToPlace[2] = dwDigits;
  129. dwToPlace[3] = dwSpecial;
  130. iType[0] = eUC;
  131. iType[1] = eLC;
  132. iType[2] = eDigit;
  133. iType[3] = eSpecial;
  134. iTypes = 4;
  135. for (int iPos = 0; iPos < iTypes; ) {
  136. if (!dwToPlace[iPos]) {
  137. for (int iNextPos = iPos + 1; iNextPos < iTypes; ++iNextPos) {
  138. dwToPlace[iNextPos - 1] = dwToPlace[iNextPos];
  139. iType[iNextPos - 1] = iType[iNextPos];
  140. }
  141. --iTypes;
  142. }
  143. else
  144. ++iPos;
  145. }
  146. // Result: dwToPlace[0..iTypes - 1] contain all non-zero values;
  147. // iType[0..iTypes - 1] contain the type of character they represent.
  148. // generate cryptographically random bytes
  149. // for choosing both the character type and character
  150. err = GenerateRandom(dwNewLength, bRandomType, bRandomChar);
  151. if (err != ERROR_SUCCESS)
  152. {
  153. return err;
  154. }
  155. // Generate a string.
  156. DWORD dwConsecAlpha = 0;
  157. int iRemainingAlpha = (int)(dwUC + dwLC);
  158. int iTypeList[PWGEN_MAX_LENGTH]; // A distributed list of types.
  159. for (int iNewChar = 0; (DWORD)iNewChar < dwNewLength; ++iNewChar) {
  160. // Determine whether the next char must be alpha or must not be alpha.
  161. BOOL bMustBeAlpha = FALSE;
  162. BOOL bMustNotBeAlpha = dwConsecAlpha == dwMaxConsecutiveAlpha;
  163. // If it can be alpha, determine whether it HAS to be alpha.
  164. if (!bMustNotBeAlpha) {
  165. // If, among the remaining chars after this one, it would be impossible to
  166. // fit the remaining alpha chars due to constraints of dwMaxConsecutiveAlpha,
  167. // then this character must be alpha.
  168. // Determine the minimum number of groups if we put remaining alpha chars
  169. // into groups that are the maximum width.
  170. int iMinGroups = iRemainingAlpha / (int)dwMaxConsecutiveAlpha;
  171. if (iRemainingAlpha % (int)dwMaxConsecutiveAlpha)
  172. ++iMinGroups;
  173. // Determine the minimum number of non-alpha characters we'll need.
  174. int iMinNonAlpha = iMinGroups - 1;
  175. // Determine the characters remaining.
  176. int iRemaining = (int)dwNewLength - iNewChar;
  177. // Is there room for a non-alpha char here?
  178. if (iRemaining <= (iRemainingAlpha + iMinNonAlpha))
  179. // no.
  180. bMustBeAlpha = TRUE;
  181. }
  182. // Determine the type range.
  183. int iMinType = 0;
  184. int iMaxType = iTypes - 1;
  185. // If next char must be alpha, then alpha chars remain.
  186. // Type position 0 contains either UC or LC.
  187. // Type position 1 contains LC, non-alpha, or nothing.
  188. if (bMustBeAlpha) {
  189. if ((iType[1] == eLC) && (iTypes > 1))
  190. iMaxType = 1;
  191. else
  192. iMaxType = 0;
  193. }
  194. // If next char may not be alpha, there may be no alpha left to generate.
  195. // If so, type position 0 is non-alpha.
  196. // O.w., type positions 0 and 1 may both be alpha.
  197. else if (bMustNotBeAlpha) {
  198. if (iRemainingAlpha) {
  199. if (iType[1] >= eDigit)
  200. iMinType = 1;
  201. else
  202. iMinType = 2;
  203. }
  204. }
  205. // Get the type to generate.
  206. int iTypePosition;
  207. int iTypeToGenerate;
  208. const TCHAR *pszSourceString;
  209. if (iMinType == iMaxType) // There's only one type. Use it.
  210. iTypePosition = iMinType;
  211. else {
  212. // This algorithm distributes the chances for various types.
  213. // If there are 13 LCs to place and one special, there's a
  214. // 13/14 chance of placing an LC and a 1/14 chance of placing a
  215. // special, due to this algorithm.
  216. int iNextTypePosition = 0;
  217. for (int i = iMinType; i <= iMaxType; ++i) {
  218. for (int j = 0; j < (int)dwToPlace[i]; ++j) {
  219. iTypeList[iNextTypePosition++] = i;
  220. }
  221. }
  222. iTypePosition = iTypeList[bRandomType[iNewChar] % iNextTypePosition];
  223. }
  224. iTypeToGenerate = iType[iTypePosition];
  225. pszSourceString = szSourceString[iTypeToGenerate];
  226. // Generate the next character.
  227. pszNewPassword[iNewChar] = pszSourceString[bRandomChar[iNewChar] % UStrLen(pszSourceString)];
  228. // Keep track of those alphas.
  229. if (iTypeToGenerate < eDigit) {
  230. ++dwConsecAlpha;
  231. --iRemainingAlpha;
  232. }
  233. else
  234. dwConsecAlpha = 0;
  235. // Update the types to generate.
  236. if (!--dwToPlace[iTypePosition]) {
  237. for (int iNextTypePosition = iTypePosition + 1; iNextTypePosition < iTypes; ++iNextTypePosition) {
  238. dwToPlace[iNextTypePosition - 1] = dwToPlace[iNextTypePosition];
  239. iType[iNextTypePosition - 1] = iType[iNextTypePosition];
  240. }
  241. --iTypes;
  242. }
  243. }
  244. pszNewPassword[dwNewLength] = '\0';
  245. UStrCpy( newPassword, pszNewPassword );
  246. return ERROR_SUCCESS;
  247. } /* PasswordGenerate() */
  248. // GenerateRandom
  249. //
  250. // Fills buffers with cryptographically random bytes.
  251. DWORD __stdcall GenerateRandom(DWORD dwCount, BYTE* pbRandomType, BYTE* pbRandomChar)
  252. {
  253. bool bGenerated = false;
  254. HCRYPTPROV hProv = NULL;
  255. if (CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
  256. {
  257. if (CryptGenRandom(hProv, dwCount, pbRandomType) && CryptGenRandom(hProv, dwCount, pbRandomChar))
  258. {
  259. bGenerated = true;
  260. }
  261. CryptReleaseContext(hProv, 0);
  262. }
  263. // if cryptographic generation fails, don't fallback, we don't
  264. // want to risk predictable passwords
  265. if (!bGenerated)
  266. {
  267. return GetLastError();
  268. }
  269. return ERROR_SUCCESS;
  270. }