Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a ContentProvider for allowing remote-application access to KeePass databases #38

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
19 changes: 17 additions & 2 deletions AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,21 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.keepass"
android:installLocation="auto" android:versionCode="127" android:versionName="1.99.9">
<uses-sdk android:minSdkVersion="3" android:targetSdkVersion="12"/>
<uses-sdk android:minSdkVersion="3" android:targetSdkVersion="14"/>
<supports-screens
android:smallScreens="true"
android:normalScreens="true"
android:largeScreens="true"
android:anyDensity="true"
/>
<permission android:protectionLevel="dangerous"
android:description="@string/permission_description"
android:icon="@drawable/launcher"
android:label="KeePassDroid database access"
android:permissionGroup="@string/app_name"
android:name="com.keepassdroid.permission.READ_DATABASE"/>
<uses-permission android:name="com.keepassdroid.permission.READ_DATABASE"/>

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.VIBRATE"></uses-permission>
<application
Expand Down Expand Up @@ -97,5 +105,12 @@
android:theme="@style/NoTitleBar"></activity>
<service android:name="com.keepassdroid.services.TimeoutService"></service>
<meta-data android:name="com.a0soft.gphone.aTrackDog.webURL" android:value="http://keepassdroid.com" />
<activity android:name="com.keepassdroid.provider.AuthzActivity"
android:exported="false"
android:theme="@android:style/Theme.DeviceDefault.Dialog"/>
<provider android:name="com.keepassdroid.provider.DatabaseProvider"
android:authorities="com.keepassdroid.provider"
android:grantUriPermissions="false"
android:readPermission="com.keepassdroid.permission.READ_DATABASE"/>
</application>
</manifest>
</manifest>
2 changes: 1 addition & 1 deletion project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@
# Indicates whether an apk should be generated for each density.
split.density=false
# Project target.
target=android-11
target=android-14
apk-configurations=
56 changes: 56 additions & 0 deletions res/layout/authz.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:padding="8dp"
android:layout_width="match_parent"
android:layout_height="240dp">


<LinearLayout
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="0dp"
>
<TextView
android:id="@+id/applications"
android:layout_gravity="top|left"
android:layout_weight="1"
android:layout_width="0dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/timer"
android:layout_gravity="top|right"
android:gravity="top|right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<TextView
android:text="@string/authz_access_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<LinearLayout
android:layout_marginTop="12dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<Button
android:id="@+id/deny"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="@string/deny"/>
<Button
android:id="@+id/allow"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="@string/allow"/>
<Button
android:id="@+id/always"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="@string/always"/>
</LinearLayout>
</LinearLayout>
12 changes: 11 additions & 1 deletion res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,17 @@
<string name="warning_read_only">Your sd card is currently read-only. You may not be able to save changes to your database.</string>
<string name="warning_unmounted">Your sd card is not currently mounted on your device. You will not be able to load or create your database.</string>
<string name="version_label">Version:</string>

<string name="permission_description">Allows searching the KeePass
database to read usernames and passwords
</string>
<string name="always">Always</string>
<string name="allow">Allow</string>
<string name="deny">Deny</string>
<string name="authz_access_message">This application is requesting
access to your KeePass database, should it be able
to search and retrieve your usernames and passwords?
</string>

<string-array name="clipboard_timeout_options">
<item>30 seconds</item>
<item>1 minute</item>
Expand Down
78 changes: 78 additions & 0 deletions src/com/keepassdroid/provider/Authz.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package com.keepassdroid.provider;

import android.content.ContentValues;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.text.SpannableStringBuilder;

import java.util.Arrays;
import java.util.Collections;

