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.

passwordcheck.c 3.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. /*-------------------------------------------------------------------------
  2. *
  3. * passwordcheck.c
  4. *
  5. *
  6. * Copyright (c) 2009-2019, 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 "libpq/crypt.h"
  22. #include "fmgr.h"
  23. PG_MODULE_MAGIC;
  24. /* passwords shorter than this will be rejected */
  25. #define MIN_PWD_LENGTH 8
  26. extern void _PG_init(void);
  27. /*
  28. * check_password
  29. *
  30. * performs checks on an encrypted or unencrypted password
  31. * ereport's if not acceptable
  32. *
  33. * username: name of role being created or changed
  34. * password: new password (possibly already encrypted)
  35. * password_type: PASSWORD_TYPE_* code, to indicate if the password is
  36. * in plaintext or encrypted form.
  37. * validuntil_time: password expiration time, as a timestamptz Datum
  38. * validuntil_null: true if password expiration time is NULL
  39. *
  40. * This sample implementation doesn't pay any attention to the password
  41. * expiration time, but you might wish to insist that it be non-null and
  42. * not too far in the future.
  43. */
  44. static void
  45. check_password(const char *username,
  46. const char *shadow_pass,
  47. PasswordType password_type,
  48. Datum validuntil_time,
  49. bool validuntil_null)
  50. {
  51. if (password_type != PASSWORD_TYPE_PLAINTEXT)
  52. {
  53. /*
  54. * Unfortunately we cannot perform exhaustive checks on encrypted
  55. * passwords - we are restricted to guessing. (Alternatively, we could
  56. * insist on the password being presented non-encrypted, but that has
  57. * its own security disadvantages.)
  58. *
  59. * We only check for username = password.
  60. */
  61. char *logdetail;
  62. if (plain_crypt_verify(username, shadow_pass, username, &logdetail) == STATUS_OK)
  63. ereport(ERROR,
  64. (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  65. errmsg("password must not equal user name")));
  66. }
  67. else
  68. {
  69. /*
  70. * For unencrypted passwords we can perform better checks
  71. */
  72. const char *password = shadow_pass;
  73. int pwdlen = strlen(password);
  74. int i;
  75. bool pwd_has_letter,
  76. pwd_has_nonletter;
  77. /* enforce minimum length */
  78. if (pwdlen < MIN_PWD_LENGTH)
  79. ereport(ERROR,
  80. (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  81. errmsg("password is too short")));
  82. /* check if the password contains the username */
  83. if (strstr(password, username))
  84. ereport(ERROR,
  85. (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  86. errmsg("password must not contain user name")));
  87. /* check if the password contains both letters and non-letters */
  88. pwd_has_letter = false;
  89. pwd_has_nonletter = false;
  90. for (i = 0; i < pwdlen; i++)
  91. {
  92. /*
  93. * isalpha() does not work for multibyte encodings but let's
  94. * consider non-ASCII characters non-letters
  95. */
  96. if (isalpha((unsigned char) password[i]))
  97. pwd_has_letter = true;
  98. else
  99. pwd_has_nonletter = true;
  100. }
  101. if (!pwd_has_letter || !pwd_has_nonletter)
  102. ereport(ERROR,
  103. (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  104. errmsg("password must contain both letters and nonletters")));
  105. #ifdef USE_CRACKLIB
  106. /* call cracklib to check password */
  107. if (FascistCheck(password, CRACKLIB_DICTPATH))
  108. ereport(ERROR,
  109. (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  110. errmsg("password is easily cracked")));
  111. #endif
  112. }
  113. /* all checks passed, password is ok */
  114. }
  115. /*
  116. * Module initialization function
  117. */
  118. void
  119. _PG_init(void)
  120. {
  121. /* activate password checks when the module is loaded */
  122. check_password_hook = check_password;
  123. }