1/* password.c - password read/update helper functions.
2 *
3 * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
4 *
5 * TODO: cleanup
6 */
7
8#include "toys.h"
9#include <time.h>
10
11// generate appropriate random salt string for given encryption algorithm.
12int get_salt(char *salt, char *algo)
13{
14  struct {
15    char *type, id, len;
16  } al[] = {{"des", 0, 2}, {"md5", 1, 8}, {"sha256", 5, 16}, {"sha512", 6, 16}};
17  int i;
18
19  for (i = 0; i < ARRAY_LEN(al); i++) {
20    if (!strcmp(algo, al[i].type)) {
21      int len = al[i].len;
22      char *s = salt;
23
24      if (al[i].id) s += sprintf(s, "$%c$", '0'+al[i].id);
25
26      // Read appropriate number of random bytes for salt
27      i = xopen("/dev/urandom", O_RDONLY);
28      xreadall(i, libbuf, ((len*6)+7)/8);
29      close(i);
30
31      // Grab 6 bit chunks and convert to characters in ./0-9a-zA-Z
32      for (i=0; i<len; i++) {
33        int bitpos = i*6, bits = bitpos/8;
34
35        bits = ((libbuf[i]+(libbuf[i+1]<<8)) >> (bitpos&7)) & 0x3f;
36        bits += 46;
37        if (bits > 57) bits += 7;
38        if (bits > 90) bits += 6;
39
40        s[i] = bits;
41      }
42      salt[len] = 0;
43
44      return s-salt;
45    }
46  }
47
48  return -1;
49}
50
51// Prompt with mesg, read password into buf, return 0 for success 1 for fail
52int read_password(char *buf, int buflen, char *mesg)
53{
54  struct termios oldtermio;
55  struct sigaction sa, oldsa;
56  int i, ret = 1;
57
58  // NOP signal handler to return from the read
59  memset(&sa, 0, sizeof(sa));
60  sa.sa_handler = generic_signal;
61  sigaction(SIGINT, &sa, &oldsa);
62
63  tcflush(0, TCIFLUSH);
64  set_terminal(0, 1, &oldtermio);
65
66  xprintf("%s", mesg);
67
68  for (i=0; i < buflen-1; i++) {
69    if ((ret = read(0, buf+i, 1)) < 0 || (!ret && !i)) {
70      i = 0;
71      ret = 1;
72
73      break;
74    } else if (!ret || buf[i] == '\n' || buf[i] == '\r') {
75      ret = 0;
76
77      break;
78    } else if (buf[i] == 8 || buf[i] == 127) i -= i ? 2 : 1;
79  }
80
81  // Restore terminal/signal state, terminate string
82  sigaction(SIGINT, &oldsa, NULL);
83  tcsetattr(0, TCSANOW, &oldtermio);
84  buf[i] = 0;
85  xputc('\n');
86
87  return ret;
88}
89
90static char *get_nextcolon(char *line, int cnt)
91{
92  while (cnt--) {
93    if (!(line = strchr(line, ':'))) error_exit("Invalid Entry\n");
94    line++; //jump past the colon
95  }
96  return line;
97}
98
99/*update_password is used by multiple utilities to update /etc/passwd,
100 * /etc/shadow, /etc/group and /etc/gshadow files,
101 * which are used as user, group databeses
102 * entry can be
103 * 1. encrypted password, when updating user password.
104 * 2. complete entry for user details, when creating new user
105 * 3. group members comma',' separated list, when adding user to group
106 * 4. complete entry for group details, when creating new group
107 * 5. entry = NULL, delete the named entry user/group
108 */
109int update_password(char *filename, char* username, char* entry)
110{
111  char *filenamesfx = NULL, *namesfx = NULL, *shadow = NULL,
112       *sfx = NULL, *line = NULL;
113  FILE *exfp, *newfp;
114  int ret = -1, found = 0;
115  struct flock lock;
116
117  shadow = strstr(filename, "shadow");
118  filenamesfx = xmprintf("%s+", filename);
119  sfx = strchr(filenamesfx, '+');
120
121  exfp = fopen(filename, "r+");
122  if (!exfp) {
123    perror_msg("Couldn't open file %s",filename);
124    goto free_storage;
125  }
126
127  *sfx = '-';
128  unlink(filenamesfx);
129  ret = link(filename, filenamesfx);
130  if (ret < 0) error_msg("can't create backup file");
131
132  *sfx = '+';
133  lock.l_type = F_WRLCK;
134  lock.l_whence = SEEK_SET;
135  lock.l_start = 0;
136  lock.l_len = 0;
137
138  ret = fcntl(fileno(exfp), F_SETLK, &lock);
139  if (ret < 0) perror_msg("Couldn't lock file %s",filename);
140
141  lock.l_type = F_UNLCK; //unlocking at a later stage
142
143  newfp = fopen(filenamesfx, "w+");
144  if (!newfp) {
145    error_msg("couldn't open file for writing");
146    ret = -1;
147    fclose(exfp);
148    goto free_storage;
149  }
150
151  ret = 0;
152  namesfx = xmprintf("%s:",username);
153  while ((line = get_line(fileno(exfp))) != NULL)
154  {
155    if (strncmp(line, namesfx, strlen(namesfx)))
156      fprintf(newfp, "%s\n", line);
157    else if (entry) {
158      char *current_ptr = NULL;
159
160      found = 1;
161      if (!strcmp(toys.which->name, "passwd")) {
162        fprintf(newfp, "%s%s:",namesfx, entry);
163        current_ptr = get_nextcolon(line, 2); //past passwd
164        if (shadow) {
165          fprintf(newfp, "%u:",(unsigned)(time(NULL))/(24*60*60));
166          current_ptr = get_nextcolon(current_ptr, 1);
167          fprintf(newfp, "%s\n",current_ptr);
168        } else fprintf(newfp, "%s\n",current_ptr);
169      } else if (!strcmp(toys.which->name, "groupadd") ||
170          !strcmp(toys.which->name, "addgroup") ||
171          !strcmp(toys.which->name, "delgroup") ||
172          !strcmp(toys.which->name, "groupdel")){
173        current_ptr = get_nextcolon(line, 3); //past gid/admin list
174        *current_ptr = '\0';
175        fprintf(newfp, "%s", line);
176        fprintf(newfp, "%s\n", entry);
177      }
178    }
179    free(line);
180  }
181  free(namesfx);
182  if (!found && entry) fprintf(newfp, "%s\n", entry);
183  fcntl(fileno(exfp), F_SETLK, &lock);
184  fclose(exfp);
185
186  errno = 0;
187  fflush(newfp);
188  fsync(fileno(newfp));
189  fclose(newfp);
190  rename(filenamesfx, filename);
191  if (errno) {
192    perror_msg("File Writing/Saving failed: ");
193    unlink(filenamesfx);
194    ret = -1;
195  }
196
197free_storage:
198  free(filenamesfx);
199  return ret;
200}
201