1*bb1c3b3fSnaddy /* $OpenBSD: cookie.c,v 1.10 2021/02/16 16:27:34 naddy Exp $ */
2bfd817adSflorian
3bfd817adSflorian /*
4bfd817adSflorian * Copyright (c) 2007 Pierre-Yves Ritschard <pyr@openbsd.org>
5bfd817adSflorian *
6bfd817adSflorian * Permission to use, copy, modify, and distribute this software for any
7bfd817adSflorian * purpose with or without fee is hereby granted, provided that the above
8bfd817adSflorian * copyright notice and this permission notice appear in all copies.
9bfd817adSflorian *
10bfd817adSflorian * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11bfd817adSflorian * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12bfd817adSflorian * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13bfd817adSflorian * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14bfd817adSflorian * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15bfd817adSflorian * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16bfd817adSflorian * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17bfd817adSflorian */
18bfd817adSflorian
19bfd817adSflorian #ifndef NOSSL
20bfd817adSflorian
21bfd817adSflorian #include <sys/types.h>
22bfd817adSflorian #include <sys/queue.h>
23bfd817adSflorian
24bfd817adSflorian #include <err.h>
25bfd817adSflorian #include <errno.h>
26bfd817adSflorian #include <fnmatch.h>
27bfd817adSflorian #include <stdio.h>
28bfd817adSflorian #include <string.h>
29bfd817adSflorian #include <stdlib.h>
30bfd817adSflorian #include <time.h>
31bfd817adSflorian
32bfd817adSflorian #include "ftp_var.h"
33bfd817adSflorian
34bfd817adSflorian struct cookie {
35bfd817adSflorian TAILQ_ENTRY(cookie) entry;
36bfd817adSflorian TAILQ_ENTRY(cookie) tempentry;
37bfd817adSflorian u_int8_t flags;
38bfd817adSflorian #define F_SECURE 0x01
39bfd817adSflorian #define F_TAILMATCH 0x02
40bfd817adSflorian #define F_NOEXPIRY 0x04
41bfd817adSflorian #define F_MATCHPATH 0x08
42bfd817adSflorian time_t expires;
43bfd817adSflorian char *domain;
44bfd817adSflorian char *path;
45bfd817adSflorian char *key;
46bfd817adSflorian char *val;
47bfd817adSflorian };
48bfd817adSflorian TAILQ_HEAD(cookiejar, cookie);
49bfd817adSflorian
50bfd817adSflorian typedef enum {
51bfd817adSflorian DOMAIN = 0, TAILMATCH = 1, PATH = 2, SECURE = 3,
52bfd817adSflorian EXPIRES = 4, NAME = 5, VALUE = 6, DONE = 7
53bfd817adSflorian } field_t;
54bfd817adSflorian
55bfd817adSflorian static struct cookiejar jar;
56bfd817adSflorian
57bfd817adSflorian void
cookie_load(void)58bfd817adSflorian cookie_load(void)
59bfd817adSflorian {
60bfd817adSflorian field_t field;
61bfd817adSflorian time_t date;
62bfd817adSflorian char *line;
63*bb1c3b3fSnaddy char *lbuf = NULL;
64*bb1c3b3fSnaddy size_t lbufsize = 0;
65bfd817adSflorian char *param;
66bfd817adSflorian const char *estr;
67bfd817adSflorian FILE *fp;
68bfd817adSflorian struct cookie *ck;
69bfd817adSflorian
70bfd817adSflorian if (cookiefile == NULL)
71bfd817adSflorian return;
72bfd817adSflorian
73bfd817adSflorian TAILQ_INIT(&jar);
74bfd817adSflorian fp = fopen(cookiefile, "r");
75bfd817adSflorian if (fp == NULL)
76bfd817adSflorian err(1, "cannot open cookie file %s", cookiefile);
77bfd817adSflorian date = time(NULL);
78*bb1c3b3fSnaddy while (getline(&lbuf, &lbufsize, fp) != -1) {
79bfd817adSflorian line = lbuf;
80*bb1c3b3fSnaddy line[strcspn(line, "\r\n")] = '\0';
81bfd817adSflorian
82bfd817adSflorian line += strspn(line, " \t");
83bfd817adSflorian if ((*line == '#') || (*line == '\0')) {
84bfd817adSflorian continue;
85bfd817adSflorian }
86bfd817adSflorian field = DOMAIN;
87bfd817adSflorian ck = calloc(1, sizeof(*ck));
88bfd817adSflorian if (ck == NULL)
89bfd817adSflorian err(1, NULL);
90bfd817adSflorian while ((param = strsep(&line, "\t")) != NULL) {
91bfd817adSflorian switch (field) {
92bfd817adSflorian case DOMAIN:
93bfd817adSflorian if (*param == '.') {
94bfd817adSflorian if (asprintf(&ck->domain,
95bfd817adSflorian "*%s", param) == -1)
96bfd817adSflorian err(1, NULL);
97bfd817adSflorian } else {
98bfd817adSflorian ck->domain = strdup(param);
99bfd817adSflorian if (ck->domain == NULL)
100bfd817adSflorian err(1, NULL);
101bfd817adSflorian }
102bfd817adSflorian break;
103bfd817adSflorian case TAILMATCH:
104bfd817adSflorian if (strcasecmp(param, "TRUE") == 0) {
105bfd817adSflorian ck->flags |= F_TAILMATCH;
106bfd817adSflorian } else if (strcasecmp(param, "FALSE") != 0) {
107bfd817adSflorian errx(1, "invalid cookie file");
108bfd817adSflorian }
109bfd817adSflorian break;
110bfd817adSflorian case PATH:
111bfd817adSflorian if (strcmp(param, "/") != 0) {
112bfd817adSflorian ck->flags |= F_MATCHPATH;
113bfd817adSflorian if (asprintf(&ck->path,
114bfd817adSflorian "%s*", param) == -1)
115bfd817adSflorian err(1, NULL);
116bfd817adSflorian }
117bfd817adSflorian break;
118bfd817adSflorian case SECURE:
119bfd817adSflorian if (strcasecmp(param, "TRUE") == 0) {
120bfd817adSflorian ck->flags |= F_SECURE;
121bfd817adSflorian } else if (strcasecmp(param, "FALSE") != 0) {
122bfd817adSflorian errx(1, "invalid cookie file");
123bfd817adSflorian }
124bfd817adSflorian break;
125bfd817adSflorian case EXPIRES:
126bfd817adSflorian /*
127bfd817adSflorian * rely on sizeof(time_t) being 4
128bfd817adSflorian */
129bfd817adSflorian ck->expires = strtonum(param, 0,
130bfd817adSflorian INT_MAX, &estr);
131bfd817adSflorian if (estr) {
132bfd817adSflorian if (errno == ERANGE)
133bfd817adSflorian ck->flags |= F_NOEXPIRY;
134bfd817adSflorian else
135bfd817adSflorian errx(1, "invalid cookie file");
136bfd817adSflorian }
137bfd817adSflorian break;
138bfd817adSflorian case NAME:
139bfd817adSflorian ck->key = strdup(param);
140bfd817adSflorian if (ck->key == NULL)
141bfd817adSflorian err(1, NULL);
142bfd817adSflorian break;
143bfd817adSflorian case VALUE:
144bfd817adSflorian ck->val = strdup(param);
145bfd817adSflorian if (ck->val == NULL)
146bfd817adSflorian err(1, NULL);
147bfd817adSflorian break;
148bfd817adSflorian case DONE:
149bfd817adSflorian errx(1, "invalid cookie file");
150bfd817adSflorian break;
151bfd817adSflorian }
152bfd817adSflorian field++;
153bfd817adSflorian }
154bfd817adSflorian if (field != DONE)
155bfd817adSflorian errx(1, "invalid cookie file");
156bfd817adSflorian if (ck->expires < date && !(ck->flags & F_NOEXPIRY)) {
157bfd817adSflorian free(ck->val);
158bfd817adSflorian free(ck->key);
159bfd817adSflorian free(ck->path);
160bfd817adSflorian free(ck->domain);
161bfd817adSflorian free(ck);
162bfd817adSflorian } else
163bfd817adSflorian TAILQ_INSERT_TAIL(&jar, ck, entry);
164bfd817adSflorian }
165bfd817adSflorian free(lbuf);
166bfd817adSflorian fclose(fp);
167bfd817adSflorian }
168bfd817adSflorian
169bfd817adSflorian void
cookie_get(const char * domain,const char * path,int secure,char ** pstr)170bfd817adSflorian cookie_get(const char *domain, const char *path, int secure, char **pstr)
171bfd817adSflorian {
172bfd817adSflorian size_t len;
173bfd817adSflorian size_t headlen;
174bfd817adSflorian char *head;
175bfd817adSflorian char *str;
176bfd817adSflorian struct cookie *ck;
177bfd817adSflorian struct cookiejar tempjar;
178bfd817adSflorian
179bfd817adSflorian *pstr = NULL;
180bfd817adSflorian
181bfd817adSflorian if (cookiefile == NULL)
182bfd817adSflorian return;
183bfd817adSflorian
184bfd817adSflorian TAILQ_INIT(&tempjar);
185bfd817adSflorian len = strlen("Cookie\r\n");
186bfd817adSflorian
187bfd817adSflorian TAILQ_FOREACH(ck, &jar, entry) {
188bfd817adSflorian if (fnmatch(ck->domain, domain, 0) == 0 &&
189bfd817adSflorian (secure || !(ck->flags & F_SECURE))) {
190bfd817adSflorian
191bfd817adSflorian if (ck->flags & F_MATCHPATH &&
192bfd817adSflorian fnmatch(ck->path, path, 0) != 0)
193bfd817adSflorian continue;
194bfd817adSflorian
195bfd817adSflorian len += strlen(ck->key) + strlen(ck->val) +
196bfd817adSflorian strlen("; =");
197bfd817adSflorian TAILQ_INSERT_TAIL(&tempjar, ck, tempentry);
198bfd817adSflorian }
199bfd817adSflorian }
200bfd817adSflorian if (TAILQ_EMPTY(&tempjar))
201bfd817adSflorian return;
202bfd817adSflorian len += 1;
203bfd817adSflorian str = malloc(len);
204bfd817adSflorian if (str == NULL)
205bfd817adSflorian err(1, NULL);
206bfd817adSflorian
207bfd817adSflorian (void)strlcpy(str, "Cookie:", len);
208bfd817adSflorian TAILQ_FOREACH(ck, &tempjar, tempentry) {
209bfd817adSflorian head = str + strlen(str);
210bfd817adSflorian headlen = len - strlen(str);
211bfd817adSflorian
212bfd817adSflorian snprintf(head, headlen, "%s %s=%s",
213bfd817adSflorian (ck == TAILQ_FIRST(&tempjar))? "" : ";", ck->key, ck->val);
214bfd817adSflorian }
215bfd817adSflorian if (strlcat(str, "\r\n", len) >= len)
216bfd817adSflorian errx(1, "cookie header truncated");
217bfd817adSflorian *pstr = str;
218bfd817adSflorian }
219bfd817adSflorian
220bfd817adSflorian #endif /* !SMALL */
221bfd817adSflorian
222