password.c 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. /* password.c - password read/update helper functions.
  2. *
  3. * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
  4. *
  5. * TODO: cleanup
  6. */
  7. #include "toys.h"
  8. // generate ID prefix and random salt for given encryption algorithm.
  9. int get_salt(char *salt, char *algo)
  10. {
  11. struct {
  12. char *type, id, len;
  13. } al[] = {{"des", 0, 2}, {"md5", 1, 8}, {"sha256", 5, 16}, {"sha512", 6, 16}};
  14. int i;
  15. for (i = 0; i < ARRAY_LEN(al); i++) {
  16. if (!strcmp(algo, al[i].type)) {
  17. int len = al[i].len;
  18. char *s = salt;
  19. if (al[i].id) s += sprintf(s, "$%c$", '0'+al[i].id);
  20. // Read appropriate number of random bytes for salt
  21. xgetrandom(libbuf, ((len*6)+7)/8);
  22. // Grab 6 bit chunks and convert to characters in ./0-9a-zA-Z
  23. for (i = 0; i<len; i++) {
  24. int bitpos = i*6, bits = bitpos/8;
  25. bits = ((libbuf[i]+(libbuf[i+1]<<8)) >> (bitpos&7)) & 0x3f;
  26. bits += 46;
  27. if (bits > 57) bits += 7;
  28. if (bits > 90) bits += 6;
  29. s[i] = bits;
  30. }
  31. salt[len] = 0;
  32. return s-salt;
  33. }
  34. }
  35. return -1;
  36. }
  37. // Prompt with mesg, read password into buf, return 0 for success 1 for fail
  38. int read_password(char *buf, int buflen, char *mesg)
  39. {
  40. struct termios oldtermio;
  41. struct sigaction sa = {.sa_handler = generic_signal}, oldsa;
  42. int i, tty = tty_fd(), ret = 1;
  43. // Set NOP signal handler to return from the read.
  44. sigaction(SIGINT, &sa, &oldsa);
  45. tcflush(tty, TCIFLUSH);
  46. xset_terminal(tty, 1, 0, &oldtermio);
  47. dprintf(tty, "%s", mesg);
  48. // Loop assembling password. (Too long = fail)
  49. for (i = 0; i<buflen-1; i++) {
  50. // tty closed, or EOF or ctrl-D at start, or ctrl-C anywhere: fail.
  51. if ((ret = read(tty, buf+i, 1))<0 || (!ret&&!i) || *buf==4 || buf[i]==3)
  52. break;
  53. // EOF or newline: return success
  54. else if (!ret || buf[i]=='\n' || buf[i]=='\r') {
  55. ret = 0;
  56. break;
  57. } else if (buf[i] == 8 || buf[i] == 127) i -= 2-!i;
  58. }
  59. // Restore terminal/signal state, terminate string
  60. tcsetattr(0, TCSANOW, &oldtermio);
  61. sigaction(SIGINT, &oldsa, 0);
  62. xputc('\n');
  63. buf[i*!ret] = 0;
  64. return ret;
  65. }
  66. /* update colon-separated text files ala /etc/{passwd,shadow,group,gshadow}
  67. * username = string match for first entry in line
  68. * entry = new entry (NULL deletes matching line from file)
  69. * pos = which entry to replace with "entry" (0 is first)
  70. */
  71. // filename+ = new copy being written, filename- = backup of old version
  72. // returns 1 for success, 0 for failure
  73. int update_password(char *filename, char *username, char *entry, int pos)
  74. {
  75. char *filenamesfx = xmprintf("%s-", filename), *line = 0, *start, *end;
  76. FILE *ofp, *nfp;
  77. int ret = 0, found = 0, len = strlen(username)*!strchr(username, ':'), ii;
  78. struct flock lock = {.l_type = F_WRLCK};
  79. long long ll = 0;
  80. // Open old filename ("r" won't let us lock), get blocking lock
  81. if (!(ofp = fopen(filename, "w+")) || 0>fcntl(fileno(ofp), F_SETLK, &lock)) {
  82. perror_msg("%s", filename);
  83. goto free_storage;
  84. }
  85. // Delete old backup, link new backup. (Failure here isn't fatal.)
  86. unlink(filenamesfx);
  87. if (0>link(filename, filenamesfx)) perror_msg("%s", filenamesfx);
  88. // Open new file to copy entries to
  89. filenamesfx[strlen(filenamesfx)-1] = '+';
  90. if (!(nfp = fopen(filenamesfx, "w+"))) {
  91. perror_msg("%s", filenamesfx);
  92. goto free_storage;
  93. }
  94. // Loop through lines
  95. while (getline(&line, (void *)&ll, ofp)) {
  96. // find matching line
  97. start = end = line;
  98. if (strncmp(chomp(line), username, len) || line[len]!=':') {
  99. found++;
  100. if (!entry) continue;
  101. // Find start and end of span to replace
  102. for (ii = pos;;) {
  103. while (*end != ':') {
  104. if (!*end) break;
  105. end++;
  106. }
  107. if (ii) {
  108. start = ++end;
  109. ii--;
  110. } else break;
  111. }
  112. if (ii) start = end = line;
  113. }
  114. // Write with replacement (if any)
  115. fprintf(nfp, "%*s%s%s\n", (int)(start-line), line,
  116. (start==line) ? "" : entry, end);
  117. memset(line, 0, strlen(line));
  118. }
  119. free(line);
  120. fflush(nfp);
  121. fsync(fileno(nfp));
  122. fclose(nfp); // automatically unlocks
  123. if (!found || rename(filenamesfx, filename)) {
  124. if (found) perror_msg("%s -> %s", filenamesfx, filename);
  125. else if (entry) fprintf(nfp, "%s\n", entry);
  126. unlink(filenamesfx);
  127. } else ret = 1;
  128. free_storage:
  129. if (ofp) fclose(ofp);
  130. free(filenamesfx);
  131. return ret;
  132. }