Source code of Windows XP (NT5)
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.

337 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. int iRand(int iMin, int iMax);
  23. void __stdcall GenerateRandom(DWORD dwCount, BYTE* pbRandomType, BYTE* pbRandomChar);
  24. /////////////////////////////////////////////////////////////////////////////////////////////////////////
  25. // Generate a password from the rules provided.
  26. // Returns ERROR_SUCCESS if successful, else ERROR_INVALID_PARAMETER.
  27. // If successful, the new password is returned in the supplied buffer.
  28. // The buffer must be long enough to hold the minimum length password
  29. // that is required by the rules, plus a terminating NULL.
  30. DWORD __stdcall // ret-EA/OS return code
  31. EaPasswordGenerate(
  32. DWORD dwMinUC, // in -minimum upper case chars
  33. DWORD dwMinLC, // in -minimum lower case chars
  34. DWORD dwMinDigits, // in -minimum numeric digits
  35. DWORD dwMinSpecial, // in -minimum special chars
  36. DWORD dwMaxConsecutiveAlpha,// in -maximum consecutive alpha chars
  37. DWORD dwMinLength, // in -minimum length
  38. WCHAR * newPassword, // out-returned password
  39. DWORD dwBufferLength // in -returned area buffer length
  40. )
  41. {
  42. DWORD dwMaxLength = PWGEN_MAX_LENGTH;
  43. DWORD dwNewLength; // actual length of new password
  44. DWORD dwUC = dwMinUC; // actual numbers of these characters
  45. DWORD dwLC = dwMinLC;
  46. DWORD dwDigits = dwMinDigits;
  47. DWORD dwSpecial = dwMinSpecial;
  48. DWORD dwActualLength = dwUC + dwLC + dwDigits + dwSpecial; // total length specified by the minima
  49. TCHAR pszNewPassword[PWGEN_MAX_LENGTH+1]; // out-returned password
  50. BYTE bRandomType[PWGEN_MAX_LENGTH]; // cryptographically generated random bytes for type
  51. BYTE bRandomChar[PWGEN_MAX_LENGTH]; // cryptographically generated random bytes for character
  52. const TCHAR *szSourceString[4] = { // the lists of characters by type
  53. { TEXT("ABDEFGHJKLMNQRTY") },
  54. { TEXT("abcdefghkmnpqrstuvwxyz") },
  55. { TEXT("23456789") },
  56. { TEXT("~!@#$%^+=") }
  57. };
  58. DWORD dwToPlace[4]; // number of characters of a type to place
  59. int iType[4]; // type of each character class
  60. int iTypes; // total number of types
  61. enum { // types of chars
  62. eUC = 0,
  63. eLC,
  64. eDigit,
  65. eSpecial
  66. };
  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. GenerateRandom(dwNewLength, bRandomType, bRandomChar);
  151. // Generate a string.
  152. DWORD dwConsecAlpha = 0;
  153. int iRemainingAlpha = (int)(dwUC + dwLC);
  154. int iTypeList[PWGEN_MAX_LENGTH]; // A distributed list of types.
  155. for (int iNewChar = 0; (DWORD)iNewChar < dwNewLength; ++iNewChar) {
  156. // Determine whether the next char must be alpha or must not be alpha.
  157. BOOL bMustBeAlpha = FALSE;
  158. BOOL bMustNotBeAlpha = dwConsecAlpha == dwMaxConsecutiveAlpha;
  159. // If it can be alpha, determine whether it HAS to be alpha.
  160. if (!bMustNotBeAlpha) {
  161. // If, among the remaining chars after this one, it would be impossible to
  162. // fit the remaining alpha chars due to constraints of dwMaxConsecutiveAlpha,
  163. // then this character must be alpha.
  164. // Determine the minimum number of groups if we put remaining alpha chars
  165. // into groups that are the maximum width.
  166. int iMinGroups = iRemainingAlpha / (int)dwMaxConsecutiveAlpha;
  167. if (iRemainingAlpha % (int)dwMaxConsecutiveAlpha)
  168. ++iMinGroups;
  169. // Determine the minimum number of non-alpha characters we'll need.
  170. int iMinNonAlpha = iMinGroups - 1;
  171. // Determine the characters remaining.
  172. int iRemaining = (int)dwNewLength - iNewChar;
  173. // Is there room for a non-alpha char here?
  174. if (iRemaining <= (iRemainingAlpha + iMinNonAlpha))
  175. // no.
  176. bMustBeAlpha = TRUE;
  177. }
  178. // Determine the type range.
  179. int iMinType = 0;
  180. int iMaxType = iTypes - 1;
  181. // If next char must be alpha, then alpha chars remain.
  182. // Type position 0 contains either UC or LC.
  183. // Type position 1 contains LC, non-alpha, or nothing.
  184. if (bMustBeAlpha) {
  185. if ((iType[1] == eLC) && (iTypes > 1))
  186. iMaxType = 1;
  187. else
  188. iMaxType = 0;
  189. }
  190. // If next char may not be alpha, there may be no alpha left to generate.
  191. // If so, type position 0 is non-alpha.
  192. // O.w., type positions 0 and 1 may both be alpha.
  193. else if (bMustNotBeAlpha) {
  194. if (iRemainingAlpha) {
  195. if (iType[1] >= eDigit)
  196. iMinType = 1;
  197. else
  198. iMinType = 2;
  199. }
  200. }
  201. // Get the type to generate.
  202. int iTypePosition;
  203. int iTypeToGenerate;
  204. const TCHAR *pszSourceString;
  205. if (iMinType == iMaxType) // There's only one type. Use it.
  206. iTypePosition = iMinType;
  207. else {
  208. // This algorithm distributes the chances for various types.
  209. // If there are 13 LCs to place and one special, there's a
  210. // 13/14 chance of placing an LC and a 1/14 chance of placing a
  211. // special, due to this algorithm.
  212. int iNextTypePosition = 0;
  213. for (int i = iMinType; i <= iMaxType; ++i) {
  214. for (int j = 0; j < (int)dwToPlace[i]; ++j) {
  215. iTypeList[iNextTypePosition++] = i;
  216. }
  217. }
  218. iTypePosition = iTypeList[bRandomType[iNewChar] % iNextTypePosition];
  219. }
  220. iTypeToGenerate = iType[iTypePosition];
  221. pszSourceString = szSourceString[iTypeToGenerate];
  222. // Generate the next character.
  223. pszNewPassword[iNewChar] = pszSourceString[bRandomChar[iNewChar] % UStrLen(pszSourceString)];
  224. // Keep track of those alphas.
  225. if (iTypeToGenerate < eDigit) {
  226. ++dwConsecAlpha;
  227. --iRemainingAlpha;
  228. }
  229. else
  230. dwConsecAlpha = 0;
  231. // Update the types to generate.
  232. if (!--dwToPlace[iTypePosition]) {
  233. for (int iNextTypePosition = iTypePosition + 1; iNextTypePosition < iTypes; ++iNextTypePosition) {
  234. dwToPlace[iNextTypePosition - 1] = dwToPlace[iNextTypePosition];
  235. iType[iNextTypePosition - 1] = iType[iNextTypePosition];
  236. }
  237. --iTypes;
  238. }
  239. }
  240. pszNewPassword[dwNewLength] = '\0';
  241. UStrCpy( newPassword, pszNewPassword );
  242. return ERROR_SUCCESS;
  243. } /* PasswordGenerate() */
  244. /////////////////////////////////////////////////////////////////////////////////////////////////////////
  245. // Return a random number in the range [iMin..iMax].
  246. // Tries to be fair by discarding values of rand() that give an advantage
  247. // to low results.
  248. int iRand(int iMin, int iMax)
  249. {
  250. int iSize = iMax - iMin + 1;
  251. int iMaxRand = (int)((((long)RAND_MAX + 1L) / (long)iSize) * (long)iSize);
  252. int i;
  253. if (iMaxRand > 0)
  254. do {
  255. i = rand();
  256. } while (i > iMaxRand);
  257. else
  258. i = rand();
  259. return (i % iSize) + iMin;
  260. }
  261. // GenerateRandom
  262. //
  263. // Fills buffers with cryptographically random bytes.
  264. void __stdcall GenerateRandom(DWORD dwCount, BYTE* pbRandomType, BYTE* pbRandomChar)
  265. {
  266. bool bGenerated = false;
  267. HCRYPTPROV hProv = NULL;
  268. if (CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
  269. {
  270. if (CryptGenRandom(hProv, dwCount, pbRandomType) && CryptGenRandom(hProv, dwCount, pbRandomChar))
  271. {
  272. bGenerated = true;
  273. }
  274. CryptReleaseContext(hProv, 0);
  275. }
  276. // if cryptographic generation fails, fallback to random number generator
  277. if (!bGenerated)
  278. {
  279. for (DWORD dwIndex = 0; dwIndex < dwCount; dwIndex++)
  280. {
  281. pbRandomType[dwIndex] = (BYTE)iRand(0, 255);
  282. pbRandomChar[dwIndex] = (BYTE)iRand(0, 255);
  283. }
  284. }
  285. }