/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdarg.h> #include <signal.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/select.h> #ifdef ANDROID_CHANGES #include <sys/ioctl.h> #include <linux/if.h> #include <android/log.h> #include <cutils/sockets.h> #include <private/android_filesystem_config.h> #include "keystore_get.h" #endif #include "config.h" #include "libpfkey.h" #include "gcmalloc.h" #include "vmbuf.h" #include "crypto_openssl.h" #include "oakley.h" #include "pfkey.h" #include "schedule.h" #include "isakmp_var.h" #include "nattraversal.h" #include "localconf.h" #include "sockmisc.h" #include "grabmyaddr.h" #include "plog.h" #include "admin.h" #include "privsep.h" #include "misc.h" #ifdef ANDROID_CHANGES static int get_control_and_arguments(int *argc, char ***argv) { static char *args[256]; int control; int i; if ((i = android_get_control_socket("racoon")) == -1) { return -1; } do_plog(LLV_DEBUG, "Waiting for control socket"); if (listen(i, 1) == -1 || (control = accept(i, NULL, 0)) == -1) { do_plog(LLV_ERROR, "Cannot get control socket"); exit(-1); } close(i); args[0] = (*argv)[0]; for (i = 1; i < 256; ++i) { unsigned char length; if (recv(control, &length, 1, 0) != 1) { do_plog(LLV_ERROR, "Cannot get argument length"); exit(-1); } if (length == 0xFF) { break; } else { int offset = 0; args[i] = malloc(length + 1); while (offset < length) { int n = recv(control, &args[i][offset], length - offset, 0); if (n > 0) { offset += n; } else { do_plog(LLV_ERROR, "Cannot get argument value"); exit(-1); } } args[i][length] = 0; } } do_plog(LLV_DEBUG, "Received %d arguments", i - 1); *argc = i; *argv = args; return control; } static void bind_interface() { struct ifreq ifreqs[64]; struct ifconf ifconf = {.ifc_len = sizeof(ifreqs), .ifc_req = ifreqs}; struct myaddrs *p = lcconf->myaddrs; if (ioctl(p->sock, SIOCGIFCONF, &ifconf) == -1) { do_plog(LLV_WARNING, "Cannot list interfaces"); return; } while (p) { int i = ifconf.ifc_len / sizeof(struct ifreq) - 1; while (i >= 0 && cmpsaddrwop(p->addr, &ifreqs[i].ifr_addr)) { --i; } if (i < 0 || setsockopt(p->sock, SOL_SOCKET, SO_BINDTODEVICE, ifreqs[i].ifr_name, IFNAMSIZ) == -1) { do_plog(LLV_WARNING, "Cannot bind socket %d to proper interface", p->sock); } p = p->next; } } #endif extern void setup(int argc, char **argv); int f_local = 0; static void terminate(int signal) { exit(1); } static void terminated() { do_plog(LLV_INFO, "Bye\n"); } int main(int argc, char **argv) { fd_set fdset; int fdset_size; struct myaddrs *p; #ifdef ANDROID_CHANGES int control = get_control_and_arguments(&argc, &argv); unsigned char code = argc - 1; #endif signal(SIGHUP, terminate); signal(SIGINT, terminate); signal(SIGTERM, terminate); signal(SIGPIPE, SIG_IGN); setup(argc, argv); do_plog(LLV_INFO, "ipsec-tools 0.7.2 (http://ipsec-tools.sf.net)\n"); atexit(terminated); eay_init(); oakley_dhinit(); compute_vendorids(); sched_init(); if (pfkey_init() < 0 || isakmp_init() < 0) { exit(1); } #ifdef ENABLE_NATT natt_keepalive_init(); #endif #ifdef ANDROID_CHANGES bind_interface(); send(control, &code, 1, 0); setuid(AID_VPN); #endif FD_ZERO(&fdset); FD_SET(lcconf->sock_pfkey, &fdset); fdset_size = lcconf->sock_pfkey; for (p = lcconf->myaddrs; p; p = p->next) { FD_SET(p->sock, &fdset); if (fdset_size < p->sock) { fdset_size = p->sock; } } ++fdset_size; while (1) { fd_set readset = fdset; struct timeval *timeout = schedular(); if (select(fdset_size, &readset, NULL, NULL, timeout) < 0) { exit(1); } if (FD_ISSET(lcconf->sock_pfkey, &readset)) { pfkey_handler(); } for (p = lcconf->myaddrs; p; p = p->next) { if (FD_ISSET(p->sock, &readset)) { isakmp_handler(p->sock); } } } return 0; } /* plog.h */ void do_plog(int level, char *format, ...) { if (level >= 0 && level <= 5) { #ifdef ANDROID_CHANGES static int levels[6] = { ANDROID_LOG_ERROR, ANDROID_LOG_WARN, ANDROID_LOG_INFO, ANDROID_LOG_INFO, ANDROID_LOG_DEBUG, ANDROID_LOG_VERBOSE }; va_list ap; va_start(ap, format); __android_log_vprint(levels[level], "racoon", format, ap); va_end(ap); #else static char *levels = "EWNIDV"; fprintf(stderr, "%c: ", levels[level]); va_list ap; va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap); #endif } } char *binsanitize(char *data, size_t length) { char *output = racoon_malloc(length + 1); if (output) { size_t i; for (i = 0; i < length; ++i) { output[i] = (data[i] < ' ' || data[i] > '~') ? '?' : data[i]; } output[length] = '\0'; } return output; } /* privsep.h */ int privsep_pfkey_open() { return pfkey_open(); } void privsep_pfkey_close(int key) { pfkey_close(key); } vchar_t *privsep_eay_get_pkcs1privkey(char *file) { return eay_get_pkcs1privkey(file); } vchar_t *privsep_getpsk(const char *key, int size) { vchar_t *p = NULL; #ifdef ANDROID_CHANGES char *value = keystore_get(key, &size); if (value) { if ((p = vmalloc(size)) != NULL) { memcpy(p->v, value, p->l); } free(value); } #else if (key && (p = vmalloc(size)) != NULL) { memcpy(p->v, key, p->l); } #endif return p; } int privsep_script_exec(char *script, int name, char * const *environ) { return 0; } /* grabmyaddr.h */ int getsockmyaddr(struct sockaddr *addr) { struct myaddrs *p; for (p = lcconf->myaddrs; p; p = p->next) { if (cmpsaddrstrict(addr, p->addr) == 0) { return p->sock; } } return -1; } /* misc.h */ int racoon_hexdump(void *data, size_t length) { return 0; }