PageRenderTime 37ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/contrib/passwordcheck/passwordcheck.c

http://github.com/postgres/postgres
C | 157 lines | 79 code | 17 blank | 61 comment | 12 complexity | 86a9aea4e63d3eea305d02ef70926104 MD5 | raw file
Possible License(s): AGPL-3.0
  1. /*-------------------------------------------------------------------------
  2. *
  3. * passwordcheck.c
  4. *
  5. *
  6. * Copyright (c) 2009-2020, PostgreSQL Global Development Group
  7. *
  8. * Author: Laurenz Albe <laurenz.albe@wien.gv.at>
  9. *
  10. * IDENTIFICATION
  11. * contrib/passwordcheck/passwordcheck.c
  12. *
  13. *-------------------------------------------------------------------------
  14. */
  15. #include "postgres.h"
  16. #include <ctype.h>
  17. #ifdef USE_CRACKLIB
  18. #include <crack.h>
  19. #endif
  20. #include "commands/user.h"
  21. #include "fmgr.h"
  22. #include "libpq/crypt.h"
  23. PG_MODULE_MAGIC;
  24. /* Saved hook value in case of unload */
  25. static check_password_hook_type prev_check_password_hook = NULL;
  26. /* passwords shorter than this will be rejected */
  27. #define MIN_PWD_LENGTH 8
  28. extern void _PG_init(void);
  29. extern void _PG_fini(void);
  30. /*
  31. * check_password
  32. *
  33. * performs checks on an encrypted or unencrypted password
  34. * ereport's if not acceptable
  35. *
  36. * username: name of role being created or changed
  37. * password: new password (possibly already encrypted)
  38. * password_type: PASSWORD_TYPE_* code, to indicate if the password is
  39. * in plaintext or encrypted form.
  40. * validuntil_time: password expiration time, as a timestamptz Datum
  41. * validuntil_null: true if password expiration time is NULL
  42. *
  43. * This sample implementation doesn't pay any attention to the password
  44. * expiration time, but you might wish to insist that it be non-null and
  45. * not too far in the future.
  46. */
  47. static void
  48. check_password(const char *username,
  49. const char *shadow_pass,
  50. PasswordType password_type,
  51. Datum validuntil_time,
  52. bool validuntil_null)
  53. {
  54. if (prev_check_password_hook)
  55. prev_check_password_hook(username, shadow_pass,
  56. password_type, validuntil_time,
  57. validuntil_null);
  58. if (password_type != PASSWORD_TYPE_PLAINTEXT)
  59. {
  60. /*
  61. * Unfortunately we cannot perform exhaustive checks on encrypted
  62. * passwords - we are restricted to guessing. (Alternatively, we could
  63. * insist on the password being presented non-encrypted, but that has
  64. * its own security disadvantages.)
  65. *
  66. * We only check for username = password.
  67. */
  68. char *logdetail;
  69. if (plain_crypt_verify(username, shadow_pass, username, &logdetail) == STATUS_OK)
  70. ereport(ERROR,
  71. (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  72. errmsg("password must not equal user name")));
  73. }
  74. else
  75. {
  76. /*
  77. * For unencrypted passwords we can perform better checks
  78. */
  79. const char *password = shadow_pass;
  80. int pwdlen = strlen(password);
  81. int i;
  82. bool pwd_has_letter,
  83. pwd_has_nonletter;
  84. /* enforce minimum length */
  85. if (pwdlen < MIN_PWD_LENGTH)
  86. ereport(ERROR,
  87. (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  88. errmsg("password is too short")));
  89. /* check if the password contains the username */
  90. if (strstr(password, username))
  91. ereport(ERROR,
  92. (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  93. errmsg("password must not contain user name")));
  94. /* check if the password contains both letters and non-letters */
  95. pwd_has_letter = false;
  96. pwd_has_nonletter = false;
  97. for (i = 0; i < pwdlen; i++)
  98. {
  99. /*
  100. * isalpha() does not work for multibyte encodings but let's
  101. * consider non-ASCII characters non-letters
  102. */
  103. if (isalpha((unsigned char) password[i]))
  104. pwd_has_letter = true;
  105. else
  106. pwd_has_nonletter = true;
  107. }
  108. if (!pwd_has_letter || !pwd_has_nonletter)
  109. ereport(ERROR,
  110. (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  111. errmsg("password must contain both letters and nonletters")));
  112. #ifdef USE_CRACKLIB
  113. /* call cracklib to check password */
  114. if (FascistCheck(password, CRACKLIB_DICTPATH))
  115. ereport(ERROR,
  116. (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  117. errmsg("password is easily cracked")));
  118. #endif
  119. }
  120. /* all checks passed, password is ok */
  121. }
  122. /*
  123. * Module initialization function
  124. */
  125. void
  126. _PG_init(void)
  127. {
  128. /* activate password checks when the module is loaded */
  129. prev_check_password_hook = check_password_hook;
  130. check_password_hook = check_password;
  131. }
  132. /*
  133. * Module unload function
  134. */
  135. void
  136. _PG_fini(void)
  137. {
  138. /* uninstall hook */
  139. check_password_hook = prev_check_password_hook;
  140. }