/**
* @author pfn
*/
public class Authz {
Authz(int uid) {
isNew = true;
this.uid = uid;
}

Authz(int uid, String signatures, long lastAuthz, boolean authz,
boolean remember) {
this.uid = uid;
this.signatures = signatures;
this.lastAuthz = lastAuthz;
this.authz = authz;
this.remember = remember;
isNew = false;
}
public final boolean isNew;

public int uid;
public String signatures;
public long lastAuthz;
public boolean remember;
public boolean authz;

public CharSequence appNames;

public ContentValues values() {
ContentValues values = new ContentValues();
values.put(AuthzDatabaseHelper.FIELD_UID, uid);
values.put(AuthzDatabaseHelper.FIELD_SIGNATURES, signatures);
values.put(AuthzDatabaseHelper.FIELD_LAST_AUTHZ, lastAuthz);
values.put(AuthzDatabaseHelper.FIELD_AUTHZ, authz);
values.put(AuthzDatabaseHelper.FIELD_REMEMBER, remember);
return values;
}
public boolean checkSignatures(Context ctx) {
PackageManager pm = ctx.getPackageManager();
String[] packages = pm.getPackagesForUid(uid);
Arrays.sort(packages);
SpannableStringBuilder b = new SpannableStringBuilder();
StringBuilder allSignatures = new StringBuilder();
for (String pkg : packages) {
try {
PackageInfo pInfo = pm.getPackageInfo(
pkg, PackageManager.GET_SIGNATURES);
ApplicationInfo appInfo = pm.getApplicationInfo(pkg, 0);
CharSequence label = pm.getApplicationLabel(appInfo);
for (Signature sig : pInfo.signatures) {
allSignatures.append(sig.toCharsString());
allSignatures.append(":");
}
b.append(label);
b.append("\n");
} catch (PackageManager.NameNotFoundException e) {
throw new IllegalStateException(e);
}
}
appNames = b;
String old = signatures;
signatures = allSignatures.toString();
return signatures.equals(old);
}
}
103 changes: 103 additions & 0 deletions src/com/keepassdroid/provider/AuthzActivity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package com.keepassdroid.provider;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.TextView;
import com.android.keepass.R;

/**
* @author pfn
*/
public class AuthzActivity extends Activity {
private String token;
private int uid;
private Authz authz;
private boolean finished = false;

private boolean broadcastSent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.authz);

final TextView timer = (TextView) findViewById(R.id.timer);
final Handler handler = new Handler();
final long finishTime =
System.currentTimeMillis() + DatabaseProvider.AUTHZ_TIMEOUT;
final Runnable finish = new Runnable() {
@Override
public void run() {
if (!finished)
finish();
}
};

final Runnable timerUpdate = new Runnable() {
@Override
public void run() {
long remaining = finishTime - System.currentTimeMillis();
timer.setText(String.valueOf(remaining / 1000));
if (!finished)
handler.postDelayed(this, 500);
}
};
timerUpdate.run();

handler.postDelayed(finish, DatabaseProvider.AUTHZ_TIMEOUT);

uid = getIntent().getIntExtra(DatabaseProvider.EXTRA_UID, 0);
token = getIntent().getStringExtra(DatabaseProvider.EXTRA_TOKEN);
final AuthzDatabaseHelper dbHelper = new AuthzDatabaseHelper(this);
authz = dbHelper.getByUid(uid);

authz.lastAuthz = System.currentTimeMillis();
// update signatures field
authz.checkSignatures(this);
View.OnClickListener l = new View.OnClickListener() {
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.always:
authz.remember = true;
case R.id.allow:
authz.authz = true;
break;
case R.id.deny:
authz.remember = false;
authz.authz = false;
break;
}
dbHelper.save(authz);
broadcastResult();
handler.removeCallbacks(finish);
handler.removeCallbacks(timerUpdate);
finish();
}
};

findViewById(R.id.allow).setOnClickListener(l);
findViewById(R.id.deny).setOnClickListener(l);
findViewById(R.id.always).setOnClickListener(l);
((TextView)findViewById(R.id.applications)).setText(authz.appNames);
}

private void broadcastResult() {
Intent i = new Intent(authz.authz ?
DatabaseProvider.ACTION_AUTHZ_SUCCESS :
DatabaseProvider.ACTION_AUTHZ_FAIL);
i.putExtra(DatabaseProvider.EXTRA_TOKEN, token);
sendBroadcast(i);
}

@Override
protected void onPause() {
super.onPause();
finished = true;
if (!broadcastSent) { // outside click results in a denial
broadcastResult();
}
}
}
Loading