• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 ID prefix and random salt for given encryption algorithm.
get_salt(char * salt,char * algo)12 int 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       xgetrandom(libbuf, ((len*6)+7)/8, 0);
28 
29       // Grab 6 bit chunks and convert to characters in ./0-9a-zA-Z
30       for (i=0; i<len; i++) {
31         int bitpos = i*6, bits = bitpos/8;
32 
33         bits = ((libbuf[i]+(libbuf[i+1]<<8)) >> (bitpos&7)) & 0x3f;
34         bits += 46;
35         if (bits > 57) bits += 7;
36         if (bits > 90) bits += 6;
37 
38         s[i] = bits;
39       }
40       salt[len] = 0;
41 
42       return s-salt;
43     }
44   }
45 
46   return -1;
47 }
48 
49 // Prompt with mesg, read password into buf, return 0 for success 1 for fail
read_password(char * buf,int buflen,char * mesg)50 int read_password(char *buf, int buflen, char *mesg)
51 {
52   struct termios oldtermio;
53   struct sigaction sa, oldsa;
54   int i, ret = 1;
55 
56   // NOP signal handler to return from the read. Use sigaction() instead
57   // of xsignal() because we want to restore the old handler afterwards.
58   memset(&sa, 0, sizeof(sa));
59   sa.sa_handler = generic_signal;
60   sigaction(SIGINT, &sa, &oldsa);
61 
62   tcflush(0, TCIFLUSH);
63   xset_terminal(0, 1, 0, &oldtermio);
64 
65   xprintf("%s", mesg);
66 
67   for (i=0; i < buflen-1; i++) {
68     if ((ret = read(0, buf+i, 1)) < 0 || (!ret && !i)) {
69       i = 0;
70       ret = 1;
71 
72       break;
73     } else if (!ret || buf[i] == '\n' || buf[i] == '\r') {
74       ret = 0;
75 
76       break;
77     } else if (buf[i] == 8 || buf[i] == 127) i -= i ? 2 : 1;
78   }
79 
80   // Restore terminal/signal state, terminate string
81   sigaction(SIGINT, &oldsa, NULL);
82   tcsetattr(0, TCSANOW, &oldtermio);
83   buf[i] = 0;
84   xputc('\n');
85 
86   return ret;
87 }
88 
get_nextcolon(char * line,int cnt)89 static char *get_nextcolon(char *line, int cnt)
90 {
91   while (cnt--) {
92     if (!(line = strchr(line, ':'))) error_exit("Invalid Entry\n");
93     line++; //jump past the colon
94   }
95   return line;
96 }
97 
98 /*update_password is used by multiple utilities to update /etc/passwd,
99  * /etc/shadow, /etc/group and /etc/gshadow files,
100  * which are used as user, group databeses
101  * entry can be
102  * 1. encrypted password, when updating user password.
103  * 2. complete entry for user details, when creating new user
104  * 3. group members comma',' separated list, when adding user to group
105  * 4. complete entry for group details, when creating new group
106  * 5. entry = NULL, delete the named entry user/group
107  */
update_password(char * filename,char * username,char * entry)108 int update_password(char *filename, char* username, char* entry)
109 {
110   char *filenamesfx = NULL, *namesfx = NULL, *shadow = NULL,
111        *sfx = NULL, *line = NULL;
112   FILE *exfp, *newfp;
113   int ret = -1, found = 0, n;
114   struct flock lock;
115   size_t allocated_length;
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 ((n = getline(&line, &allocated_length, exfp)) > 0) {
154     line[n-1] = 0;
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   }
180   free(line);
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 
197 free_storage:
198   free(filenamesfx);
199   return ret;
200 }
201