Updated through tag hostap_2_5 from git://w1.fi/hostap.git
[mech_eap.git] / libeap / src / utils / os_unix.c
index c696fae..ffa2e78 100644 (file)
@@ -2,28 +2,37 @@
  * OS specific functions for UNIX/POSIX systems
  * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
 
+#include <time.h>
+#include <sys/wait.h>
+
+#ifdef ANDROID
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include <private/android_filesystem_config.h>
+#endif /* ANDROID */
+
+#ifdef __MACH__
+#include <CoreServices/CoreServices.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#endif /* __MACH__ */
+
 #include "os.h"
+#include "common.h"
 
 #ifdef WPA_TRACE
 
-#include "common.h"
-#include "list.h"
 #include "wpa_debug.h"
 #include "trace.h"
+#include "list.h"
 
-static struct dl_list alloc_list;
+static struct dl_list alloc_list = DL_LIST_HEAD_INIT(alloc_list);
 
 #define ALLOC_MAGIC 0xa84ef1b2
 #define FREED_MAGIC 0x67fd487a
@@ -33,7 +42,7 @@ struct os_alloc_trace {
        struct dl_list list;
        size_t len;
        WPA_TRACE_INFO
-};
+} __attribute__((aligned(16)));
 
 #endif /* WPA_TRACE */
 
@@ -58,6 +67,61 @@ int os_get_time(struct os_time *t)
 }
 
 
