2 * Copyright (C) 2012 Tobias Brunner
3 * Copyright (C) 2012 Giuliano Grassi
4 * Copyright (C) 2012 Ralf Sager
5 * Hochschule fuer Technik Rapperswil
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 package org
.strongswan
.android
.logic
;
20 import java
.net
.InetAddress
;
21 import java
.net
.NetworkInterface
;
22 import java
.net
.SocketException
;
23 import java
.security
.cert
.CertificateEncodingException
;
24 import java
.security
.cert
.X509Certificate
;
25 import java
.util
.ArrayList
;
26 import java
.util
.Enumeration
;
28 import org
.strongswan
.android
.data
.VpnProfile
;
29 import org
.strongswan
.android
.data
.VpnProfileDataSource
;
30 import org
.strongswan
.android
.logic
.VpnStateService
.ErrorState
;
31 import org
.strongswan
.android
.logic
.VpnStateService
.State
;
33 import android
.app
.Service
;
34 import android
.content
.ComponentName
;
35 import android
.content
.Intent
;
36 import android
.content
.ServiceConnection
;
37 import android
.net
.VpnService
;
38 import android
.os
.Bundle
;
39 import android
.os
.IBinder
;
40 import android
.util
.Log
;
42 public class CharonVpnService
extends VpnService
implements Runnable
44 private static final String TAG
= CharonVpnService
.class.getSimpleName();
45 private VpnProfileDataSource mDataSource
;
46 private Thread mConnectionHandler
;
47 private VpnProfile mCurrentProfile
;
48 private VpnProfile mNextProfile
;
49 private volatile boolean mProfileUpdated
;
50 private volatile boolean mTerminate
;
51 private VpnStateService mService
;
52 private final Object mServiceLock
= new Object();
53 private final ServiceConnection mServiceConnection
= new ServiceConnection() {
55 public void onServiceDisconnected(ComponentName name
)
56 { /* since the service is local this is theoretically only called when the process is terminated */
57 synchronized (mServiceLock
)
64 public void onServiceConnected(ComponentName name
, IBinder service
)
66 synchronized (mServiceLock
)
68 mService
= ((VpnStateService
.LocalBinder
)service
).getService();
70 /* we are now ready to start the handler thread */
71 mConnectionHandler
.start();
76 * as defined in charonservice.h
78 static final int STATE_CHILD_SA_UP
= 1;
79 static final int STATE_CHILD_SA_DOWN
= 2;
80 static final int STATE_AUTH_ERROR
= 3;
81 static final int STATE_PEER_AUTH_ERROR
= 4;
82 static final int STATE_LOOKUP_ERROR
= 5;
83 static final int STATE_UNREACHABLE_ERROR
= 6;
84 static final int STATE_GENERIC_ERROR
= 7;
87 public int onStartCommand(Intent intent
, int flags
, int startId
)
91 Bundle bundle
= intent
.getExtras();
92 VpnProfile profile
= null
;
95 profile
= mDataSource
.getVpnProfile(bundle
.getLong(VpnProfileDataSource
.KEY_ID
));
98 String password
= bundle
.getString(VpnProfileDataSource
.KEY_PASSWORD
);
99 profile
.setPassword(password
);
102 setNextProfile(profile
);
104 return START_NOT_STICKY
;
108 public void onCreate()
110 mDataSource
= new VpnProfileDataSource(this);
112 /* use a separate thread as main thread for charon */
113 mConnectionHandler
= new Thread(this);
114 /* the thread is started when the service is bound */
115 bindService(new Intent(this, VpnStateService
.class),
116 mServiceConnection
, Service
.BIND_AUTO_CREATE
);
120 public void onRevoke()
121 { /* the system revoked the rights grated with the initial prepare() call.
122 * called when the user clicks disconnect in the system's VPN dialog */
123 setNextProfile(null
);
127 public void onDestroy()
130 setNextProfile(null
);
133 mConnectionHandler
.join();
135 catch (InterruptedException e
)
139 if (mService
!= null
)
141 unbindService(mServiceConnection
);
147 * Set the profile that is to be initiated next. Notify the handler thread.
149 * @param profile the profile to initiate
151 private void setNextProfile(VpnProfile profile
)
155 this.mNextProfile
= profile
;
156 mProfileUpdated
= true
;
170 while (!mProfileUpdated
)
175 mProfileUpdated
= false
;
176 stopCurrentConnection();
177 if (mNextProfile
== null
)
180 setState(State
.DISABLED
);
188 mCurrentProfile
= mNextProfile
;
191 setProfile(mCurrentProfile
);
192 setError(ErrorState
.NO_ERROR
);
193 setState(State
.CONNECTING
);
196 Log
.i(TAG
, "charon started");
199 catch (InterruptedException ex
)
201 stopCurrentConnection();
202 setState(State
.DISABLED
);
209 * Stop any existing connection by deinitializing charon.
211 private void stopCurrentConnection()
215 if (mCurrentProfile
!= null
)
217 setState(State
.DISCONNECTING
);
218 deinitializeCharon();
219 Log
.i(TAG
, "charon stopped");
220 mCurrentProfile
= null
;
226 * Update the VPN profile on the state service. Called by the handler thread.
228 * @param profile currently active VPN profile
230 private void setProfile(VpnProfile profile
)
232 synchronized (mServiceLock
)
234 if (mService
!= null
)
236 mService
.setProfile(profile
);
242 * Update the current VPN state on the state service. Called by the handler
243 * thread and any of charon's threads.
245 * @param state current state
247 private void setState(State state
)
249 synchronized (mServiceLock
)
251 if (mService
!= null
)
253 mService
.setState(state
);
259 * Set an error on the state service. Called by the handler thread and any
260 * of charon's threads.
262 * @param error error state
264 private void setError(ErrorState error
)
266 synchronized (mServiceLock
)
268 if (mService
!= null
)
270 mService
.setError(error
);
276 * Set an error on the state service and disconnect the current connection.
277 * This is not done by calling stopCurrentConnection() above, but instead
278 * is done asynchronously via state service.
280 * @param error error state
282 private void setErrorDisconnect(ErrorState error
)
284 synchronized (mServiceLock
)
286 if (mService
!= null
)
288 mService
.setError(error
);
289 mService
.disconnect();
295 * Updates the state of the current connection.
296 * Called via JNI by different threads (but not concurrently).
298 * @param status new state
300 public void updateStatus(int status
)
304 case STATE_CHILD_SA_DOWN
:
305 synchronized (mServiceLock
)
307 /* since this state is also reached when the SA is closed remotely,
308 * we call disconnect() to make sure charon is properly deinitialized */
309 if (mService
!= null
)
311 mService
.disconnect();
315 case STATE_CHILD_SA_UP
:
316 setState(State
.CONNECTED
);
318 case STATE_AUTH_ERROR
:
319 setErrorDisconnect(ErrorState
.AUTH_FAILED
);
321 case STATE_PEER_AUTH_ERROR
:
322 setErrorDisconnect(ErrorState
.PEER_AUTH_FAILED
);
324 case STATE_LOOKUP_ERROR
:
325 setErrorDisconnect(ErrorState
.LOOKUP_FAILED
);
327 case STATE_UNREACHABLE_ERROR
:
328 setErrorDisconnect(ErrorState
.UNREACHABLE
);
330 case STATE_GENERIC_ERROR
:
331 setErrorDisconnect(ErrorState
.GENERIC_ERROR
);
334 Log
.e(TAG
, "Unknown status code received");
340 * Function called via JNI to generate a list of DER encoded CA certificates
343 * @param hash optional alias (only hash part), if given matching certificates are returned
344 * @return a list of DER encoded CA certificates
346 private synchronized byte[][] getTrustedCertificates(String hash
)
348 ArrayList
<byte[]> certs
= new ArrayList
<byte[]>();
349 TrustedCertificateManager certman
= TrustedCertificateManager
.getInstance();
354 String alias
= "user:" + hash
+ ".0";
355 X509Certificate cert
= certman
.getCACertificateFromAlias(alias
);
358 alias
= "system:" + hash
+ ".0";
359 cert
= certman
.getCACertificateFromAlias(alias
);
365 certs
.add(cert
.getEncoded());
369 String alias
= this.mCurrentProfile
.getCertificateAlias();
372 X509Certificate cert
= certman
.getCACertificateFromAlias(alias
);
377 certs
.add(cert
.getEncoded());
381 for (X509Certificate cert
: certman
.getAllCACertificates().values())
383 certs
.add(cert
.getEncoded());
388 catch (CertificateEncodingException e
)
393 return certs
.toArray(new byte[certs
.size()][]);
397 * Initialization of charon, provided by libandroidbridge.so
399 public native void initializeCharon();
402 * Deinitialize charon, provided by libandroidbridge.so
404 public native void deinitializeCharon();
407 * Helper function that retrieves a local IPv4 address.
409 * @return string representation of an IPv4 address, or null if none found
411 private static String
getLocalIPv4Address()
415 Enumeration
<NetworkInterface
> en
= NetworkInterface
.getNetworkInterfaces();
416 while (en
.hasMoreElements())
418 NetworkInterface intf
= en
.nextElement();
420 Enumeration
<InetAddress
> enumIpAddr
= intf
.getInetAddresses();
421 while (enumIpAddr
.hasMoreElements())
423 InetAddress inetAddress
= enumIpAddr
.nextElement();
424 if (!inetAddress
.isLoopbackAddress() && inetAddress
.getAddress().length
== 4)
426 return inetAddress
.getHostAddress().toString();
431 catch (SocketException ex
)
433 ex
.printStackTrace();
440 * The libraries are extracted to /data/data/org.strongswan.android/...
441 * during installation.
445 System
.loadLibrary("crypto");
446 System
.loadLibrary("strongswan");
447 System
.loadLibrary("hydra");
448 System
.loadLibrary("charon");
449 System
.loadLibrary("ipsec");
450 System
.loadLibrary("androidbridge");