cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

commands-posix-ssh.c (12997B)


      1 /*
      2  * This work is licensed under the terms of the GNU GPL, version 2 or later.
      3  * See the COPYING file in the top-level directory.
      4  */
      5#include "qemu/osdep.h"
      6
      7#include <glib-unix.h>
      8#include <glib/gstdio.h>
      9#include <locale.h>
     10#include <pwd.h>
     11
     12#include "qapi/error.h"
     13#include "qga-qapi-commands.h"
     14
     15#ifdef QGA_BUILD_UNIT_TEST
     16static struct passwd *
     17test_get_passwd_entry(const gchar *user_name, GError **error)
     18{
     19    struct passwd *p;
     20    int ret;
     21
     22    if (!user_name || g_strcmp0(user_name, g_get_user_name())) {
     23        g_set_error(error, G_UNIX_ERROR, 0, "Invalid user name");
     24        return NULL;
     25    }
     26
     27    p = g_new0(struct passwd, 1);
     28    p->pw_dir = (char *)g_get_home_dir();
     29    p->pw_uid = geteuid();
     30    p->pw_gid = getegid();
     31
     32    ret = g_mkdir_with_parents(p->pw_dir, 0700);
     33    g_assert(ret == 0);
     34
     35    return p;
     36}
     37
     38#define g_unix_get_passwd_entry_qemu(username, err) \
     39   test_get_passwd_entry(username, err)
     40#endif
     41
     42static struct passwd *
     43get_passwd_entry(const char *username, Error **errp)
     44{
     45    g_autoptr(GError) err = NULL;
     46    struct passwd *p;
     47
     48    p = g_unix_get_passwd_entry_qemu(username, &err);
     49    if (p == NULL) {
     50        error_setg(errp, "failed to lookup user '%s': %s",
     51                   username, err->message);
     52        return NULL;
     53    }
     54
     55    return p;
     56}
     57
     58static bool
     59mkdir_for_user(const char *path, const struct passwd *p,
     60               mode_t mode, Error **errp)
     61{
     62    if (g_mkdir(path, mode) == -1) {
     63        error_setg(errp, "failed to create directory '%s': %s",
     64                   path, g_strerror(errno));
     65        return false;
     66    }
     67
     68    if (chown(path, p->pw_uid, p->pw_gid) == -1) {
     69        error_setg(errp, "failed to set ownership of directory '%s': %s",
     70                   path, g_strerror(errno));
     71        return false;
     72    }
     73
     74    if (chmod(path, mode) == -1) {
     75        error_setg(errp, "failed to set permissions of directory '%s': %s",
     76                   path, g_strerror(errno));
     77        return false;
     78    }
     79
     80    return true;
     81}
     82
     83static bool
     84check_openssh_pub_key(const char *key, Error **errp)
     85{
     86    /* simple sanity-check, we may want more? */
     87    if (!key || key[0] == '#' || strchr(key, '\n')) {
     88        error_setg(errp, "invalid OpenSSH public key: '%s'", key);
     89        return false;
     90    }
     91
     92    return true;
     93}
     94
     95static bool
     96check_openssh_pub_keys(strList *keys, size_t *nkeys, Error **errp)
     97{
     98    size_t n = 0;
     99    strList *k;
    100
    101    for (k = keys; k != NULL; k = k->next) {
    102        if (!check_openssh_pub_key(k->value, errp)) {
    103            return false;
    104        }
    105        n++;
    106    }
    107
    108    if (nkeys) {
    109        *nkeys = n;
    110    }
    111    return true;
    112}
    113
    114static bool
    115write_authkeys(const char *path, const GStrv keys,
    116               const struct passwd *p, Error **errp)
    117{
    118    g_autofree char *contents = NULL;
    119    g_autoptr(GError) err = NULL;
    120
    121    contents = g_strjoinv("\n", keys);
    122    if (!g_file_set_contents(path, contents, -1, &err)) {
    123        error_setg(errp, "failed to write to '%s': %s", path, err->message);
    124        return false;
    125    }
    126
    127    if (chown(path, p->pw_uid, p->pw_gid) == -1) {
    128        error_setg(errp, "failed to set ownership of directory '%s': %s",
    129                   path, g_strerror(errno));
    130        return false;
    131    }
    132
    133    if (chmod(path, 0600) == -1) {
    134        error_setg(errp, "failed to set permissions of '%s': %s",
    135                   path, g_strerror(errno));
    136        return false;
    137    }
    138
    139    return true;
    140}
    141
    142static GStrv
    143read_authkeys(const char *path, Error **errp)
    144{
    145    g_autoptr(GError) err = NULL;
    146    g_autofree char *contents = NULL;
    147
    148    if (!g_file_get_contents(path, &contents, NULL, &err)) {
    149        error_setg(errp, "failed to read '%s': %s", path, err->message);
    150        return NULL;
    151    }
    152
    153    return g_strsplit(contents, "\n", -1);
    154
    155}
    156
    157void
    158qmp_guest_ssh_add_authorized_keys(const char *username, strList *keys,
    159                                  bool has_reset, bool reset,
    160                                  Error **errp)
    161{
    162    g_autofree struct passwd *p = NULL;
    163    g_autofree char *ssh_path = NULL;
    164    g_autofree char *authkeys_path = NULL;
    165    g_auto(GStrv) authkeys = NULL;
    166    strList *k;
    167    size_t nkeys, nauthkeys;
    168
    169    reset = has_reset && reset;
    170
    171    if (!check_openssh_pub_keys(keys, &nkeys, errp)) {
    172        return;
    173    }
    174
    175    p = get_passwd_entry(username, errp);
    176    if (p == NULL) {
    177        return;
    178    }
    179
    180    ssh_path = g_build_filename(p->pw_dir, ".ssh", NULL);
    181    authkeys_path = g_build_filename(ssh_path, "authorized_keys", NULL);
    182
    183    if (!reset) {
    184        authkeys = read_authkeys(authkeys_path, NULL);
    185    }
    186    if (authkeys == NULL) {
    187        if (!g_file_test(ssh_path, G_FILE_TEST_IS_DIR) &&
    188            !mkdir_for_user(ssh_path, p, 0700, errp)) {
    189            return;
    190        }
    191    }
    192
    193    nauthkeys = authkeys ? g_strv_length(authkeys) : 0;
    194    authkeys = g_realloc_n(authkeys, nauthkeys + nkeys + 1, sizeof(char *));
    195    memset(authkeys + nauthkeys, 0, (nkeys + 1) * sizeof(char *));
    196
    197    for (k = keys; k != NULL; k = k->next) {
    198        if (g_strv_contains((const gchar * const *)authkeys, k->value)) {
    199            continue;
    200        }
    201        authkeys[nauthkeys++] = g_strdup(k->value);
    202    }
    203
    204    write_authkeys(authkeys_path, authkeys, p, errp);
    205}
    206
    207void
    208qmp_guest_ssh_remove_authorized_keys(const char *username, strList *keys,
    209                                     Error **errp)
    210{
    211    g_autofree struct passwd *p = NULL;
    212    g_autofree char *authkeys_path = NULL;
    213    g_autofree GStrv new_keys = NULL; /* do not own the strings */
    214    g_auto(GStrv) authkeys = NULL;
    215    GStrv a;
    216    size_t nkeys = 0;
    217
    218    if (!check_openssh_pub_keys(keys, NULL, errp)) {
    219        return;
    220    }
    221
    222    p = get_passwd_entry(username, errp);
    223    if (p == NULL) {
    224        return;
    225    }
    226
    227    authkeys_path = g_build_filename(p->pw_dir, ".ssh",
    228                                     "authorized_keys", NULL);
    229    if (!g_file_test(authkeys_path, G_FILE_TEST_EXISTS)) {
    230        return;
    231    }
    232    authkeys = read_authkeys(authkeys_path, errp);
    233    if (authkeys == NULL) {
    234        return;
    235    }
    236
    237    new_keys = g_new0(char *, g_strv_length(authkeys) + 1);
    238    for (a = authkeys; *a != NULL; a++) {
    239        strList *k;
    240
    241        for (k = keys; k != NULL; k = k->next) {
    242            if (g_str_equal(k->value, *a)) {
    243                break;
    244            }
    245        }
    246        if (k != NULL) {
    247            continue;
    248        }
    249
    250        new_keys[nkeys++] = *a;
    251    }
    252
    253    write_authkeys(authkeys_path, new_keys, p, errp);
    254}
    255
    256GuestAuthorizedKeys *
    257qmp_guest_ssh_get_authorized_keys(const char *username, Error **errp)
    258{
    259    g_autofree struct passwd *p = NULL;
    260    g_autofree char *authkeys_path = NULL;
    261    g_auto(GStrv) authkeys = NULL;
    262    g_autoptr(GuestAuthorizedKeys) ret = NULL;
    263    int i;
    264
    265    p = get_passwd_entry(username, errp);
    266    if (p == NULL) {
    267        return NULL;
    268    }
    269
    270    authkeys_path = g_build_filename(p->pw_dir, ".ssh",
    271                                     "authorized_keys", NULL);
    272    authkeys = read_authkeys(authkeys_path, errp);
    273    if (authkeys == NULL) {
    274        return NULL;
    275    }
    276
    277    ret = g_new0(GuestAuthorizedKeys, 1);
    278    for (i = 0; authkeys[i] != NULL; i++) {
    279        g_strstrip(authkeys[i]);
    280        if (!authkeys[i][0] || authkeys[i][0] == '#') {
    281            continue;
    282        }
    283
    284        QAPI_LIST_PREPEND(ret->keys, g_strdup(authkeys[i]));
    285    }
    286
    287    return g_steal_pointer(&ret);
    288}
    289
    290#ifdef QGA_BUILD_UNIT_TEST
    291#if GLIB_CHECK_VERSION(2, 60, 0)
    292static const strList test_key2 = {
    293    .value = (char *)"algo key2 comments"
    294};
    295
    296static const strList test_key1_2 = {
    297    .value = (char *)"algo key1 comments",
    298    .next = (strList *)&test_key2,
    299};
    300
    301static char *
    302test_get_authorized_keys_path(void)
    303{
    304    return g_build_filename(g_get_home_dir(), ".ssh", "authorized_keys", NULL);
    305}
    306
    307static void
    308test_authorized_keys_set(const char *contents)
    309{
    310    g_autoptr(GError) err = NULL;
    311    g_autofree char *path = NULL;
    312    int ret;
    313
    314    path = g_build_filename(g_get_home_dir(), ".ssh", NULL);
    315    ret = g_mkdir_with_parents(path, 0700);
    316    g_assert(ret == 0);
    317    g_free(path);
    318
    319    path = test_get_authorized_keys_path();
    320    g_file_set_contents(path, contents, -1, &err);
    321    g_assert(err == NULL);
    322}
    323
    324static void
    325test_authorized_keys_equal(const char *expected)
    326{
    327    g_autoptr(GError) err = NULL;
    328    g_autofree char *path = NULL;
    329    g_autofree char *contents = NULL;
    330
    331    path = test_get_authorized_keys_path();
    332    g_file_get_contents(path, &contents, NULL, &err);
    333    g_assert(err == NULL);
    334
    335    g_assert(g_strcmp0(contents, expected) == 0);
    336}
    337
    338static void
    339test_invalid_user(void)
    340{
    341    Error *err = NULL;
    342
    343    qmp_guest_ssh_add_authorized_keys("", NULL, FALSE, FALSE, &err);
    344    error_free_or_abort(&err);
    345
    346    qmp_guest_ssh_remove_authorized_keys("", NULL, &err);
    347    error_free_or_abort(&err);
    348}
    349
    350static void
    351test_invalid_key(void)
    352{
    353    strList key = {
    354        .value = (char *)"not a valid\nkey"
    355    };
    356    Error *err = NULL;
    357
    358    qmp_guest_ssh_add_authorized_keys(g_get_user_name(), &key,
    359                                      FALSE, FALSE, &err);
    360    error_free_or_abort(&err);
    361
    362    qmp_guest_ssh_remove_authorized_keys(g_get_user_name(), &key, &err);
    363    error_free_or_abort(&err);
    364}
    365
    366static void
    367test_add_keys(void)
    368{
    369    Error *err = NULL;
    370
    371    qmp_guest_ssh_add_authorized_keys(g_get_user_name(),
    372                                      (strList *)&test_key2,
    373                                      FALSE, FALSE,
    374                                      &err);
    375    g_assert(err == NULL);
    376
    377    test_authorized_keys_equal("algo key2 comments");
    378
    379    qmp_guest_ssh_add_authorized_keys(g_get_user_name(),
    380                                      (strList *)&test_key1_2,
    381                                      FALSE, FALSE,
    382                                      &err);
    383    g_assert(err == NULL);
    384
    385    /*  key2 came first, and should'nt be duplicated */
    386    test_authorized_keys_equal("algo key2 comments\n"
    387                               "algo key1 comments");
    388}
    389
    390static void
    391test_add_reset_keys(void)
    392{
    393    Error *err = NULL;
    394
    395    qmp_guest_ssh_add_authorized_keys(g_get_user_name(),
    396                                      (strList *)&test_key1_2,
    397                                      FALSE, FALSE,
    398                                      &err);
    399    g_assert(err == NULL);
    400
    401    /* reset with key2 only */
    402    test_authorized_keys_equal("algo key1 comments\n"
    403                               "algo key2 comments");
    404
    405    qmp_guest_ssh_add_authorized_keys(g_get_user_name(),
    406                                      (strList *)&test_key2,
    407                                      TRUE, TRUE,
    408                                      &err);
    409    g_assert(err == NULL);
    410
    411    test_authorized_keys_equal("algo key2 comments");
    412
    413    /* empty should clear file */
    414    qmp_guest_ssh_add_authorized_keys(g_get_user_name(),
    415                                      (strList *)NULL,
    416                                      TRUE, TRUE,
    417                                      &err);
    418    g_assert(err == NULL);
    419
    420    test_authorized_keys_equal("");
    421}
    422
    423static void
    424test_remove_keys(void)
    425{
    426    Error *err = NULL;
    427    static const char *authkeys =
    428        "algo key1 comments\n"
    429        /* originally duplicated */
    430        "algo key1 comments\n"
    431        "# a commented line\n"
    432        "algo some-key another\n";
    433
    434    test_authorized_keys_set(authkeys);
    435    qmp_guest_ssh_remove_authorized_keys(g_get_user_name(),
    436                                         (strList *)&test_key2, &err);
    437    g_assert(err == NULL);
    438    test_authorized_keys_equal(authkeys);
    439
    440    qmp_guest_ssh_remove_authorized_keys(g_get_user_name(),
    441                                         (strList *)&test_key1_2, &err);
    442    g_assert(err == NULL);
    443    test_authorized_keys_equal("# a commented line\n"
    444                               "algo some-key another\n");
    445}
    446
    447static void
    448test_get_keys(void)
    449{
    450    Error *err = NULL;
    451    static const char *authkeys =
    452        "algo key1 comments\n"
    453        "# a commented line\n"
    454        "algo some-key another\n";
    455    g_autoptr(GuestAuthorizedKeys) ret = NULL;
    456    strList *k;
    457    size_t len = 0;
    458
    459    test_authorized_keys_set(authkeys);
    460
    461    ret = qmp_guest_ssh_get_authorized_keys(g_get_user_name(), &err);
    462    g_assert(err == NULL);
    463
    464    for (len = 0, k = ret->keys; k != NULL; k = k->next) {
    465        g_assert(g_str_has_prefix(k->value, "algo "));
    466        len++;
    467    }
    468
    469    g_assert(len == 2);
    470}
    471
    472int main(int argc, char *argv[])
    473{
    474    setlocale(LC_ALL, "");
    475
    476    g_test_init(&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
    477
    478    g_test_add_func("/qga/ssh/invalid_user", test_invalid_user);
    479    g_test_add_func("/qga/ssh/invalid_key", test_invalid_key);
    480    g_test_add_func("/qga/ssh/add_keys", test_add_keys);
    481    g_test_add_func("/qga/ssh/add_reset_keys", test_add_reset_keys);
    482    g_test_add_func("/qga/ssh/remove_keys", test_remove_keys);
    483    g_test_add_func("/qga/ssh/get_keys", test_get_keys);
    484
    485    return g_test_run();
    486}
    487#else
    488int main(int argc, char *argv[])
    489{
    490    g_test_message("test skipped, needs glib >= 2.60");
    491    return 0;
    492}
    493#endif /* GLIB_2_60 */
    494#endif /* BUILD_UNIT_TEST */