+int os_get_reltime(struct os_reltime *t)
+{
+#ifndef __MACH__
+#if defined(CLOCK_BOOTTIME)
+       static clockid_t clock_id = CLOCK_BOOTTIME;
+#elif defined(CLOCK_MONOTONIC)
+       static clockid_t clock_id = CLOCK_MONOTONIC;
+#else
+       static clockid_t clock_id = CLOCK_REALTIME;
+#endif
+       struct timespec ts;
+       int res;
+
+       while (1) {
+               res = clock_gettime(clock_id, &ts);
+               if (res == 0) {
+                       t->sec = ts.tv_sec;
+                       t->usec = ts.tv_nsec / 1000;
+                       return 0;
+               }
+               switch (clock_id) {
+#ifdef CLOCK_BOOTTIME
+               case CLOCK_BOOTTIME:
+                       clock_id = CLOCK_MONOTONIC;
+                       break;
+#endif
+#ifdef CLOCK_MONOTONIC
+               case CLOCK_MONOTONIC:
+                       clock_id = CLOCK_REALTIME;
+                       break;
+#endif
+               case CLOCK_REALTIME:
+                       return -1;
+               }
+       }
+#else /* __MACH__ */
+       uint64_t abstime, nano;
+       static mach_timebase_info_data_t info = { 0, 0 };
+
+       if (!info.denom) {
+               if (mach_timebase_info(&info) != KERN_SUCCESS)
+                       return -1;
+       }
+
+       abstime = mach_absolute_time();
+       nano = (abstime * info.numer) / info.denom;
+
+       t->sec = nano / NSEC_PER_SEC;
+       t->usec = (nano - (((uint64_t) t->sec) * NSEC_PER_SEC)) / NSEC_PER_USEC;
+
+       return 0;
+#endif /* __MACH__ */
+}
+
+
 int os_mktime(int year, int month, int day, int hour, int min, int sec,
              os_time_t *t)
 {
@@ -98,6 +162,24 @@ int os_mktime(int year, int month, int day, int hour, int min, int sec,
 }
 
 
+int os_gmtime(os_time_t t, struct os_tm *tm)
+{
+       struct tm *tm2;
+       time_t t2 = t;
+
+       tm2 = gmtime(&t2);
+       if (tm2 == NULL)
+               return -1;
+       tm->sec = tm2->tm_sec;
+       tm->min = tm2->tm_min;
+       tm->hour = tm2->tm_hour;
+       tm->day = tm2->tm_mday;
+       tm->month = tm2->tm_mon + 1;
+       tm->year = tm2->tm_year + 1900;
+       return 0;
+}
+
+
 #ifdef __APPLE__
 #include <fcntl.h>
 static int os_daemon(int nochdir, int noclose)
@@ -168,6 +250,9 @@ int os_get_random(unsigned char *buf, size_t len)
        FILE *f;
        size_t rc;
 
+       if (TEST_FAIL())
+               return -1;
+
        f = fopen("/dev/urandom", "rb");
        if (f == NULL) {
                printf("Could not open /dev/urandom.\n");
@@ -193,6 +278,9 @@ char * os_rel2abs_path(const char *rel_path)
        size_t len = 128, cwd_len, rel_len, ret_len;
        int last_errno;
 
+       if (!rel_path)
+               return NULL;
+
        if (rel_path[0] == '/')
                return os_strdup(rel_path);
 
@@ -232,9 +320,34 @@ char * os_rel2abs_path(const char *rel_path)
 
 int os_program_init(void)
 {
-#ifdef WPA_TRACE
-       dl_list_init(&alloc_list);
-#endif /* WPA_TRACE */
+#ifdef ANDROID
+       /*
+        * We ignore errors here since errors are normal if we
+        * are already running as non-root.
+        */
+#ifdef ANDROID_SETGROUPS_OVERRIDE
+       gid_t groups[] = { ANDROID_SETGROUPS_OVERRIDE };
+#else /* ANDROID_SETGROUPS_OVERRIDE */
+       gid_t groups[] = { AID_INET, AID_WIFI, AID_KEYSTORE };
+#endif /* ANDROID_SETGROUPS_OVERRIDE */
+       struct __user_cap_header_struct header;
+       struct __user_cap_data_struct cap;
+
+       setgroups(ARRAY_SIZE(groups), groups);
+
+       prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
+
+       setgid(AID_WIFI);
+       setuid(AID_WIFI);
+
+       header.version = _LINUX_CAPABILITY_VERSION;
+       header.pid = 0;
+       cap.effective = cap.permitted =
+               (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
+       cap.inheritable = 0;
+       capset(&header, &cap);
+#endif /* ANDROID */
+
        return 0;
 }
 
@@ -285,14 +398,21 @@ char * os_readfile(const char *name, size_t *len)
 {
        FILE *f;
        char *buf;
+       long pos;
 
        f = fopen(name, "rb");
        if (f == NULL)
                return NULL;
 
-       fseek(f, 0, SEEK_END);
-       *len = ftell(f);
-       fseek(f, 0, SEEK_SET);
+       if (fseek(f, 0, SEEK_END) < 0 || (pos = ftell(f)) < 0) {
+               fclose(f);
+               return NULL;
+       }
+       *len = pos;
+       if (fseek(f, 0, SEEK_SET) < 0) {
+               fclose(f);
+               return NULL;
+       }
 
        buf = os_malloc(*len);
        if (buf == NULL) {
@@ -312,6 +432,35 @@ char * os_readfile(const char *name, size_t *len)
 }
 
 
+int os_file_exists(const char *fname)
+{
+       FILE *f = fopen(fname, "rb");
+       if (f == NULL)
+               return 0;
+       fclose(f);
+       return 1;
+}
+
+
+int os_fdatasync(FILE *stream)
+{
+       if (!fflush(stream)) {
+#ifndef __MACH__
+               return fdatasync(fileno(stream));
+#else /* __MACH__ */
+#ifdef F_FULLFSYNC
+               /* OS X does not implement fdatasync(). */
+               return fcntl(fileno(stream), F_FULLFSYNC);
+#else /* F_FULLFSYNC */
+#error Neither fdatasync nor F_FULLSYNC are defined
+#endif /* F_FULLFSYNC */
+#endif /* __MACH__ */
+       }
+
+       return -1;
+}
+
+
 #ifndef WPA_TRACE
 void * os_zalloc(size_t size)
 {
@@ -345,11 +494,193 @@ size_t os_strlcpy(char *dest, const char *src, size_t siz)
 }
 
 
+int os_memcmp_const(const void *a, const void *b, size_t len)
+{
+       const u8 *aa = a;
+       const u8 *bb = b;
+       size_t i;
+       u8 res;
+
+       for (res = 0, i = 0; i < len; i++)
+               res |= aa[i] ^ bb[i];
+
+       return res;
+}
+
+
 #ifdef WPA_TRACE
 
+#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
+char wpa_trace_fail_func[256] = { 0 };
+unsigned int wpa_trace_fail_after;
+
+static int testing_fail_alloc(void)
+{
+       const char *func[WPA_TRACE_LEN];
+       size_t i, res, len;
+       char *pos, *next;
+       int match;
+
+       if (!wpa_trace_fail_after)
+               return 0;
+
+       res = wpa_trace_calling_func(func, WPA_TRACE_LEN);
+       i = 0;
+       if (i < res && os_strcmp(func[i], __func__) == 0)
+               i++;
+       if (i < res && os_strcmp(func[i], "os_malloc") == 0)
+               i++;
+       if (i < res && os_strcmp(func[i], "os_zalloc") == 0)
+               i++;
+       if (i < res && os_strcmp(func[i], "os_calloc") == 0)
+               i++;
+       if (i < res && os_strcmp(func[i], "os_realloc") == 0)
+               i++;
+       if (i < res && os_strcmp(func[i], "os_realloc_array") == 0)
+               i++;
+       if (i < res && os_strcmp(func[i], "os_strdup") == 0)
+               i++;
+
+       pos = wpa_trace_fail_func;
+
+       match = 0;
+       while (i < res) {
+               int allow_skip = 1;
+               int maybe = 0;
+
+               if (*pos == '=') {
+                       allow_skip = 0;
+                       pos++;
+               } else if (*pos == '?') {
+                       maybe = 1;
+                       pos++;
+               }
+               next = os_strchr(pos, ';');
+               if (next)
+                       len = next - pos;
+               else
+                       len = os_strlen(pos);
+               if (os_memcmp(pos, func[i], len) != 0) {
+                       if (maybe && next) {
+                               pos = next + 1;
+                               continue;
+                       }
+                       if (allow_skip) {
+                               i++;
+                               continue;
+                       }
+                       return 0;
+               }
+               if (!next) {
+                       match = 1;
+                       break;
+               }
+               pos = next + 1;
+               i++;
+       }
+       if (!match)
+               return 0;
+
+       wpa_trace_fail_after--;
+       if (wpa_trace_fail_after == 0) {
+               wpa_printf(MSG_INFO, "TESTING: fail allocation at %s",
+                          wpa_trace_fail_func);
+               for (i = 0; i < res; i++)
+                       wpa_printf(MSG_INFO, "backtrace[%d] = %s",
+                                  (int) i, func[i]);
+               return 1;
+       }
+
+       return 0;
+}
+
+
+char wpa_trace_test_fail_func[256] = { 0 };
+unsigned int wpa_trace_test_fail_after;
+
+int testing_test_fail(void)
+{
+       const char *func[WPA_TRACE_LEN];
+       size_t i, res, len;
+       char *pos, *next;
+       int match;
+
+       if (!wpa_trace_test_fail_after)
+               return 0;
+
+       res = wpa_trace_calling_func(func, WPA_TRACE_LEN);
+       i = 0;
+       if (i < res && os_strcmp(func[i], __func__) == 0)
+               i++;
+
+       pos = wpa_trace_test_fail_func;
+
+       match = 0;
+       while (i < res) {
+               int allow_skip = 1;
+               int maybe = 0;
+
+               if (*pos == '=') {
+                       allow_skip = 0;
+                       pos++;
+               } else if (*pos == '?') {
+                       maybe = 1;
+                       pos++;
+               }
+               next = os_strchr(pos, ';');
+               if (next)
+                       len = next - pos;
+               else
+                       len = os_strlen(pos);
+               if (os_memcmp(pos, func[i], len) != 0) {
+                       if (maybe && next) {
+                               pos = next + 1;
+                               continue;
+                       }
+                       if (allow_skip) {
+                               i++;
+                               continue;
+                       }
+                       return 0;
+               }
+               if (!next) {
+                       match = 1;
+                       break;
+               }
+               pos = next + 1;
+               i++;
+       }
+       if (!match)
+               return 0;
+
+       wpa_trace_test_fail_after--;
+       if (wpa_trace_test_fail_after == 0) {
+               wpa_printf(MSG_INFO, "TESTING: fail at %s",
+                          wpa_trace_test_fail_func);
+               for (i = 0; i < res; i++)
+                       wpa_printf(MSG_INFO, "backtrace[%d] = %s",
+                                  (int) i, func[i]);
+               return 1;
+       }
+
+       return 0;
+}
+
+#else
+
+static inline int testing_fail_alloc(void)
+{
+       return 0;
+}
+#endif
+
 void * os_malloc(size_t size)
 {
        struct os_alloc_trace *a;
+
+       if (testing_fail_alloc())
+               return NULL;
+
        a = malloc(sizeof(*a) + size);
        if (a == NULL)
                return NULL;
@@ -435,3 +766,57 @@ char * os_strdup(const char *s)
 }
 
 #endif /* WPA_TRACE */
+
+
+int os_exec(const char *program, const char *arg, int wait_completion)
+{
+       pid_t pid;
+       int pid_status;
+
+       pid = fork();
+       if (pid < 0) {
+               perror("fork");
+               return -1;
+       }
+
+       if (pid == 0) {
+               /* run the external command in the child process */
+               const int MAX_ARG = 30;
+               char *_program, *_arg, *pos;
+               char *argv[MAX_ARG + 1];
+               int i;
+
+               _program = os_strdup(program);
+               _arg = os_strdup(arg);
+
+               argv[0] = _program;
+
+               i = 1;
+               pos = _arg;
+               while (i < MAX_ARG && pos && *pos) {
+                       while (*pos == ' ')
+                               pos++;
+                       if (*pos == '\0')
+                               break;
+                       argv[i++] = pos;
+                       pos = os_strchr(pos, ' ');
+                       if (pos)
+                               *pos++ = '\0';
+               }
+               argv[i] = NULL;
+
+               execv(program, argv);
+               perror("execv");
+               os_free(_program);
+               os_free(_arg);
+               exit(0);
+               return -1;
+       }
+
+       if (wait_completion) {
+               /* wait for the child process to complete in the parent */
+               waitpid(pid, &pid_status, 0);
+       }
+
+       return 0;
+}