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