PageRenderTime 41ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/contrib/passwordcheck/passwordcheck.c

https://bitbucket.org/gencer/postgres
C | 148 lines | 73 code | 19 blank | 56 comment | 11 complexity | 13112cd43e9e91f8a6025f0c5a031148 MD5 | raw file
Possible License(s): AGPL-3.0
  1. /*-------------------------------------------------------------------------
  2. *
  3. * passwordcheck.c
  4. *
  5. *
  6. * Copyright (c) 2009-2014, 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/md5.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_PLAINTEXT or PASSWORD_TYPE_MD5 (there
  36. * could be other encryption schemes in future)
  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 *password,
  47. int password_type,
  48. Datum validuntil_time,
  49. bool validuntil_null)
  50. {
  51. int namelen = strlen(username);
  52. int pwdlen = strlen(password);
  53. char encrypted[MD5_PASSWD_LEN + 1];
  54. int i;
  55. bool pwd_has_letter,
  56. pwd_has_nonletter;
  57. switch (password_type)
  58. {
  59. case PASSWORD_TYPE_MD5:
  60. /*
  61. * Unfortunately we cannot perform exhaustive checks on encrypted
  62. * passwords - we are restricted to guessing. (Alternatively, we
  63. * could insist on the password being presented non-encrypted, but
  64. * that has its own security disadvantages.)
  65. *
  66. * We only check for username = password.
  67. */
  68. if (!pg_md5_encrypt(username, username, namelen, encrypted))
  69. elog(ERROR, "password encryption failed");
  70. if (strcmp(password, encrypted) == 0)
  71. ereport(ERROR,
  72. (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  73. errmsg("password must not contain user name")));
  74. break;
  75. case PASSWORD_TYPE_PLAINTEXT:
  76. /*
  77. * For unencrypted passwords we can perform better checks
  78. */
  79. /* enforce minimum length */
  80. if (pwdlen < MIN_PWD_LENGTH)
  81. ereport(ERROR,
  82. (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  83. errmsg("password is too short")));
  84. /* check if the password contains the username */
  85. if (strstr(password, username))
  86. ereport(ERROR,
  87. (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  88. errmsg("password must not contain user name")));
  89. /* check if the password contains both letters and non-letters */
  90. pwd_has_letter = false;
  91. pwd_has_nonletter = false;
  92. for (i = 0; i < pwdlen; i++)
  93. {
  94. /*
  95. * isalpha() does not work for multibyte encodings but let's
  96. * consider non-ASCII characters non-letters
  97. */
  98. if (isalpha((unsigned char) password[i]))
  99. pwd_has_letter = true;
  100. else
  101. pwd_has_nonletter = true;
  102. }
  103. if (!pwd_has_letter || !pwd_has_nonletter)
  104. ereport(ERROR,
  105. (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  106. errmsg("password must contain both letters and nonletters")));
  107. #ifdef USE_CRACKLIB
  108. /* call cracklib to check password */
  109. if (FascistCheck(password, CRACKLIB_DICTPATH))
  110. ereport(ERROR,
  111. (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  112. errmsg("password is easily cracked")));
  113. #endif
  114. break;
  115. default:
  116. elog(ERROR, "unrecognized password type: %d", password_type);
  117. break;
  118. }
  119. /* all checks passed, password is ok */
  120. }
  121. /*
  122. * Module initialization function
  123. */
  124. void
  125. _PG_init(void)
  126. {
  127. /* activate password checks when the module is loaded */
  128. check_password_hook = check_password;
  129. }