File: ssh_key.ml

package info (click to toggle)
guestfs-tools 1.48.2-1%2Bdeb12u1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 60,976 kB
  • sloc: ansic: 14,910; ml: 14,596; sh: 7,403; makefile: 3,639; perl: 1,496; xml: 1,472; lex: 135; yacc: 128; python: 80
file content (137 lines) | stat: -rw-r--r-- 3,998 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
(* virt-customize
 * Copyright (C) 2014 Red Hat Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *)

open Printf
open Sys
open Unix

open Std_utils
open Tools_utils
open Common_gettext.Gettext

module G = Guestfs

type ssh_key_selector =
| SystemKey
| KeyFile of string
| KeyString of string

let rec parse_selector arg =
  parse_selector_list arg (String.nsplit ":" arg)

and parse_selector_list orig_arg = function
  | [] | [ "" ] ->
    SystemKey
  | [ "file"; f ] ->
    KeyFile f
  | [ "string"; s ] ->
    KeyString s
  | _ ->
    error (f_"invalid ssh-inject selector ā€˜%s’; see the man page") orig_arg

(* Find the local [on the host] user's SSH public key.  See
 * ssh-copy-id(1) default_ID_file for rationale.
 *)
let pubkey_re = PCRE.compile "^id.*\\.pub$"
let pubkey_ignore_re = PCRE.compile ".*-cert\\.pub$"

let local_user_ssh_pubkey () =
  let home_dir =
    try getenv "HOME"
    with Not_found ->
      error (f_"ssh-inject: $HOME environment variable is not set") in
  let ssh_dir = home_dir // ".ssh" in
  let files = Sys.readdir ssh_dir in
  let files = Array.to_list files in
  let files = List.filter (
    fun file ->
      PCRE.matches pubkey_re file && not (PCRE.matches pubkey_ignore_re file)
  ) files in
  if files = [] then
    error (f_"ssh-inject: no public key file found in %s") ssh_dir;

  (* Newest file. *)
  let files = List.map (
    fun file ->
      let file = ssh_dir // file in
      let stat = stat file in
      (file, stat.st_mtime)
  ) files in
  let files = List.sort (fun (_,m1) (_,m2) -> compare m2 m1) files in

  fst (List.hd files)

let read_key file =
  (* Read and return the public key. *)
  let key = read_whole_file file in
  if key = "" then
    error (f_"ssh-inject: public key file (%s) is empty") file;
  key

let key_string_from_selector = function
  | SystemKey ->
    read_key (local_user_ssh_pubkey ())
  | KeyFile f ->
    read_key f
  | KeyString s ->
    if String.length s < 1 then
      error (f_"ssh-inject: key is an empty string");
    s

(* Inject SSH key, where possible. *)
let do_ssh_inject_unix (g : Guestfs.guestfs) user selector =
  let key = key_string_from_selector selector in
  assert (String.length key > 0);

  (* If the key doesn't have \n at the end, add it. *)
  let len = String.length key in
  let key = if key.[len-1] = '\n' then key else key ^ "\n" in

  (* Get user's home directory. *)
  g#aug_init "/" 0;
  let read_user_detail what =
    try
      let expr = sprintf "/files/etc/passwd/%s/%s" user what in
      g#aug_get expr
    with G.Error _ ->
      error (f_"ssh-inject: the user %s does not exist on the guest")
        user
  in
  let home_dir = read_user_detail "home" in
  let uid = int_of_string (read_user_detail "uid") in
  let gid = int_of_string (read_user_detail "gid") in
  g#aug_close ();

  (* Create ~user/.ssh if it doesn't exist. *)
  let ssh_dir = sprintf "%s/.ssh" home_dir in
  if not (g#exists ssh_dir) then (
    g#mkdir ssh_dir;
    g#chmod 0o700 ssh_dir;
    g#chown uid gid ssh_dir;
  );

  (* Create ~user/.ssh/authorized_keys if it doesn't exist. *)
  let auth_keys = sprintf "%s/authorized_keys" ssh_dir in
  if not (g#exists auth_keys) then (
    g#touch auth_keys;
    g#chmod 0o600 auth_keys;
    g#chown uid gid auth_keys;
  );

  (* Append the key. *)
  g#write_append auth_keys key