pop3.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. #include "common.h"
  2. #include <ctype.h>
  3. #include <plumb.h>
  4. #include <libsec.h>
  5. #include <auth.h>
  6. #include "dat.h"
  7. #pragma varargck type "M" uchar*
  8. #pragma varargck argpos pop3cmd 2
  9. typedef struct Pop Pop;
  10. struct Pop {
  11. char *freep; // free this to free the strings below
  12. char *host;
  13. char *user;
  14. char *port;
  15. int ppop;
  16. int refreshtime;
  17. int debug;
  18. int pipeline;
  19. int encrypted;
  20. int needtls;
  21. int notls;
  22. int needssl;
  23. // open network connection
  24. Biobuf bin;
  25. Biobuf bout;
  26. int fd;
  27. Thumbprint *thumb;
  28. };
  29. char*
  30. geterrstr(void)
  31. {
  32. static char err[64];
  33. err[0] = '\0';
  34. errstr(err, sizeof(err));
  35. return err;
  36. }
  37. //
  38. // get pop3 response line , without worrying
  39. // about multiline responses; the clients
  40. // will deal with that.
  41. //
  42. static int
  43. isokay(char *s)
  44. {
  45. return s!=nil && strncmp(s, "+OK", 3)==0;
  46. }
  47. static void
  48. pop3cmd(Pop *pop, char *fmt, ...)
  49. {
  50. char buf[128], *p;
  51. va_list va;
  52. va_start(va, fmt);
  53. vseprint(buf, buf+sizeof(buf), fmt, va);
  54. va_end(va);
  55. p = buf+strlen(buf);
  56. if(p > (buf+sizeof(buf)-3))
  57. sysfatal("pop3 command too long");
  58. if(pop->debug)
  59. fprint(2, "<- %s\n", buf);
  60. strcpy(p, "\r\n");
  61. Bwrite(&pop->bout, buf, strlen(buf));
  62. Bflush(&pop->bout);
  63. }
  64. static char*
  65. pop3resp(Pop *pop)
  66. {
  67. char *s;
  68. char *p;
  69. alarm(60*1000);
  70. if((s = Brdline(&pop->bin, '\n')) == nil){
  71. close(pop->fd);
  72. pop->fd = -1;
  73. alarm(0);
  74. return "unexpected eof";
  75. }
  76. alarm(0);
  77. p = s+Blinelen(&pop->bin)-1;
  78. while(p >= s && (*p == '\r' || *p == '\n'))
  79. *p-- = '\0';
  80. if(pop->debug)
  81. fprint(2, "-> %s\n", s);
  82. return s;
  83. }
  84. static int
  85. pop3log(char *fmt, ...)
  86. {
  87. va_list ap;
  88. va_start(ap,fmt);
  89. syslog(0, "/sys/log/pop3", fmt, ap);
  90. va_end(ap);
  91. return 0;
  92. }
  93. static char*
  94. pop3pushtls(Pop *pop)
  95. {
  96. int fd;
  97. uchar digest[SHA1dlen];
  98. TLSconn conn;
  99. memset(&conn, 0, sizeof conn);
  100. // conn.trace = pop3log;
  101. fd = tlsClient(pop->fd, &conn);
  102. if(fd < 0)
  103. return "tls error";
  104. if(conn.cert==nil || conn.certlen <= 0){
  105. close(fd);
  106. return "server did not provide TLS certificate";
  107. }
  108. sha1(conn.cert, conn.certlen, digest, nil);
  109. if(!pop->thumb || !okThumbprint(digest, pop->thumb)){
  110. fmtinstall('H', encodefmt);
  111. close(fd);
  112. free(conn.cert);
  113. fprint(2, "upas/fs pop3: server certificate %.*H not recognized\n", SHA1dlen, digest);
  114. return "bad server certificate";
  115. }
  116. free(conn.cert);
  117. close(pop->fd);
  118. pop->fd = fd;
  119. pop->encrypted = 1;
  120. Binit(&pop->bin, pop->fd, OREAD);
  121. Binit(&pop->bout, pop->fd, OWRITE);
  122. return nil;
  123. }
  124. //
  125. // get capability list, possibly start tls
  126. //
  127. static char*
  128. pop3capa(Pop *pop)
  129. {
  130. char *s;
  131. int hastls;
  132. pop3cmd(pop, "CAPA");
  133. if(!isokay(pop3resp(pop)))
  134. return nil;
  135. hastls = 0;
  136. for(;;){
  137. s = pop3resp(pop);
  138. if(strcmp(s, ".") == 0 || strcmp(s, "unexpected eof") == 0)
  139. break;
  140. if(strcmp(s, "STLS") == 0)
  141. hastls = 1;
  142. if(strcmp(s, "PIPELINING") == 0)
  143. pop->pipeline = 1;
  144. }
  145. if(hastls && !pop->notls){
  146. pop3cmd(pop, "STLS");
  147. if(!isokay(s = pop3resp(pop)))
  148. return s;
  149. if((s = pop3pushtls(pop)) != nil)
  150. return s;
  151. }
  152. return nil;
  153. }
  154. //
  155. // log in using APOP if possible, password if allowed by user
  156. //
  157. static char*
  158. pop3login(Pop *pop)
  159. {
  160. int n;
  161. char *s, *p, *q;
  162. char ubuf[128], user[128];
  163. char buf[500];
  164. UserPasswd *up;
  165. s = pop3resp(pop);
  166. if(!isokay(s))
  167. return "error in initial handshake";
  168. if(pop->user)
  169. snprint(ubuf, sizeof ubuf, " user=%q", pop->user);
  170. else
  171. ubuf[0] = '\0';
  172. // look for apop banner
  173. if(pop->ppop==0 && (p = strchr(s, '<')) && (q = strchr(p+1, '>'))) {
  174. *++q = '\0';
  175. if((n=auth_respond(p, q-p, user, sizeof user, buf, sizeof buf, auth_getkey, "proto=apop role=client server=%q%s",
  176. pop->host, ubuf)) < 0)
  177. return "factotum failed";
  178. if(user[0]=='\0')
  179. return "factotum did not return a user name";
  180. if(s = pop3capa(pop))
  181. return s;
  182. pop3cmd(pop, "APOP %s %.*s", user, n, buf);
  183. if(!isokay(s = pop3resp(pop)))
  184. return s;
  185. return nil;
  186. } else {
  187. if(pop->ppop == 0)
  188. return "no APOP hdr from server";
  189. if(s = pop3capa(pop))
  190. return s;
  191. if(pop->needtls && !pop->encrypted)
  192. return "could not negotiate TLS";
  193. up = auth_getuserpasswd(auth_getkey, "proto=pass service=pop dom=%q%s",
  194. pop->host, ubuf);
  195. if(up == nil)
  196. return "no usable keys found";
  197. pop3cmd(pop, "USER %s", up->user);
  198. if(!isokay(s = pop3resp(pop))){
  199. free(up);
  200. return s;
  201. }
  202. pop3cmd(pop, "PASS %s", up->passwd);
  203. free(up);
  204. if(!isokay(s = pop3resp(pop)))
  205. return s;
  206. return nil;
  207. }
  208. }
  209. //
  210. // dial and handshake with pop server
  211. //
  212. static char*
  213. pop3dial(Pop *pop)
  214. {
  215. char *err;
  216. if((pop->fd = dial(netmkaddr(pop->host, "net", pop->needssl ? "pop3s" : "pop3"), 0, 0, 0)) < 0)
  217. return geterrstr();
  218. if(pop->needssl){
  219. if((err = pop3pushtls(pop)) != nil)
  220. return err;
  221. }else{
  222. Binit(&pop->bin, pop->fd, OREAD);
  223. Binit(&pop->bout, pop->fd, OWRITE);
  224. }
  225. if(err = pop3login(pop)) {
  226. close(pop->fd);
  227. return err;
  228. }
  229. return nil;
  230. }
  231. //
  232. // close connection
  233. //
  234. static void
  235. pop3hangup(Pop *pop)
  236. {
  237. pop3cmd(pop, "QUIT");
  238. pop3resp(pop);
  239. close(pop->fd);
  240. }
  241. //
  242. // download a single message
  243. //
  244. static char*
  245. pop3download(Pop *pop, Message *m)
  246. {
  247. char *s, *f[3], *wp, *ep;
  248. char sdigest[SHA1dlen*2+1];
  249. int i, l, sz;
  250. if(!pop->pipeline)
  251. pop3cmd(pop, "LIST %d", m->mesgno);
  252. if(!isokay(s = pop3resp(pop)))
  253. return s;
  254. if(tokenize(s, f, 3) != 3)
  255. return "syntax error in LIST response";
  256. if(atoi(f[1]) != m->mesgno)
  257. return "out of sync with pop3 server";
  258. sz = atoi(f[2])+200; /* 200 because the plan9 pop3 server lies */
  259. if(sz == 0)
  260. return "invalid size in LIST response";
  261. m->start = wp = emalloc(sz+1);
  262. ep = wp+sz;
  263. if(!pop->pipeline)
  264. pop3cmd(pop, "RETR %d", m->mesgno);
  265. if(!isokay(s = pop3resp(pop))) {
  266. m->start = nil;
  267. free(wp);
  268. return s;
  269. }
  270. s = nil;
  271. while(wp <= ep) {
  272. s = pop3resp(pop);
  273. if(strcmp(s, "unexpected eof") == 0) {
  274. free(m->start);
  275. m->start = nil;
  276. return "unexpected end of conversation";
  277. }
  278. if(strcmp(s, ".") == 0)
  279. break;
  280. l = strlen(s)+1;
  281. if(s[0] == '.') {
  282. s++;
  283. l--;
  284. }
  285. /*
  286. * grow by 10%/200bytes - some servers
  287. * lie about message sizes
  288. */
  289. if(wp+l > ep) {
  290. int pos = wp - m->start;
  291. sz += ((sz / 10) < 200)? 200: sz/10;
  292. m->start = erealloc(m->start, sz+1);
  293. wp = m->start+pos;
  294. ep = m->start+sz;
  295. }
  296. memmove(wp, s, l-1);
  297. wp[l-1] = '\n';
  298. wp += l;
  299. }
  300. if(s == nil || strcmp(s, ".") != 0)
  301. return "out of sync with pop3 server";
  302. m->end = wp;
  303. // make sure there's a trailing null
  304. // (helps in body searches)
  305. *m->end = 0;
  306. m->bend = m->rbend = m->end;
  307. m->header = m->start;
  308. // digest message
  309. sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
  310. for(i = 0; i < SHA1dlen; i++)
  311. sprint(sdigest+2*i, "%2.2ux", m->digest[i]);
  312. m->sdigest = s_copy(sdigest);
  313. return nil;
  314. }
  315. //
  316. // check for new messages on pop server
  317. // UIDL is not required by RFC 1939, but
  318. // netscape requires it, so almost every server supports it.
  319. // we'll use it to make our lives easier.
  320. //
  321. static char*
  322. pop3read(Pop *pop, Mailbox *mb, int doplumb)
  323. {
  324. char *s, *p, *uidl, *f[2];
  325. int mesgno, ignore, nnew;
  326. Message *m, *next, **l;
  327. // Some POP servers disallow UIDL if the maildrop is empty.
  328. pop3cmd(pop, "STAT");
  329. if(!isokay(s = pop3resp(pop)))
  330. return s;
  331. // fetch message listing; note messages to grab
  332. l = &mb->root->part;
  333. if(strncmp(s, "+OK 0 ", 6) != 0) {
  334. pop3cmd(pop, "UIDL");
  335. if(!isokay(s = pop3resp(pop)))
  336. return s;
  337. for(;;){
  338. p = pop3resp(pop);
  339. if(strcmp(p, ".") == 0 || strcmp(p, "unexpected eof") == 0)
  340. break;
  341. if(tokenize(p, f, 2) != 2)
  342. continue;
  343. mesgno = atoi(f[0]);
  344. uidl = f[1];
  345. if(strlen(uidl) > 75) // RFC 1939 says 70 characters max
  346. continue;
  347. ignore = 0;
  348. while(*l != nil) {
  349. if(strcmp((*l)->uidl, uidl) == 0) {
  350. // matches mail we already have, note mesgno for deletion
  351. (*l)->mesgno = mesgno;
  352. ignore = 1;
  353. l = &(*l)->next;
  354. break;
  355. } else {
  356. // old mail no longer in box mark deleted
  357. if(doplumb)
  358. mailplumb(mb, *l, 1);
  359. (*l)->inmbox = 0;
  360. (*l)->deleted = 1;
  361. l = &(*l)->next;
  362. }
  363. }
  364. if(ignore)
  365. continue;
  366. m = newmessage(mb->root);
  367. m->mallocd = 1;
  368. m->inmbox = 1;
  369. m->mesgno = mesgno;
  370. strcpy(m->uidl, uidl);
  371. // chain in; will fill in message later
  372. *l = m;
  373. l = &m->next;
  374. }
  375. }
  376. // whatever is left has been removed from the mbox, mark as deleted
  377. while(*l != nil) {
  378. if(doplumb)
  379. mailplumb(mb, *l, 1);
  380. (*l)->inmbox = 0;
  381. (*l)->deleted = 1;
  382. l = &(*l)->next;
  383. }
  384. // download new messages
  385. nnew = 0;
  386. if(pop->pipeline){
  387. switch(rfork(RFPROC|RFMEM)){
  388. case -1:
  389. fprint(2, "rfork: %r\n");
  390. pop->pipeline = 0;
  391. default:
  392. break;
  393. case 0:
  394. for(m = mb->root->part; m != nil; m = m->next){
  395. if(m->start != nil)
  396. continue;
  397. Bprint(&pop->bout, "LIST %d\r\nRETR %d\r\n", m->mesgno, m->mesgno);
  398. }
  399. Bflush(&pop->bout);
  400. _exits(nil);
  401. }
  402. }
  403. for(m = mb->root->part; m != nil; m = next) {
  404. next = m->next;
  405. if(m->start != nil)
  406. continue;
  407. if(s = pop3download(pop, m)) {
  408. // message disappeared? unchain
  409. fprint(2, "download %d: %s\n", m->mesgno, s);
  410. delmessage(mb, m);
  411. mb->root->subname--;
  412. continue;
  413. }
  414. nnew++;
  415. parse(m, 0, mb, 1);
  416. if(doplumb)
  417. mailplumb(mb, m, 0);
  418. }
  419. if(pop->pipeline)
  420. waitpid();
  421. if(nnew || mb->vers == 0) {
  422. mb->vers++;
  423. henter(PATH(0, Qtop), mb->name,
  424. (Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
  425. }
  426. return nil;
  427. }
  428. //
  429. // delete marked messages
  430. //
  431. static void
  432. pop3purge(Pop *pop, Mailbox *mb)
  433. {
  434. Message *m, *next;
  435. if(pop->pipeline){
  436. switch(rfork(RFPROC|RFMEM)){
  437. case -1:
  438. fprint(2, "rfork: %r\n");
  439. pop->pipeline = 0;
  440. default:
  441. break;
  442. case 0:
  443. for(m = mb->root->part; m != nil; m = next){
  444. next = m->next;
  445. if(m->deleted && m->refs == 0){
  446. if(m->inmbox)
  447. Bprint(&pop->bout, "DELE %d\r\n", m->mesgno);
  448. }
  449. }
  450. Bflush(&pop->bout);
  451. _exits(nil);
  452. }
  453. }
  454. for(m = mb->root->part; m != nil; m = next) {
  455. next = m->next;
  456. if(m->deleted && m->refs == 0) {
  457. if(m->inmbox) {
  458. if(!pop->pipeline)
  459. pop3cmd(pop, "DELE %d", m->mesgno);
  460. if(isokay(pop3resp(pop)))
  461. delmessage(mb, m);
  462. } else
  463. delmessage(mb, m);
  464. }
  465. }
  466. }
  467. // connect to pop3 server, sync mailbox
  468. static char*
  469. pop3sync(Mailbox *mb, int doplumb)
  470. {
  471. char *err;
  472. Pop *pop;
  473. pop = mb->aux;
  474. if(err = pop3dial(pop)) {
  475. mb->waketime = time(0) + pop->refreshtime;
  476. return err;
  477. }
  478. if((err = pop3read(pop, mb, doplumb)) == nil){
  479. pop3purge(pop, mb);
  480. mb->d->atime = mb->d->mtime = time(0);
  481. }
  482. pop3hangup(pop);
  483. mb->waketime = time(0) + pop->refreshtime;
  484. return err;
  485. }
  486. static char Epop3ctl[] = "bad pop3 control message";
  487. static char*
  488. pop3ctl(Mailbox *mb, int argc, char **argv)
  489. {
  490. int n;
  491. Pop *pop;
  492. pop = mb->aux;
  493. if(argc < 1)
  494. return Epop3ctl;
  495. if(argc==1 && strcmp(argv[0], "debug")==0){
  496. pop->debug = 1;
  497. return nil;
  498. }
  499. if(argc==1 && strcmp(argv[0], "nodebug")==0){
  500. pop->debug = 0;
  501. return nil;
  502. }
  503. if(argc==1 && strcmp(argv[0], "thumbprint")==0){
  504. if(pop->thumb)
  505. freeThumbprints(pop->thumb);
  506. pop->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude");
  507. }
  508. if(strcmp(argv[0], "refresh")==0){
  509. if(argc==1){
  510. pop->refreshtime = 60;
  511. return nil;
  512. }
  513. if(argc==2){
  514. n = atoi(argv[1]);
  515. if(n < 15)
  516. return Epop3ctl;
  517. pop->refreshtime = n;
  518. return nil;
  519. }
  520. }
  521. return Epop3ctl;
  522. }
  523. // free extra memory associated with mb
  524. static void
  525. pop3close(Mailbox *mb)
  526. {
  527. Pop *pop;
  528. pop = mb->aux;
  529. free(pop->freep);
  530. free(pop);
  531. }
  532. //
  533. // open mailboxes of the form /pop/host/user or /apop/host/user
  534. //
  535. char*
  536. pop3mbox(Mailbox *mb, char *path)
  537. {
  538. char *f[10];
  539. int nf, apop, ppop, popssl, apopssl, apoptls, popnotls, apopnotls, poptls;
  540. Pop *pop;
  541. quotefmtinstall();
  542. popssl = strncmp(path, "/pops/", 6) == 0;
  543. apopssl = strncmp(path, "/apops/", 7) == 0;
  544. poptls = strncmp(path, "/poptls/", 8) == 0;
  545. popnotls = strncmp(path, "/popnotls/", 10) == 0;
  546. ppop = popssl || poptls || popnotls || strncmp(path, "/pop/", 5) == 0;
  547. apoptls = strncmp(path, "/apoptls/", 9) == 0;
  548. apopnotls = strncmp(path, "/apopnotls/", 11) == 0;
  549. apop = apopssl || apoptls || apopnotls || strncmp(path, "/apop/", 6) == 0;
  550. if(!ppop && !apop)
  551. return Enotme;
  552. path = strdup(path);
  553. if(path == nil)
  554. return "out of memory";
  555. nf = getfields(path, f, nelem(f), 0, "/");
  556. if(nf != 3 && nf != 4) {
  557. free(path);
  558. return "bad pop3 path syntax /[a]pop[tls|ssl]/system[/user]";
  559. }
  560. pop = emalloc(sizeof(*pop));
  561. pop->freep = path;
  562. pop->host = f[2];
  563. if(nf < 4)
  564. pop->user = nil;
  565. else
  566. pop->user = f[3];
  567. pop->ppop = ppop;
  568. pop->needssl = popssl || apopssl;
  569. pop->needtls = poptls || apoptls;
  570. pop->refreshtime = 60;
  571. pop->notls = popnotls || apopnotls;
  572. pop->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude");
  573. mb->aux = pop;
  574. mb->sync = pop3sync;
  575. mb->close = pop3close;
  576. mb->ctl = pop3ctl;
  577. mb->d = emalloc(sizeof(*mb->d));
  578. return nil;
  579. }