1189251Ssam/*
2214734Srpaulo * OS specific functions for UNIX/POSIX systems
3214734Srpaulo * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
4189251Ssam *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7189251Ssam */
8189251Ssam
9189251Ssam#include "includes.h"
10189251Ssam
11252726Srpaulo#include <time.h>
12252726Srpaulo
13252726Srpaulo#ifdef ANDROID
14252726Srpaulo#include <linux/capability.h>
15252726Srpaulo#include <linux/prctl.h>
16252726Srpaulo#include <private/android_filesystem_config.h>
17252726Srpaulo#endif /* ANDROID */
18252726Srpaulo
19189251Ssam#include "os.h"
20189251Ssam
21214734Srpaulo#ifdef WPA_TRACE
22214734Srpaulo
23214734Srpaulo#include "common.h"
24214734Srpaulo#include "wpa_debug.h"
25214734Srpaulo#include "trace.h"
26252726Srpaulo#include "list.h"
27214734Srpaulo
28214734Srpaulostatic struct dl_list alloc_list;
29214734Srpaulo
30214734Srpaulo#define ALLOC_MAGIC 0xa84ef1b2
31214734Srpaulo#define FREED_MAGIC 0x67fd487a
32214734Srpaulo
33214734Srpaulostruct os_alloc_trace {
34214734Srpaulo	unsigned int magic;
35214734Srpaulo	struct dl_list list;
36214734Srpaulo	size_t len;
37214734Srpaulo	WPA_TRACE_INFO
38214734Srpaulo};
39214734Srpaulo
40214734Srpaulo#endif /* WPA_TRACE */
41214734Srpaulo
42214734Srpaulo
43189251Ssamvoid os_sleep(os_time_t sec, os_time_t usec)
44189251Ssam{
45189251Ssam	if (sec)
46189251Ssam		sleep(sec);
47189251Ssam	if (usec)
48189251Ssam		usleep(usec);
49189251Ssam}
50189251Ssam
51189251Ssam
52189251Ssamint os_get_time(struct os_time *t)
53189251Ssam{
54189251Ssam	int res;
55189251Ssam	struct timeval tv;
56189251Ssam	res = gettimeofday(&tv, NULL);
57189251Ssam	t->sec = tv.tv_sec;
58189251Ssam	t->usec = tv.tv_usec;
59189251Ssam	return res;
60189251Ssam}
61189251Ssam
62189251Ssam
63189251Ssamint os_mktime(int year, int month, int day, int hour, int min, int sec,
64189251Ssam	      os_time_t *t)
65189251Ssam{
66189251Ssam	struct tm tm, *tm1;
67189251Ssam	time_t t_local, t1, t2;
68189251Ssam	os_time_t tz_offset;
69189251Ssam
70189251Ssam	if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 ||
71189251Ssam	    hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 ||
72189251Ssam	    sec > 60)
73189251Ssam		return -1;
74189251Ssam
75189251Ssam	memset(&tm, 0, sizeof(tm));
76189251Ssam	tm.tm_year = year - 1900;
77189251Ssam	tm.tm_mon = month - 1;
78189251Ssam	tm.tm_mday = day;
79189251Ssam	tm.tm_hour = hour;
80189251Ssam	tm.tm_min = min;
81189251Ssam	tm.tm_sec = sec;
82189251Ssam
83189251Ssam	t_local = mktime(&tm);
84189251Ssam
85189251Ssam	/* figure out offset to UTC */
86189251Ssam	tm1 = localtime(&t_local);
87189251Ssam	if (tm1) {
88189251Ssam		t1 = mktime(tm1);
89189251Ssam		tm1 = gmtime(&t_local);
90189251Ssam		if (tm1) {
91189251Ssam			t2 = mktime(tm1);
92189251Ssam			tz_offset = t2 - t1;
93189251Ssam		} else
94189251Ssam			tz_offset = 0;
95189251Ssam	} else
96189251Ssam		tz_offset = 0;
97189251Ssam
98189251Ssam	*t = (os_time_t) t_local - tz_offset;
99189251Ssam	return 0;
100189251Ssam}
101189251Ssam
102189251Ssam
103252726Srpauloint os_gmtime(os_time_t t, struct os_tm *tm)
104252726Srpaulo{
105252726Srpaulo	struct tm *tm2;
106252726Srpaulo	time_t t2 = t;
107252726Srpaulo
108252726Srpaulo	tm2 = gmtime(&t2);
109252726Srpaulo	if (tm2 == NULL)
110252726Srpaulo		return -1;
111252726Srpaulo	tm->sec = tm2->tm_sec;
112252726Srpaulo	tm->min = tm2->tm_min;
113252726Srpaulo	tm->hour = tm2->tm_hour;
114252726Srpaulo	tm->day = tm2->tm_mday;
115252726Srpaulo	tm->month = tm2->tm_mon + 1;
116252726Srpaulo	tm->year = tm2->tm_year + 1900;
117252726Srpaulo	return 0;
118252726Srpaulo}
119252726Srpaulo
120252726Srpaulo
121189251Ssam#ifdef __APPLE__
122189251Ssam#include <fcntl.h>
123189251Ssamstatic int os_daemon(int nochdir, int noclose)
124189251Ssam{
125189251Ssam	int devnull;
126189251Ssam
127189251Ssam	if (chdir("/") < 0)
128189251Ssam		return -1;
129189251Ssam
130189251Ssam	devnull = open("/dev/null", O_RDWR);
131189251Ssam	if (devnull < 0)
132189251Ssam		return -1;
133189251Ssam
134189251Ssam	if (dup2(devnull, STDIN_FILENO) < 0) {
135189251Ssam		close(devnull);
136189251Ssam		return -1;
137189251Ssam	}
138189251Ssam
139189251Ssam	if (dup2(devnull, STDOUT_FILENO) < 0) {
140189251Ssam		close(devnull);
141189251Ssam		return -1;
142189251Ssam	}
143189251Ssam
144189251Ssam	if (dup2(devnull, STDERR_FILENO) < 0) {
145189251Ssam		close(devnull);
146189251Ssam		return -1;
147189251Ssam	}
148189251Ssam
149189251Ssam	return 0;
150189251Ssam}
151189251Ssam#else /* __APPLE__ */
152189251Ssam#define os_daemon daemon
153189251Ssam#endif /* __APPLE__ */
154189251Ssam
155189251Ssam
156189251Ssamint os_daemonize(const char *pid_file)
157189251Ssam{
158252726Srpaulo#if defined(__uClinux__) || defined(__sun__)
159189251Ssam	return -1;
160252726Srpaulo#else /* defined(__uClinux__) || defined(__sun__) */
161189251Ssam	if (os_daemon(0, 0)) {
162189251Ssam		perror("daemon");
163189251Ssam		return -1;
164189251Ssam	}
165189251Ssam
166189251Ssam	if (pid_file) {
167189251Ssam		FILE *f = fopen(pid_file, "w");
168189251Ssam		if (f) {
169189251Ssam			fprintf(f, "%u\n", getpid());
170189251Ssam			fclose(f);
171189251Ssam		}
172189251Ssam	}
173189251Ssam
174189251Ssam	return -0;
175252726Srpaulo#endif /* defined(__uClinux__) || defined(__sun__) */
176189251Ssam}
177189251Ssam
178189251Ssam
179189251Ssamvoid os_daemonize_terminate(const char *pid_file)
180189251Ssam{
181189251Ssam	if (pid_file)
182189251Ssam		unlink(pid_file);
183189251Ssam}
184189251Ssam
185189251Ssam
186189251Ssamint os_get_random(unsigned char *buf, size_t len)
187189251Ssam{
188189251Ssam	FILE *f;
189189251Ssam	size_t rc;
190189251Ssam
191189251Ssam	f = fopen("/dev/urandom", "rb");
192189251Ssam	if (f == NULL) {
193189251Ssam		printf("Could not open /dev/urandom.\n");
194189251Ssam		return -1;
195189251Ssam	}
196189251Ssam
197189251Ssam	rc = fread(buf, 1, len, f);
198189251Ssam	fclose(f);
199189251Ssam
200189251Ssam	return rc != len ? -1 : 0;
201189251Ssam}
202189251Ssam
203189251Ssam
204189251Ssamunsigned long os_random(void)
205189251Ssam{
206189251Ssam	return random();
207189251Ssam}
208189251Ssam
209189251Ssam
210189251Ssamchar * os_rel2abs_path(const char *rel_path)
211189251Ssam{
212189251Ssam	char *buf = NULL, *cwd, *ret;
213189251Ssam	size_t len = 128, cwd_len, rel_len, ret_len;
214189251Ssam	int last_errno;
215189251Ssam
216189251Ssam	if (rel_path[0] == '/')
217214734Srpaulo		return os_strdup(rel_path);
218189251Ssam
219189251Ssam	for (;;) {
220214734Srpaulo		buf = os_malloc(len);
221189251Ssam		if (buf == NULL)
222189251Ssam			return NULL;
223189251Ssam		cwd = getcwd(buf, len);
224189251Ssam		if (cwd == NULL) {
225189251Ssam			last_errno = errno;
226214734Srpaulo			os_free(buf);
227189251Ssam			if (last_errno != ERANGE)
228189251Ssam				return NULL;
229189251Ssam			len *= 2;
230189251Ssam			if (len > 2000)
231189251Ssam				return NULL;
232189251Ssam		} else {
233189251Ssam			buf[len - 1] = '\0';
234189251Ssam			break;
235189251Ssam		}
236189251Ssam	}
237189251Ssam
238214734Srpaulo	cwd_len = os_strlen(cwd);
239214734Srpaulo	rel_len = os_strlen(rel_path);
240189251Ssam	ret_len = cwd_len + 1 + rel_len + 1;
241214734Srpaulo	ret = os_malloc(ret_len);
242189251Ssam	if (ret) {
243214734Srpaulo		os_memcpy(ret, cwd, cwd_len);
244189251Ssam		ret[cwd_len] = '/';
245214734Srpaulo		os_memcpy(ret + cwd_len + 1, rel_path, rel_len);
246189251Ssam		ret[ret_len - 1] = '\0';
247189251Ssam	}
248214734Srpaulo	os_free(buf);
249189251Ssam	return ret;
250189251Ssam}
251189251Ssam
252189251Ssam
253189251Ssamint os_program_init(void)
254189251Ssam{
255252726Srpaulo#ifdef ANDROID
256252726Srpaulo	/*
257252726Srpaulo	 * We ignore errors here since errors are normal if we
258252726Srpaulo	 * are already running as non-root.
259252726Srpaulo	 */
260252726Srpaulo	gid_t groups[] = { AID_INET, AID_WIFI, AID_KEYSTORE };
261252726Srpaulo	struct __user_cap_header_struct header;
262252726Srpaulo	struct __user_cap_data_struct cap;
263252726Srpaulo
264252726Srpaulo	setgroups(sizeof(groups)/sizeof(groups[0]), groups);
265252726Srpaulo
266252726Srpaulo	prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
267252726Srpaulo
268252726Srpaulo	setgid(AID_WIFI);
269252726Srpaulo	setuid(AID_WIFI);
270252726Srpaulo
271252726Srpaulo	header.version = _LINUX_CAPABILITY_VERSION;
272252726Srpaulo	header.pid = 0;
273252726Srpaulo	cap.effective = cap.permitted =
274252726Srpaulo		(1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
275252726Srpaulo	cap.inheritable = 0;
276252726Srpaulo	capset(&header, &cap);
277252726Srpaulo#endif /* ANDROID */
278252726Srpaulo
279214734Srpaulo#ifdef WPA_TRACE
280214734Srpaulo	dl_list_init(&alloc_list);
281214734Srpaulo#endif /* WPA_TRACE */
282189251Ssam	return 0;
283189251Ssam}
284189251Ssam
285189251Ssam
286189251Ssamvoid os_program_deinit(void)
287189251Ssam{
288214734Srpaulo#ifdef WPA_TRACE
289214734Srpaulo	struct os_alloc_trace *a;
290214734Srpaulo	unsigned long total = 0;
291214734Srpaulo	dl_list_for_each(a, &alloc_list, struct os_alloc_trace, list) {
292214734Srpaulo		total += a->len;
293214734Srpaulo		if (a->magic != ALLOC_MAGIC) {
294214734Srpaulo			wpa_printf(MSG_INFO, "MEMLEAK[%p]: invalid magic 0x%x "
295214734Srpaulo				   "len %lu",
296214734Srpaulo				   a, a->magic, (unsigned long) a->len);
297214734Srpaulo			continue;
298214734Srpaulo		}
299214734Srpaulo		wpa_printf(MSG_INFO, "MEMLEAK[%p]: len %lu",
300214734Srpaulo			   a, (unsigned long) a->len);
301214734Srpaulo		wpa_trace_dump("memleak", a);
302214734Srpaulo	}
303214734Srpaulo	if (total)
304214734Srpaulo		wpa_printf(MSG_INFO, "MEMLEAK: total %lu bytes",
305214734Srpaulo			   (unsigned long) total);
306214734Srpaulo#endif /* WPA_TRACE */
307189251Ssam}
308189251Ssam
309189251Ssam
310189251Ssamint os_setenv(const char *name, const char *value, int overwrite)
311189251Ssam{
312189251Ssam	return setenv(name, value, overwrite);
313189251Ssam}
314189251Ssam
315189251Ssam
316189251Ssamint os_unsetenv(const char *name)
317189251Ssam{
318209158Srpaulo#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) || \
319209158Srpaulo    defined(__OpenBSD__)
320189251Ssam	unsetenv(name);
321189251Ssam	return 0;
322189251Ssam#else
323189251Ssam	return unsetenv(name);
324189251Ssam#endif
325189251Ssam}
326189251Ssam
327189251Ssam
328189251Ssamchar * os_readfile(const char *name, size_t *len)
329189251Ssam{
330189251Ssam	FILE *f;
331189251Ssam	char *buf;
332252726Srpaulo	long pos;
333189251Ssam
334189251Ssam	f = fopen(name, "rb");
335189251Ssam	if (f == NULL)
336189251Ssam		return NULL;
337189251Ssam
338252726Srpaulo	if (fseek(f, 0, SEEK_END) < 0 || (pos = ftell(f)) < 0) {
339252726Srpaulo		fclose(f);
340252726Srpaulo		return NULL;
341252726Srpaulo	}
342252726Srpaulo	*len = pos;
343252726Srpaulo	if (fseek(f, 0, SEEK_SET) < 0) {
344252726Srpaulo		fclose(f);
345252726Srpaulo		return NULL;
346252726Srpaulo	}
347189251Ssam
348214734Srpaulo	buf = os_malloc(*len);
349189251Ssam	if (buf == NULL) {
350189251Ssam		fclose(f);
351189251Ssam		return NULL;
352189251Ssam	}
353189251Ssam
354189251Ssam	if (fread(buf, 1, *len, f) != *len) {
355189251Ssam		fclose(f);
356214734Srpaulo		os_free(buf);
357189251Ssam		return NULL;
358189251Ssam	}
359189251Ssam
360189251Ssam	fclose(f);
361189251Ssam
362189251Ssam	return buf;
363189251Ssam}
364189251Ssam
365189251Ssam
366214734Srpaulo#ifndef WPA_TRACE
367189251Ssamvoid * os_zalloc(size_t size)
368189251Ssam{
369189251Ssam	return calloc(1, size);
370189251Ssam}
371214734Srpaulo#endif /* WPA_TRACE */
372189251Ssam
373189251Ssam
374189251Ssamsize_t os_strlcpy(char *dest, const char *src, size_t siz)
375189251Ssam{
376189251Ssam	const char *s = src;
377189251Ssam	size_t left = siz;
378189251Ssam
379189251Ssam	if (left) {
380189251Ssam		/* Copy string up to the maximum size of the dest buffer */
381189251Ssam		while (--left != 0) {
382189251Ssam			if ((*dest++ = *s++) == '\0')
383189251Ssam				break;
384189251Ssam		}
385189251Ssam	}
386189251Ssam
387189251Ssam	if (left == 0) {
388189251Ssam		/* Not enough room for the string; force NUL-termination */
389189251Ssam		if (siz != 0)
390189251Ssam			*dest = '\0';
391189251Ssam		while (*s++)
392189251Ssam			; /* determine total src string length */
393189251Ssam	}
394189251Ssam
395189251Ssam	return s - src - 1;
396189251Ssam}
397214734Srpaulo
398214734Srpaulo
399214734Srpaulo#ifdef WPA_TRACE
400214734Srpaulo
401214734Srpaulovoid * os_malloc(size_t size)
402214734Srpaulo{
403214734Srpaulo	struct os_alloc_trace *a;
404214734Srpaulo	a = malloc(sizeof(*a) + size);
405214734Srpaulo	if (a == NULL)
406214734Srpaulo		return NULL;
407214734Srpaulo	a->magic = ALLOC_MAGIC;
408214734Srpaulo	dl_list_add(&alloc_list, &a->list);
409214734Srpaulo	a->len = size;
410214734Srpaulo	wpa_trace_record(a);
411214734Srpaulo	return a + 1;
412214734Srpaulo}
413214734Srpaulo
414214734Srpaulo
415214734Srpaulovoid * os_realloc(void *ptr, size_t size)
416214734Srpaulo{
417214734Srpaulo	struct os_alloc_trace *a;
418214734Srpaulo	size_t copy_len;
419214734Srpaulo	void *n;
420214734Srpaulo
421214734Srpaulo	if (ptr == NULL)
422214734Srpaulo		return os_malloc(size);
423214734Srpaulo
424214734Srpaulo	a = (struct os_alloc_trace *) ptr - 1;
425214734Srpaulo	if (a->magic != ALLOC_MAGIC) {
426214734Srpaulo		wpa_printf(MSG_INFO, "REALLOC[%p]: invalid magic 0x%x%s",
427214734Srpaulo			   a, a->magic,
428214734Srpaulo			   a->magic == FREED_MAGIC ? " (already freed)" : "");
429214734Srpaulo		wpa_trace_show("Invalid os_realloc() call");
430214734Srpaulo		abort();
431214734Srpaulo	}
432214734Srpaulo	n = os_malloc(size);
433214734Srpaulo	if (n == NULL)
434214734Srpaulo		return NULL;
435214734Srpaulo	copy_len = a->len;
436214734Srpaulo	if (copy_len > size)
437214734Srpaulo		copy_len = size;
438214734Srpaulo	os_memcpy(n, a + 1, copy_len);
439214734Srpaulo	os_free(ptr);
440214734Srpaulo	return n;
441214734Srpaulo}
442214734Srpaulo
443214734Srpaulo
444214734Srpaulovoid os_free(void *ptr)
445214734Srpaulo{
446214734Srpaulo	struct os_alloc_trace *a;
447214734Srpaulo
448214734Srpaulo	if (ptr == NULL)
449214734Srpaulo		return;
450214734Srpaulo	a = (struct os_alloc_trace *) ptr - 1;
451214734Srpaulo	if (a->magic != ALLOC_MAGIC) {
452214734Srpaulo		wpa_printf(MSG_INFO, "FREE[%p]: invalid magic 0x%x%s",
453214734Srpaulo			   a, a->magic,
454214734Srpaulo			   a->magic == FREED_MAGIC ? " (already freed)" : "");
455214734Srpaulo		wpa_trace_show("Invalid os_free() call");
456214734Srpaulo		abort();
457214734Srpaulo	}
458214734Srpaulo	dl_list_del(&a->list);
459214734Srpaulo	a->magic = FREED_MAGIC;
460214734Srpaulo
461214734Srpaulo	wpa_trace_check_ref(ptr);
462214734Srpaulo	free(a);
463214734Srpaulo}
464214734Srpaulo
465214734Srpaulo
466214734Srpaulovoid * os_zalloc(size_t size)
467214734Srpaulo{
468214734Srpaulo	void *ptr = os_malloc(size);
469214734Srpaulo	if (ptr)
470214734Srpaulo		os_memset(ptr, 0, size);
471214734Srpaulo	return ptr;
472214734Srpaulo}
473214734Srpaulo
474214734Srpaulo
475214734Srpaulochar * os_strdup(const char *s)
476214734Srpaulo{
477214734Srpaulo	size_t len;
478214734Srpaulo	char *d;
479214734Srpaulo	len = os_strlen(s);
480214734Srpaulo	d = os_malloc(len + 1);
481214734Srpaulo	if (d == NULL)
482214734Srpaulo		return NULL;
483214734Srpaulo	os_memcpy(d, s, len);
484214734Srpaulo	d[len] = '\0';
485214734Srpaulo	return d;
486214734Srpaulo}
487214734Srpaulo
488214734Srpaulo#endif /* WPA_TRACE */
489