getpw_a.c 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. #include <pthread.h>
  2. #include <byteswap.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include "pwf.h"
  6. #include "nscd.h"
  7. static char *itoa(char *p, uint32_t x)
  8. {
  9. // number of digits in a uint32_t + NUL
  10. p += 11;
  11. *--p = 0;
  12. do {
  13. *--p = '0' + x % 10;
  14. x /= 10;
  15. } while (x);
  16. return p;
  17. }
  18. int __getpw_a(const char *name, uid_t uid, struct passwd *pw, char **buf, size_t *size, struct passwd **res)
  19. {
  20. FILE *f;
  21. int cs;
  22. int rv = 0;
  23. *res = 0;
  24. pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
  25. f = fopen("/etc/passwd", "rbe");
  26. if (!f) {
  27. rv = errno;
  28. goto done;
  29. }
  30. while (!(rv = __getpwent_a(f, pw, buf, size, res)) && *res) {
  31. if (name && !strcmp(name, (*res)->pw_name)
  32. || !name && (*res)->pw_uid == uid)
  33. break;
  34. }
  35. fclose(f);
  36. if (!*res && (rv == 0 || rv == ENOENT || rv == ENOTDIR)) {
  37. int32_t req = name ? GETPWBYNAME : GETPWBYUID;
  38. const char *key;
  39. int32_t passwdbuf[PW_LEN] = {0};
  40. size_t len = 0;
  41. char uidbuf[11] = {0};
  42. if (name) {
  43. key = name;
  44. } else {
  45. /* uid outside of this range can't be queried with the
  46. * nscd interface, but might happen if uid_t ever
  47. * happens to be a larger type (this is not true as of
  48. * now)
  49. */
  50. if(uid < 0 || uid > UINT32_MAX) {
  51. rv = 0;
  52. goto done;
  53. }
  54. key = itoa(uidbuf, uid);
  55. }
  56. f = __nscd_query(req, key, passwdbuf, sizeof passwdbuf, (int[]){0});
  57. if (!f) { rv = errno; goto done; }
  58. if(!passwdbuf[PWFOUND]) { rv = 0; goto cleanup_f; }
  59. /* A zero length response from nscd is invalid. We ignore
  60. * invalid responses and just report an error, rather than
  61. * trying to do something with them.
  62. */
  63. if (!passwdbuf[PWNAMELEN] || !passwdbuf[PWPASSWDLEN]
  64. || !passwdbuf[PWGECOSLEN] || !passwdbuf[PWDIRLEN]
  65. || !passwdbuf[PWSHELLLEN]) {
  66. rv = EIO;
  67. goto cleanup_f;
  68. }
  69. if ((passwdbuf[PWNAMELEN]|passwdbuf[PWPASSWDLEN]
  70. |passwdbuf[PWGECOSLEN]|passwdbuf[PWDIRLEN]
  71. |passwdbuf[PWSHELLLEN]) >= SIZE_MAX/8) {
  72. rv = ENOMEM;
  73. goto cleanup_f;
  74. }
  75. len = passwdbuf[PWNAMELEN] + passwdbuf[PWPASSWDLEN]
  76. + passwdbuf[PWGECOSLEN] + passwdbuf[PWDIRLEN]
  77. + passwdbuf[PWSHELLLEN];
  78. if (len > *size || !*buf) {
  79. char *tmp = realloc(*buf, len);
  80. if (!tmp) {
  81. rv = errno;
  82. goto cleanup_f;
  83. }
  84. *buf = tmp;
  85. *size = len;
  86. }
  87. if (!fread(*buf, len, 1, f)) {
  88. rv = ferror(f) ? errno : EIO;
  89. goto cleanup_f;
  90. }
  91. pw->pw_name = *buf;
  92. pw->pw_passwd = pw->pw_name + passwdbuf[PWNAMELEN];
  93. pw->pw_gecos = pw->pw_passwd + passwdbuf[PWPASSWDLEN];
  94. pw->pw_dir = pw->pw_gecos + passwdbuf[PWGECOSLEN];
  95. pw->pw_shell = pw->pw_dir + passwdbuf[PWDIRLEN];
  96. pw->pw_uid = passwdbuf[PWUID];
  97. pw->pw_gid = passwdbuf[PWGID];
  98. /* Don't assume that nscd made sure to null terminate strings.
  99. * It's supposed to, but malicious nscd should be ignored
  100. * rather than causing a crash.
  101. */
  102. if (pw->pw_passwd[-1] || pw->pw_gecos[-1] || pw->pw_dir[-1]
  103. || pw->pw_shell[passwdbuf[PWSHELLLEN]-1]) {
  104. rv = EIO;
  105. goto cleanup_f;
  106. }
  107. if (name && strcmp(name, pw->pw_name)
  108. || !name && uid != pw->pw_uid) {
  109. rv = EIO;
  110. goto cleanup_f;
  111. }
  112. *res = pw;
  113. cleanup_f:
  114. fclose(f);
  115. goto done;
  116. }
  117. done:
  118. pthread_setcancelstate(cs, 0);
  119. if (rv) errno = rv;
  120. return rv;
  121. }