Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 10e371f1 authored by Chung-yih Wang's avatar Chung-yih Wang
Browse files

Provide the Keystore feature in the framework.

-- added the keystore library for Java application.
-- changed the marshalling of the keystore function return.
parent a05487dd
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -40,7 +40,7 @@ static int list_files(const char *dir, char reply[REPLY_MAX]) {
    reply[0]=0;
    while ((de = readdir(d))) {
        if (de->d_type != DT_REG) continue;
        strlcat(reply, " ", REPLY_MAX);
        if (reply[0] != 0) strlcat(reply, " ", REPLY_MAX);
        if (strlcat(reply, de->d_name, REPLY_MAX) >= REPLY_MAX) {
            LOGE("reply is too long(too many files under '%s'\n", dir);
            return -1;
+26 −19
Original line number Diff line number Diff line
@@ -113,7 +113,7 @@ static int execute(int s, char cmd[BUFFER_MAX])
    unsigned i;
    unsigned n = 0;
    unsigned short count;
    int ret = -1;
    short ret = -1;

    /* default reply is "" */
    reply[0] = 0;
@@ -139,7 +139,7 @@ static int execute(int s, char cmd[BUFFER_MAX])
                LOGE("%s requires %d arguments (%d given)\n",
                     cmds[i].name, cmds[i].numargs, n);
            } else {
                ret = cmds[i].func(arg + 1, reply);
                ret = (short) cmds[i].func(arg + 1, reply);
            }
            goto done;
        }
@@ -148,24 +148,26 @@ static int execute(int s, char cmd[BUFFER_MAX])

done:
    if (reply[0]) {
        n = snprintf(cmd, BUFFER_MAX, "%d %s", ret, reply);
        strlcpy(cmd, reply, BUFFER_MAX);
        count = strlen(cmd);
    } else {
        n = snprintf(cmd, BUFFER_MAX, "%d", ret);
        count = 0;
    }
    if (n > BUFFER_MAX) n = BUFFER_MAX;
    count = n;

    if (writex(s, &ret, sizeof(ret))) return -1;
    if (ret == 0) {
        if (writex(s, &count, sizeof(count))) return -1;
        if (writex(s, cmd, count)) return -1;
    }

    return 0;
}

