getspnam_r.c 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. #include <fcntl.h>
  2. #include <unistd.h>
  3. #include <sys/stat.h>
  4. #include <ctype.h>
  5. #include <pthread.h>
  6. #include "pwf.h"
  7. /* This implementation support Openwall-style TCB passwords in place of
  8. * traditional shadow, if the appropriate directories and files exist.
  9. * Thus, it is careful to avoid following symlinks or blocking on fifos
  10. * which a malicious user might create in place of his or her TCB shadow
  11. * file. It also avoids any allocation to prevent memory-exhaustion
  12. * attacks via huge TCB shadow files. */
  13. static long xatol(const char *s)
  14. {
  15. return isdigit(*s) ? atol(s) : -1;
  16. }
  17. static void cleanup(void *p)
  18. {
  19. fclose(p);
  20. }
  21. int getspnam_r(const char *name, struct spwd *sp, char *buf, size_t size, struct spwd **res)
  22. {
  23. char path[20+NAME_MAX];
  24. FILE *f = 0;
  25. int rv = 0;
  26. int fd;
  27. size_t k, l = strlen(name);
  28. char *s;
  29. int skip = 0;
  30. int cs;
  31. *res = 0;
  32. /* Disallow potentially-malicious user names */
  33. if (*name=='.' || strchr(name, '/') || !l)
  34. return EINVAL;
  35. /* Buffer size must at least be able to hold name, plus some.. */
  36. if (size < l+100) return ERANGE;
  37. /* Protect against truncation */
  38. if (snprintf(path, sizeof path, "/etc/tcb/%s/shadow", name) >= sizeof path)
  39. return EINVAL;
  40. fd = open(path, O_RDONLY|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC);
  41. if (fd >= 0) {
  42. struct stat st = { 0 };
  43. errno = EINVAL;
  44. if (fstat(fd, &st) || !S_ISREG(st.st_mode) || !(f = fdopen(fd, "rb"))) {
  45. pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
  46. close(fd);
  47. pthread_setcancelstate(cs, 0);
  48. return errno;
  49. }
  50. } else {
  51. f = fopen("/etc/shadow", "rbe");
  52. if (!f) return errno;
  53. }
  54. pthread_cleanup_push(cleanup, f);
  55. while (fgets(buf, size, f) && (k=strlen(buf))>0) {
  56. if (skip || strncmp(name, buf, l)) {
  57. skip = buf[k-1] != '\n';
  58. continue;
  59. }
  60. if (buf[k-1] != '\n') {
  61. rv = ERANGE;
  62. break;
  63. }
  64. buf[k-1] = 0;
  65. s = buf;
  66. sp->sp_namp = s;
  67. if (!(s = strchr(s, ':'))) continue;
  68. *s++ = 0; sp->sp_pwdp = s;
  69. if (!(s = strchr(s, ':'))) continue;
  70. *s++ = 0; sp->sp_lstchg = xatol(s);
  71. if (!(s = strchr(s, ':'))) continue;
  72. *s++ = 0; sp->sp_min = xatol(s);
  73. if (!(s = strchr(s, ':'))) continue;
  74. *s++ = 0; sp->sp_max = xatol(s);
  75. if (!(s = strchr(s, ':'))) continue;
  76. *s++ = 0; sp->sp_warn = xatol(s);
  77. if (!(s = strchr(s, ':'))) continue;
  78. *s++ = 0; sp->sp_inact = xatol(s);
  79. if (!(s = strchr(s, ':'))) continue;
  80. *s++ = 0; sp->sp_expire = xatol(s);
  81. if (!(s = strchr(s, ':'))) continue;
  82. *s++ = 0; sp->sp_flag = xatol(s);
  83. *res = sp;
  84. break;
  85. }
  86. pthread_cleanup_pop(1);
  87. return rv;
  88. }