1implement Changelogin; 2 3include "sys.m"; 4 sys: Sys; 5 6include "daytime.m"; 7 daytime: Daytime; 8 9include "draw.m"; 10 11include "keyring.m"; 12 kr: Keyring; 13 14Changelogin: module 15{ 16 init: fn(ctxt: ref Draw->Context, argv: list of string); 17}; 18 19stderr, stdin, stdout: ref Sys->FD; 20keydb := "/mnt/keys"; 21 22init(nil: ref Draw->Context, args: list of string) 23{ 24 ok: int; 25 word: string; 26 27 sys = load Sys Sys->PATH; 28 kr = load Keyring Keyring->PATH; 29 30 stdin = sys->fildes(0); 31 stdout = sys->fildes(1); 32 stderr = sys->fildes(2); 33 34 argv0 := hd args; 35 args = tl args; 36 37 if(args == nil){ 38 sys->fprint(stderr, "usage: %s userid\n", argv0); 39 raise "fail:usage"; 40 } 41 42 daytime = load Daytime Daytime->PATH; 43 if(daytime == nil) { 44 sys->fprint(stderr, "%s: can't load Daytime: %r\n", argv0); 45 raise "fail:load"; 46 } 47 48 # get password 49 id := hd args; 50 (dbdir, secret, expiry, err) := getuser(id); 51 if(dbdir == nil){ 52 if(err != nil){ 53 sys->fprint(stderr, "%s: can't get auth info for %s in %s: %s\n", argv0, id, keydb, err); 54 raise "fail:no key"; 55 } 56 sys->print("new account\n"); 57 } 58 for(;;){ 59 if(secret != nil) 60 sys->print("secret [default = don't change]: "); 61 else 62 sys->print("secret: "); 63 (ok, word) = readline(stdin, "rawon"); 64 if(!ok) 65 exit; 66 if(word == "" && secret != nil) 67 break; 68 if(len word >= 8) 69 break; 70 sys->print("!secret must be at least 8 characters\n"); 71 } 72 newsecret: array of byte; 73 if(word != ""){ 74 # confirm password change 75 word1 := word; 76 sys->print("confirm: "); 77 (ok, word) = readline(stdin, "rawon"); 78 if(!ok || word != word1) { 79 sys->print("Entries do not match. Authinfo record unchanged.\n"); 80 raise "fail:mismatch"; 81 } 82 83 pwbuf := array of byte word; 84 newsecret = array[Keyring->SHA1dlen] of byte; 85 kr->sha1(pwbuf, len pwbuf, newsecret, nil); 86 } 87 88 # get expiration time (midnight of date specified) 89 maxdate := "17012038"; # largest date possible without incurring integer overflow 90 now := daytime->now(); 91 tm := daytime->local(now); 92 tm.sec = 59; 93 tm.min = 59; 94 tm.hour = 23; 95 tm.year += 1; 96 if(dbdir == nil) 97 expsecs := daytime->tm2epoch(tm); # set expiration date to 23:59:59 one year from today 98 else 99 expsecs = expiry; 100 for(;;){ 101 defexpdate := "permanent"; 102 if(expsecs != 0) { 103 otm := daytime->local(expsecs); 104 defexpdate = sys->sprint("%2.2d%2.2d%4.4d", otm.mday, otm.mon+1, otm.year+1900); 105 } 106 sys->print("expires [DDMMYYYY/permanent, return = %s]: ", defexpdate); 107 (ok, word) = readline(stdin, "rawoff"); 108 if(!ok) 109 exit; 110 if(word == "") 111 word = defexpdate; 112 if(word == "permanent"){ 113 expsecs = 0; 114 break; 115 } 116 if(len word != 8){ 117 sys->print("!bad date format %s\n", word); 118 continue; 119 } 120 tm.mday = int word[0:2]; 121 if(tm.mday > 31 || tm.mday < 1){ 122 sys->print("!bad day of month %d\n", tm.mday); 123 continue; 124 } 125 tm.mon = int word[2:4] - 1; 126 if(tm.mon > 11 || tm.mday < 0){ 127 sys->print("!bad month %d\n", tm.mon + 1); 128 continue; 129 } 130 tm.year = int word[4:8] - 1900; 131 if(tm.year < 70){ 132 sys->print("!bad year %d (year may be no earlier than 1970)\n", tm.year + 1900); 133 continue; 134 } 135 expsecs = daytime->tm2epoch(tm); 136 if(expsecs > now) 137 break; 138 else { 139 newexpdate := sys->sprint("%2.2d%2.2d%4.4d", tm.mday, tm.mon+1, tm.year+1900); 140 tm = daytime->local(daytime->now()); 141 today := sys->sprint("%2.2d%2.2d%4.4d", tm.mday, tm.mon+1, tm.year+1900); 142 sys->print("!bad expiration date %s (must be between %s and %s)\n", newexpdate, today, maxdate); 143 expsecs = now; 144 } 145 } 146 newexpiry := expsecs; 147 148# # get the free form field 149# if(pw != nil) 150# npw.other = pw.other; 151# else 152# npw.other = ""; 153# sys->print("free form info [return = %s]: ", npw.other); 154# (ok, word) = readline(stdin,"rawoff"); 155# if(!ok) 156# exit; 157# if(word != "") 158# npw.other = word; 159 160 if(dbdir == nil){ 161 dbdir = keydb+"/"+id; 162 fd := sys->create(dbdir, Sys->OREAD, Sys->DMDIR|8r700); 163 if(fd == nil){ 164 sys->fprint(stderr, "%s: can't create account %s: %r\n", argv0, id); 165 raise "fail:create user"; 166 } 167 } 168 changed := 0; 169 if(!eq(newsecret, secret)){ 170 if(putsecret(dbdir, newsecret) < 0){ 171 sys->fprint(stderr, "%s: can't update secret for %s: %r\n", argv0, id); 172 raise "fail:update"; 173 } 174 changed = 1; 175 } 176 if(newexpiry != expiry){ 177 if(putexpiry(dbdir, newexpiry) < 0){ 178 sys->fprint(stderr, "%s: can't update expiry time for %s: %r\n", argv0, id); 179 raise "fail:update"; 180 } 181 changed = 1; 182 } 183 sys->print("change written\n"); 184} 185 186getuser(id: string): (string, array of byte, int, string) 187{ 188 (ok, nil) := sys->stat(keydb); 189 if(ok < 0) 190 return (nil, nil, 0, sys->sprint("can't stat %s: %r", id)); 191 dbdir := keydb+"/"+id; 192 (ok, nil) = sys->stat(dbdir); 193 if(ok < 0) 194 return (nil, nil, 0, nil); 195 fd := sys->open(dbdir+"/secret", Sys->OREAD); 196 if(fd == nil) 197 return (nil, nil, 0, sys->sprint("can't open %s/secret: %r", id)); 198 d: Sys->Dir; 199 (ok, d) = sys->fstat(fd); 200 if(ok < 0) 201 return (nil, nil, 0, sys->sprint("can't stat %s/secret: %r", id)); 202 l := int d.length; 203 secret: array of byte; 204 if(l > 0){ 205 secret = array[l] of byte; 206 if(sys->read(fd, secret, len secret) != len secret) 207 return (nil, nil, 0, sys->sprint("error reading %s/secret: %r", id)); 208 } 209 fd = sys->open(dbdir+"/expire", Sys->OREAD); 210 if(fd == nil) 211 return (nil, nil, 0, sys->sprint("can't open %s/expiry: %r", id)); 212 b := array[32] of byte; 213 n := sys->read(fd, b, len b); 214 if(n <= 0) 215 return (nil, nil, 0, sys->sprint("error reading %s/expiry: %r", id)); 216 return (dbdir, secret, int string b[0:n], nil); 217} 218 219eq(a, b: array of byte): int 220{ 221 if(len a != len b) 222 return 0; 223 for(i := 0; i < len a; i++) 224 if(a[i] != b[i]) 225 return 0; 226 return 1; 227} 228 229putsecret(dir: string, secret: array of byte): int 230{ 231 fd := sys->create(dir+"/secret", Sys->OWRITE, 8r600); 232 if(fd == nil) 233 return -1; 234 return sys->write(fd, secret, len secret); 235} 236 237putexpiry(dir: string, expiry: int): int 238{ 239 fd := sys->open(dir+"/expire", Sys->OWRITE); 240 if(fd == nil) 241 return -1; 242 return sys->fprint(fd, "%d", expiry); 243} 244 245readline(io: ref Sys->FD, mode: string): (int, string) 246{ 247 r : int; 248 line : string; 249 buf := array[8192] of byte; 250 fdctl : ref Sys->FD; 251 rawoff := array of byte "rawoff"; 252 253 # 254 # Change console mode to rawon 255 # 256 if(mode == "rawon"){ 257 fdctl = sys->open("/dev/consctl", sys->OWRITE); 258 if(fdctl == nil || sys->write(fdctl,array of byte mode,len mode) != len mode){ 259 sys->fprint(stderr, "unable to change console mode"); 260 return (0,nil); 261 } 262 } 263 264 # 265 # Read up to the CRLF 266 # 267 line = ""; 268 for(;;) { 269 r = sys->read(io, buf, len buf); 270 if(r <= 0){ 271 sys->fprint(stderr, "error read from console mode"); 272 if(mode == "rawon") 273 sys->write(fdctl,rawoff,6); 274 return (0, nil); 275 } 276 277 line += string buf[0:r]; 278 if ((len line >= 1) && (line[(len line)-1] == '\n')){ 279 if(mode == "rawon"){ 280 r = sys->write(stdout,array of byte "\n",1); 281 if(r <= 0) { 282 sys->write(fdctl,rawoff,6); 283 return (0, nil); 284 } 285 } 286 break; 287 } 288 else { 289 if(mode == "rawon"){ 290 #r = sys->write(stdout, array of byte "*",1); 291 if(r <= 0) { 292 sys->write(fdctl,rawoff,6); 293 return (0, nil); 294 } 295 } 296 } 297 } 298 299 if(mode == "rawon") 300 sys->write(fdctl,rawoff,6); 301 302 # Total success! 303 return (1, line[0:len line - 1]); 304} 305