普通文本  |  206行  |  6.15 KB

#!/usr/bin/python
#
# Copyright (c) 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import os
from autotest_lib.client.common_lib import error
from autotest_lib.client.bin import test
from autotest_lib.client.bin import utils
from autotest_lib.client.cros import kernel_config

import ctypes
import hashlib
import logging
import binascii


AF_ALG = 38
SOCK_SEQPACKET = 5

class sockaddr_alg(ctypes.Structure):
    """
    A python definition of the same struct from <linux/if_alg.h>

    struct sockaddr_alg {
            __u16   salg_family;
            __u8    salg_type[14];
            __u32   salg_feat;
            __u32   salg_mask;
            __u8    salg_name[64];
    };
    """
    _fields_ = [
        ('salg_family', ctypes.c_uint16),
        ('salg_type', ctypes.c_char * 14),
        ('salg_feat', ctypes.c_uint32),
        ('salg_mask', ctypes.c_uint32),
        ('salg_name', ctypes.c_char * 64),
    ]


    def __init__(self, alg_family, alg_type, alg_name, alg_feat=0, alg_mask=0):
        super(sockaddr_alg, self).__init__(alg_family, alg_type, alg_feat,
                                           alg_mask, alg_name)


class kernel_CryptoAPI(test.test):
    """
    Verify that the crypto user API can't be used to load arbitrary modules.
    Uses the kernel module 'test_module'
    """
    version = 1
    preserve_srcdir = True

    def initialize(self):
        self.job.require_gcc()


    def setup(self):
        os.chdir(self.srcdir)
        utils.make()


    def try_load_mod(self, module):
        """
        Try to load a (non-crypto) module using the crypto UAPI
        @param module: name of the kernel module to try to load
        """
        if utils.module_is_loaded(module):
            utils.unload_module(module)

        path = os.path.join(self.srcdir, 'crypto_load_mod ')
        utils.system(path + module)

        if utils.module_is_loaded(module):
            utils.unload_module(module)
            raise error.TestFail('Able to load module "%s" using crypto UAPI' %
                                 module)


    def do_ifalg_digest(self, name, data, outlen):
        """
        Use ctypes to run a digest through one of the available kernel ifalg
        digest types

        @param name: digest name
        @param data: string data to digest
        @param outlen: length of the digest output (e.g., SHA1 is 160-bit, so
                outlen==20)
        @param return string containing the output digest, or None if
                experiencing an error
        """
        libc = ctypes.CDLL("libc.so.6", use_errno=True)

        # If you don't specify the function parameters this way, ctypes may try
        # to treat pointers as 32-bit (and then later sign-extend them)
        libc.socket.argtypes = [ ctypes.c_int, ctypes.c_int, ctypes.c_int ]
        libc.bind.argtypes = [ ctypes.c_int, ctypes.c_void_p, ctypes.c_int ]
        libc.send.argtypes = [ ctypes.c_int, ctypes.c_void_p, ctypes.c_size_t,
                               ctypes.c_int ]
        libc.recv.argtypes = [ ctypes.c_int, ctypes.c_void_p, ctypes.c_size_t,
                               ctypes.c_int ]

        sock = libc.socket(AF_ALG, SOCK_SEQPACKET, 0)
        if sock == -1:
            libc.perror("socket")
            return None

        alg = sockaddr_alg(AF_ALG, "hash", name)
        if libc.bind(sock, ctypes.addressof(alg), ctypes.sizeof(alg)) == -1:
            libc.perror("bind")
            return None

        fd = libc.accept(sock, None, 0)
        if fd == -1:
            libc.perror("accept")
            return None

        message = ctypes.create_string_buffer(data, len(data))
        if libc.send(fd, ctypes.addressof(message), ctypes.sizeof(message), 0) == -1:
            libc.perror("send")
            return None

        out = (ctypes.c_uint8 * outlen)()
        ret = libc.recv(fd, ctypes.addressof(out), ctypes.sizeof(out), 0)
        if ret == -1:
            libc.perror("recv")
            return None

        h = ctypes.string_at(ctypes.addressof(out), ret)

        libc.close(sock)

        return h


    def test_digest(self, name, lib, data):
        """
        Run a digest through both the kernel UAPI and through hashlib, throwing
        an error if the two don't match

        @param name: name of the digest (according to AF_ALG)
        @param lib: a hashlib digest object
        @param data: data to digest
        """

        logging.info("Testing digest %s", name)

        h1 = self.do_ifalg_digest(name, data, lib.digestsize)
        if h1 is None:
            raise error.TestFail("ifalg digest %s failed", name)

        lib.update(data)
        h2 = lib.digest()

        if h1 != h2:
            logging.error("%s: digests do not match", name)
            logging.error(" hash 1: %s", binascii.hexlify(h1))
            logging.error(" hash 2: %s", binascii.hexlify(h2))
            raise error.TestFail("digest mismatch (%s)" % name)

        logging.debug("hash 1: %s", binascii.hexlify(h1))
        logging.debug("hash 2: %s", binascii.hexlify(h2))


    def test_digests(self, data):
        """
        Test several digests, using both the kernel crypto APIs and python
        hashlib

        @param data: the data to digest
        """

        digests = [
                ( "sha1", hashlib.sha1()),
                ( "md5", hashlib.md5()),
                ( "sha512", hashlib.sha512()),
        ]

        for (name, lib) in digests:
            self.test_digest(name, lib, data)


    def test_is_valid(self):
        """
        Check if this test is worth running, based on whether the kernel
        .config has the right features
        """
        config = kernel_config.KernelConfig()
        config.initialize()
        config.is_enabled('CRYPTO_USER_API_HASH')
        config.is_enabled('CRYPTO_USER_API')
        return len(config.failures()) == 0


    def run_once(self):
        # crypto tests only work with AF_ALG support
        if not self.test_is_valid():
            raise error.TestNAError("Crypto tests only run with AF_ALG support")

        module = "test_module"
        self.try_load_mod(module)

        self.test_digests("This is a not-so-secret message")