]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | /* | |
4 | * Ceph - scalable distributed file system | |
5 | * | |
6 | * Copyright (C) 2004-2009 Sage Weil <sage@newdream.net> | |
7 | * | |
8 | * This is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Lesser General Public | |
10 | * License version 2.1, as published by the Free Software | |
11 | * Foundation. See file COPYING. | |
12 | * | |
13 | */ | |
14 | ||
15 | #include "common/ConfUtils.h" | |
16 | #include "common/ceph_argparse.h" | |
17 | #include "common/config.h" | |
18 | #include "global/global_context.h" | |
19 | #include "global/global_init.h" | |
20 | ||
21 | #include "auth/Crypto.h" | |
22 | #include "auth/Auth.h" | |
23 | #include "auth/KeyRing.h" | |
24 | ||
25 | void usage() | |
26 | { | |
27 | cout << "usage: ceph-authtool keyringfile [OPTIONS]...\n" | |
28 | << "where the options are:\n" | |
29 | << " -l, --list will list all keys and capabilities present in\n" | |
30 | << " the keyring\n" | |
31 | << " -p, --print-key will print an encoded key for the specified\n" | |
32 | << " entityname. This is suitable for the\n" | |
33 | << " 'mount -o secret=..' argument\n" | |
34 | << " -C, --create-keyring will create a new keyring, overwriting any\n" | |
35 | << " existing keyringfile\n" | |
36 | << " -g, --gen-key will generate a new secret key for the\n" | |
37 | << " specified entityname\n" | |
38 | << " --gen-print-key will generate a new secret key without set it\n" | |
39 | << " to the keyringfile, prints the secret to stdout\n" | |
40 | << " --import-keyring FILE will import the content of a given keyring\n" | |
41 | << " into the keyringfile\n" | |
42 | << " -n NAME, --name NAME specify entityname to operate on\n" | |
43 | << " -u AUID, --set-uid AUID sets the auid (authenticated user id) for the\n" | |
44 | << " specified entityname\n" | |
45 | << " -a BASE64, --add-key BASE64 will add an encoded key to the keyring\n" | |
46 | << " --cap SUBSYSTEM CAPABILITY will set the capability for given subsystem\n" | |
47 | << " --caps CAPSFILE will set all of capabilities associated with a\n" | |
94b18763 FG |
48 | << " given key, for all subsystems\n" |
49 | << " --mode MODE will set the desired file mode to the keyring\n" | |
50 | << " e.g: '0644', defaults to '0600'" | |
7c673cae FG |
51 | << std::endl; |
52 | exit(1); | |
53 | } | |
54 | ||
55 | int main(int argc, const char **argv) | |
56 | { | |
57 | vector<const char*> args; | |
58 | argv_to_vec(argc, argv, args); | |
59 | env_to_vec(args); | |
60 | ||
61 | std::string add_key; | |
62 | std::string caps_fn; | |
63 | std::string import_keyring; | |
64 | uint64_t auid = CEPH_AUTH_UID_DEFAULT; | |
65 | map<string,bufferlist> caps; | |
66 | std::string fn; | |
67 | ||
68 | auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT, | |
69 | CODE_ENVIRONMENT_UTILITY, | |
70 | CINIT_FLAG_NO_DEFAULT_CONFIG_FILE); | |
71 | ||
72 | bool gen_key = false; | |
73 | bool gen_print_key = false; | |
74 | bool list = false; | |
75 | bool print_key = false; | |
76 | bool create_keyring = false; | |
77 | bool set_auid = false; | |
94b18763 | 78 | int mode = 0600; // keyring file mode |
7c673cae FG |
79 | std::vector<const char*>::iterator i; |
80 | ||
81 | /* Handle options unique to ceph-authtool | |
82 | * -n NAME, --name NAME is handled by global_init | |
83 | * */ | |
84 | for (i = args.begin(); i != args.end(); ) { | |
85 | std::string val; | |
86 | if (ceph_argparse_double_dash(args, i)) { | |
87 | break; | |
88 | } else if (ceph_argparse_flag(args, i, "-g", "--gen-key", (char*)NULL)) { | |
89 | gen_key = true; | |
90 | } else if (ceph_argparse_flag(args, i, "--gen-print-key", (char*)NULL)) { | |
91 | gen_print_key = true; | |
92 | } else if (ceph_argparse_witharg(args, i, &val, "-a", "--add-key", (char*)NULL)) { | |
93 | if (val.empty()) { | |
94 | cerr << "Option --add-key requires an argument" << std::endl; | |
95 | exit(1); | |
96 | } | |
97 | add_key = val; | |
98 | } else if (ceph_argparse_flag(args, i, "-l", "--list", (char*)NULL)) { | |
99 | list = true; | |
100 | } else if (ceph_argparse_witharg(args, i, &val, "--caps", (char*)NULL)) { | |
101 | caps_fn = val; | |
102 | } else if (ceph_argparse_witharg(args, i, &val, "--cap", (char*)NULL)) { | |
103 | std::string my_key = val; | |
104 | if (i == args.end()) { | |
105 | cerr << "must give two arguments to --cap: key and val." << std::endl; | |
106 | exit(1); | |
107 | } | |
108 | std::string my_val = *i; | |
109 | ++i; | |
110 | ::encode(my_val, caps[my_key]); | |
111 | } else if (ceph_argparse_flag(args, i, "-p", "--print-key", (char*)NULL)) { | |
112 | print_key = true; | |
113 | } else if (ceph_argparse_flag(args, i, "-C", "--create-keyring", (char*)NULL)) { | |
114 | create_keyring = true; | |
115 | } else if (ceph_argparse_witharg(args, i, &val, "--import-keyring", (char*)NULL)) { | |
116 | import_keyring = val; | |
117 | } else if (ceph_argparse_witharg(args, i, &val, "-u", "--set-uid", (char*)NULL)) { | |
118 | std::string err; | |
119 | auid = strict_strtoll(val.c_str(), 10, &err); | |
120 | if (!err.empty()) { | |
121 | cerr << "error parsing UID: " << err << std::endl; | |
122 | exit(1); | |
123 | } | |
124 | set_auid = true; | |
94b18763 FG |
125 | } else if (ceph_argparse_witharg(args, i, &val, "--mode", (char*)NULL)) { |
126 | std::string err; | |
127 | mode = strict_strtoll(val.c_str(), 8, &err); | |
128 | if (!err.empty()) { | |
129 | cerr << "Option --mode requires an argument" << std::endl; | |
130 | exit(1); | |
131 | } | |
7c673cae FG |
132 | } else if (fn.empty()) { |
133 | fn = *i++; | |
134 | } else { | |
135 | cerr << argv[0] << ": unexpected '" << *i << "'" << std::endl; | |
136 | usage(); | |
137 | } | |
138 | } | |
139 | ||
140 | if (fn.empty() && !gen_print_key) { | |
141 | cerr << argv[0] << ": must specify filename" << std::endl; | |
142 | usage(); | |
143 | } | |
144 | if (!(gen_key || | |
145 | gen_print_key || | |
146 | !add_key.empty() || | |
147 | list || | |
148 | !caps_fn.empty() || | |
149 | !caps.empty() || | |
150 | set_auid || | |
151 | print_key || | |
152 | create_keyring || | |
153 | !import_keyring.empty())) { | |
154 | cerr << "no command specified" << std::endl; | |
155 | usage(); | |
156 | } | |
157 | if (gen_key && (!add_key.empty())) { | |
158 | cerr << "can't both gen-key and add-key" << std::endl; | |
159 | usage(); | |
160 | } | |
161 | ||
162 | common_init_finish(g_ceph_context); | |
163 | EntityName ename(g_conf->name); | |
164 | ||
165 | // Enforce the use of gen-key or add-key when creating to avoid ending up | |
166 | // with an "empty" key (key = AAAAAAAAAAAAAAAA) | |
167 | if (create_keyring && !gen_key && add_key.empty() && !caps.empty()) { | |
168 | cerr << "must specify either gen-key or add-key when creating" << std::endl; | |
169 | usage(); | |
170 | } | |
171 | ||
172 | if (gen_print_key) { | |
173 | CryptoKey key; | |
174 | key.create(g_ceph_context, CEPH_CRYPTO_AES); | |
175 | cout << key << std::endl; | |
176 | return 0; | |
177 | } | |
178 | ||
179 | // keyring -------- | |
180 | bool modified = false; | |
181 | KeyRing keyring; | |
182 | ||
183 | bufferlist bl; | |
184 | int r = 0; | |
185 | if (create_keyring) { | |
186 | cout << "creating " << fn << std::endl; | |
187 | modified = true; | |
188 | } else { | |
189 | std::string err; | |
190 | r = bl.read_file(fn.c_str(), &err); | |
191 | if (r >= 0) { | |
192 | try { | |
193 | bufferlist::iterator iter = bl.begin(); | |
194 | ::decode(keyring, iter); | |
195 | } catch (const buffer::error &err) { | |
196 | cerr << "error reading file " << fn << std::endl; | |
197 | exit(1); | |
198 | } | |
199 | } else { | |
200 | cerr << "can't open " << fn << ": " << err << std::endl; | |
201 | exit(1); | |
202 | } | |
203 | } | |
204 | ||
205 | // Validate that "name" actually has an existing key in this keyring if we | |
206 | // have not given gen-key or add-key options | |
207 | if (!gen_key && add_key.empty() && !caps.empty()) { | |
208 | CryptoKey key; | |
209 | if (!keyring.get_secret(ename, key)) { | |
210 | cerr << "can't find existing key for " << ename | |
211 | << " and neither gen-key nor add-key specified" << std::endl; | |
212 | exit(1); | |
213 | } | |
214 | } | |
215 | ||
216 | // write commands | |
217 | if (!import_keyring.empty()) { | |
218 | KeyRing other; | |
219 | bufferlist obl; | |
220 | std::string err; | |
221 | int r = obl.read_file(import_keyring.c_str(), &err); | |
222 | if (r >= 0) { | |
223 | try { | |
224 | bufferlist::iterator iter = obl.begin(); | |
225 | ::decode(other, iter); | |
226 | } catch (const buffer::error &err) { | |
227 | cerr << "error reading file " << import_keyring << std::endl; | |
228 | exit(1); | |
229 | } | |
230 | ||
231 | cout << "importing contents of " << import_keyring << " into " << fn << std::endl; | |
232 | //other.print(cout); | |
233 | keyring.import(g_ceph_context, other); | |
234 | modified = true; | |
235 | } else { | |
236 | cerr << "can't open " << import_keyring << ": " << err << std::endl; | |
237 | exit(1); | |
238 | } | |
239 | } | |
240 | if (gen_key) { | |
241 | EntityAuth eauth; | |
242 | eauth.key.create(g_ceph_context, CEPH_CRYPTO_AES); | |
243 | keyring.add(ename, eauth); | |
244 | modified = true; | |
245 | } | |
246 | if (!add_key.empty()) { | |
247 | EntityAuth eauth; | |
248 | try { | |
249 | eauth.key.decode_base64(add_key); | |
250 | } catch (const buffer::error &err) { | |
251 | cerr << "can't decode key '" << add_key << "'" << std::endl; | |
252 | exit(1); | |
253 | } | |
254 | keyring.add(ename, eauth); | |
255 | modified = true; | |
256 | cout << "added entity " << ename << " auth " << eauth << std::endl; | |
257 | } | |
258 | if (!caps_fn.empty()) { | |
259 | ConfFile cf; | |
260 | std::deque<std::string> parse_errors; | |
261 | if (cf.parse_file(caps_fn, &parse_errors, &cerr) != 0) { | |
262 | cerr << "could not parse caps file " << caps_fn << std::endl; | |
263 | exit(1); | |
264 | } | |
265 | complain_about_parse_errors(g_ceph_context, &parse_errors); | |
266 | map<string, bufferlist> caps; | |
267 | const char *key_names[] = { "mon", "osd", "mds", "mgr", NULL }; | |
268 | for (int i=0; key_names[i]; i++) { | |
269 | std::string val; | |
270 | if (cf.read("global", key_names[i], val) == 0) { | |
271 | bufferlist bl; | |
272 | ::encode(val, bl); | |
273 | string s(key_names[i]); | |
274 | caps[s] = bl; | |
275 | } | |
276 | } | |
277 | keyring.set_caps(ename, caps); | |
278 | modified = true; | |
279 | } | |
280 | if (!caps.empty()) { | |
281 | keyring.set_caps(ename, caps); | |
282 | modified = true; | |
283 | } | |
284 | if (set_auid) { | |
285 | keyring.set_uid(ename, auid); | |
286 | modified = true; | |
287 | } | |
288 | ||
289 | // read commands | |
290 | if (list) { | |
291 | try { | |
292 | keyring.print(cout); | |
293 | } catch (ceph::buffer::end_of_buffer &eob) { | |
294 | cout << "Exception (end_of_buffer) in print(), exit." << std::endl; | |
295 | exit(1); | |
296 | } | |
297 | } | |
298 | if (print_key) { | |
299 | CryptoKey key; | |
300 | if (keyring.get_secret(ename, key)) { | |
301 | cout << key << std::endl; | |
302 | } else { | |
303 | cerr << "entity " << ename << " not found" << std::endl; | |
304 | exit(1); | |
305 | } | |
306 | } | |
307 | ||
308 | // write result? | |
309 | if (modified) { | |
310 | bufferlist bl; | |
311 | keyring.encode_plaintext(bl); | |
94b18763 | 312 | r = bl.write_file(fn.c_str(), mode); |
7c673cae FG |
313 | if (r < 0) { |
314 | cerr << "could not write " << fn << std::endl; | |
315 | exit(1); | |
316 | } | |
317 | //cout << "wrote " << bl.length() << " bytes to " << fn << std::endl; | |
318 | } | |
319 | return 0; | |
320 | } |