int shell_command(const int argc, const char **argv)
{
    int fd, i, r;
    int fd, i;
    short ret;
    unsigned short count;
    char cmd[BUFFER_MAX]="";
    char buf[BUFFER_MAX]="";

    fd = socket_local_client(SOCKET_PATH,
                             ANDROID_SOCKET_NAMESPACE_RESERVED,
@@ -175,19 +177,24 @@ int shell_command(const int argc, const char **argv)
        exit(1);
    }
    for(i = 0; i < argc; i++) {
        if (i > 0) strlcat(cmd, " ", BUFFER_MAX);
        if(strlcat(cmd, argv[i], BUFFER_MAX) >= BUFFER_MAX) {
        if (i > 0) strlcat(buf, " ", BUFFER_MAX);
        if(strlcat(buf, argv[i], BUFFER_MAX) >= BUFFER_MAX) {
            fprintf(stderr, "Arguments are too long\n");
            exit(1);
        }
    }
    count = strlen(cmd);
    count = strlen(buf);
    if (writex(fd, &count, sizeof(count))) return -1;
    if (writex(fd, cmd, strlen(cmd))) return -1;
    if (writex(fd, buf, strlen(buf))) return -1;
    if (readx(fd, &ret, sizeof(ret))) return -1;
    if (ret == 0) {
        if (readx(fd, &count, sizeof(count))) return -1;
    if (readx(fd, cmd, count)) return -1;
    cmd[count]=0;
    fprintf(stdout, "%s\n", cmd);
        if (readx(fd, buf, count)) return -1;
        buf[count]=0;
        fprintf(stdout, "%s\n", buf);
    } else {
        fprintf(stderr, "Failed, please check log!\n");
    }
    return 0;
}

+103 −0
Original line number Diff line number Diff line
/*
 * 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.
 */

package android.security;

/**
 * The Keystore class provides the functions to list the certs/keys in keystore.
 * {@hide}
 */
public abstract class Keystore {
    private static final String TAG = "Keystore";
    private static final String[] NOTFOUND = new String[0];

    /**
     */
    public static Keystore getInstance() {
        return new FileKeystore();
    }

    /**
     */
    public abstract String getUserkey(String key);

    /**
     */
    public abstract String getCertificate(String key);

    /**
     */
    public abstract String[] getAllCertificateKeys();

    /**
     */
    public abstract String[] getAllUserkeyKeys();

    private static class FileKeystore extends Keystore {
        private static final String SERVICE_NAME = "keystore";
        private static final String LIST_CERTIFICATES = "listcerts";
        private static final String LIST_USERKEYS = "listuserkeys";
        private static final String PATH = "/data/misc/keystore/";
        private static final String USERKEY_PATH = PATH + "userkeys/";
        private static final String CERT_PATH = PATH + "certs/";
        private static final ServiceCommand mServiceCommand =
                new ServiceCommand(SERVICE_NAME);

        @Override
        public String getUserkey(String key) {
            return USERKEY_PATH + key;
        }

        @Override
        public String getCertificate(String key) {
            return CERT_PATH + key;
        }

        /**
         * Returns the array of the certificate names in keystore if successful.
         * Or return an empty array if error.
         *
         * @return array of the certificates
         */
        @Override
        public String[] getAllCertificateKeys() {
            try {
                String result = mServiceCommand.execute(LIST_CERTIFICATES);
                if (result != null) return result.split("\\s+");
                return NOTFOUND;
            } catch (NumberFormatException ex) {
                return NOTFOUND;
            }
        }

        /**
         * Returns the array of the names of private keys in keystore if successful.
         * Or return an empty array if errors.
         *
         * @return array of the user keys
         */
        @Override
        public String[] getAllUserkeyKeys() {
            try {
                String result = mServiceCommand.execute(LIST_USERKEYS);
                if (result != null) return result.split("\\s+");
                return NOTFOUND;
            } catch (NumberFormatException ex) {
                return NOTFOUND;
            }
        }
    }
}
+178 −0
Original line number Diff line number Diff line
/*
 * 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.
 */

package android.security;

import android.net.LocalSocketAddress;
import android.net.LocalSocket;
import android.util.Config;
import android.util.Log;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

/*
 * ServiceCommand is used to connect to a service throught the local socket,
 * and send out the command, return the result to the caller.
 * {@hide}
 */
public class ServiceCommand {
    public static final String SUCCESS = "0";
    public static final String FAILED = "-1";

    private String mServiceName;
    private String mTag;
    private InputStream mIn;
    private OutputStream mOut;
    private LocalSocket mSocket;
    private static final int BUFFER_LENGTH = 1024;

    private byte buf[] = new byte[BUFFER_LENGTH];
    private int buflen = 0;

    private boolean connect() {
        if (mSocket != null) {
            return true;
        }
        Log.i(mTag, "connecting...");
        try {
            mSocket = new LocalSocket();

            LocalSocketAddress address = new LocalSocketAddress(
                    mServiceName, LocalSocketAddress.Namespace.RESERVED);

            mSocket.connect(address);

            mIn = mSocket.getInputStream();
            mOut = mSocket.getOutputStream();
        } catch (IOException ex) {
            disconnect();
            return false;
        }
        return true;
    }

    private void disconnect() {
        Log.i(mTag,"disconnecting...");
        try {
            if (mSocket != null) mSocket.close();
        } catch (IOException ex) { }
        try {
            if (mIn != null) mIn.close();
        } catch (IOException ex) { }
        try {
            if (mOut != null) mOut.close();
        } catch (IOException ex) { }
        mSocket = null;
        mIn = null;
        mOut = null;
    }

    private boolean readBytes(byte buffer[], int len) {
        int off = 0, count;
        if (len < 0) return false;
        while (off != len) {
            try {
                count = mIn.read(buffer, off, len - off);
                if (count <= 0) {
                    Log.e(mTag, "read error " + count);
                    break;
                }
                off += count;
            } catch (IOException ex) {
                Log.e(mTag,"read exception");
                break;
            }
        }
        if (off == len) return true;
        disconnect();
        return false;
    }

    private boolean readReply() {
        int len, ret;
        buflen = 0;

        if (!readBytes(buf, 2)) return false;
        ret = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
        if (ret != 0) return false;

        if (!readBytes(buf, 2)) return false;
        len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
        if (len > BUFFER_LENGTH) {
            Log.e(mTag,"invalid reply length (" + len + ")");
            disconnect();
            return false;
        }
        if (!readBytes(buf, len)) return false;
        buflen = len;
        return true;
    }

    private boolean writeCommand(String _cmd) {
        byte[] cmd = _cmd.getBytes();
        int len = cmd.length;
        if ((len < 1) || (len > BUFFER_LENGTH)) return false;
        buf[0] = (byte) (len & 0xff);
        buf[1] = (byte) ((len >> 8) & 0xff);
        try {
            mOut.write(buf, 0, 2);
            mOut.write(cmd, 0, len);
        } catch (IOException ex) {
            Log.e(mTag,"write error");
            disconnect();
            return false;
        }
        return true;
    }

    private String executeCommand(String cmd) {
        if (!writeCommand(cmd)) {
            /* If service died and restarted in the background
             * (unlikely but possible) we'll fail on the next
             * write (this one).  Try to reconnect and write
             * the command one more time before giving up.
             */
            Log.e(mTag, "write command failed? reconnect!");
            if (!connect() || !writeCommand(cmd)) {
                return null;
            }
        }
        if (readReply()) {
            return new String(buf, 0, buflen);
        } else {
            return null;
        }
    }

    public synchronized String execute(String cmd) {
      String result;
      if (!connect()) {
          Log.e(mTag, "connection failed");
          return null;
      }
      result = executeCommand(cmd);
      disconnect();
      return result;
    }

    public ServiceCommand(String service) {
        mServiceName = service;
        mTag = service;
    }
}