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