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