1 /*
2  *  Off-the-Record Messaging library
3  *  Copyright (C) 2004-2015  Ian Goldberg, Rob Smits, Chris Alexander,
4  *  			      Willy Lew, Lisa Du, Nikita Borisov
5  *                           <otr@cypherpunks.ca>
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of version 2.1 of the GNU Lesser General
9  *  Public License as published by the Free Software Foundation.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 /* system headers */
22 #include <stdio.h>
23 #include <stdlib.h>
24 
25 /* libgcrypt headers */
26 #include <gcrypt.h>
27 
28 /* libotr headers */
29 #include "instag.h"
30 #include "userstate.h"
31 
32 /* Forget the given instag. */
otrl_instag_forget(OtrlInsTag * instag)33 void otrl_instag_forget(OtrlInsTag* instag) {
34     if (!instag) return;
35 
36     if (instag->accountname) free(instag->accountname);
37     if (instag->protocol) free(instag->protocol);
38 
39     /* Re-link the list */
40     *(instag->tous) = instag->next;
41     if (instag->next) {
42 	instag->next->tous = instag->tous;
43     }
44 
45     free(instag);
46 }
47 
48 /* Forget all instags in a given OtrlUserState. */
otrl_instag_forget_all(OtrlUserState us)49 void otrl_instag_forget_all(OtrlUserState us) {
50     while(us->instag_root) {
51 	otrl_instag_forget(us->instag_root);
52     }
53 }
54 
55 /* Fetch the instance tag from the given OtrlUserState associated with
56  * the given account */
otrl_instag_find(OtrlUserState us,const char * accountname,const char * protocol)57 OtrlInsTag * otrl_instag_find(OtrlUserState us, const char *accountname,
58 	const char *protocol)
59 {
60     OtrlInsTag *p;
61 
62     for(p=us->instag_root; p; p=p->next) {
63 	if (!strcmp(p->accountname, accountname) &&
64 		!strcmp(p->protocol, protocol)) {
65 	    return p;
66 	}
67     }
68     return NULL;
69 }
70 
71 /* Read our instance tag from a file on disk into the given
72  * OtrlUserState. */
otrl_instag_read(OtrlUserState us,const char * filename)73 gcry_error_t otrl_instag_read(OtrlUserState us, const char *filename)
74 {
75     gcry_error_t err;
76     FILE *instf;
77 
78     /* Open the instance tag file. */
79     instf = fopen(filename, "rb");
80     if (!instf) {
81 	return gcry_error_from_errno(errno);
82     }
83 
84     err = otrl_instag_read_FILEp(us, instf);
85     fclose(instf);
86     return err;
87 }
88 
89 /* Read our instance tag from a file on disk into the given
90  * OtrlUserState. The FILE* must be open for reading. */
otrl_instag_read_FILEp(OtrlUserState us,FILE * instf)91 gcry_error_t otrl_instag_read_FILEp(OtrlUserState us, FILE *instf)
92 {
93     if (!instf) return gcry_error(GPG_ERR_NO_ERROR);
94 
95     OtrlInsTag *p;
96     char storeline[1000];
97     size_t maxsize = sizeof(storeline);
98 
99     while(fgets(storeline, maxsize, instf)) {
100 	char *prevpos;
101 	char *pos;
102 	unsigned int instag = 0;
103 
104 	p = malloc(sizeof(*p));
105 	if (!p) {
106 	    return gcry_error(GPG_ERR_ENOMEM);
107 	}
108 
109 	/* Parse the line, which should be of the form:
110 	 * accountname\tprotocol\t40_hex_nybbles\n          */
111 	prevpos = storeline;
112 	pos = strchr(prevpos, '\t');
113 	if (!pos) {
114 	    free(p);
115 	    continue;
116 	}
117 	*pos = '\0';
118 	pos++;
119 	p->accountname = malloc(pos - prevpos);
120 	if (!(p->accountname)) {
121 	    free(p);
122 	    return gcry_error(GPG_ERR_ENOMEM);
123 	}
124 	memmove(p->accountname, prevpos, pos - prevpos);
125 
126 	prevpos = pos;
127 	pos = strchr(prevpos, '\t');
128 	if (!pos) {
129 	    free(p->accountname);
130 	    free(p);
131 	    continue;
132 	}
133 	*pos = '\0';
134 	pos++;
135 	p->protocol = malloc(pos - prevpos);
136 	if (!(p->protocol)) {
137 	    free(p->accountname);
138 	    free(p);
139 	    return gcry_error(GPG_ERR_ENOMEM);
140 	}
141 	memmove(p->protocol, prevpos, pos - prevpos);
142 
143 	prevpos = pos;
144 	pos = strchr(prevpos, '\r');
145 	if (!pos) pos = strchr(prevpos, '\n');
146 	if (!pos) {
147 	    free(p->accountname);
148 	    free(p->protocol);
149 	    free(p);
150 	    continue;
151 	}
152 	*pos = '\0';
153 	pos++;
154 	/* hex str of length 8 */
155 	if (strlen(prevpos) != 8) {
156 	    free(p->accountname);
157 	    free(p->protocol);
158 	    free(p);
159 	    continue;
160 	}
161 
162 	sscanf(prevpos, "%08x", &instag);
163 
164 	if (instag < OTRL_MIN_VALID_INSTAG) {
165 	    free(p->accountname);
166 	    free(p->protocol);
167 	    free(p);
168 	    continue;
169 	}
170 	p->instag = instag;
171 
172 	/* Link it up */
173 	p->next = us->instag_root;
174 	if (p->next) {
175 	    p->next->tous = &(p->next);
176 	}
177 	p->tous = &(us->instag_root);
178 	us->instag_root = p;
179     }
180 
181     return gcry_error(GPG_ERR_NO_ERROR);
182 }
183 
184 /* Generate a new instance tag for the given account and write to file */
otrl_instag_generate(OtrlUserState us,const char * filename,const char * accountname,const char * protocol)185 gcry_error_t otrl_instag_generate(OtrlUserState us, const char *filename,
186 	const char *accountname, const char *protocol)
187 {
188     gcry_error_t err;
189     FILE *instf;
190 
191     /* Open the instance tag file. */
192     instf = fopen(filename, "wb");
193     if (!instf) {
194 	return gcry_error_from_errno(errno);
195     }
196 
197     err = otrl_instag_generate_FILEp(us, instf, accountname, protocol);
198     fclose(instf);
199     return err;
200 }
201 
202 /* Return a new valid instance tag */
otrl_instag_get_new()203 otrl_instag_t otrl_instag_get_new()
204 {
205     otrl_instag_t result = 0;
206 
207     while(result < OTRL_MIN_VALID_INSTAG) {
208 	otrl_instag_t * instag = (otrl_instag_t *)gcry_random_bytes(
209 		sizeof(otrl_instag_t), GCRY_STRONG_RANDOM);
210 	result = *instag;
211 	gcry_free(instag);
212     }
213 
214     return result;
215 }
216 
217 /* Generate a new instance tag for the given account and write to file
218  * The FILE* must be open for writing. */
otrl_instag_generate_FILEp(OtrlUserState us,FILE * instf,const char * accountname,const char * protocol)219 gcry_error_t otrl_instag_generate_FILEp(OtrlUserState us, FILE *instf,
220 	const char *accountname, const char *protocol)
221 {
222     OtrlInsTag *p;
223     if (!accountname || !protocol) return gcry_error(GPG_ERR_NO_ERROR);
224 
225     p = (OtrlInsTag *)malloc(sizeof(OtrlInsTag));
226     p->accountname = strdup(accountname);
227     p->protocol = strdup(protocol);
228 
229     p->instag = otrl_instag_get_new();
230 
231     /* Add to our list in OtrlUserState */
232     p->next = us->instag_root;
233     if (p->next) {
234 	p->next->tous = &(p->next);
235     }
236     p->tous = &(us->instag_root);
237     us->instag_root = p;
238 
239     otrl_instag_write_FILEp(us, instf);
240 
241     return gcry_error(GPG_ERR_NO_ERROR);
242 }
243 
244 /* Write our instance tags to a file on disk. */
otrl_instag_write(OtrlUserState us,const char * filename)245 gcry_error_t otrl_instag_write(OtrlUserState us, const char *filename)
246 {
247     gcry_error_t err;
248     FILE *instf;
249 
250     /* Open the instance tag file. */
251     instf = fopen(filename, "wb");
252     if (!instf) {
253 	return gcry_error_from_errno(errno);
254     }
255 
256     err = otrl_instag_write_FILEp(us, instf);
257     fclose(instf);
258     return err;
259 }
260 
261 /* Write our instance tags to a file on disk.
262  * The FILE* must be open for writing. */
otrl_instag_write_FILEp(OtrlUserState us,FILE * instf)263 gcry_error_t otrl_instag_write_FILEp(OtrlUserState us, FILE *instf)
264 {
265     OtrlInsTag *p;
266     /* This line should be ignored when read back in, since there are no
267     tabs. */
268     fprintf(instf, "# WARNING! You shouldn't copy this file to another"
269     " computer. It is unnecessary and can cause problems.\n");
270     for(p=us->instag_root; p; p=p->next) {
271 	fprintf(instf, "%s\t%s\t%08x\n", p->accountname, p->protocol,
272 		p->instag);
273     }
274 
275     return gcry_error(GPG_ERR_NO_ERROR);
276 }
277 
278