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