Followup for [309dea0] in imap_cache.c..
[s-mailx.git] / urlcrecry.c
blob4f0185ce5d97e0f1b359b84452be1b4b5f20a659
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ URL parsing, credential handling and crypto hooks.
3 *@ .netrc parser quite loosely based upon NetBSD usr.bin/ftp/
4 *@ $NetBSD: ruserpass.c,v 1.33 2007/04/17 05:52:04 lukem Exp $
6 * Copyright (c) 2014 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
8 * Permission to use, copy, modify, and/or distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 #ifndef HAVE_AMALGAMATION
22 # include "nail.h"
23 #endif
25 #ifdef HAVE_NETRC
26 /* NetBSD usr.bin/ftp/ruserpass.c uses 100 bytes for that, we need four
27 * concurrently (dummy, host, user, pass), so make it a KB */
28 # define NRC_TOKEN_MAXLEN (1024 / 4)
30 enum nrc_token {
31 NRC_ERROR = -1,
32 NRC_NONE = 0,
33 NRC_DEFAULT,
34 NRC_LOGIN,
35 NRC_PASSWORD,
36 NRC_ACCOUNT,
37 NRC_MACDEF,
38 NRC_MACHINE,
39 NRC_INPUT
42 struct nrc_node {
43 struct nrc_node *nrc_next;
44 struct nrc_node *nrc_result; /* In match phase, former possible one */
45 ui32_t nrc_mlen; /* Length of machine name */
46 ui32_t nrc_ulen; /* Length of user name */
47 ui32_t nrc_plen; /* Length of password */
48 char nrc_dat[VFIELD_SIZE(4)];
50 # define NRC_NODE_ERR ((struct nrc_node*)-1)
52 static struct nrc_node *_nrc_list;
53 #endif /* HAVE_NETRC */
55 /* Find the last @ before a slash
56 * TODO Casts off the const but this is ok here; obsolete function! */
57 #ifdef HAVE_SOCKETS /* temporary (we'll have file://..) */
58 static char * _url_last_at_before_slash(char const *sp);
59 #endif
61 #ifdef HAVE_NETRC
62 /* Initialize .netrc cache */
63 static void _nrc_init(void);
64 static enum nrc_token __nrc_token(FILE *fi, char buffer[NRC_TOKEN_MAXLEN]);
66 /* We shall lookup a machine in .netrc says ok_blook(netrc_lookup).
67 * only_pass is true then the lookup is for the password only, otherwise we
68 * look for a user (and add password only if we have an exact machine match) */
69 static bool_t _nrc_lookup(struct url *urlp, bool_t only_pass);
71 /* 0=no match; 1=exact match; -1=wildcard match */
72 static int __nrc_host_match(struct nrc_node const *nrc,
73 struct url const *urlp);
74 static bool_t __nrc_find_user(struct url *urlp,
75 struct nrc_node const *nrc);
76 static bool_t __nrc_find_pass(struct url *urlp, bool_t user_match,
77 struct nrc_node const *nrc);
78 #endif /* HAVE_NETRC */
80 /* The password can also be gained through external agents */
81 #ifdef HAVE_AGENT
82 static bool_t _agent_shell_lookup(struct url *urlp, char const *comm);
83 #endif
85 #ifdef HAVE_SOCKETS
86 static char *
87 _url_last_at_before_slash(char const *sp)
89 char const *cp;
90 char c;
91 NYD2_ENTER;
93 for (cp = sp; (c = *cp) != '\0'; ++cp)
94 if (c == '/')
95 break;
96 while (cp > sp && *--cp != '@')
98 if (*cp != '@')
99 cp = NULL;
100 NYD2_LEAVE;
101 return UNCONST(cp);
103 #endif
105 #ifdef HAVE_NETRC
106 static void
107 _nrc_init(void)
109 char buffer[NRC_TOKEN_MAXLEN], host[NRC_TOKEN_MAXLEN],
110 user[NRC_TOKEN_MAXLEN], pass[NRC_TOKEN_MAXLEN], *netrc_load;
111 struct stat sb;
112 FILE *fi;
113 enum nrc_token t;
114 bool_t seen_default;
115 struct nrc_node *ntail = NULL /* CC happy */, *nhead = NULL,
116 *nrc = NRC_NODE_ERR;
117 NYD_ENTER;
119 if ((netrc_load = getenv("NETRC")/* TODO */) == NULL)
120 netrc_load = UNCONST(NETRC);
121 if ((netrc_load = file_expand(netrc_load)) == NULL)
122 goto j_leave;
124 if ((fi = Fopen(netrc_load, "r")) == NULL) {
125 fprintf(stderr, _("Cannot open `%s'\n"), netrc_load);
126 goto j_leave;
129 /* Be simple and apply rigid (permission) check(s) */
130 if (fstat(fileno(fi), &sb) == -1 || !S_ISREG(sb.st_mode) ||
131 (sb.st_mode & (S_IRWXG | S_IRWXO))) {
132 fprintf(stderr,
133 _("Not a regular file, or accessible by non-user: `%s'\n"),
134 netrc_load);
135 goto jleave;
138 seen_default = FAL0;
139 jnext:
140 switch((t = __nrc_token(fi, buffer))) {
141 case NRC_NONE:
142 break;
143 default: /* Doesn't happen (but on error?), keep CC happy */
144 case NRC_DEFAULT:
145 jdef:
146 /* We ignore the default entry (require an exact host match), and we also
147 * ignore anything after such an entry (faulty syntax) */
148 seen_default = TRU1;
149 /* FALLTHRU */
150 case NRC_MACHINE:
151 jm_h:
152 /* Normalize HOST to lowercase */
153 *host = '\0';
154 if (!seen_default && (t = __nrc_token(fi, host)) != NRC_INPUT)
155 goto jerr;
156 else {
157 char *cp;
158 for (cp = host; *cp != '\0'; ++cp)
159 *cp = lowerconv(*cp);
162 *user = *pass = '\0';
163 while ((t = __nrc_token(fi, buffer)) != NRC_NONE && t != NRC_MACHINE &&
164 t != NRC_DEFAULT) {
165 switch(t) {
166 case NRC_LOGIN:
167 if ((t = __nrc_token(fi, user)) != NRC_INPUT)
168 goto jerr;
169 break;
170 case NRC_PASSWORD:
171 if ((t = __nrc_token(fi, pass)) != NRC_INPUT)
172 goto jerr;
173 break;
174 case NRC_ACCOUNT:
175 if ((t = __nrc_token(fi, buffer)) != NRC_INPUT)
176 goto jerr;
177 break;
178 case NRC_MACDEF:
179 if ((t = __nrc_token(fi, buffer)) != NRC_INPUT)
180 goto jerr;
181 else {
182 int i = 0, c;
183 while ((c = getc(fi)) != EOF)
184 if (c == '\n') { /* xxx */
185 if (i)
186 break;
187 i = 1;
188 } else
189 i = 0;
191 break;
192 default:
193 case NRC_ERROR:
194 goto jerr;
198 if (!seen_default && (*user != '\0' || *pass != '\0')) {
199 size_t hl = strlen(host), ul = strlen(user), pl = strlen(pass);
200 struct nrc_node *nx = smalloc(sizeof(*nx) -
201 VFIELD_SIZEOF(struct nrc_node, nrc_dat) + hl +1 + ul +1 + pl +1);
203 if (nhead != NULL)
204 ntail->nrc_next = nx;
205 else
206 nhead = nx;
207 ntail = nx;
208 nx->nrc_next = NULL;
209 nx->nrc_mlen = hl;
210 nx->nrc_ulen = ul;
211 nx->nrc_plen = pl;
212 memcpy(nx->nrc_dat, host, ++hl);
213 memcpy(nx->nrc_dat + hl, user, ++ul);
214 memcpy(nx->nrc_dat + hl + ul, pass, ++pl);
216 if (t == NRC_MACHINE)
217 goto jm_h;
218 if (t == NRC_DEFAULT)
219 goto jdef;
220 if (t != NRC_NONE)
221 goto jnext;
222 break;
223 case NRC_ERROR:
224 jerr:
225 if (options & OPT_D_V)
226 fprintf(stderr, _("Errors occurred while parsing `%s'\n"), netrc_load);
227 assert(nrc == NRC_NODE_ERR);
228 goto jleave;
231 if (nhead != NULL)
232 nrc = nhead;
233 jleave:
234 Fclose(fi);
235 if (nrc == NRC_NODE_ERR)
236 while (nhead != NULL) {
237 ntail = nhead;
238 nhead = nhead->nrc_next;
239 free(ntail);
241 j_leave:
242 _nrc_list = nrc;
243 NYD_LEAVE;
246 static enum nrc_token
247 __nrc_token(FILE *fi, char buffer[NRC_TOKEN_MAXLEN])
249 int c;
250 char *cp;
251 enum nrc_token rv = NRC_NONE;
252 NYD2_ENTER;
254 c = EOF;
255 if (feof(fi) || ferror(fi))
256 goto jleave;
258 while ((c = getc(fi)) != EOF && whitechar(c))
260 if (c == EOF)
261 goto jleave;
263 cp = buffer;
264 /* Is it a quoted token? At least IBM syntax also supports ' quotes */
265 if (c == '"' || c == '\'') {
266 int quotec = c;
268 /* Not requiring the closing QM is (Net)BSD syntax */
269 while ((c = getc(fi)) != EOF && c != quotec) {
270 /* Backslash escaping the next character is (Net)BSD syntax */
271 if (c == '\\')
272 if ((c = getc(fi)) == EOF)
273 break;
274 *cp++ = c;
275 if (PTRCMP(cp, ==, buffer + NRC_TOKEN_MAXLEN)) {
276 rv = NRC_ERROR;
277 goto jleave;
280 } else {
281 *cp++ = c;
282 while ((c = getc(fi)) != EOF && !whitechar(c)) {
283 /* Backslash escaping the next character is (Net)BSD syntax */
284 if (c == '\\' && (c = getc(fi)) == EOF)
285 break;
286 *cp++ = c;
287 if (PTRCMP(cp, ==, buffer + NRC_TOKEN_MAXLEN)) {
288 rv = NRC_ERROR;
289 goto jleave;
293 *cp = '\0';
295 if (*buffer == '\0')
296 do {/*rv = NRC_NONE*/} while (0);
297 else if (!strcmp(buffer, "default"))
298 rv = NRC_DEFAULT;
299 else if (!strcmp(buffer, "login"))
300 rv = NRC_LOGIN;
301 else if (!strcmp(buffer, "password") || !strcmp(buffer, "passwd"))
302 rv = NRC_PASSWORD;
303 else if (!strcmp(buffer, "account"))
304 rv = NRC_ACCOUNT;
305 else if (!strcmp(buffer, "macdef"))
306 rv = NRC_MACDEF;
307 else if (!strcmp(buffer, "machine"))
308 rv = NRC_MACHINE;
309 else
310 rv = NRC_INPUT;
311 jleave:
312 if (c == EOF && !feof(fi))
313 rv = NRC_ERROR;
314 NYD2_LEAVE;
315 return rv;
318 static bool_t
319 _nrc_lookup(struct url *urlp, bool_t only_pass) /* TODO optimize; too tricky!! */
321 struct nrc_node *nrc, *nrc_wild, *nrc_exact;
322 bool_t rv = FAL0;
323 NYD_ENTER;
325 assert(!only_pass || urlp->url_user.s != NULL);
326 assert(only_pass || urlp->url_user.s == NULL);
328 if (_nrc_list == NULL)
329 _nrc_init();
330 if (_nrc_list == NRC_NODE_ERR)
331 goto jleave;
333 nrc_wild = nrc_exact = NULL;
334 for (nrc = _nrc_list; nrc != NULL; nrc = nrc->nrc_next)
335 switch (__nrc_host_match(nrc, urlp)) {
336 case 1:
337 nrc->nrc_result = nrc_exact;
338 nrc_exact = nrc;
339 continue;
340 case -1:
341 nrc->nrc_result = nrc_wild;
342 nrc_wild = nrc;
343 /* FALLTHRU */
344 case 0:
345 continue;
348 /* TODO _nrc_lookup(): PAIN! init: build sorted tree, single walk that!!
349 * TODO then: verify .netrc (unique fallback entries etc.) */
350 if (!only_pass && !__nrc_find_user(urlp, nrc_exact) &&
351 !__nrc_find_user(urlp, nrc_wild))
352 goto jleave;
354 if (__nrc_find_pass(urlp, TRU1, nrc_exact) ||
355 __nrc_find_pass(urlp, TRU1, nrc_wild) ||
356 /* Do not try to find a password without exact user match unless we've
357 * been called during credential lookup, a.k.a. the second time */
358 !only_pass ||
359 __nrc_find_pass(urlp, FAL0, nrc_exact) ||
360 __nrc_find_pass(urlp, FAL0, nrc_wild))
361 rv = TRU1;
362 jleave:
363 NYD_LEAVE;
364 return rv;
367 static int
368 __nrc_host_match(struct nrc_node const *nrc, struct url const *urlp)
370 char const *d2, *d1;
371 size_t l2, l1;
372 int rv = 0;
373 NYD2_ENTER;
375 /* Find a matching machine -- entries are all lowercase normalized */
376 if (nrc->nrc_mlen == urlp->url_host.l) {
377 if (LIKELY(!memcmp(nrc->nrc_dat, urlp->url_host.s, urlp->url_host.l)))
378 rv = 1;
379 goto jleave;
382 /* Cannot be an exact match, but maybe the .netrc machine starts with
383 * a `*.' glob, which we recognize as an extension, meaning "skip
384 * a single subdomain, then match the rest" */
385 d1 = nrc->nrc_dat + 2;
386 l1 = nrc->nrc_mlen;
387 if (l1 <= 2 || d1[-1] != '.' || d1[-2] != '*')
388 goto jleave;
389 l1 -= 2;
391 /* Brute skipping over one subdomain, no RFC 1035 or RFC 1122 checks;
392 * in fact this even succeeds for `.host.com', but - why care, here? */
393 d2 = urlp->url_host.s;
394 l2 = urlp->url_host.l;
395 while (l2 > 0) {
396 --l2;
397 if (*d2++ == '.')
398 break;
401 if (l2 == l1 && !memcmp(d1, d2, l1))
402 /* This matches, but we won't use it directly but watch out for an
403 * exact match first! */
404 rv = -1;
405 jleave:
406 NYD2_LEAVE;
407 return rv;
410 static bool_t
411 __nrc_find_user(struct url *urlp, struct nrc_node const *nrc)
413 NYD2_ENTER;
415 for (; nrc != NULL; nrc = nrc->nrc_result)
416 if (nrc->nrc_ulen > 0 && urlp->url_user.s == NULL) {
417 /* Fake it was part of URL otherwise XXX */
418 urlp->url_had_user = TRU1;
419 /* That buffer will be duplicated by url_parse() in this case! */
420 urlp->url_user.s = UNCONST(nrc->nrc_dat + nrc->nrc_mlen +1);
421 urlp->url_user.l = nrc->nrc_ulen;
422 break;
425 NYD2_LEAVE;
426 return (nrc != NULL);
429 static bool_t
430 __nrc_find_pass(struct url *urlp, bool_t user_match, struct nrc_node const *nrc)
432 NYD2_ENTER;
434 for (; nrc != NULL; nrc = nrc->nrc_result) {
435 if (user_match && (nrc->nrc_ulen != urlp->url_user.l ||
436 memcmp(nrc->nrc_dat + nrc->nrc_mlen +1, urlp->url_user.s,
437 urlp->url_user.l)))
438 continue;
439 if (nrc->nrc_plen == 0)
440 continue;
442 /* We are responsible for duplicating this buffer! */
443 urlp->url_pass.s = savestrbuf(nrc->nrc_dat + nrc->nrc_mlen +1 +
444 nrc->nrc_ulen + 1, (urlp->url_pass.l = nrc->nrc_plen));
445 break;
448 NYD2_LEAVE;
449 return (nrc != NULL);
451 #endif /* HAVE_NETRC */
453 #ifdef HAVE_AGENT
454 static bool_t
455 _agent_shell_lookup(struct url *urlp, char const *comm)
457 char buf[128];
458 char const *env_addon[8];
459 struct str s;
460 FILE *pbuf;
461 union {char const *cp; int c; sighandler_type sht;} u;
462 size_t cl, l;
463 bool_t rv = FAL0;
464 NYD2_ENTER;
466 env_addon[0] = str_concat_csvl(&s, AGENT_USER, "=", urlp->url_user.s,
467 NULL)->s;
468 env_addon[1] = str_concat_csvl(&s, AGENT_USER_ENC, "=", urlp->url_user_enc.s,
469 NULL)->s;
470 env_addon[2] = str_concat_csvl(&s, AGENT_HOST, "=", urlp->url_host.s,
471 NULL)->s;
472 env_addon[3] = str_concat_csvl(&s, AGENT_HOST_PORT, "=", urlp->url_h_p.s,
473 NULL)->s;
474 env_addon[4] = NULL;
476 if ((u.cp = ok_vlook(SHELL)) == NULL)
477 u.cp = XSHELL;
478 if ((pbuf = Popen(comm, "r", u.cp, env_addon, -1)) == NULL) {
479 fprintf(stderr, _("*agent-shell-lookup* startup failed (`%s')\n"),
480 comm);
481 goto jleave;
484 for (s.s = NULL, s.l = cl = l = 0; (u.c = getc(pbuf)) != EOF; ++cl) {
485 if (u.c == '\n') /* xxx */
486 continue;
487 buf[l++] = u.c;
488 if (l == sizeof(buf) - 1) {
489 n_str_add_buf(&s, buf, l);
490 l = 0;
493 if (l > 0)
494 n_str_add_buf(&s, buf, l);
496 if (!Pclose(pbuf, TRU1)) {
497 if (options & OPT_D_V)
498 fprintf(stderr, _("*agent-shell-lookup* execution failure (`%s')\n"),
499 comm);
500 goto jleave;
503 /* We are responsible for duplicating this buffer! */
504 if (s.s != NULL)
505 urlp->url_pass.s = savestrbuf(s.s, urlp->url_pass.l = s.l);
506 else if (cl > 0)
507 urlp->url_pass.s = UNCONST(""), urlp->url_pass.l = 0;
508 rv = TRU1;
509 jleave:
510 if (s.s != NULL)
511 free(s.s);
512 NYD2_LEAVE;
513 return rv;
515 #endif
517 FL char *
518 (urlxenc)(char const *cp, bool_t ispath SALLOC_DEBUG_ARGS)
520 char *n, *np, c1;
521 NYD2_ENTER;
523 np = n = (salloc)(strlen(cp) * 3 +1 SALLOC_DEBUG_ARGSCALL);
525 for (; (c1 = *cp) != '\0'; ++cp) {
526 /* (RFC 1738) RFC 3986, 2.3 Unreserved Characters:
527 * ALPHA / DIGIT / "-" / "." / "_" / "~"
528 * However add a special is[file]path mode for file-system friendliness */
529 if (alnumchar(c1) || c1 == '_')
530 *np++ = c1;
531 else if (!ispath) {
532 if (c1 != '-' && c1 != '.' && c1 != '~')
533 goto jesc;
534 *np++ = c1;
535 } else if (PTRCMP(np, >, n) && (*cp == '-' || *cp == '.')) /* XXX imap */
536 *np++ = c1;
537 else {
538 jesc:
539 np[0] = '%';
540 mime_char_to_hexseq(np + 1, c1);
541 np += 3;
544 *np = '\0';
545 NYD2_LEAVE;
546 return n;
549 FL char *
550 (urlxdec)(char const *cp SALLOC_DEBUG_ARGS)
552 char *n, *np;
553 si32_t c;
554 NYD2_ENTER;
556 np = n = (salloc)(strlen(cp) +1 SALLOC_DEBUG_ARGSCALL);
558 while ((c = (uc_it)*cp++) != '\0') {
559 if (c == '%' && cp[0] != '\0' && cp[1] != '\0') {
560 si32_t o = c;
561 if (LIKELY((c = mime_hexseq_to_char(cp)) >= '\0'))
562 cp += 2;
563 else
564 c = o;
566 *np++ = (char)c;
568 *np = '\0';
569 NYD2_LEAVE;
570 return n;
573 #ifdef HAVE_SOCKETS /* Note: not indented for that -- later: file:// etc.! */
574 FL bool_t
575 url_parse(struct url *urlp, enum cproto cproto, char const *data)
577 #if defined HAVE_SMTP && defined HAVE_POP3 && defined HAVE_IMAP
578 # define __ALLPROTO
579 #endif
580 #if defined HAVE_SMTP || defined HAVE_POP3 || defined HAVE_IMAP
581 # define __ANYPROTO
582 char *cp, *x;
583 #endif
584 bool_t rv = FAL0;
585 NYD_ENTER;
586 UNUSED(data);
588 memset(urlp, 0, sizeof *urlp);
589 urlp->url_input = data;
590 urlp->url_cproto = cproto;
592 /* Network protocol */
593 #define _protox(X,Y) \
594 urlp->url_portno = Y;\
595 memcpy(urlp->url_proto, X "://", sizeof(X "://"));\
596 urlp->url_proto[sizeof(X) -1] = '\0';\
597 urlp->url_proto_len = sizeof(X) -1;\
598 urlp->url_proto_xlen = sizeof(X "://") -1
599 #define __if(X,Y,Z) \
600 if (!ascncasecmp(data, X "://", sizeof(X "://") -1)) {\
601 _protox(X, Y);\
602 data += sizeof(X "://") -1;\
603 do { Z; } while (0);\
604 goto juser;\
606 #define _if(X,Y) __if(X, Y, (void)0)
607 #ifdef HAVE_SSL
608 # define _ifs(X,Y) __if(X, Y, urlp->url_needs_tls = TRU1)
609 #else
610 # define _ifs(X,Y) goto jeproto;
611 #endif
613 switch (cproto) {
614 case CPROTO_SMTP:
615 #ifdef HAVE_SMTP
616 _if ("smtp", 25)
617 _if ("submission", 587)
618 _ifs ("smtps", 465)
619 _protox("smtp", 25);
620 break;
621 #else
622 goto jeproto;
623 #endif
624 case CPROTO_POP3:
625 #ifdef HAVE_POP3
626 _if ("pop3", 110)
627 _ifs ("pop3s", 995)
628 _protox("pop3", 110);
629 break;
630 #else
631 goto jeproto;
632 #endif
633 case CPROTO_IMAP:
634 #ifdef HAVE_IMAP
635 _if ("imap", 143)
636 _ifs ("imaps", 993)
637 _protox("imap", 143);
638 break;
639 #else
640 goto jeproto;
641 #endif
644 #undef _ifs
645 #undef _if
646 #undef __if
647 #undef _protox
649 if (strstr(data, "://") != NULL) {
650 #if !defined __ALLPROTO || !defined HAVE_SSL
651 jeproto:
652 #endif
653 fprintf(stderr, _("URL `proto://' prefix invalid: `%s'\n"),
654 urlp->url_input);
655 goto jleave;
657 #ifdef __ANYPROTO
659 /* User and password, I */
660 juser:
661 if ((cp = _url_last_at_before_slash(data)) != NULL) {
662 size_t l = PTR2SIZE(cp - data);
663 char const *d = data;
664 char *ub = ac_alloc(l +1);
666 urlp->url_had_user = TRU1;
667 data = cp + 1;
669 /* And also have a password? */
670 if ((cp = memchr(d, ':', l)) != NULL) {
671 size_t i = PTR2SIZE(cp - d);
673 l -= i + 1;
674 memcpy(ub, cp + 1, l);
675 ub[l] = '\0';
676 urlp->url_pass.l = strlen(urlp->url_pass.s = urlxdec(ub));
678 if (strcmp(ub, urlxenc(urlp->url_pass.s, FAL0))) {
679 fprintf(stderr,
680 _("String is not properly URL percent encoded: `%s'\n"), ub);
681 goto jleave;
683 l = i;
686 memcpy(ub, d, l);
687 ub[l] = '\0';
688 urlp->url_user.l = strlen(urlp->url_user.s = urlxdec(ub));
689 urlp->url_user_enc.l = strlen(
690 urlp->url_user_enc.s = urlxenc(urlp->url_user.s, FAL0));
692 if (urlp->url_user_enc.l != l || memcmp(urlp->url_user_enc.s, ub, l)) {
693 fprintf(stderr,
694 _("String is not properly URL percent encoded: `%s'\n"), ub);
695 goto jleave;
698 ac_free(ub);
701 /* Servername and port -- and possible path suffix */
702 if ((cp = strchr(data, ':')) != NULL) { /* TODO URL parse, IPv6 support */
703 char *eptr;
704 long l;
706 urlp->url_port = x = savestr(x = cp + 1);
707 if ((x = strchr(x, '/')) != NULL)
708 *x = '\0';
709 l = strtol(urlp->url_port, &eptr, 10);
710 if (*eptr != '\0' || l <= 0 || UICMP(32, l, >=, 0xFFFFu)) {
711 fprintf(stderr, _("URL with invalid port number: `%s'\n"),
712 urlp->url_input);
713 goto jleave;
715 urlp->url_portno = (ui16_t)l;
716 } else {
717 if ((x = strchr(data, '/')) != NULL)
718 data = savestrbuf(data, PTR2SIZE(x - data));
719 cp = UNCONST(data + strlen(data));
722 /* A (non-empty) path may only occur with IMAP */
723 if (x != NULL && x[1] != '\0') {
724 if (cproto != CPROTO_IMAP) {
725 fprintf(stderr, _("URL protocol doesn't support paths: `%s'\n"),
726 urlp->url_input);
727 goto jleave;
729 urlp->url_path.l = strlen(++x);
730 urlp->url_path.s = savestrbuf(x, urlp->url_path.l);
733 urlp->url_host.s = savestrbuf(data, urlp->url_host.l = PTR2SIZE(cp - data));
734 { size_t i;
735 for (cp = urlp->url_host.s, i = urlp->url_host.l; i != 0; ++cp, --i)
736 *cp = lowerconv(*cp);
739 /* .url_h_p: HOST:PORT */
740 { size_t i;
741 struct str *s = &urlp->url_h_p;
743 s->s = salloc(urlp->url_host.l + 1 + sizeof("65536")-1 +1);
744 memcpy(s->s, urlp->url_host.s, i = urlp->url_host.l);
745 if (urlp->url_port != NULL) {
746 size_t j = strlen(urlp->url_port);
747 s->s[i++] = ':';
748 memcpy(s->s + i, urlp->url_port, j);
749 i += j;
751 s->s[i] = '\0';
752 s->l = i;
755 /* User, II
756 * If there was no user in the URL, do we have *user-HOST* or *user*? */
757 if (!urlp->url_had_user) {
758 if ((urlp->url_user.s = xok_vlook(user, urlp, OXM_PLAIN | OXM_H_P))
759 == NULL) {
760 /* No, check wether .netrc lookup is desired */
761 #ifdef HAVE_NETRC
762 if (!ok_blook(v15_compat) ||
763 !xok_blook(netrc_lookup, urlp, OXM_PLAIN | OXM_H_P) ||
764 !_nrc_lookup(urlp, FAL0))
765 #endif
766 urlp->url_user.s = UNCONST(myname);
769 urlp->url_user.l = strlen(urlp->url_user.s);
770 urlp->url_user.s = savestrbuf(urlp->url_user.s, urlp->url_user.l);
771 urlp->url_user_enc.l = strlen(
772 urlp->url_user_enc.s = urlxenc(urlp->url_user.s, FAL0));
775 /* And then there are a lot of prebuild string combinations TODO do lazy */
777 /* .url_u_h: .url_user@.url_host
778 * For SMTP we apply ridiculously complicated *v15-compat* plus
779 * *smtp-hostname* / *hostname* dependent rules */
780 { struct str h, *s;
781 size_t i;
783 if (cproto == CPROTO_SMTP && ok_blook(v15_compat) &&
784 (cp = ok_vlook(smtp_hostname)) != NULL) {
785 if (*cp == '\0')
786 cp = nodename(1);
787 h.s = savestrbuf(cp, h.l = strlen(cp));
788 } else
789 h = urlp->url_host;
791 s = &urlp->url_u_h;
792 i = urlp->url_user.l;
794 s->s = salloc(i + 1 + h.l +1);
795 if (i > 0) {
796 memcpy(s->s, urlp->url_user.s, i);
797 s->s[i++] = '@';
799 memcpy(s->s + i, h.s, h.l +1);
800 i += h.l;
801 s->l = i;
804 /* .url_u_h_p: .url_user@.url_host[:.url_port] */
805 { struct str *s = &urlp->url_u_h_p;
806 size_t i = urlp->url_user.l;
808 s->s = salloc(i + 1 + urlp->url_h_p.l +1);
809 if (i > 0) {
810 memcpy(s->s, urlp->url_user.s, i);
811 s->s[i++] = '@';
813 memcpy(s->s + i, urlp->url_h_p.s, urlp->url_h_p.l +1);
814 i += urlp->url_h_p.l;
815 s->l = i;
818 /* .url_eu_h_p: .url_user_enc@.url_host[:.url_port] */
819 { struct str *s = &urlp->url_eu_h_p;
820 size_t i = urlp->url_user_enc.l;
822 s->s = salloc(i + 1 + urlp->url_h_p.l +1);
823 if (i > 0) {
824 memcpy(s->s, urlp->url_user_enc.s, i);
825 s->s[i++] = '@';
827 memcpy(s->s + i, urlp->url_h_p.s, urlp->url_h_p.l +1);
828 i += urlp->url_h_p.l;
829 s->l = i;
832 /* .url_p_u_h_p: .url_proto://.url_u_h_p */
833 { size_t i;
834 char *ud = salloc((i = urlp->url_proto_xlen + urlp->url_u_h_p.l) +1);
836 urlp->url_proto[urlp->url_proto_len] = ':';
837 memcpy(sstpcpy(ud, urlp->url_proto), urlp->url_u_h_p.s,
838 urlp->url_u_h_p.l +1);
839 urlp->url_proto[urlp->url_proto_len] = '\0';
841 urlp->url_p_u_h_p = ud;
844 /* .url_p_eu_h_p, .url_p_eu_h_p_p: .url_proto://.url_eu_h_p[/.url_path] */
845 { size_t i;
846 char *ud = salloc((i = urlp->url_proto_xlen + urlp->url_eu_h_p.l) +
847 1 + urlp->url_path.l +1);
849 urlp->url_proto[urlp->url_proto_len] = ':';
850 memcpy(sstpcpy(ud, urlp->url_proto), urlp->url_eu_h_p.s,
851 urlp->url_eu_h_p.l +1);
852 urlp->url_proto[urlp->url_proto_len] = '\0';
854 if (urlp->url_path.l == 0)
855 urlp->url_p_eu_h_p = urlp->url_p_eu_h_p_p = ud;
856 else {
857 urlp->url_p_eu_h_p = savestrbuf(ud, i);
858 urlp->url_p_eu_h_p_p = ud;
859 ud += i;
860 *ud++ = '/';
861 memcpy(ud, urlp->url_path.s, urlp->url_path.l +1);
865 rv = TRU1;
866 #endif /* __ANYPROTO */
867 jleave:
868 NYD_LEAVE;
869 return rv;
870 #undef __ANYPROTO
871 #undef __ALLPROTO
874 FL bool_t
875 ccred_lookup_old(struct ccred *ccp, enum cproto cproto, char const *addr)
877 char const *pname, *pxstr, *authdef;
878 size_t pxlen, addrlen, i;
879 char *vbuf, *s;
880 ui8_t authmask;
881 enum {NONE=0, WANT_PASS=1<<0, REQ_PASS=1<<1, WANT_USER=1<<2, REQ_USER=1<<3}
882 ware = NONE;
883 bool_t addr_is_nuser = FAL0; /* XXX v15.0 legacy! v15_compat */
884 NYD_ENTER;
886 memset(ccp, 0, sizeof *ccp);
888 switch (cproto) {
889 default:
890 case CPROTO_SMTP:
891 pname = "SMTP";
892 pxstr = "smtp-auth";
893 pxlen = sizeof("smtp-auth") -1;
894 authmask = AUTHTYPE_NONE | AUTHTYPE_PLAIN | AUTHTYPE_LOGIN |
895 AUTHTYPE_CRAM_MD5 | AUTHTYPE_GSSAPI;
896 authdef = "none";
897 addr_is_nuser = TRU1;
898 break;
899 case CPROTO_POP3:
900 pname = "POP3";
901 pxstr = "pop3-auth";
902 pxlen = sizeof("pop3-auth") -1;
903 authmask = AUTHTYPE_PLAIN;
904 authdef = "plain";
905 break;
906 case CPROTO_IMAP:
907 pname = "IMAP";
908 pxstr = "imap-auth";
909 pxlen = sizeof("imap-auth") -1;
910 authmask = AUTHTYPE_LOGIN | AUTHTYPE_CRAM_MD5 | AUTHTYPE_GSSAPI;
911 authdef = "login";
912 break;
915 ccp->cc_cproto = cproto;
916 addrlen = strlen(addr);
917 vbuf = ac_alloc(pxlen + addrlen + sizeof("-password-")-1 +1);
918 memcpy(vbuf, pxstr, pxlen);
920 /* Authentication type */
921 vbuf[pxlen] = '-';
922 memcpy(vbuf + pxlen + 1, addr, addrlen +1);
923 if ((s = vok_vlook(vbuf)) == NULL) {
924 vbuf[pxlen] = '\0';
925 if ((s = vok_vlook(vbuf)) == NULL)
926 s = UNCONST(authdef);
929 if (!asccasecmp(s, "none")) {
930 ccp->cc_auth = "NONE";
931 ccp->cc_authtype = AUTHTYPE_NONE;
932 /*ware = NONE;*/
933 } else if (!asccasecmp(s, "plain")) {
934 ccp->cc_auth = "PLAIN";
935 ccp->cc_authtype = AUTHTYPE_PLAIN;
936 ware = REQ_PASS | REQ_USER;
937 } else if (!asccasecmp(s, "login")) {
938 ccp->cc_auth = "LOGIN";
939 ccp->cc_authtype = AUTHTYPE_LOGIN;
940 ware = REQ_PASS | REQ_USER;
941 } else if (!asccasecmp(s, "cram-md5")) {
942 ccp->cc_auth = "CRAM-MD5";
943 ccp->cc_authtype = AUTHTYPE_CRAM_MD5;
944 ware = REQ_PASS | REQ_USER;
945 } else if (!asccasecmp(s, "gssapi")) {
946 ccp->cc_auth = "GSS-API";
947 ccp->cc_authtype = AUTHTYPE_GSSAPI;
948 ware = REQ_USER;
949 } /* no else */
951 /* Verify method */
952 if (!(ccp->cc_authtype & authmask)) {
953 fprintf(stderr, _("Unsupported %s authentication method: %s\n"),
954 pname, s);
955 ccp = NULL;
956 goto jleave;
958 # ifndef HAVE_MD5
959 if (ccp->cc_authtype == AUTHTYPE_CRAM_MD5) {
960 fprintf(stderr, _("No CRAM-MD5 support compiled in.\n"));
961 ccp = NULL;
962 goto jleave;
964 # endif
965 # ifndef HAVE_GSSAPI
966 if (ccp->cc_authtype == AUTHTYPE_GSSAPI) {
967 fprintf(stderr, _("No GSS-API support compiled in.\n"));
968 ccp = NULL;
969 goto jleave;
971 # endif
973 /* User name */
974 if (!(ware & (WANT_USER | REQ_USER)))
975 goto jpass;
977 if (!addr_is_nuser) {
978 if ((s = _url_last_at_before_slash(addr)) != NULL) {
979 ccp->cc_user.s = urlxdec(savestrbuf(addr, PTR2SIZE(s - addr)));
980 ccp->cc_user.l = strlen(ccp->cc_user.s);
981 } else if (ware & REQ_USER)
982 goto jgetuser;
983 goto jpass;
986 memcpy(vbuf + pxlen, "-user-", i = sizeof("-user-") -1);
987 i += pxlen;
988 memcpy(vbuf + i, addr, addrlen +1);
989 if ((s = vok_vlook(vbuf)) == NULL) {
990 vbuf[--i] = '\0';
991 if ((s = vok_vlook(vbuf)) == NULL && (ware & REQ_USER)) {
992 if ((s = getuser(NULL)) == NULL) {
993 jgetuser: /* TODO v15.0: today we simply bail, but we should call getuser().
994 * TODO even better: introduce `PROTO-user' and `PROTO-pass' and
995 * TODO check that first, then! change control flow, grow `vbuf' */
996 fprintf(stderr, _("A user is necessary for %s authentication.\n"),
997 pname);
998 ccp = NULL;
999 goto jleave;
1003 ccp->cc_user.l = strlen(ccp->cc_user.s = savestr(s));
1005 /* Password */
1006 jpass:
1007 if (!(ware & (WANT_PASS | REQ_PASS)))
1008 goto jleave;
1010 if (!addr_is_nuser) {
1011 memcpy(vbuf, "password-", i = sizeof("password-") -1);
1012 } else {
1013 memcpy(vbuf + pxlen, "-password-", i = sizeof("-password-") -1);
1014 i += pxlen;
1016 memcpy(vbuf + i, addr, addrlen +1);
1017 if ((s = vok_vlook(vbuf)) == NULL) {
1018 vbuf[--i] = '\0';
1019 if ((!addr_is_nuser || (s = vok_vlook(vbuf)) == NULL) &&
1020 (ware & REQ_PASS)) {
1021 if ((s = getpassword(NULL)) == NULL) {
1022 fprintf(stderr,
1023 _("A password is necessary for %s authentication.\n"), pname);
1024 ccp = NULL;
1025 goto jleave;
1029 if (s != NULL)
1030 ccp->cc_pass.l = strlen(ccp->cc_pass.s = savestr(s));
1032 jleave:
1033 ac_free(vbuf);
1034 if (ccp != NULL && (options & OPT_D_VV))
1035 fprintf(stderr, _("Credentials: host `%s', user `%s', pass `%s'\n"),
1036 addr, (ccp->cc_user.s != NULL ? ccp->cc_user.s : ""),
1037 (ccp->cc_pass.s != NULL ? ccp->cc_pass.s : ""));
1038 NYD_LEAVE;
1039 return (ccp != NULL);
1042 FL bool_t
1043 ccred_lookup(struct ccred *ccp, struct url *urlp)
1045 char const *pstr, *authdef;
1046 char *s;
1047 enum okeys authokey;
1048 ui8_t authmask;
1049 enum {NONE=0, WANT_PASS=1<<0, REQ_PASS=1<<1, WANT_USER=1<<2, REQ_USER=1<<3}
1050 ware = NONE;
1051 NYD_ENTER;
1053 memset(ccp, 0, sizeof *ccp);
1054 ccp->cc_user = urlp->url_user;
1056 switch ((ccp->cc_cproto = urlp->url_cproto)) {
1057 default:
1058 case CPROTO_SMTP:
1059 pstr = "smtp";
1060 authokey = ok_v_smtp_auth;
1061 authmask = AUTHTYPE_NONE | AUTHTYPE_PLAIN | AUTHTYPE_LOGIN |
1062 AUTHTYPE_CRAM_MD5 | AUTHTYPE_GSSAPI;
1063 authdef = "none";
1064 break;
1065 case CPROTO_POP3:
1066 pstr = "pop3";
1067 authokey = ok_v_pop3_auth;
1068 authmask = AUTHTYPE_PLAIN;
1069 authdef = "plain";
1070 break;
1071 case CPROTO_IMAP:
1072 pstr = "imap";
1073 authokey = ok_v_imap_auth;
1074 authmask = AUTHTYPE_LOGIN | AUTHTYPE_CRAM_MD5 | AUTHTYPE_GSSAPI;
1075 authdef = "login";
1076 break;
1079 /* Authentication type */
1080 if ((s = xok_VLOOK(authokey, urlp, OXM_ALL)) == NULL)
1081 s = UNCONST(authdef);
1083 if (!asccasecmp(s, "none")) {
1084 ccp->cc_auth = "NONE";
1085 ccp->cc_authtype = AUTHTYPE_NONE;
1086 /*ware = NONE;*/
1087 } else if (!asccasecmp(s, "plain")) {
1088 ccp->cc_auth = "PLAIN";
1089 ccp->cc_authtype = AUTHTYPE_PLAIN;
1090 ware = REQ_PASS | REQ_USER;
1091 } else if (!asccasecmp(s, "login")) {
1092 ccp->cc_auth = "LOGIN";
1093 ccp->cc_authtype = AUTHTYPE_LOGIN;
1094 ware = REQ_PASS | REQ_USER;
1095 } else if (!asccasecmp(s, "cram-md5")) {
1096 ccp->cc_auth = "CRAM-MD5";
1097 ccp->cc_authtype = AUTHTYPE_CRAM_MD5;
1098 ware = REQ_PASS | REQ_USER;
1099 } else if (!asccasecmp(s, "gssapi")) {
1100 ccp->cc_auth = "GSS-API";
1101 ccp->cc_authtype = AUTHTYPE_GSSAPI;
1102 ware = REQ_USER;
1103 } /* no else */
1105 /* Verify method */
1106 if (!(ccp->cc_authtype & authmask)) {
1107 fprintf(stderr, _("Unsupported %s authentication method: %s\n"), pstr, s);
1108 ccp = NULL;
1109 goto jleave;
1111 # ifndef HAVE_MD5
1112 if (ccp->cc_authtype == AUTHTYPE_CRAM_MD5) {
1113 fprintf(stderr, _("No CRAM-MD5 support compiled in.\n"));
1114 ccp = NULL;
1115 goto jleave;
1117 # endif
1118 # ifndef HAVE_GSSAPI
1119 if (ccp->cc_authtype == AUTHTYPE_GSSAPI) {
1120 fprintf(stderr, _("No GSS-API support compiled in.\n"));
1121 ccp = NULL;
1122 goto jleave;
1124 # endif
1126 /* Password */
1127 if ((ccp->cc_pass = urlp->url_pass).s != NULL)
1128 goto jleave;
1130 if ((s = xok_vlook(password, urlp, OXM_ALL)) != NULL)
1131 goto js2pass;
1132 # ifdef HAVE_AGENT
1133 if ((s = xok_vlook(agent_shell_lookup, urlp, OXM_ALL)) != NULL) {
1134 if (!_agent_shell_lookup(urlp, s)) {
1135 ccp = NULL;
1136 goto jleave;
1137 } else if (urlp->url_pass.s != NULL) {
1138 ccp->cc_pass = urlp->url_pass;
1139 goto jleave;
1142 # endif
1143 # ifdef HAVE_NETRC
1144 if (xok_blook(netrc_lookup, urlp, OXM_ALL) && _nrc_lookup(urlp, TRU1)) {
1145 ccp->cc_pass = urlp->url_pass;
1146 goto jleave;
1148 # endif
1149 if (ware & REQ_PASS) {
1150 if ((s = getpassword(NULL)) != NULL)
1151 js2pass:
1152 ccp->cc_pass.l = strlen(ccp->cc_pass.s = savestr(s));
1153 else {
1154 fprintf(stderr, _("A password is necessary for %s authentication.\n"),
1155 pstr);
1156 ccp = NULL;
1160 jleave:
1161 if (ccp != NULL && (options & OPT_D_VV))
1162 fprintf(stderr, _("Credentials: host `%s', user `%s', pass `%s'\n"),
1163 urlp->url_h_p.s, (ccp->cc_user.s != NULL ? ccp->cc_user.s : ""),
1164 (ccp->cc_pass.s != NULL ? ccp->cc_pass.s : ""));
1165 NYD_LEAVE;
1166 return (ccp != NULL);
1168 #endif /* HAVE_SOCKETS */
1170 #ifdef HAVE_NETRC
1171 FL int
1172 c_netrc(void *v)
1174 char **argv = v;
1175 struct nrc_node *nrc;
1176 NYD_ENTER;
1178 if (*argv == NULL)
1179 goto jlist;
1180 if (argv[1] != NULL)
1181 goto jerr;
1182 if (!asccasecmp(*argv, "show"))
1183 goto jlist;
1184 if (!asccasecmp(*argv, "clear"))
1185 goto jclear;
1186 jerr:
1187 fprintf(stderr, "Synopsis: netrc: %s\n",
1188 _("Either <show> (default) or <clear> the .netrc cache"));
1189 v = NULL;
1190 jleave:
1191 NYD_LEAVE;
1192 return (v == NULL ? !STOP : !OKAY); /* xxx 1:bad 0:good -- do some */
1194 jlist: {
1195 FILE *fp;
1196 size_t l;
1198 if (_nrc_list == NULL)
1199 _nrc_init();
1200 if (_nrc_list == NRC_NODE_ERR) {
1201 fprintf(stderr, _("Interpolate what file?\n"));
1202 v = NULL;
1203 goto jleave;
1206 if ((fp = Ftmp(NULL, "netrc", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600)
1207 ) == NULL) {
1208 perror("tmpfile");
1209 v = NULL;
1210 goto jleave;
1213 for (l = 0, nrc = _nrc_list; nrc != NULL; ++l, nrc = nrc->nrc_next) {
1214 fprintf(fp, _("Host %s: "), nrc->nrc_dat);
1215 if (nrc->nrc_ulen > 0)
1216 fprintf(fp, _("user %s, "), nrc->nrc_dat + nrc->nrc_mlen +1);
1217 else
1218 fputs(_("no user, "), fp);
1219 if (nrc->nrc_plen > 0)
1220 fprintf(fp, _("password %s.\n"),
1221 nrc->nrc_dat + nrc->nrc_mlen +1 + nrc->nrc_ulen +1);
1222 else
1223 fputs(_("no password.\n"), fp);
1226 page_or_print(fp, l);
1227 Fclose(fp);
1229 goto jleave;
1231 jclear:
1232 if (_nrc_list == NRC_NODE_ERR)
1233 _nrc_list = NULL;
1234 while ((nrc = _nrc_list) != NULL) {
1235 _nrc_list = nrc->nrc_next;
1236 free(nrc);
1238 goto jleave;
1240 #endif /* HAVE_NETRC */
1242 #ifdef HAVE_MD5
1243 FL char *
1244 md5tohex(char hex[MD5TOHEX_SIZE], void const *vp)
1246 char const *cp = vp;
1247 size_t i, j;
1248 NYD_ENTER;
1250 for (i = 0; i < MD5TOHEX_SIZE / 2; ++i) {
1251 j = i << 1;
1252 # define __hex(n) ((n) > 9 ? (n) - 10 + 'a' : (n) + '0')
1253 hex[j] = __hex((cp[i] & 0xF0) >> 4);
1254 hex[++j] = __hex(cp[i] & 0x0F);
1255 # undef __hex
1257 NYD_LEAVE;
1258 return hex;
1261 FL char *
1262 cram_md5_string(struct str const *user, struct str const *pass,
1263 char const *b64)
1265 struct str in, out;
1266 char digest[16], *cp;
1267 NYD_ENTER;
1269 out.s = NULL;
1270 in.s = UNCONST(b64);
1271 in.l = strlen(in.s);
1272 b64_decode(&out, &in, NULL);
1273 assert(out.s != NULL);
1275 hmac_md5((uc_it*)out.s, out.l, (uc_it*)pass->s, pass->l, digest);
1276 free(out.s);
1277 cp = md5tohex(salloc(MD5TOHEX_SIZE +1), digest);
1279 in.l = user->l + MD5TOHEX_SIZE +1;
1280 in.s = ac_alloc(user->l + 1 + MD5TOHEX_SIZE +1);
1281 memcpy(in.s, user->s, user->l);
1282 in.s[user->l] = ' ';
1283 memcpy(in.s + user->l + 1, cp, MD5TOHEX_SIZE);
1284 b64_encode(&out, &in, B64_SALLOC | B64_CRLF);
1285 ac_free(in.s);
1286 NYD_LEAVE;
1287 return out.s;
1290 FL void
1291 hmac_md5(unsigned char *text, int text_len, unsigned char *key, int key_len,
1292 void *digest)
1295 * This code is taken from
1297 * Network Working Group H. Krawczyk
1298 * Request for Comments: 2104 IBM
1299 * Category: Informational M. Bellare
1300 * UCSD
1301 * R. Canetti
1302 * IBM
1303 * February 1997
1306 * HMAC: Keyed-Hashing for Message Authentication
1308 md5_ctx context;
1309 unsigned char k_ipad[65]; /* inner padding - key XORd with ipad */
1310 unsigned char k_opad[65]; /* outer padding - key XORd with opad */
1311 unsigned char tk[16];
1312 int i;
1313 NYD_ENTER;
1315 /* if key is longer than 64 bytes reset it to key=MD5(key) */
1316 if (key_len > 64) {
1317 md5_ctx tctx;
1319 md5_init(&tctx);
1320 md5_update(&tctx, key, key_len);
1321 md5_final(tk, &tctx);
1323 key = tk;
1324 key_len = 16;
1327 /* the HMAC_MD5 transform looks like:
1329 * MD5(K XOR opad, MD5(K XOR ipad, text))
1331 * where K is an n byte key
1332 * ipad is the byte 0x36 repeated 64 times
1333 * opad is the byte 0x5c repeated 64 times
1334 * and text is the data being protected */
1336 /* start out by storing key in pads */
1337 memset(k_ipad, 0, sizeof k_ipad);
1338 memset(k_opad, 0, sizeof k_opad);
1339 memcpy(k_ipad, key, key_len);
1340 memcpy(k_opad, key, key_len);
1342 /* XOR key with ipad and opad values */
1343 for (i=0; i<64; i++) {
1344 k_ipad[i] ^= 0x36;
1345 k_opad[i] ^= 0x5c;
1348 /* perform inner MD5 */
1349 md5_init(&context); /* init context for 1st pass */
1350 md5_update(&context, k_ipad, 64); /* start with inner pad */
1351 md5_update(&context, text, text_len); /* then text of datagram */
1352 md5_final(digest, &context); /* finish up 1st pass */
1354 /* perform outer MD5 */
1355 md5_init(&context); /* init context for 2nd pass */
1356 md5_update(&context, k_opad, 64); /* start with outer pad */
1357 md5_update(&context, digest, 16); /* then results of 1st hash */
1358 md5_final(digest, &context); /* finish up 2nd pass */
1359 NYD_LEAVE;
1361 #endif /* HAVE_MD5 */
1363 /* s-it-mode */