nym.c (15627B)
1 /* Mixmaster version 3.0 -- (C) 1999 - 2006 Anonymizer Inc. and others. 2 3 Mixmaster may be redistributed and modified under certain conditions. 4 This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF 5 ANY KIND, either express or implied. See the file COPYRIGHT for 6 details. 7 8 Create nym server messages 9 $Id: nym.c 934 2006-06-24 13:40:39Z rabbi $ */ 10 11 12 #ifdef NYMSUPPORT 13 14 #include "mix3.h" 15 #include "pgp.h" 16 #include <string.h> 17 #include <time.h> 18 #include <assert.h> 19 20 int nym_config(int mode, char *nym, char *nymserver, BUFFER *pseudonym, 21 char *sendchain, int sendnumcopies, BUFFER *chains, 22 BUFFER *options) 23 { 24 #ifndef USE_PGP 25 return (-1); 26 #else /* end of not USE_PGP */ 27 REMAILER remailer[MAXREM]; 28 int badchains[MAXREM][MAXREM]; 29 KEYRING *r; 30 int maxrem; 31 int chain[20]; 32 char rchain[CHAINMAX]; 33 BUFFER *userid, *msg, *req, *k, *line, *ek, *eklist, *key, *pubkey, *out, 34 *oldchains; 35 int latency; 36 int err = -1; 37 int status; 38 int desttype = MSG_MAIL; 39 int rblock = 0; 40 BUFFER *nymlist, *userpass, *config; 41 LOCK *nymlock; 42 43 userid = buf_new(); 44 msg = buf_new(); 45 req = buf_new(); 46 k = buf_new(); 47 line = buf_new(); 48 ek = buf_new(); 49 eklist = buf_new(); 50 key = buf_new(); 51 pubkey = buf_new(); 52 out = buf_new(); 53 config = buf_new(); 54 nymlist = buf_new(); 55 userpass = buf_new(); 56 oldchains = buf_new(); 57 58 for (;;) { 59 user_pass(userpass); 60 if (user_confirmpass(userpass)) 61 break; 62 user_delpass(); 63 } 64 65 if (nymserver) { 66 maxrem = t1_rlist(remailer, badchains); 67 if (maxrem < 1) 68 return (-1); 69 if (chain_select(chain, nymserver, maxrem, remailer, 2, NULL) != 1) 70 return (-1); 71 if (chain[0] == 0) 72 chain[0] = chain_randfinal(MSG_MAIL, remailer, badchains, maxrem, 2, NULL, -1); 73 if (chain[0] == -1) 74 return (-1); 75 assert(strchr(nym, '@') == NULL && strchr(remailer[chain[0]].addr, '@')); 76 strcatn(nym, strchr(remailer[chain[0]].addr, '@'), LINELEN); 77 buf_appends(config, remailer[chain[0]].addr); 78 } else 79 assert(strchr(nym, '@') != NULL); 80 81 status = nymlist_getnym(nym, config->length ? NULL : config, eklist, NULL, 82 NULL, oldchains); 83 if (mode == NYM_CREATE && status == NYM_OK) 84 mode = NYM_MODIFY; 85 86 buf_appendc(userid, '<'); 87 buf_appends(userid, nym); 88 buf_appendc(userid, '>'); 89 90 buf_sets(req, "Config:\nFrom: "); 91 buf_append(req, nym, strchr(nym, '@') - nym); 92 buf_appends(req, "\nNym-Commands:"); 93 if (mode == NYM_CREATE) 94 buf_appends(req, " create?"); 95 if (mode == NYM_DELETE) 96 buf_appends(req, " delete"); 97 else { 98 if (options && options->length > 0) { 99 if (!bufleft(options, " ")) 100 buf_appendc(req, ' '); 101 buf_cat(req, options); 102 } 103 if (pseudonym && pseudonym->length > 0) { 104 buf_appends(req, " name=\""); 105 buf_cat(req, pseudonym); 106 buf_appendc(req, '\"'); 107 } 108 } 109 buf_nl(req); 110 if (mode == NYM_CREATE) { 111 buf_appends(req, "Public-Key:\n"); 112 113 getkey: 114 r = pgpdb_open(NYMSECRING, userpass, 0, PGP_TYPE_PRIVATE); 115 if (r == NULL) { 116 err = -3; 117 goto end; 118 } 119 if (r->filetype == -1) 120 r->filetype = 0; 121 122 while (pgpdb_getnext(r, key, NULL, userid) != -1) 123 if (pgp_makepubkey(key, NULL, pubkey, userpass, 0) == 0) 124 err = 0; 125 pgpdb_close(r); 126 if (err != 0) { 127 if (err == -2) 128 goto end; 129 err = -2; 130 if (pseudonym && pseudonym->length) { 131 buf_set(userid, pseudonym); 132 buf_appendc(userid, ' '); 133 } else 134 buf_clear(userid); 135 buf_appendf(userid, "<%s>", nym); 136 pgp_keygen(PGP_ES_RSA, 0, userid, userpass, NULL, NYMSECRING, 2); 137 goto getkey; 138 } 139 pgp_armor(pubkey, PGP_ARMOR_NYMKEY); 140 buf_cat(req, pubkey); 141 } 142 if (mode != NYM_DELETE) { 143 if (nymlist_read(nymlist) == -1) { 144 user_delpass(); 145 err = -1; 146 goto end; 147 } 148 if (chains) 149 for (;;) { 150 err = buf_getheader(chains, k, line); 151 if (err == -1 && rblock == 0) 152 break; 153 if (err != 0 && rblock == 1) { 154 buf_setrnd(ek, 16); 155 if (t1_encrypt(desttype, msg, rchain, latency, ek, NULL) != 0) { 156 err = -2; 157 goto end; 158 } 159 encode(ek, 0); 160 buf_cat(eklist, ek); 161 buf_nl(eklist); 162 buf_appends(req, "Reply-Block:\n"); 163 buf_cat(req, msg); 164 buf_clear(msg); 165 rblock = 0; 166 continue; 167 } 168 if (bufieq(k, "Chain")) 169 strncpy(rchain, line->data, sizeof(rchain)); 170 else if (bufieq(k, "Latency")) 171 sscanf(line->data, "%d", &latency); 172 else if (bufieq(k, "Null")) 173 desttype = MSG_NULL, rblock = 1; 174 else { 175 buf_appendheader(msg, k, line); 176 if (bufieq(k, "To")) 177 desttype = MSG_MAIL, rblock = 1; 178 if (bufieq(k, "Newsgroups")) 179 desttype = MSG_POST, rblock = 1; 180 } 181 } 182 } 183 nymlock = lockfile(NYMDB); 184 if (nymlist_read(nymlist) == 0) { 185 nymlist_del(nymlist, nym); 186 nymlist_append(nymlist, nym, config, options, pseudonym, 187 chains ? chains : oldchains, eklist, 188 mode == NYM_DELETE ? NYM_DELETED : 189 (status == -1 ? NYM_WAITING : status)); 190 nymlist_write(nymlist); 191 } else 192 err = -1; 193 unlockfile(nymlock); 194 195 #ifdef DEBUG 196 buf_write(req, stderr); 197 #endif /* DEBUG */ 198 buf_clear(line); 199 buf_appendc(line, '<'); 200 buf_cat(line, config); 201 buf_appendc(line, '>'); 202 203 err = pgp_encrypt(PGP_ENCRYPT | PGP_SIGN | PGP_TEXT | PGP_REMAIL, 204 req, line, userid, userpass, NULL, NYMSECRING); 205 if (err != 0) 206 goto end; 207 #ifdef DEBUG 208 buf_write(req, stderr); 209 #endif /* DEBUG */ 210 buf_sets(out, "To: "); 211 buf_cat(out, config); 212 buf_nl(out); 213 buf_nl(out); 214 buf_cat(out, req); 215 216 err = mix_encrypt(desttype, out, sendchain, sendnumcopies, line); 217 if (err) 218 mix_status("%s\n", line->data); 219 220 end: 221 if (strchr(nym, '@')) *strchr(nym, '@') = '\0'; 222 buf_free(userid); 223 buf_free(msg); 224 buf_free(req); 225 buf_free(k); 226 buf_free(line); 227 buf_free(ek); 228 buf_free(eklist); 229 buf_free(key); 230 buf_free(pubkey); 231 buf_free(out); 232 buf_free(nymlist); 233 buf_free(userpass); 234 buf_free(oldchains); 235 buf_free(config); 236 return (err); 237 #endif /* else if USE_PGP */ 238 } 239 240 int nym_encrypt(BUFFER *msg, char *nym, int type) 241 { 242 #ifndef USE_PGP 243 return (-1); 244 #else /* end of not USE_PGP */ 245 BUFFER *out, *userpass, *sig, *config; 246 int err = -1; 247 248 out = buf_new(); 249 userpass = buf_new(); 250 sig = buf_new(); 251 config = buf_new(); 252 253 if (nymlist_getnym(nym, config, NULL, NULL, NULL, NULL) == NYM_OK) { 254 buf_appends(out, "From: "); 255 buf_append(out, nym, strchr(nym, '@') - nym); 256 buf_nl(out); 257 if (type == MSG_POST) { 258 buf_appends(out, "To: "); 259 buf_appends(out, MAILtoNEWS); 260 buf_nl(out); 261 } 262 buf_cat(out, msg); 263 mail_encode(out, 0); 264 buf_appendc(sig, '<'); 265 buf_appends(sig, nym); 266 buf_appendc(sig, '>'); 267 #ifdef DEBUG 268 buf_write(out, stderr); 269 #endif /* DEBUG */ 270 user_pass(userpass); 271 err = pgp_encrypt(PGP_ENCRYPT | PGP_SIGN | PGP_TEXT | PGP_REMAIL, 272 out, config, sig, userpass, NULL, NYMSECRING); 273 if (err == 0) { 274 buf_clear(msg); 275 buf_appends(msg, "To: send"); 276 buf_appends(msg, strchr(nym, '@')); 277 buf_nl(msg); 278 buf_nl(msg); 279 buf_cat(msg, out); 280 } 281 } 282 buf_free(out); 283 buf_free(config); 284 buf_free(userpass); 285 buf_free(sig); 286 return (err); 287 #endif /* else if USE_PGP */ 288 } 289 290 int nym_decrypt(BUFFER *msg, char *thisnym, BUFFER *log) 291 { 292 #ifndef USE_PGP 293 return (-1); 294 #else /* end of not USE_PGP */ 295 BUFFER *pgpmsg, *out, *line; 296 BUFFER *nymlist, *userpass; 297 BUFFER *decr, *sig, *mid; 298 BUFFER *name, *rblocks, *eklist, *config; 299 int decrypted = 0; 300 int err = 1; 301 long ptr; 302 char nym[LINELEN]; 303 BUFFER *ek, *opt; 304 int status; 305 LOCK *nymlock; 306 time_t t; 307 struct tm *tc; 308 char timeline[LINELEN]; 309 310 pgpmsg = buf_new(); 311 out = buf_new(); 312 line = buf_new(); 313 nymlist = buf_new(); 314 userpass = buf_new(); 315 config = buf_new(); 316 ek = buf_new(); 317 decr = buf_new(); 318 sig = buf_new(); 319 mid = buf_new(); 320 opt = buf_new(); 321 name = buf_new(); 322 rblocks = buf_new(); 323 eklist = buf_new(); 324 325 if (thisnym) 326 thisnym[0] = '\0'; 327 while ((ptr = msg->ptr, err = buf_getline(msg, line)) != -1) { 328 if (bufleft(line, begin_pgpmsg)) { 329 err = -1; 330 msg->ptr = ptr; 331 pgp_dearmor(msg, pgpmsg); 332 if (pgp_isconventional(pgpmsg)) { 333 user_pass(userpass); 334 nymlock = lockfile(NYMDB); 335 if (nymlist_read(nymlist) == -1) 336 user_delpass(); 337 while (nymlist_get(nymlist, nym, config, eklist, opt, name, 338 rblocks, &status) >= 0) { 339 while (buf_getline(eklist, ek) == 0) { 340 decode(ek, ek); 341 if (t1_getreply(pgpmsg, ek, 20) == 0) { 342 buf_clear(out); 343 err = pgp_decrypt(pgpmsg, userpass, sig, NULL, 344 NYMSECRING); 345 buf_sets(out, "From nymserver "); 346 if (strchr(sig->data, '[') && strchr(sig->data, ']')) 347 buf_append(out, strchr(sig->data, '[') + 1, 348 strchr(sig->data, ']') - 349 strchr(sig->data, '[') - 1); 350 else { 351 t = time(NULL); 352 tc = localtime(&t); 353 strftime(timeline, LINELEN, "%a %b %d %H:%M:%S %Y", tc); 354 buf_appends(out, timeline); 355 } 356 buf_nl(out); 357 if (err == PGP_SIGOK && 358 bufifind(sig, config->data)) { 359 buf_appends(out, "Nym: "); 360 if (status == NYM_WAITING) 361 buf_appends(out, "confirm "); 362 buf_appends(out, nym); 363 buf_nl(out); 364 if (thisnym && status == NYM_OK) 365 strncpy(thisnym, nym, LINELEN); 366 } else 367 buf_appends(out, "Warning: Signature verification failed!\n"); 368 buf_cat(out, pgpmsg); 369 decrypted = 2; 370 if (log) { 371 digest_md5(out, mid); 372 encode(mid, 0); 373 if (buffind(log, mid->data)) { 374 decrypted = -1; 375 unlockfile(nymlock); 376 goto end; 377 } else { 378 buf_cat(log, mid); 379 buf_nl(log); 380 } 381 } 382 if (status == NYM_WAITING) { 383 nymlist_del(nymlist, nym); 384 nymlist_append(nymlist, nym, config, opt, 385 name, rblocks, eklist, NYM_OK); 386 } 387 break; 388 } 389 } 390 } 391 nymlist_write(nymlist); 392 unlockfile(nymlock); 393 } 394 if (decrypted == 0) { 395 user_pass(userpass); 396 err = pgp_decrypt(pgpmsg, userpass, sig, PGPPUBRING, PGPSECRING); 397 if (err == PGP_ERR) 398 err = pgp_decrypt(pgpmsg, userpass, sig, PGPPUBRING, 399 NYMSECRING); 400 #if 0 401 if (err == PGP_PASS || err == PGP_ERR) 402 user_delpass(); 403 #endif /* 0 */ 404 if (err != PGP_ERR && err != PGP_PASS && err != PGP_NOMSG && 405 err != PGP_NODATA) { 406 buf_appends(out, info_beginpgp); 407 if (err == PGP_SIGOK) 408 buf_appendf(out, " (SIGNED)\n%s%b", info_pgpsig, sig); 409 buf_nl(out); 410 buf_cat(out, pgpmsg); 411 buf_appends(out, info_endpgp); 412 buf_nl(out); 413 decrypted = 1; 414 } 415 } 416 if (decrypted == 0) { 417 buf_cat(out, line); 418 buf_nl(out); 419 } 420 } else { 421 if (bufileft(line, info_beginpgp)) 422 buf_appendc(out, ' '); /* escape info line in text */ 423 buf_cat(out, line); 424 buf_nl(out); 425 } 426 } 427 428 if (decrypted) 429 buf_move(msg, out); 430 else 431 buf_rewind(msg); 432 if (decrypted == 2) 433 nym_decrypt(msg, thisnym, NULL); 434 end: 435 buf_free(pgpmsg); 436 buf_free(out); 437 buf_free(line); 438 buf_free(decr); 439 buf_free(sig); 440 buf_free(mid); 441 buf_free(opt); 442 buf_free(name); 443 buf_free(config); 444 buf_free(rblocks); 445 buf_free(eklist); 446 buf_free(nymlist); 447 buf_free(userpass); 448 buf_free(ek); 449 return (decrypted); 450 #endif /* else if USE_PGP */ 451 } 452 453 int nymlist_read(BUFFER *list) 454 { 455 #ifdef USE_PGP 456 BUFFER *key; 457 458 #endif /* USE_PGP */ 459 FILE *f; 460 int err = 0; 461 462 buf_clear(list); 463 f = mix_openfile(NYMDB, "rb"); 464 if (f != NULL) { 465 buf_read(list, f); 466 fclose(f); 467 #ifdef USE_PGP 468 key = buf_new(); 469 user_pass(key); 470 if (key->length) 471 if (pgp_decrypt(list, key, NULL, NULL, NULL) < 0) { 472 buf_clear(list); 473 err = -1; 474 } 475 buf_free(key); 476 #endif /* USE_PGP */ 477 } 478 return (err); 479 } 480 481 int nymlist_write(BUFFER *list) 482 { 483 #ifdef USE_PGP 484 BUFFER *key; 485 486 #endif /* USE_PGP */ 487 FILE *f; 488 489 if (list->length == 0) 490 return (-1); 491 492 #ifdef USE_PGP 493 key = buf_new(); 494 user_pass(key); 495 if (key->length) 496 pgp_encrypt(PGP_NCONVENTIONAL | PGP_NOARMOR, list, key, NULL, NULL, NULL, 497 NULL); 498 buf_free(key); 499 #endif /* USE_PGP */ 500 f = mix_openfile(NYMDB, "wb"); 501 if (f == NULL) 502 return (-1); 503 else { 504 buf_write(list, f); 505 fclose(f); 506 } 507 return (0); 508 } 509 510 int nymlist_get(BUFFER *list, char *nym, BUFFER *config, BUFFER *ek, 511 BUFFER *opt, BUFFER *name, BUFFER *chains, int *status) 512 { 513 BUFFER *line; 514 int err = -1; 515 516 line = buf_new(); 517 if (ek) 518 buf_clear(ek); 519 if (opt) 520 buf_clear(opt); 521 if (name) 522 buf_clear(name); 523 if (chains) 524 buf_clear(chains); 525 if (config) 526 buf_clear(config); 527 528 for (;;) { 529 if (buf_getline(list, line) == -1) 530 goto end; 531 if (bufleft(line, "nym=")) 532 break; 533 } 534 strncpy(nym, line->data + 4, LINELEN); 535 536 for (;;) { 537 if (buf_getline(list, line) == -1) 538 break; 539 if (opt && bufleft(line, "opt=")) 540 line->ptr = 4, buf_rest(opt, line); 541 if (name && bufleft(line, "name=")) 542 line->ptr = 5, buf_rest(name, line); 543 if (config && bufleft(line, "config=")) 544 line->ptr = 7, buf_rest(config, line); 545 if (bufeq(line, "ek=")) { 546 while (buf_getline(list, line) == 0 && !bufeq(line, "end ek")) 547 if (ek) { 548 buf_cat(ek, line); 549 buf_nl(ek); 550 } 551 } 552 if (bufeq(line, "chains=")) { 553 while (buf_getline(list, line) == 0 && !bufeq(line, "end chains")) 554 if (chains) { 555 buf_cat(chains, line); 556 buf_nl(chains); 557 } 558 } 559 if (status && bufleft(line, "status=")) 560 *status = line->data[7] - '0'; 561 if (bufeq(line, "end")) { 562 err = 0; 563 break; 564 } 565 } 566 end: 567 buf_free(line); 568 return (err); 569 } 570 571 int nymlist_append(BUFFER *list, char *nym, BUFFER *config, BUFFER *opt, 572 BUFFER *name, BUFFER *rblocks, BUFFER *eklist, int status) 573 { 574 buf_appends(list, "nym="); 575 buf_appends(list, nym); 576 buf_nl(list); 577 buf_appends(list, "config="); 578 buf_cat(list, config); 579 buf_nl(list); 580 buf_appends(list, "status="); 581 buf_appendc(list, (byte) (status + '0')); 582 buf_nl(list); 583 if (name) { 584 buf_appends(list, "name="); 585 buf_cat(list, name); 586 buf_nl(list); 587 } 588 buf_appends(list, "opt="); 589 buf_cat(list, opt); 590 buf_nl(list); 591 buf_appends(list, "chains=\n"); 592 buf_cat(list, rblocks); 593 buf_appends(list, "end chains\n"); 594 buf_appends(list, "ek=\n"); 595 buf_cat(list, eklist); 596 buf_appends(list, "end ek\n"); 597 buf_appends(list, "end\n"); 598 return (0); 599 } 600 601 int nymlist_del(BUFFER *list, char *nym) 602 { 603 BUFFER *new; 604 char thisnym[LINELEN]; 605 BUFFER *config, *ek, *name, *rblocks, *opt; 606 int thisstatus; 607 608 new = buf_new(); 609 config = buf_new(); 610 ek = buf_new(); 611 name = buf_new(); 612 rblocks = buf_new(); 613 opt = buf_new(); 614 615 buf_rewind(list); 616 while (nymlist_get(list, thisnym, config, ek, opt, name, rblocks, 617 &thisstatus) >= 0) 618 if (!streq(nym, thisnym)) 619 nymlist_append(new, thisnym, config, opt, name, rblocks, ek, 620 thisstatus); 621 622 buf_move(list, new); 623 buf_free(new); 624 buf_free(name); 625 buf_free(opt); 626 buf_free(rblocks); 627 buf_free(config); 628 buf_free(ek); 629 return (0); 630 } 631 632 int nymlist_getnym(char *nym, BUFFER *config, BUFFER *ek, BUFFER *opt, 633 BUFFER *name, BUFFER *rblocks) 634 /* "nym@nymserver.domain" or "nym@" */ 635 { 636 BUFFER *nymlist, *userpass; 637 char n[LINELEN]; 638 int err = -1; 639 int status; 640 641 nymlist = buf_new(); 642 userpass = buf_new(); 643 644 user_pass(userpass); 645 if (nymlist_read(nymlist) != -1) { 646 while (nymlist_get(nymlist, n, config, ek, opt, name, rblocks, 647 &status) >= 0) 648 if (streq(nym, n) || (nym[strlen(nym) - 1] == '@' && strleft(n, nym))) { 649 err = status; 650 strncpy(nym, n, LINELEN); 651 break; 652 } 653 } 654 buf_free(userpass); 655 buf_free(nymlist); 656 return (err); 657 } 658 659 int nymlist_getstatus(char *nym) 660 { 661 int status; 662 663 if ((status = nymlist_getnym(nym, NULL, NULL, NULL, NULL, NULL)) == 0) 664 return (status); 665 else 666 return (-1); 667 } 668 669 #endif /* NYMSUPPORT */