xref: /plan9/sys/src/cmd/auth/secstore/secuser.c (revision 4d44ba9b9ee4246ddbd96c7fcaf0918ab92ab35a)
1 #include <u.h>
2 #include <libc.h>
3 #include <mp.h>
4 #include <libsec.h>
5 #include "SConn.h"
6 #include "secstore.h"
7 
8 int verbose;
9 
10 static void userinput(char *, int);
11 
12 static void
13 ensure_exists(char *f, ulong perm)
14 {
15 	int fd;
16 
17 	if(access(f, AEXIST) >= 0)
18 		return;
19 	if(verbose)
20 		fprint(2,"first time setup for secstore: create %s %lo\n", f, perm);
21 	fd = create(f, OREAD, perm);
22 	if(fd < 0){
23 		fprint(2, "unable to create %s\n", f);
24 		exits("secstored directories");
25 	}
26 	close(fd);
27 }
28 
29 
30 int
31 main(int argc, char **argv)
32 {
33 	int isnew;
34 	char *id, buf[Maxmsg], home[Maxmsg], prompt[100], *hexHi;
35 	char *pass, *passck;
36 	long expsecs;
37 	mpint *H = mpnew(0), *Hi = mpnew(0);
38 	PW *pw;
39 	Tm *tm;
40 
41 	ARGBEGIN{
42 	case 'v':
43 		verbose++;
44 		break;
45 	}ARGEND;
46 	if(argc!=1){
47 		print("usage: secuser [-v] <user>\n");
48 		exits("usage");
49 	}
50 
51 	ensure_exists(SECSTORE_DIR, DMDIR|0755L);
52 	snprint(home, sizeof(home), "%s/who", SECSTORE_DIR);
53 	ensure_exists(home, DMDIR|0755L);
54 	snprint(home, sizeof(home), "%s/store", SECSTORE_DIR);
55 	ensure_exists(home, DMDIR|0700L);
56 
57 	id = argv[0];
58 	if(verbose)
59 		fprint(2,"secuser %s\n", id);
60 	if((pw = getPW(id,1)) == nil){
61 		isnew = 1;
62 		print("new account (because %s/%s %r)\n", SECSTORE_DIR, id);
63 		pw = emalloc(sizeof(*pw));
64 		pw->id = estrdup(id);
65 		snprint(home, sizeof(home), "%s/store/%s", SECSTORE_DIR, id);
66 		if(access(home, AEXIST) == 0){
67 			print("new user, but directory %s already exists\n", home);
68 			exits(home);
69 		}
70 	}else{
71 		isnew = 0;
72 	}
73 
74 	/* get main password for id */
75 	for(;;){
76 		if(isnew)
77 			snprint(prompt, sizeof(prompt), "%s password: ", id);
78 		else
79 			snprint(prompt, sizeof(prompt), "%s password [default = don't change]: ", id);
80 		pass = getpassm(prompt);
81 		if(pass == nil){
82 			print("getpassm failed\n");
83 			exits("getpassm failed");
84 		}
85 		if(verbose)
86 			print("%ld characters\n", strlen(pass));
87 		if(pass[0] == '\0' && isnew == 0)
88 			break;
89 		if(strlen(pass) >= 7)
90 			break;
91 		print("password must be at least 7 characters\n");
92 	}
93 
94 	if(pass[0] != '\0'){
95 		snprint(prompt, sizeof(prompt), "retype password: ");
96 		if(verbose)
97 			print("confirming...\n");
98 		passck = getpassm(prompt);
99 		if(passck == nil){
100 			print("getpassm failed\n");
101 			exits("getpassm failed");
102 		}
103 		if(strcmp(pass, passck) != 0){
104 			print("passwords didn't match\n");
105 			exits("no match");
106 		}
107 		memset(passck, 0, strlen(passck));
108 		free(passck);
109 		hexHi = PAK_Hi(id, pass, H, Hi);
110 		memset(pass, 0, strlen(pass));
111 		free(pass);
112 		free(hexHi);
113 		mpfree(H);
114 		pw->Hi = Hi;
115 	}
116 
117 	/* get expiration time (midnight of date specified) */
118 	if(isnew)
119 		expsecs = time(0) + 365*24*60*60;
120 	else
121 		expsecs = pw->expire;
122 
123 	for(;;){
124 		tm = localtime(expsecs);
125 		print("expires [DDMMYYYY, default = %2.2d%2.2d%4.4d]: ",
126 				tm->mday, tm->mon, tm->year+1900);
127 		userinput(buf, sizeof(buf));
128 		if(strlen(buf) == 0)
129 			break;
130 		if(strlen(buf) != 8){
131 			print("!bad date format: %s\n", buf);
132 			continue;
133 		}
134 		tm->mday = (buf[0]-'0')*10 + (buf[1]-'0');
135 		if(tm->mday > 31 || tm->mday < 1){
136 			print("!bad day of month: %d\n", tm->mday);
137 			continue;
138 		}
139 		tm->mon = (buf[2]-'0')*10 + (buf[3]-'0') - 1;
140 		if(tm->mon > 11 || tm->mday < 0){
141 			print("!bad month: %d\n", tm->mon + 1);
142 			continue;
143 		}
144 		tm->year = atoi(buf+4) - 1900;
145 		if(tm->year < 70){
146 			print("!bad year: %d\n", tm->year + 1900);
147 			continue;
148 		}
149 		tm->sec = 59;
150 		tm->min = 59;
151 		tm->hour = 23;
152 		tm->yday = 0;
153 		expsecs = tm2sec(tm);
154 		break;
155 	}
156 	pw->expire = expsecs;
157 
158 	/* failed logins */
159 	if(pw->failed != 0 )
160 		print("clearing %d failed login attempts\n", pw->failed);
161 	pw->failed = 0;
162 
163 	/* status bits */
164 	if(isnew)
165 		pw->status = Enabled;
166 	for(;;){
167 		print("Enabled or Disabled [default %s]: ",
168 			(pw->status & Enabled) ? "Enabled" : "Disabled" );
169 		userinput(buf, sizeof(buf));
170 		if(strlen(buf) == 0)
171 			break;
172 		if(buf[0]=='E' || buf[0]=='e'){
173 			pw->status |= Enabled;
174 			break;
175 		}
176 		if(buf[0]=='D' || buf[0]=='d'){
177 			pw->status = pw->status & ~Enabled;
178 			break;
179 		}
180 	}
181 	for(;;){
182 		print("require STA? [default %s]: ",
183 			(pw->status & STA) ? "yes" : "no" );
184 		userinput(buf, sizeof(buf));
185 		if(strlen(buf) == 0)
186 			break;
187 		if(buf[0]=='Y' || buf[0]=='y'){
188 			pw->status |= STA;
189 			break;
190 		}
191 		if(buf[0]=='N' || buf[0]=='n'){
192 			pw->status = pw->status & ~STA;
193 			break;
194 		}
195 	}
196 
197 	/* free form field */
198 	if(isnew)
199 		pw->other = nil;
200 	print("comments [default = %s]: ", (pw->other == nil) ? "" : pw->other);
201 	userinput(buf, 72);  /* 72 comes from password.h */
202 	if(buf[0])
203 		if((pw->other = strdup(buf)) == nil)
204 			sysfatal("strdup");
205 
206 	syslog(0, LOG, "CHANGELOGIN for '%s'", pw->id);
207 	if(putPW(pw) < 0){
208 		print("error writing entry: %r\n");
209 		exits("can't write password file");
210 	}else{
211 		print("change written\n");
212 		if(isnew && create(home, OREAD, DMDIR | 0775L) < 0){
213 			print("unable to create %s: %r\n", home);
214 			exits(home);
215 		}
216 	}
217 
218 	exits("");
219 	return 1;  /* keep  other compilers happy */
220 }
221 
222 
223 static void
224 userinput(char *buf, int blen)
225 {
226 	int n;
227 
228 	while(1){
229 		n = read(0, buf, blen);
230 		if(n<=0)
231 			exits("read error");
232 		if(buf[n-1]=='\n'){
233 			buf[n-1] = '\0';
234 			return;
235 		}
236 		buf += n;  blen -= n;
237 		if(blen<=0)
238 			exits("input too large");
239 	}
240 }
241 
242