p9sk1.c (7389B)
1 /* 2 * p9sk1, p9sk2 - Plan 9 secret (private) key authentication. 3 * p9sk2 is an incomplete flawed variant of p9sk1. 4 * 5 * Client protocol: 6 * write challenge[challen] (p9sk1 only) 7 * read tickreq[tickreqlen] 8 * write ticket[ticketlen] 9 * read authenticator[authentlen] 10 * 11 * Server protocol: 12 * read challenge[challen] (p9sk1 only) 13 * write tickreq[tickreqlen] 14 * read ticket[ticketlen] 15 * write authenticator[authentlen] 16 */ 17 18 #include "std.h" 19 #include "dat.h" 20 21 extern Proto p9sk1, p9sk2; 22 static int gettickets(Ticketreq*, char*, Key*); 23 24 #define max(a, b) ((a) > (b) ? (a) : (b)) 25 enum 26 { 27 MAXAUTH = max(TICKREQLEN, TICKETLEN+max(TICKETLEN, AUTHENTLEN)) 28 }; 29 30 static int 31 p9skclient(Conv *c) 32 { 33 char *user; 34 char cchal[CHALLEN]; 35 uchar secret[8]; 36 char buf[MAXAUTH]; 37 int speakfor, ret; 38 Attr *a; 39 Authenticator au; 40 Key *k; 41 Ticket t; 42 Ticketreq tr; 43 44 ret = -1; 45 a = nil; 46 k = nil; 47 48 /* p9sk1: send client challenge */ 49 if(c->proto == &p9sk1){ 50 c->state = "write challenge"; 51 memrandom(cchal, CHALLEN); 52 if(convwrite(c, cchal, CHALLEN) < 0) 53 goto out; 54 } 55 56 /* read ticket request */ 57 c->state = "read tickreq"; 58 if(convread(c, buf, TICKREQLEN) < 0) 59 goto out; 60 convM2TR(buf, &tr); 61 62 /* p9sk2: use server challenge as client challenge */ 63 if(c->proto == &p9sk2) 64 memmove(cchal, tr.chal, CHALLEN); 65 66 /* 67 * find a key. 68 * 69 * if the user is the factotum owner, any key will do. 70 * if not, then if we have a speakfor key, 71 * we will only vouch for the user's local identity. 72 * 73 * this logic is duplicated in p9any.c 74 */ 75 user = strfindattr(c->attr, "user"); 76 a = delattr(copyattr(c->attr), "role"); 77 a = addattr(a, "proto=p9sk1"); 78 79 if(strcmp(c->sysuser, owner) == 0){ 80 speakfor = 0; 81 a = addattr(a, "proto=p9sk1 user? dom=%q", tr.authdom); 82 }else if(user==nil || strcmp(c->sysuser, user)==0){ 83 speakfor = 1; 84 a = delattr(a, "user"); 85 a = addattr(a, "proto=p9sk1 user? dom=%q role=speakfor", tr.authdom); 86 }else{ 87 werrstr("will not authenticate for %q as %q", c->sysuser, user); 88 goto out; 89 } 90 91 for(;;){ 92 c->state = "find key"; 93 k = keyfetch(c, "%A", a); 94 if(k == nil) 95 goto out; 96 97 /* relay ticket request to auth server, get tickets */ 98 strcpy(tr.hostid, strfindattr(k->attr, "user")); 99 if(speakfor) 100 strcpy(tr.uid, c->sysuser); 101 else 102 strcpy(tr.uid, tr.hostid); 103 104 c->state = "get tickets"; 105 if(gettickets(&tr, buf, k) < 0) 106 goto out; 107 108 convM2T(buf, &t, k->priv); 109 if(t.num == AuthTc) 110 break; 111 112 /* we don't agree with the auth server about the key; try again */ 113 c->state = "replace key"; 114 if((k = keyreplace(c, k, "key mismatch with auth server")) == nil){ 115 werrstr("key mismatch with auth server"); 116 goto out; 117 } 118 } 119 120 /* send second ticket and authenticator to server */ 121 c->state = "write ticket+auth"; 122 memmove(buf, buf+TICKETLEN, TICKETLEN); 123 au.num = AuthAc; 124 memmove(au.chal, tr.chal, CHALLEN); 125 au.id = 0; 126 convA2M(&au, buf+TICKETLEN, t.key); 127 if(convwrite(c, buf, TICKETLEN+AUTHENTLEN) < 0) 128 goto out; 129 130 /* read authenticator from server */ 131 c->state = "read auth"; 132 if(convread(c, buf, AUTHENTLEN) < 0) 133 goto out; 134 convM2A(buf, &au, t.key); 135 if(au.num != AuthAs || memcmp(au.chal, cchal, CHALLEN) != 0 || au.id != 0){ 136 werrstr("server lies through his teeth"); 137 goto out; 138 } 139 140 /* success */ 141 c->attr = addcap(c->attr, c->sysuser, &t); 142 flog("p9skclient success %A", c->attr); /* before adding secret! */ 143 des56to64((uchar*)t.key, secret); 144 c->attr = addattr(c->attr, "secret=%.8H", secret); 145 ret = 0; 146 147 out: 148 if(ret < 0) 149 flog("p9skclient: %r"); 150 freeattr(a); 151 keyclose(k); 152 return ret; 153 } 154 155 static int 156 p9skserver(Conv *c) 157 { 158 char cchal[CHALLEN], buf[MAXAUTH]; 159 uchar secret[8]; 160 int ret; 161 Attr *a; 162 Authenticator au; 163 Key *k; 164 Ticketreq tr; 165 Ticket t; 166 167 ret = -1; 168 169 a = addattr(copyattr(c->attr), "user? dom?"); 170 a = addattr(a, "user? dom? proto=p9sk1"); 171 if((k = keyfetch(c, "%A", a)) == nil) 172 goto out; 173 174 /* p9sk1: read client challenge */ 175 if(c->proto == &p9sk1){ 176 if(convread(c, cchal, CHALLEN) < 0) 177 goto out; 178 } 179 180 /* send ticket request */ 181 memset(&tr, 0, sizeof tr); 182 tr.type = AuthTreq; 183 strcpy(tr.authid, strfindattr(k->attr, "user")); 184 strcpy(tr.authdom, strfindattr(k->attr, "dom")); 185 memrandom(tr.chal, sizeof tr.chal); 186 convTR2M(&tr, buf); 187 if(convwrite(c, buf, TICKREQLEN) < 0) 188 goto out; 189 190 /* p9sk2: use server challenge as client challenge */ 191 if(c->proto == &p9sk2) 192 memmove(cchal, tr.chal, sizeof tr.chal); 193 194 /* read ticket+authenticator */ 195 if(convread(c, buf, TICKETLEN+AUTHENTLEN) < 0) 196 goto out; 197 198 convM2T(buf, &t, k->priv); 199 if(t.num != AuthTs || memcmp(t.chal, tr.chal, CHALLEN) != 0){ 200 /* BUG badkey */ 201 werrstr("key mismatch with auth server"); 202 goto out; 203 } 204 205 convM2A(buf+TICKETLEN, &au, t.key); 206 if(au.num != AuthAc || memcmp(au.chal, tr.chal, CHALLEN) != 0 || au.id != 0){ 207 werrstr("client lies through his teeth"); 208 goto out; 209 } 210 211 /* send authenticator */ 212 au.num = AuthAs; 213 memmove(au.chal, cchal, CHALLEN); 214 convA2M(&au, buf, t.key); 215 if(convwrite(c, buf, AUTHENTLEN) < 0) 216 goto out; 217 218 /* success */ 219 c->attr = addcap(c->attr, c->sysuser, &t); 220 flog("p9skserver success %A", c->attr); /* before adding secret! */ 221 des56to64((uchar*)t.key, secret); 222 c->attr = addattr(c->attr, "secret=%.8H", secret); 223 ret = 0; 224 225 out: 226 if(ret < 0) 227 flog("p9skserver: %r"); 228 freeattr(a); 229 keyclose(k); 230 return ret; 231 } 232 233 int 234 _asgetticket(int fd, char *trbuf, char *tbuf) 235 { 236 if(write(fd, trbuf, TICKREQLEN) < 0){ 237 close(fd); 238 return -1; 239 } 240 return _asrdresp(fd, tbuf, 2*TICKETLEN); 241 } 242 static int 243 getastickets(Ticketreq *tr, char *buf) 244 { 245 int asfd; 246 int ret; 247 248 if((asfd = xioauthdial(nil, tr->authdom)) < 0) 249 return -1; 250 convTR2M(tr, buf); 251 ret = xioasgetticket(asfd, buf, buf); 252 xioclose(asfd); 253 return ret; 254 } 255 256 static int 257 mktickets(Ticketreq *tr, char *buf, Key *k) 258 { 259 Ticket t; 260 261 if(strcmp(tr->authid, tr->hostid) != 0) 262 return -1; 263 264 memset(&t, 0, sizeof t); 265 memmove(t.chal, tr->chal, CHALLEN); 266 strcpy(t.cuid, tr->uid); 267 strcpy(t.suid, tr->uid); 268 memrandom(t.key, DESKEYLEN); 269 t.num = AuthTc; 270 convT2M(&t, buf, k->priv); 271 t.num = AuthTs; 272 convT2M(&t, buf+TICKETLEN, k->priv); 273 return 0; 274 } 275 276 static int 277 gettickets(Ticketreq *tr, char *buf, Key *k) 278 { 279 if(getastickets(tr, buf) == 0) 280 return 0; 281 if(mktickets(tr, buf, k) == 0) 282 return 0; 283 werrstr("gettickets: %r"); 284 return -1; 285 } 286 287 static int 288 p9sk1check(Key *k) 289 { 290 char *user, *dom, *pass; 291 Ticketreq tr; 292 293 user = strfindattr(k->attr, "user"); 294 dom = strfindattr(k->attr, "dom"); 295 if(user==nil || dom==nil){ 296 werrstr("need user and dom attributes"); 297 return -1; 298 } 299 if(strlen(user) >= sizeof tr.authid){ 300 werrstr("user name too long"); 301 return -1; 302 } 303 if(strlen(dom) >= sizeof tr.authdom){ 304 werrstr("auth dom name too long"); 305 return -1; 306 } 307 308 k->priv = emalloc(DESKEYLEN); 309 if(pass = strfindattr(k->privattr, "!password")) 310 passtokey(k->priv, pass); 311 else if(pass = strfindattr(k->privattr, "!hex")){ 312 if(hexparse(pass, k->priv, 7) < 0){ 313 werrstr("malformed !hex key data"); 314 return -1; 315 } 316 }else{ 317 werrstr("need !password or !hex attribute"); 318 return -1; 319 } 320 321 return 0; 322 } 323 324 static void 325 p9sk1close(Key *k) 326 { 327 free(k->priv); 328 k->priv = nil; 329 } 330 331 static Role 332 p9sk1roles[] = 333 { 334 "client", p9skclient, 335 "server", p9skserver, 336 0 337 }; 338 339 static Role 340 p9sk2roles[] = 341 { 342 "client", p9skclient, 343 "server", p9skserver, 344 0 345 }; 346 347 Proto p9sk1 = { 348 "p9sk1", 349 p9sk1roles, 350 "user? dom? !password?", 351 p9sk1check, 352 p9sk1close 353 }; 354 355 Proto p9sk2 = { 356 "p9sk2", 357 p9sk2roles 358 };