Branch data Line data Source code
1 : : /* zxutil.c - Utility functions
2 : : * Copyright (c) 2010 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.
3 : : * Copyright (c) 2006-2008 Symlabs (symlabs@symlabs.com), All Rights Reserved.
4 : : * Author: Sampo Kellomaki (sampo@iki.fi)
5 : : * This is confidential unpublished proprietary source code of the author.
6 : : * NO WARRANTY, not even implied warranties. Contains trade secrets.
7 : : * Distribution prohibited unless authorized in writing.
8 : : * Licensed under Apache License 2.0, see file COPYING.
9 : : * $Id: zxutil.c,v 1.53 2009-11-29 12:23:06 sampo Exp $
10 : : *
11 : : * 15.4.2006, created over Easter holiday --Sampo
12 : : * 7.10.2008, added documentation --Sampo
13 : : * 21.5.2010, added file copy --Sampo
14 : : */
15 : :
16 : : #include "platform.h"
17 : : #include "errmac.h"
18 : :
19 : : #include <stdarg.h>
20 : : #include <stdio.h>
21 : : #include <string.h>
22 : : #include <sys/types.h>
23 : : #include <fcntl.h>
24 : : #include <errno.h>
25 : : #include <time.h>
26 : : #include <openssl/sha.h>
27 : : #include <zlib.h>
28 : :
29 : : #ifdef MINGW
30 : : #include <windows.h>
31 : : #define fdtype HANDLE
32 : : #else
33 : : #define fdtype int
34 : : #include <sys/stat.h>
35 : : #endif
36 : :
37 : : #include "zx.h"
38 : : #include "zxidconf.h"
39 : :
40 : : #if !defined(USE_STDIO) && !defined(MINGW)
41 : : /* *** Static initialization of struct flock is suspect since man fcntl() documentation
42 : : * does not guarantee ordering of the fields, or that they would be the first fields.
43 : : * On Linux-2.4 and 2.6 as well as Solaris-8 the ordering is as follows, but this needs
44 : : * to be checked on other platforms.
45 : : * l_type, l_whence, l_start, l_len */
46 : : struct flock zx_rdlk = { F_RDLCK, SEEK_SET, 0, 1 };
47 : : struct flock zx_wrlk = { F_WRLCK, SEEK_SET, 0, 1 };
48 : : struct flock zx_unlk = { F_UNLCK, SEEK_SET, 0, 1 };
49 : : #endif
50 : :
51 : : int close_file(fdtype fd, const char* logkey);
52 : :
53 : : /*() Generate formatted file name path. */
54 : :
55 : : /* Called by: name_from_path, vopen_fd_from_path */
56 : : int vname_from_path(char* buf, int buf_len, const char* name_fmt, va_list ap)
57 : 13300 : {
58 : 13300 : int len = vsnprintf(buf, buf_len, name_fmt, ap);
59 : 13300 : buf[buf_len-1] = 0; /* must terminate manually as on win32 nul is not guaranteed */
60 [ - + ]: 13300 : if (len < 0) {
61 : 0 : perror("vsnprintf");
62 [ # # # # ]: 0 : D("Broken vsnprintf? Impossible to compute length of string. Be sure to `export LANG=C' if you get errors about multibyte characters. Length returned: %d", len);
63 : 0 : return 0;
64 : : }
65 : 13300 : return 1;
66 : : }
67 : :
68 : : /*() Generate formatted file name path. */
69 : :
70 : : /* Called by: main, zxid_check_fed x3, zxid_del_ses x3, zxid_di_query, zxid_find_epr, zxid_find_ses, zxid_gen_boots, zxid_idp_as_do x2, zxid_mk_transient_nid x2, zxid_mk_usr_a7n_to_sp x2, zxid_print_session, zxid_put_ses, zxid_put_user, zxlog_output x2 */
71 : : int name_from_path(char* buf, int buf_len, const char* name_fmt, ...)
72 : 6231 : {
73 : : int ret;
74 : : va_list ap;
75 : 6231 : va_start(ap, name_fmt);
76 : 6231 : ret = vname_from_path(buf, buf_len, name_fmt, ap);
77 : 6231 : va_end(ap);
78 : 6231 : return ret;
79 : : }
80 : :
81 : : /*() Open a file with formatted file name path. */
82 : :
83 : : /* Called by: open_fd_from_path, read_all, read_all_alloc */
84 : : fdtype vopen_fd_from_path(int flags, int mode, const char* logkey, int reperr, const char* name_fmt, va_list ap)
85 : 7069 : {
86 : : fdtype fd;
87 : : char buf[ZXID_MAX_BUF];
88 [ - + ]: 7069 : if (!vname_from_path(buf, sizeof(buf), name_fmt, ap))
89 : 0 : return BADFD;
90 : : #ifdef MINGW
91 : : if (flags == O_RDONLY) {
92 : : fd = openfile_ro(buf);
93 : : } else {
94 : : fd = CreateFile(buf, MINGW_RW_PERM, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
95 : : }
96 : : #else
97 : 7069 : fd = open(buf, flags, mode);
98 : : #endif
99 [ + + ]: 7069 : if (fd == BADFD) {
100 [ + + + + ]: 3152 : if (reperr && logkey[0] != '-') {
101 : 475 : perror("open (vopen_fd_from_path)");
102 [ + - ]: 475 : ERR("File(%s) not found lk(%s) errno=%d err(%s). flags=0x%x, euid=%d egid=%d", buf, logkey, errno, STRERROR(errno), flags, geteuid(), getegid());
103 : : } else {
104 [ + + + - : 2202 : D("File(%s) not found lk(%s) errno=%d err(%s). flags=0x%x, euid=%d egid=%d", buf, logkey, errno, STRERROR(errno), flags, geteuid(), getegid());
- + ]
105 : : }
106 : 2677 : return BADFD;
107 : : }
108 : 4392 : return fd;
109 : : }
110 : :
111 : : /*() Open a file with formatted file name path. */
112 : :
113 : : /* Called by: main x2, write_all_path_fmt, zxid_addmd, zxid_cache_epr, zxid_get_ent_file, zxid_get_meta, zxid_reg_svc x2, zxid_write_ent_to_cache */
114 : : fdtype open_fd_from_path(int flags, int mode, const char* logkey, int reperr, const char* name_fmt, ...)
115 : 1181 : {
116 : : va_list ap;
117 : : fdtype fd;
118 : 1181 : va_start(ap, name_fmt);
119 : 1181 : fd = vopen_fd_from_path(flags, mode, logkey, reperr, name_fmt, ap);
120 : 1181 : va_end(ap);
121 : 1181 : return fd;
122 : : }
123 : :
124 : : /*() Low level function that keeps on sucking from a file descriptor until
125 : : * want is satisfied or error happens. May block (though usually will not if
126 : : * the file is in cache or local disk) in process. Buffer p must have been allocated.
127 : : * Return value reflects last got, i.e. what last read(2) system call returned.
128 : : * got_all reflects the total number of bytes received. */
129 : :
130 : : /* Called by: main x10, opt x6, read_all, read_all_alloc, test_ibm_cert_problem, zxcall_main, zxdecode_main, zxid_addmd, zxid_get_ent_file, zxid_reg_svc, zxid_simple_cf_ses, zxidwspcgi_main, zxidwspcgi_parent */
131 : : int read_all_fd(fdtype fd, char* p, int want, int* got_all)
132 : 4359 : {
133 : : #ifdef USE_STDIO
134 : : int got;
135 : : got = fread(p, 1, want, (FILE*)fd);
136 : : if (got_all) *got_all = got;
137 : : #elif defined(MINGW)
138 : : DWORD got;
139 : : if (!ReadFile(fd, p, want, &got, 0))
140 : : return -1;
141 : : if (got_all) *got_all = got;
142 : : #else /* The Unix way */
143 : 4359 : int got = 0;
144 [ + - ]: 4359 : if (got_all) *got_all = 0;
145 [ + + ]: 13335 : while (want) {
146 : 6559 : got = read(fd, p, want);
147 [ + + ]: 6559 : if (got <= 0) break; /* EOF, possibly premature */
148 [ + - ]: 4617 : if (got_all)
149 : 4617 : *got_all += got;
150 : 4617 : p += got;
151 : 4617 : want -= got;
152 : : }
153 : : #endif
154 : 4359 : return got; /* N.B. This is the last got, not the total. Use got_all for total. */
155 : : }
156 : :
157 : : /*() Read all data from a file at formatted file name path.
158 : : *
159 : : * maxlen:: Length of buffer
160 : : * buf:: Result parameter. This buffer will be populated with data from the file.
161 : : * logkey:: Logging key to help debugging
162 : : * name_fmt:: Format string for building file name
163 : : * return:: actual total length. The buffer will always be nul terminated. */
164 : :
165 : : /* Called by: covimp_test, list_user x2, list_users, main x4, opt x10, test_mode x2, zx_get_symkey, zxid_check_fed, zxid_get_ses, zxid_get_user_nameid, zxid_idp_map_nid2uid, zxid_lscot_line, zxid_nidmap_do, zxid_ps_accept_invite, zxid_ps_finalize_invite, zxid_pw_authn x3, zxid_read_cert, zxid_read_private_key, zxid_template_page_cf */
166 : : int read_all(int maxlen, char* buf, const char* logkey, int reperr, const char* name_fmt, ...)
167 : 1906 : {
168 : : va_list ap;
169 : : int gotall;
170 : : fdtype fd;
171 : 1906 : va_start(ap, name_fmt);
172 : 1906 : fd = vopen_fd_from_path(O_RDONLY, 0, logkey, reperr, name_fmt, ap);
173 : 1906 : va_end(ap);
174 [ + + + - ]: 1906 : if (fd == BADFD) { if (buf) buf[0] = 0; return 0; }
175 [ - + ]: 1869 : if (read_all_fd(fd, buf, maxlen, &gotall) == -1) {
176 : 0 : perror("Trouble reading.");
177 [ # # # # ]: 0 : D("read error lk(%s)", logkey);
178 : 0 : close_file(fd, logkey);
179 : 0 : buf[maxlen-1] = 0;
180 : 0 : return 0;
181 : : }
182 : 1869 : close_file(fd, logkey);
183 : 1869 : buf[MIN(gotall, maxlen-1)] = 0; /* nul terminate */
184 : 1869 : return gotall;
185 : : }
186 : :
187 : : /* Called by: read_all_alloc, zxid_get_ent_file */
188 : : int get_file_size(fdtype fd)
189 : 2267 : {
190 : : #ifdef MINGW
191 : : return GetFileSize(fd,0);
192 : : #else
193 : : struct stat st;
194 : 2267 : fstat(fd, &st);
195 : 2267 : return st.st_size;
196 : : #endif
197 : : }
198 : :
199 : : /*() Read all data from a file at formatted file name path, allocating
200 : : * the buffer as needed.
201 : : *
202 : : * c:: ZX allocation context
203 : : * logkey:: Logging key to help debugging
204 : : * lenp:: Optional result parameter returning the length of the data read. Null ok.
205 : : * name_fmt:: Format string for building file name
206 : : * return:: The data or null on fail. The buffer will always be nul terminated. */
207 : :
208 : : /* Called by: covimp_test, list_user x3, list_users, zxid_conf_to_cf_len, zxid_di_query, zxid_find_epr, zxid_gen_boots, zxid_get_ses_sso_a7n, zxid_map_val_ss x4, zxid_parse_conf_raw, zxid_print_session, zxid_read_ldif_attrs, zxid_read_map, zxid_ses_to_pool x3, zxid_sha1_file */
209 : : char* read_all_alloc(struct zx_ctx* c, const char* logkey, int reperr, int* lenp, const char* name_fmt, ...)
210 : 3982 : {
211 : : va_list ap;
212 : : char* buf;
213 : : int len, gotall;
214 : : fdtype fd;
215 : 3982 : va_start(ap, name_fmt);
216 : 3982 : fd = vopen_fd_from_path(O_RDONLY, 0, logkey, reperr, name_fmt, ap);
217 : 3982 : va_end(ap);
218 [ + + ]: 3982 : if (fd == BADFD) {
219 [ + + ]: 2332 : if (lenp)
220 : 1751 : *lenp = 0;
221 : 2332 : return 0;
222 : : }
223 : :
224 : 1650 : len = get_file_size(fd);
225 : 1650 : buf = ZX_ALLOC(c, len+1);
226 : :
227 [ - + ]: 1650 : if (read_all_fd(fd, buf, len, &gotall) == -1) {
228 : 0 : perror("Trouble reading.");
229 [ # # # # ]: 0 : D("read error lk(%s)", logkey);
230 : 0 : close_file(fd, logkey);
231 : 0 : buf[len] = 0;
232 [ # # ]: 0 : if (lenp)
233 : 0 : *lenp = 0;
234 : 0 : return 0;
235 : : }
236 : 1650 : close_file(fd, logkey);
237 : 1650 : buf[MIN(gotall, len)] = 0; /* nul terminate */
238 [ + + ]: 1650 : if (lenp)
239 : 1263 : *lenp = gotall;
240 : 1650 : return buf;
241 : : }
242 : :
243 : : /*() Low level function that keeps writing data to a file descriptor unil
244 : : * everything is written. It may block in the process. */
245 : :
246 : : /* Called by: main x4, write2_or_append_lock_c_path x4, write_all_path_fmt, zxid_addmd x2, zxid_cache_epr, zxid_curl_write_data, zxid_reg_svc x3, zxid_send_sp_meta x2, zxid_snarf_eprs_from_ses, zxid_write_ent_to_cache, zxidwspcgi_child */
247 : : int write_all_fd(fdtype fd, const char* p, int pending)
248 : 12439 : {
249 : : #ifdef MINGW
250 : : DWORD wrote;
251 : : if (fd == BADFD || !pending || !p) return 0;
252 : : if (!WriteFile(fd, p, pending, &wrote, 0))
253 : : return 0;
254 : : FlushFileBuffers(fd);
255 : : DD("write_all_fd(%x, `%.*s', %d) wrote=%d\n", fd, pending, p, pending, wrote);
256 : : #else
257 : : int wrote;
258 [ + - + + : 12439 : if ((fd == BADFD) || !pending || !p) return 0;
- + ]
259 [ + + ]: 37239 : while (pending) {
260 : 12413 : wrote = write(fd, (char*)p, pending);
261 [ - + ]: 12413 : if (wrote <= 0) return 0;
262 : 12413 : pending -= wrote;
263 : 12413 : p += wrote;
264 : : }
265 : : #endif
266 : 12413 : return 1;
267 : : }
268 : :
269 : : /*() Write all data to a file at the formatted path. The buf is used
270 : : * for formatting data. The path_fmt can have up to two %s specifiers,
271 : : * which will be satisfied by prepath and postpath.
272 : : *
273 : : * logkey:: Used for debug prints and error messages
274 : : * maxlen:: Size of the buffer
275 : : * buf:: Fied size buffer for dendering the formatted data
276 : : * path_fmt:: Format string for filesystem path to the file
277 : : * prepath:: Argument to satisfy first %s in path_fmt
278 : : * postpath:: Argument to satisfy second %s in path_fmt
279 : : * data_fmt:: Format string for the data to be written. Following arguments satisfy the format string. Overall the data length is constrained to maxlen. Caller needs to allocate/provide buffer sufficient to satisfy the data_fmt.
280 : : * Returns:: 1 on success, 0 on fail. */
281 : :
282 : : /* Called by: main x6, zx_get_symkey, zxid_check_fed x2, zxid_mk_at_cert, zxid_mk_self_sig_cert x2, zxid_mk_transient_nid, zxid_put_invite, zxid_put_psobj, zxid_put_ses, zxid_put_user, zxid_pw_authn */
283 : : int write_all_path_fmt(const char* logkey, int maxlen, char* buf, const char* path_fmt, const char* prepath, const char* postpath, const char* data_fmt, ...)
284 : 150 : {
285 : : int len;
286 : : va_list ap;
287 : : fdtype fd;
288 : 150 : fd = open_fd_from_path(O_CREAT | O_RDWR | O_TRUNC, 0666, logkey, 1, path_fmt, prepath, postpath);
289 : : DD("write_all_path_fmt(%s, %x)", logkey, fd);
290 [ - + ]: 150 : if (fd == BADFD) return 0;
291 : :
292 : 150 : va_start(ap, data_fmt);
293 : 150 : len = vsnprintf(buf, maxlen-1, data_fmt, ap); /* Format data into buf */
294 : 150 : buf[maxlen-1] = 0; /* must terminate manually as on win32 nul is not guaranteed */
295 : 150 : va_end(ap);
296 [ - + ]: 150 : if (len < 0) {
297 : 0 : perror("vsnprintf");
298 : 0 : ERR("%s, Broken snprintf? Impossible to compute length of string. Be sure to `export LANG=C' if you get errors about multibyte characters. Length returned: %d", logkey, len);
299 : 0 : len = 0;
300 : : }
301 [ - + ]: 150 : if (write_all_fd(fd, buf, len) == -1) {
302 : 0 : perror("Trouble writing");
303 : 0 : close_file(fd, logkey);
304 : 0 : return 0;
305 : : }
306 : 150 : close_file(fd, logkey);
307 : 150 : return 1;
308 : : }
309 : :
310 : : /*() Write or append all data to a file at the formatted path. The
311 : : * file is opened for appending, data written, and file closed
312 : : * (flushing the data). Will perform file locking to ensure
313 : : * consistent results. Will create the file if needed, but will not
314 : : * create parent directories. Up to two items of data can
315 : : * be written/appended. If you have only one item, supply null
316 : : * for the second. For overwrite behaviour supply seeky=SEEK_SET and
317 : : * flag=O_TRUNC (the seek offset is always 0). For append behaviour
318 : : * supply seeky=SEEK_END and flag=O_APPEND.
319 : : * Returns 1 on success, 0 on err */
320 : :
321 : : /* Called by: main, zxlog_blob, zxlog_write_line x2 */
322 : : int write2_or_append_lock_c_path(const char* c_path,
323 : : int len1, const char* data1,
324 : : int len2, const char* data2,
325 : : const char* which, /* log key */
326 : : int seeky, /* SEEK_END,0 O_APPEND == append */
327 : : int flag) /* SEEK_SET,0 O_TRUNC == overwr */
328 : 6618 : {
329 : : fdtype fd;
330 [ - + ]: 6618 : if (!c_path)
331 : 0 : return 0;
332 : : #ifdef MINGW
333 : : fd = CreateFile(c_path, MINGW_RW_PERM, /*0*/FILE_SHARE_READ | FILE_SHARE_WRITE /* 0 means no sharing allowed */, 0 /* security */,
334 : : (flag == O_APPEND) ? OPEN_ALWAYS : CREATE_ALWAYS /* truncates, too? */,
335 : : FILE_ATTRIBUTE_NORMAL, 0);
336 : : if (fd == BADFD) goto badopen;
337 : : if (flag == O_APPEND) {
338 : : MS_LONG zero = 0;
339 : : SetFilePointer(fd, 0, &zero, FILE_END); /* seek to end */
340 : : }
341 : :
342 : : if (len1 && data1) {
343 : : if (!write_all_fd(fd, data1, len1)) {
344 : : ERR("%s: Writing to file `%s' %d bytes failed: %d %s. Check permissions and disk space. euid=%d egid=%d", which, c_path, len1, errno, STRERROR(errno), geteuid(), getegid());
345 : : close_file(fd, which);
346 : : return 0;
347 : : }
348 : : }
349 : : if (len2 && data2) {
350 : : if (!write_all_fd(fd, data2, len2)) {
351 : : ERR("%s: Writing to file `%s' %d bytes failed: %d %s. Check permissions and disk space. euid=%d egid=%d", which, c_path, len2, errno, STRERROR(errno), geteuid(), getegid());
352 : : close_file(fd, which);
353 : : return 0;
354 : : }
355 : : }
356 : : #else
357 : 6618 : fd = open(c_path, O_RDWR | O_CREAT | flag, 0666);
358 [ + + ]: 6618 : if (fd == BADFD) goto badopen;
359 [ - + ]: 6616 : if (FLOCKEX(fd) == -1) {
360 [ # # ]: 0 : ERR("%s: Locking exclusively file `%s' failed: %d %s. Check permissions and that the file system supports locking. euid=%d egid=%d", which, c_path, errno, STRERROR(errno), geteuid(), getegid());
361 : 0 : close_file(fd, which);
362 : 0 : return 0;
363 : : }
364 : :
365 : 6616 : lseek(fd,0,seeky);
366 [ + + + - ]: 6616 : if (len1 && data1) {
367 [ - + ]: 6607 : if (!write_all_fd(fd, data1, len1)) {
368 [ # # ]: 0 : ERR("%s: Writing to file(%s) fd=%d %d bytes failed: %d %s. Check permissions and disk space. euid=%d egid=%d", which, c_path, fd, len1, errno, STRERROR(errno), geteuid(), getegid());
369 : 0 : FUNLOCK(fd);
370 : 0 : close_file(fd, which);
371 : 0 : return 0;
372 : : }
373 : : }
374 : :
375 [ + + + - ]: 6616 : if (len2 && data2) {
376 [ - + ]: 5573 : if (!write_all_fd(fd, data2, len2)) {
377 [ # # ]: 0 : ERR("%s: Writing to file(%s) %d bytes failed: %d %s. Check permissions and disk space. euid=%d egid=%d", which, c_path, len2, errno, STRERROR(errno), geteuid(), getegid());
378 : 0 : FUNLOCK(fd);
379 : 0 : close_file(fd, which);
380 : 0 : return 0;
381 : : }
382 : : }
383 : :
384 : 6616 : FUNLOCK(fd);
385 : : #endif
386 [ - + ]: 6616 : if (close_file(fd, which) < 0) {
387 [ # # ]: 0 : ERR("%s: closing file(%s) after write failed: %d %s. Check permissions and disk space. Could be NFS problem. euid=%d egid=%d", which, c_path, errno, STRERROR(errno), geteuid(), getegid());
388 : 0 : return 0;
389 : : }
390 : 6616 : return 1;
391 : 2 : badopen:
392 [ + - ]: 2 : ERR("%s: Opening file(%s) for writing failed: %d %s. Check permissions and that directories exist. euid=%d egid=%d", which, c_path, errno, STRERROR(errno), geteuid(), getegid());
393 : 2 : return 0;
394 : : }
395 : :
396 : : /*() Close a file rather than just any file descriptor and check error
397 : : * return. It is important that it is a file since on MS Windows closing
398 : : * files is different from closing descriptors. Checking error return
399 : : * from close is important because in NFS environments you may not know
400 : : * that your write has failed until you actually attempt to close the file. */
401 : :
402 : : /* Called by: copy_file, main x2, read_all x2, read_all_alloc x2, write2_or_append_lock_c_path x6, write_all_path_fmt x2, zxid_addmd, zxid_cache_epr, zxid_get_ent_file x2, zxid_reg_svc x2, zxid_write_ent_to_cache */
403 : : int close_file(fdtype fd, const char* logkey)
404 : 11010 : {
405 : 11010 : int res = closefile(fd);
406 [ - + ]: 11010 : if (res) {
407 : 0 : perror("close file");
408 : 0 : ERR("%s: Errors on closing file, after write, could indicate write back cache problems, especially under NFS. Ignoring the error. euid=%d egid=%d", logkey, geteuid(), getegid());
409 : : }
410 : 11010 : return res;
411 : : }
412 : :
413 : : /*() Copy contents of a file, i.e. first read a file, then write a file.
414 : : * Many places use copy_file() as opposed to hardlinking file because
415 : : * actually copying file is more portable. Even in Unix, hardlinking
416 : : * can be troublesome if the from and to are on different file systems. */
417 : :
418 : : /* Called by: covimp_test x5, zxid_cp_usr_eprs2ses */
419 : : int copy_file(const char* from, const char* to, const char* logkey, int may_link)
420 : 5 : {
421 : : fdtype fd_from;
422 : : fdtype fd_to;
423 : : int ret, pending, wrote;
424 : : char buf[4096];
425 : : char* p;
426 : :
427 : : #ifndef MINGW
428 [ + + + ]: 5 : switch (may_link) {
429 : : case 2:
430 : 1 : ret = symlink(from, to);
431 : 1 : goto linkrest;
432 : : case 1:
433 : 1 : ret = link(from, to);
434 : 2 : linkrest:
435 [ + - ]: 2 : if (ret) {
436 : 2 : perror("{hard|sym}link");
437 : 2 : ERR("%s: Error linking(%d) from(%s) to(%s) euid=%d egid=%d", logkey, may_link, from, to, geteuid(), getegid());
438 : 2 : return -1;
439 : : }
440 : 0 : return 0;
441 : : }
442 : : #endif
443 : 3 : fd_from = openfile_ro(from);
444 [ + + ]: 3 : if (fd_from == BADFD) {
445 : 1 : perror("openfile_ro");
446 : 1 : ERR("%s: Error opening from(%s) euid=%d egid=%d", logkey, from, geteuid(), getegid());
447 : 1 : return BADFD;
448 : :
449 : : }
450 : : #ifdef MINGW
451 : : fd_to = CreateFile(to, MINGW_RW_PERM, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
452 : : #else
453 : 2 : fd_to = open(to, O_RDWR | O_CREAT, 0666);
454 : : #endif
455 [ - + ]: 2 : if (fd_to == BADFD) {
456 : 0 : perror("openfile_ro");
457 : 0 : ERR("%s: Error opening to(%s) euid=%d egid=%d", logkey, to, geteuid(), getegid());
458 : 0 : return BADFD;
459 : : }
460 : :
461 : : #ifdef USE_STDIO
462 : : while (1) {
463 : : pending = fread(buf, 1, sizeof(buf), (FILE*)fd_from);
464 : : if (pending <= 0) break; /* EOF */
465 : : p = buf;
466 : : while (pending) {
467 : : wrote = fwrite(p, 1, pending, fd_to);
468 : : if (wrote <= 0) return 0;
469 : : pending -= wrote;
470 : : p += wrote;
471 : : }
472 : : }
473 : : #elif defined(MINGW)
474 : : while (1) {
475 : : DWORD wrot;
476 : : DWORD pend;
477 : : if (!ReadFile(fd_from, buf, sizeof(buf), &pend, 0))
478 : : return -1;
479 : : if (!pend)
480 : : break;
481 : : p = buf;
482 : : while (pend) {
483 : : if (!WriteFile(fd_to, p, pend, &wrot, 0))
484 : : return BADFD;
485 : : pend -= wrot;
486 : : p += wrot;
487 : : }
488 : : }
489 : : FlushFileBuffers(fd_to);
490 : : #else
491 : : while (1) {
492 : 4 : pending = read(fd_from, buf, sizeof(buf));
493 [ + + ]: 4 : if (!pending) break; /* EOF */
494 : 2 : p = buf;
495 [ + + ]: 6 : while (pending) {
496 : 2 : wrote = write(fd_to, p, pending);
497 [ - + ]: 2 : if (wrote <= 0) return 0;
498 : 2 : pending -= wrote;
499 : 2 : p += wrote;
500 : : }
501 : 2 : }
502 : : #endif
503 : :
504 : 2 : close_file(fd_to, logkey);
505 : 2 : closefile(fd_from);
506 : 2 : return 0;
507 : : }
508 : :
509 : : /*() Output a hexdump to stderr. Used for debugging purposes. */
510 : :
511 : : /* Called by: hexdmp, zxsig_data x2, zxsig_verify_data x5 */
512 : : int hexdump(char* msg, char* p, char* lim, int max)
513 : 2 : {
514 : : int i;
515 : : char* lim16;
516 : : char buf[3*16+1+1+16+1];
517 [ - + ]: 2 : if (!msg)
518 : 0 : msg = "";
519 [ - + ]: 2 : if (lim-p > max)
520 : 0 : lim = p + max;
521 : :
522 : 2 : buf[sizeof(buf)-1] = '\0';
523 : :
524 [ + + ]: 6 : while (p<lim) {
525 : 2 : memset(buf, ' ', sizeof(buf)-1);
526 : 2 : lim16 = MIN(p+16, lim);
527 [ + + ]: 18 : for (i = 0; p<lim16; ++p, ++i) {
528 [ + - ]: 16 : buf[3*i+(i>7?1:0)] = HEX_DIGIT((*p >> 4) & 0x0f);
529 [ + + ]: 16 : buf[3*i+1+(i>7?1:0)] = HEX_DIGIT(*p & 0x0f);
530 [ + + + + : 16 : switch (*p) {
+ + + ]
531 : 2 : case '\0': buf[3*16+1+1+i] = '~'; break;
532 : 1 : case '\r': buf[3*16+1+1+i] = '['; break;
533 : 1 : case '\n': buf[3*16+1+1+i] = ']'; break;
534 : 1 : case '~': buf[3*16+1+1+i] = '^'; break;
535 : 1 : case '[': buf[3*16+1+1+i] = '^'; break;
536 : 1 : case ']': buf[3*16+1+1+i] = '^'; break;
537 : : default:
538 [ + - ]: 9 : buf[3*16+1+1+i] = *p < ' ' ? '^' : *p;
539 : : }
540 : : }
541 : 2 : fprintf(stderr, "%s%s\n", msg, buf);
542 : : }
543 : 2 : return 0;
544 : : }
545 : :
546 : : /* Called by: covimp_test x2, zxsig_validate x6 */
547 : 2 : int hexdmp(char* msg, char* p, int len, int max) {
548 : 2 : return hexdump(msg, p, p+len, max);
549 : : }
550 : :
551 : : /*
552 : : * Base 64 encoding and decoding in its canonical and URL safe forms.
553 : : */
554 : :
555 : : /* Base64 std, RFC3548, defines also safe base64, the form that does not need URL encoding
556 : : * and is also otherwise more filesystem safe (i.e. / is not used). The pw_basis
557 : : * is used by md5_crypt() and other password hashing schemes. */
558 : :
559 : : #define MAX_LINE 76 /* size of encoded lines */
560 : : const char std_basis_64[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /*=*/
561 : : const char safe_basis_64[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; /*=*/
562 : : const char pw_basis_64[64] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
563 : :
564 : : /*() Raw version. Can use any encoding table and arbitrary line length.
565 : : * Known bug: line_len is not fully respected on last line - it can
566 : : * be up to 3 characters longer than specified due to padding.
567 : : * Every three chars (from alphabet of 256) of input map to
568 : : * four chars (from alphabet of 64) of output. See also SIMPLE_BASE64_LEN().
569 : : *
570 : : * p:: input
571 : : * len:: length of input
572 : : * r:: Output buffer. Will not be NUL terminated.
573 : : * basis_64:: The 64 character alphabet to be used, such as ~std_basis_64~ or ~safe_basis_64~
574 : : * line_len:: Length of each line. 76 is customary. Or use very large value to
575 : : * avoid any line breaks
576 : : * eol_len:: Length of End-of-Line string.
577 : : * eol:: End-of-Line string, inserted every line_len.
578 : : * eq_pad:: Padding character, usually equals (=)
579 : : * return:: Pointer one past last byte written in r. This function never fails. */
580 : :
581 : : /* Called by: base64_fancy, safe_base64 */
582 : : char* base64_fancy_raw(const char* p, int len, /* input and its length */
583 : : char* r, /* Output buffer. Will not be NUL terminated. */
584 : : const char* basis_64, /* 64 character alphabet to be used, see above */
585 : : int line_len, /* Length of each line. 76 is customary. */
586 : : int eol_len, /* Length of End-of-Line string. */
587 : : const char* eol, /* End-of-Line string, inserted every line_len. */
588 : : char eq_pad) /* Padding character, usually equals (=) */
589 : 16789 : {
590 : : unsigned char c1,c2,c3;
591 : : int chunk;
592 : 16789 : line_len /= 4;
593 : :
594 [ + + ]: 4648043 : for (chunk=0; len > 2; len -= 3, ++chunk) {
595 [ + + + + ]: 4631254 : if ((chunk == line_len) && eol_len) { /* 19 chunks (3x19=57chars) per line */
596 : 20881 : memcpy(r, eol, eol_len);
597 : 20881 : r += eol_len;
598 : 20881 : chunk = 0;
599 : : }
600 : 4631254 : c1 = *p++;
601 : 4631254 : c2 = *p++; // *** len==1 causes bug if no null term
602 : 4631254 : *r++ = basis_64[c1>>2];
603 : 4631254 : *r++ = basis_64[((c1 & 0x0003)<< 4) | ((c2 & 0x00f0) >> 4)];
604 : :
605 : 4631254 : c3 = *p++;
606 : 4631254 : *r++ = basis_64[((c2 & 0x000f) << 2) | ((c3 & 0x00c0) >> 6)];
607 : 4631254 : *r++ = basis_64[c3 & 0x003f];
608 : : }
609 : :
610 : : /* Post processing to handle the last line, which is often incomplete. */
611 : :
612 : 16789 : c1 = *p++;
613 [ + + + - ]: 16789 : switch (len) {
614 : : case 2:
615 : 12730 : c2 = *p++; // *** len==1 causes bug if no null term
616 : 12730 : *r++ = basis_64[c1>>2];
617 : 12730 : *r++ = basis_64[((c1 & 0x0003)<< 4) | ((c2 & 0x00f0) >> 4)];
618 : 12730 : *r++ = basis_64[(c2 & 0x000f) << 2];
619 : 12730 : *r++ = eq_pad;
620 : 12730 : break;
621 : : case 1:
622 : 970 : *r++ = basis_64[c1>>2];
623 : 970 : *r++ = basis_64[(c1 & 0x0003)<< 4];
624 : 970 : *r++ = eq_pad;
625 : 970 : *r++ = eq_pad;
626 : 970 : break;
627 : : case 0:
628 : 3089 : break; /* no padding needed */
629 : : default:
630 [ # # ]: 0 : NEVERNEVER("Corrupt len=%d", len);
631 : : }
632 [ + + ]: 16789 : if (eol_len) {
633 : 975 : memcpy(r, eol, eol_len);
634 : 975 : r += eol_len;
635 : : }
636 : 16789 : return r;
637 : : }
638 : :
639 : : #define XX 255 /* illegal base64 char */
640 : : #define Eq 254 /* padding */
641 : : #define INVALID XX
642 : :
643 : : unsigned char zx_std_index_64[256] = {
644 : : XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
645 : : XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
646 : : XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, Eq,62,XX,63, /* `+' ',' '-' `/' */
647 : : 52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,Eq,XX,XX, /* `=' */
648 : : XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
649 : : 15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,63, /* `_' */
650 : : XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
651 : : 41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX,
652 : :
653 : : XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
654 : : XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
655 : : XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
656 : : XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
657 : : XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
658 : : XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
659 : : XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
660 : : XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX
661 : : };
662 : :
663 : : /*() Raw version. Can use any decoding table. Assumes receiving buffer r has been allocated
664 : : * to correct length. Is able to perform the operation in place, i.e. p and r
665 : : * can point to the same buffer. Both canonical and safe base64 are handled.
666 : : * If string contains URL encoding (as it might for + or =) it is automatically
667 : : * unraveled as well. This is useful for SAMLRequest field in Redirect signing.
668 : : * Returns pointer one past last output char written. Does not nul terminate.
669 : : * Never fails. See also SIMPLE_BASE64_PESSIMISTIC_DECODE_LEN(). */
670 : :
671 : : /* Called by: decode, main x5, mk_test_cert x5, zxenc_privkey_dec, zxenc_symkey_dec, zxid_cdc_check, zxid_decode_redir_or_post x2, zxid_decode_ssoreq, zxid_extract_cert, zxid_extract_private_key, zxid_idp_as_do, zxid_map_val_ss x3, zxid_process_keys, zxid_psobj_dec, zxid_sp_deref_art, zxsig_validate x2 */
672 : : char* unbase64_raw(const char* p, const char* lim, char* r, const unsigned char* index_64)
673 : 3400 : {
674 : : int i;
675 : : unsigned char c[4];
676 : : unsigned char uc;
677 : :
678 [ + + ]: 2486261 : while (p < lim) {
679 : 2482572 : i = 0;
680 : : do {
681 [ + + + - : 9986425 : if (*p == '%' && p+2 < lim && IS_HEX(p[1]) && IS_HEX(p[2])) {
+ - - + #
# # # # #
# # + - +
- + - - +
# # # # ]
682 : : /* Percent sign from URL encoding: decode */
683 [ + - + - : 363 : uc = index_64[(HEX(p[1]) << 4) | HEX(p[2])];
+ - - + ]
684 : 363 : p += 3;
685 : : } else
686 : 9985699 : uc = index_64[(int)*p++];
687 [ + + ]: 9986062 : if (uc != INVALID)
688 : 9926855 : c[i++] = uc;
689 : :
690 [ + + ]: 9986062 : if (p == lim) {
691 [ + + ]: 2194 : if (i < 4) {
692 : : /*if (i) ERR("Premature end of base64 data. (incomplete base64 input) i=%d", i);*/
693 [ + + ]: 859 : if (i < 2) return r;
694 [ + - ]: 1 : if (i == 2) c[2] = Eq;
695 : 1 : c[3] = Eq;
696 : : }
697 : 1336 : break;
698 : : }
699 [ + + ]: 9983868 : } while (i < 4);
700 : :
701 [ + - - + ]: 2481714 : if (c[0] == Eq || c[1] == Eq) {
702 : 0 : ERR("Premature end of base64 data. (incomplete base64 input) c0(%x)", c[0]);
703 : 0 : break;
704 : : }
705 : : /* D("c0=%d,c1=%d,c2=%d,c3=%d\n", c[0],c[1],c[2],c[3]); */
706 : :
707 : 2481714 : *r++ = (c[0] << 2) | ((c[1] & 0x30) >> 4);
708 [ + + ]: 2481714 : if (c[2] == Eq) break;
709 : 2481526 : *r++ = ((c[1] & 0x0f) << 4) | ((c[2] & 0x3c) >> 2);
710 [ + + ]: 2481526 : if (c[3] == Eq) break;
711 : 2479461 : *r++ = ((c[2] & 0x03) << 6) | c[3];
712 : : }
713 : 2542 : return r;
714 : : }
715 : :
716 : : /*() The out_buf should be 28 chars in length. The buffer is not automatically nul termianated.
717 : : * There will be 27 characters of payload, plus one termination character "." (which
718 : : * caller can overwrite with nul, if you like).
719 : : *
720 : : * out_buf:: Buffer where result will be written. It must be 28 characters long and already allocated. The buffer will not be null terminated.
721 : : * len:: Length of data. -2=use strlen(data)
722 : : * data:: Data to be digested
723 : : * return:: Pointer one past last character written (not nul terminated) */
724 : :
725 : : /* Called by: zxcot_main, zxdecode_main, zxid_decode_redir_or_post x2, zxid_get_ent_cache, zxid_mk_ent, zxid_nice_sha1, zxid_reg_svc, zxid_user_sha1_name x2, zxlog_path x2, zxlog_write_line */
726 : : char* sha1_safe_base64(char* out_buf, int len, const char* data)
727 : 7384 : {
728 : : char sha1[20];
729 [ - + ]: 7384 : if (len == -2)
730 : 0 : len = strlen(data);
731 : 7384 : SHA1((unsigned char*)data, len, (unsigned char*)sha1);
732 : 7384 : return base64_fancy_raw(sha1, 20, out_buf, safe_basis_64, 1<<31, 0, 0, '.');
733 : : }
734 : :
735 : : /*(-) zlib integration internal */
736 : : /* Called by: */
737 : : voidpf zx_zlib_zalloc(void* opaque, uInt items, uInt size)
738 : 2829 : {
739 : 2829 : return ZX_ALLOC(opaque, items*size);
740 : : }
741 : :
742 : : /*(-) zlib integration internal */
743 : : /* Called by: */
744 : : void zx_zlib_zfree(void* opaque, voidpf addr)
745 : 2829 : {
746 : 2829 : ZX_FREE(opaque, addr);
747 : 2829 : }
748 : :
749 : : /*() Compress data using zlib-deflate (RFC1951). The deflated data will be in new
750 : : * buffer, which is returned. out_len will indicate the length
751 : : * of the comressed data. Since the compressed data will be
752 : : * binary, there is no provision for nul termination. Caveat: RFC1951 is not same a gzip. */
753 : :
754 : : /* Called by: zxid_map_val_ss, zxid_saml2_redir_enc, zxid_simple_idp_show_an, zxlog_write_line */
755 : : char* zx_zlib_raw_deflate(struct zx_ctx* c, int in_len, const char* in, int* out_len)
756 : 563 : {
757 : : int ret, dlen;
758 : : char* out;
759 : : z_stream z;
760 : 563 : *out_len = 0;
761 : 563 : ZERO(&z, sizeof(z_stream));
762 : 563 : z.zalloc = zx_zlib_zalloc;
763 : 563 : z.zfree = zx_zlib_zfree;
764 : 563 : z.opaque = c;
765 : 563 : z.next_in = (unsigned char*)in;
766 : 563 : z.avail_in = in_len;
767 : 563 : ret = deflateInit2(&z, 9, Z_DEFLATED, -15, 9, Z_DEFAULT_STRATEGY);
768 [ - + ]: 563 : if (ret != Z_OK) {
769 : 0 : ERR("zlib deflateInit2 error: %d", ret);
770 : 0 : return 0;
771 : : }
772 : :
773 : 563 : dlen = in_len + (in_len >> 8) + 12; /* worst case: orig_size * 1.001 + 12 */
774 : 563 : out = ZX_ALLOC(c, dlen);
775 : 563 : z.next_out = (unsigned char*)out;
776 : 563 : z.avail_out = dlen;
777 : :
778 : 563 : ret = deflate(&z, Z_FINISH);
779 [ - + ]: 563 : if (ret != Z_STREAM_END) {
780 : 0 : deflateEnd(&z);
781 : 0 : ERR("zlib deflate error: %d", ret);
782 : 0 : return 0;
783 : : }
784 : 563 : *out_len = z.total_out;
785 : 563 : deflateEnd(&z);
786 : 563 : return out;
787 : : }
788 : :
789 : : /*() Decompress zlib-deflate (RFC1951) compressed data. The decompressed data will
790 : : * be in a newly allocated buffer which is returned. The length
791 : : * of the decompressed data is returned via out_len. The buffer
792 : : * will always be at least byte one longer than indicated by out_len - this
793 : : * should allow safe nul termination (but the decompressed data itself
794 : : * may contain any number of nuls). Caveat: RFC1951 is not same a gzip. */
795 : :
796 : : /* Called by: decode, zxid_decode_redir_or_post, zxid_decode_ssoreq, zxid_map_val_ss, zxlog_zsig_verify_print */
797 : : char* zx_zlib_raw_inflate(struct zx_ctx* c, int in_len, const char* in, int* out_len)
798 : 14 : {
799 : 14 : int ret, dlen, iter = 30;
800 : : char* out;
801 : : char* old_out;
802 : : z_stream z;
803 : 14 : *out_len = 0;
804 : 14 : ZERO(&z, sizeof(z_stream));
805 : 14 : z.zalloc = zx_zlib_zalloc;
806 : 14 : z.zfree = zx_zlib_zfree;
807 : 14 : z.opaque = c;
808 : 14 : z.next_in = (unsigned char*)in;
809 : 14 : z.avail_in = in_len;
810 : :
811 : 14 : dlen = in_len << 3; /* guess inflated size: orig_size * 8 */
812 : 14 : out = ZX_ALLOC(c, dlen+1);
813 : 14 : z.next_out = (unsigned char*)out;
814 : 14 : z.avail_out = dlen;
815 : :
816 : 14 : ret = inflateInit2(&z, -15);
817 [ - + ]: 14 : if (ret != Z_OK) {
818 : 0 : ERR("zlib inflateInit failed with error code %d", ret);
819 : 0 : return 0;
820 : : }
821 : :
822 : : #if 0
823 : : ret = inflate(&z, Z_FINISH);
824 : : if (ret != Z_STREAM_END) {
825 : : inflateEnd(&z);
826 : : ERR("zlib inflate failed with error code %d. Most probably the input data is empty, corrupt, or not in zlib format.", ret);
827 : : return 0;
828 : : }
829 : : #else
830 [ + - ]: 29 : while (--iter) { /* Make sure we can never be caught in infinite loop */
831 : 15 : ret = inflate(&z, Z_SYNC_FLUSH);
832 [ + + + ]: 15 : switch (ret) {
833 : 13 : case Z_STREAM_END: goto done;
834 : : case Z_OK: /* avail_out should be 0 now. Time to grow the buffer. */
835 : 1 : ret = z.next_out - (Bytef*)out;
836 : 1 : dlen += dlen;
837 : 1 : old_out = out;
838 : 1 : out = ZX_ALLOC(c, dlen+1);
839 : 1 : memcpy(out, old_out, ret);
840 : 1 : z.next_out = (unsigned char*)out + ret;
841 : 1 : z.avail_out = dlen - ret;
842 : 1 : break;
843 : : default:
844 : 1 : inflateEnd(&z);
845 : 1 : ERR("zlib inflate failed with error code %d. Most probably the input data is empty, corrupt, or not in RFC1951 (zlib deflate) format. A common error is incomplete data (due to read(2) not returing all data on first iteration) resulting a failed detection of uncompressed data (the detection looks for '<' in beginning and '>' in end of base64 decoded data - often the latter is missing in incomplete data). iter=%d in_len=%d dlen=%d", ret, iter, in_len, dlen);
846 : 1 : return 0;
847 : : }
848 : : }
849 : : #endif
850 : 13 : done:
851 : 13 : *out_len = z.total_out;
852 : 13 : inflateEnd(&z);
853 : 13 : return out;
854 : : }
855 : :
856 : : #if 1
857 : : /* N.B. Many other Liberty implementations expect nearly everything to be URL encoded. */
858 : : #define URL_BAD(x) (!AZaz_09_(x))
859 : : #else
860 : : #define URL_BAD(x) (((x)<=' ')||((x)>=0x7f)||ONE_OF_4((x),'+','%','=','&')||ONE_OF_2((x),'#','?'))
861 : : #endif
862 : :
863 : : /*() Compute length of the URL encoded string. The encoding is done
864 : : * to characters listed in URL_BAD() macro in zxutil.c.
865 : : * return: Required buffer size, including nul term. Subtract 1 for string length. */
866 : :
867 : : /* Called by: zx_url_encode, zxid_pool_to_qs x5, zxid_saml2_redir_enc x2 */
868 : : int zx_url_encode_len(int in_len, const char* in)
869 : 289 : {
870 : : int n;
871 : : const char* lim;
872 : : /* scan through to see how many escape expansions are needed */
873 : 289 : lim = in + in_len;
874 [ + + ]: 2308386 : for (n = 0; in < lim; ++in)
875 [ + + + + : 2308097 : if (URL_BAD(*in)) ++n;
+ + + + +
+ + + -
+ ]
876 : 289 : return n+n+in_len+1; /* nul-terminated length */
877 : : }
878 : :
879 : : /*() URL encode input into output. The encoding is done to
880 : : * characters listed in URL_BAD() macro in zxutil.c. The output must already
881 : : * have been allocated to correct length, which can be obtained from
882 : : * zx_url_encode_len() function. zx_url_encode() is higher
883 : : * level function that does just that. Raw version does not nul terminate.
884 : : * Returns pointer one past last byte written. */
885 : :
886 : : /* Called by: zx_url_encode, zxid_pool_to_qs x4, zxid_saml2_redir_enc x2 */
887 : : char* zx_url_encode_raw(int in_len, const char* in, char* out)
888 : 154 : {
889 : : const char* lim;
890 [ + + ]: 1158383 : for (lim = in+in_len; in < lim; ++in)
891 [ + + + + : 1200689 : if (URL_BAD(*in)) {
+ + + + +
+ + + -
+ ]
892 : 42460 : *out++ = '%';
893 [ + - ]: 42460 : *out++ = HEX_DIGIT((*in >> 4) & 0x0f);
894 [ + + ]: 42460 : *out++ = HEX_DIGIT(*in & 0x0f);
895 : : } else
896 : 1115769 : *out++ = *in;
897 : 154 : return out;
898 : : }
899 : :
900 : : /*() Perform URL encoding on buffer. New output buffer is allocated.
901 : : * The low level work is performed by zx_url_encode_raw().
902 : : * Returns the length of the output string (not including nul termination,
903 : : * but nul termination is actually allocated and made).
904 : : *
905 : : * N.B. For zx_url_decode() operation see URL_DECODE() macro in errmac.h */
906 : :
907 : : /* Called by: covimp_test */
908 : : char* zx_url_encode(struct zx_ctx* c, int in_len, const char* in, int* out_len)
909 : 1 : {
910 : : int olen;
911 : : char* out;
912 : : char* p;
913 [ - + ]: 1 : if (in_len == -2)
914 : 0 : in_len = strlen(in);
915 : 1 : olen = zx_url_encode_len(in_len, in) + 1;
916 : 1 : out = ZX_ALLOC(c, olen);
917 : 1 : p = zx_url_encode_raw(in_len, in, out);
918 : 1 : *p = '\0';
919 [ + - ]: 1 : if (out_len)
920 : 1 : *out_len = p - out;
921 : 1 : return out;
922 : : }
923 : :
924 : : const unsigned char const * hex_trans = (unsigned char*)"0123456789abcdef";
925 : : const unsigned char const * ykmodhex_trans = (unsigned char*)"cbdefghijklnrtuv"; /* as of libyubikey-1.5 */
926 : :
927 : : /*() Especially useful as yubikey_modhex_decode() replacement.
928 : : * Supports inplace conversion. Does not nul terminate. */
929 : :
930 : : /* Called by: covimp_test, main x2, zxid_pw_authn x2 */
931 : : char* zx_hexdec(char* dst, char* src, int src_len, const unsigned char* trans)
932 : 1 : {
933 : : const unsigned char* hi;
934 : : const unsigned char* lo;
935 [ + + ]: 4 : for (; src_len>1; src_len-=2, ++dst, src+=2) {
936 : 3 : hi = (const unsigned char*)strchr((char*)trans, src[0]);
937 [ - + ]: 3 : if (!hi) {
938 : 0 : ERR("Bad hi character(%x) in hex string using trans(%s) len left=%d src(%.*s)", src[0], trans, src_len, src_len, src);
939 : 0 : hi = trans;
940 : : }
941 : 3 : lo = (const unsigned char*)strchr((char*)trans, src[1]);
942 [ - + ]: 3 : if (!lo) {
943 : 0 : ERR("Bad lo character(%x) in hex string using trans(%s) len left=%d src(%.*s)", src[1], trans, src_len, src_len, src);
944 : 0 : lo = trans;
945 : : }
946 : 3 : *dst = ((hi-trans) << 4) | (lo-trans);
947 : : }
948 : 1 : return dst;
949 : : }
950 : :
951 : : static short zx_mmdd[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
952 : :
953 : : /*() Map from tm struct back to seconds since Unix epoch. The tm struct
954 : : * is assumed to be on GMT. This function is needed because mktime(3) is
955 : : * tainted by local time zone brain damage. This function aims to be
956 : : * equivalent to GNU extension timegm(3) (see Linux man pages). */
957 : :
958 : : /* Called by: zx_date_time_to_secs */
959 : : static int zx_timegm(const struct tm* t)
960 : 161 : {
961 : : int x;
962 : 161 : int aa = t->tm_year - 70, mon = t->tm_mon, dd = t->tm_mday;
963 : 161 : int hh = t->tm_hour, mm = t->tm_min, ss = t->tm_sec;
964 : :
965 [ - + ]: 161 : if (ss > 60) {
966 : 0 : mm += ss/60;
967 : 0 : ss %= 60;
968 : : }
969 [ - + ]: 161 : if (mm > 60) {
970 : 0 : hh += mm/60;
971 : 0 : mm %= 60;
972 : : }
973 [ - + ]: 161 : if (hh > 60) {
974 : 0 : dd += hh/60;
975 : 0 : hh %= 60;
976 : : }
977 [ - + ]: 161 : if (mon > 12) {
978 : 0 : aa += mon/12;
979 : 0 : mon %= 12;
980 : : }
981 [ - + ]: 322 : while (dd > zx_mmdd[mon+1]) {
982 [ # # # # : 0 : if (mon == 1 && LEAP(aa+1970))
# # # # ]
983 : 0 : --dd;
984 : 0 : dd -= zx_mmdd[mon];
985 : 0 : ++mon;
986 [ # # ]: 0 : if (mon > 11) {
987 : 0 : mon = 0;
988 : 0 : ++aa;
989 : : }
990 : : }
991 [ - + ]: 161 : if (aa < 0)
992 : 0 : return -1;
993 : :
994 : 161 : x = aa * 365 + (aa + 1) / 4; /* Account for leap year every 4 years */
995 : :
996 [ - + ]: 161 : if ((aa -= 131) >= 0) {
997 : 0 : aa /= 100;
998 : 0 : x -= (aa >> 2) * 3 + 1;
999 [ # # ]: 0 : if ((aa &= 3) == 3)
1000 : 0 : --aa;
1001 : 0 : x -= aa;
1002 : : }
1003 : :
1004 [ - + # # : 161 : x += zx_mmdd[mon] + dd-1 + (LEAP(aa+1970) && mon>1?1:0);
# # # # ]
1005 : 161 : x *= 24; /* Days to hours */
1006 : 161 : return ((x + hh) * 60 + mm) * 60 + ss;
1007 : : }
1008 : :
1009 : : /*() Convert a date-time format timestamp into seconds since Unix epoch.
1010 : : * Format is as follows
1011 : : * 01234567890123456789
1012 : : * yyyy-MM-ddThh:mm:ssZ */
1013 : :
1014 : : /* Called by: zxid_parse_invite x2, zxid_sp_sso_finalize, zxid_sso_issue_a7n, zxid_timestamp_chk, zxid_validate_cond x2 */
1015 : : int zx_date_time_to_secs(const char* dt)
1016 : 161 : {
1017 : : struct tm t;
1018 : 161 : ZERO(&t, sizeof(t));
1019 : 161 : sscanf(dt, "%d-%d-%dT%d:%d:%dZ",
1020 : : &t.tm_year, &t.tm_mon, &t.tm_mday,
1021 : : &t.tm_hour, &t.tm_min, &t.tm_sec);
1022 : 161 : t.tm_year -= 1900;
1023 : 161 : --t.tm_mon;
1024 : 161 : return zx_timegm(&t);
1025 : : }
1026 : :
1027 : : /* EOF -- zxutil.c */
|