1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 *
3 * Copyright (C) 2015 Peter Hatina <phatina@redhat.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #include "config.h"
21
22 #include <string.h>
23 #include <libiscsi.h>
24
25 #include <src/udisksdaemon.h>
26 #include <src/udisksmodulemanager.h>
27 #include <src/udiskslogging.h>
28
29 #include "udisksiscsitypes.h"
30 #include "udisksiscsiutil.h"
31
32 #ifndef HAVE_LIBISCSI_ERR
33 /* XXX: We need to expose these in libiscsi.h. If we can't make it appear in
34 * the libiscsi.h, then we need to keep this in sync with iscsi_err.h.
35 */
36 enum {
37 ISCSI_SUCCESS = 0,
38 /* Generic error */
39 ISCSI_ERR = 1,
40 /* session could not be found */
41 ISCSI_ERR_SESS_NOT_FOUND = 2,
42 /* Could not allocate resource for operation */
43 ISCSI_ERR_NOMEM = 3,
44 /* Transport error caused operation to fail */
45 ISCSI_ERR_TRANS = 4,
46 /* Generic login failure */
47 ISCSI_ERR_LOGIN = 5,
48 /* Error accessing/managing iSCSI DB */
49 ISCSI_ERR_IDBM = 6,
50 /* Invalid argument */
51 ISCSI_ERR_INVAL = 7,
52 /* Connection timer exired while trying to connect */
53 ISCSI_ERR_TRANS_TIMEOUT = 8,
54 /* Generic internal iscsid failure */
55 ISCSI_ERR_INTERNAL = 9,
56 /* Logout failed */
57 ISCSI_ERR_LOGOUT = 10,
58 /* iSCSI PDU timedout */
59 ISCSI_ERR_PDU_TIMEOUT = 11,
60 /* iSCSI transport module not loaded in kernel or iscsid */
61 ISCSI_ERR_TRANS_NOT_FOUND = 12,
62 /* Permission denied */
63 ISCSI_ERR_ACCESS = 13,
64 /* Transport module did not support operation */
65 ISCSI_ERR_TRANS_CAPS = 14,
66 /* Session is logged in */
67 ISCSI_ERR_SESS_EXISTS = 15,
68 /* Invalid IPC MGMT request */
69 ISCSI_ERR_INVALID_MGMT_REQ = 16,
70 /* iSNS service is not supported */
71 ISCSI_ERR_ISNS_UNAVAILABLE = 17,
72 /* A read/write to iscsid failed */
73 ISCSI_ERR_ISCSID_COMM_ERR = 18,
74 /* Fatal login error */
75 ISCSI_ERR_FATAL_LOGIN = 19,
76 /* Could ont connect to iscsid */
77 ISCSI_ERR_ISCSID_NOTCONN = 20,
78 /* No records/targets/sessions/portals found to execute operation on */
79 ISCSI_ERR_NO_OBJS_FOUND = 21,
80 /* Could not lookup object in sysfs */
81 ISCSI_ERR_SYSFS_LOOKUP = 22,
82 /* Could not lookup host */
83 ISCSI_ERR_HOST_NOT_FOUND = 23,
84 /* Login failed due to authorization failure */
85 ISCSI_ERR_LOGIN_AUTH_FAILED = 24,
86 /* iSNS query failure */
87 ISCSI_ERR_ISNS_QUERY = 25,
88 /* iSNS registration/deregistration failed */
89 ISCSI_ERR_ISNS_REG_FAILED = 26,
90 /* operation not supported */
91 ISCSI_ERR_OP_NOT_SUPP = 27,
92 /* device or resource in use */
93 ISCSI_ERR_BUSY = 28,
94 /* Operation failed, but retrying layer may succeed */
95 ISCSI_ERR_AGAIN = 29,
96 /* unknown discovery type */
97 ISCSI_ERR_UNKNOWN_DISCOVERY_TYPE = 30,
98
99 /* Always last. Indicates end of error code space */
100 ISCSI_MAX_ERR_VAL,
101 };
102 #endif /* HAVE_LIBISCSI_ERR */
103
104
105 const gchar *iscsi_nodes_fmt = "a(sisis)";
106 const gchar *iscsi_node_fmt = "(sisis)";
107
108 static void
iscsi_make_auth_info(struct libiscsi_auth_info * auth_info,const gchar * username,const gchar * password,const gchar * reverse_username,const gchar * reverse_password)109 iscsi_make_auth_info (struct libiscsi_auth_info *auth_info,
110 const gchar *username,
111 const gchar *password,
112 const gchar *reverse_username,
113 const gchar *reverse_password)
114 {
115 g_return_if_fail (auth_info);
116
117 memset (auth_info, 0, sizeof (struct libiscsi_auth_info));
118 auth_info->method = libiscsi_auth_none;
119
120 /* CHAP username + password */
121 if (username && *username)
122 {
123 auth_info->method = libiscsi_auth_chap;
124 strncpy (auth_info->chap.username, username, LIBISCSI_VALUE_MAXLEN - 1);
125 if (password && *password)
126 strncpy (auth_info->chap.password, password, LIBISCSI_VALUE_MAXLEN - 1);
127 }
128
129 /* CHAP reverse username + reverse password */
130 if (reverse_username && *reverse_username)
131 {
132 auth_info->method = libiscsi_auth_chap;
133 strncpy (auth_info->chap.reverse_username, reverse_username, LIBISCSI_VALUE_MAXLEN - 1);
134 if (reverse_password && *reverse_password)
135 strncpy (auth_info->chap.reverse_password, reverse_password, LIBISCSI_VALUE_MAXLEN - 1);
136 }
137 }
138
139 static void
iscsi_make_node(struct libiscsi_node * node,const gchar * name,const gint tpgt,const gchar * address,const gint port,const gchar * iface)140 iscsi_make_node (struct libiscsi_node *node,
141 const gchar *name,
142 const gint tpgt,
143 const gchar *address,
144 const gint port,
145 const gchar *iface)
146 {
147 g_return_if_fail (node);
148
149 memset (node, 0, sizeof (struct libiscsi_node));
150
151 /* Fill libiscsi parameters. */
152 strncpy (node->name, name, LIBISCSI_VALUE_MAXLEN - 1);
153 strncpy (node->address, address, NI_MAXHOST - 1);
154 strncpy (node->iface, iface, LIBISCSI_VALUE_MAXLEN - 1);
155 node->tpgt = tpgt;
156 node->port = port;
157 }
158
159 static gint
iscsi_perform_login_action(UDisksLinuxModuleISCSI * module,libiscsi_login_action action,struct libiscsi_node * node,struct libiscsi_auth_info * auth_info,gchar ** errorstr)160 iscsi_perform_login_action (UDisksLinuxModuleISCSI *module,
161 libiscsi_login_action action,
162 struct libiscsi_node *node,
163 struct libiscsi_auth_info *auth_info,
164 gchar **errorstr)
165 {
166 struct libiscsi_context *ctx;
167 gint err;
168
169 g_return_val_if_fail (UDISKS_IS_LINUX_MODULE_ISCSI (module), 1);
170
171 /* Get a libiscsi context. */
172 ctx = udisks_linux_module_iscsi_get_libiscsi_context (module);
173
174 if (action == ACTION_LOGIN &&
175 auth_info && auth_info->method == libiscsi_auth_chap)
176 {
177 libiscsi_node_set_auth (ctx, node, auth_info);
178 }
179
180 /* Login or Logout */
181 err = action == ACTION_LOGIN ?
182 libiscsi_node_login (ctx, node) :
183 libiscsi_node_logout (ctx, node);
184
185 if (errorstr && err != 0)
186 *errorstr = g_strdup (libiscsi_get_error_string (ctx));
187
188 return err;
189 }
190
191 static gint
iscsi_node_set_parameters(struct libiscsi_context * ctx,struct libiscsi_node * node,GVariant * params)192 iscsi_node_set_parameters (struct libiscsi_context *ctx,
193 struct libiscsi_node *node,
194 GVariant *params)
195 {
196 GVariantIter iter;
197 GVariant *value;
198 gchar *key;
199 gchar *param_value;
200 gint err = 0;
201
202 g_return_val_if_fail (ctx, ISCSI_ERR_INVAL);
203 g_return_val_if_fail (node, ISCSI_ERR_INVAL);
204 g_return_val_if_fail (params, ISCSI_ERR_INVAL);
205
206 g_variant_iter_init (&iter, params);
207 while (err == 0 && g_variant_iter_next (&iter, "{sv}", &key, &value))
208 {
209 g_variant_get (value, "&s", ¶m_value);
210
211 /* Update the node parameter value. */
212 err = libiscsi_node_set_parameter (ctx, node, key, param_value);
213
214 g_variant_unref (value);
215 g_free ((gpointer) key);
216 }
217
218 return 0;
219 }
220
221 static void
iscsi_params_get_chap_data(GVariant * params,const gchar ** username,const gchar ** password,const gchar ** reverse_username,const gchar ** reverse_password)222 iscsi_params_get_chap_data (GVariant *params,
223 const gchar **username,
224 const gchar **password,
225 const gchar **reverse_username,
226 const gchar **reverse_password)
227 {
228 g_return_if_fail (params);
229
230 g_variant_lookup (params, "username", "&s", username);
231 g_variant_lookup (params, "password", "&s", password);
232 g_variant_lookup (params, "reverse-username", "&s", reverse_username);
233 g_variant_lookup (params, "reverse-password", "&s", reverse_password);
234 }
235
236 static GVariant *
iscsi_params_pop_chap_data(GVariant * params,const gchar ** username,const gchar ** password,const gchar ** reverse_username,const gchar ** reverse_password)237 iscsi_params_pop_chap_data (GVariant *params,
238 const gchar **username,
239 const gchar **password,
240 const gchar **reverse_username,
241 const gchar **reverse_password)
242 {
243 GVariantDict dict;
244
245 g_return_val_if_fail (params, NULL);
246
247 /* Pop CHAP parameters */
248 g_variant_dict_init (&dict, params);
249 g_variant_dict_lookup (&dict, "username", "&s", username);
250 g_variant_dict_lookup (&dict, "password", "&s", password);
251 g_variant_dict_lookup (&dict, "reverse-username", "&s", reverse_username);
252 g_variant_dict_lookup (&dict, "reverse-password", "&s", reverse_password);
253
254 if (username)
255 g_variant_dict_remove (&dict, "username");
256 if (password)
257 g_variant_dict_remove (&dict, "password");
258 if (reverse_username)
259 g_variant_dict_remove (&dict, "reverse-username");
260 if (reverse_password)
261 g_variant_dict_remove (&dict, "reverse-password");
262
263 /* Update the params, so that it doesn't contain CHAP parameters. */
264 return g_variant_dict_end (&dict);
265 }
266
267 gint
iscsi_login(UDisksLinuxModuleISCSI * module,const gchar * name,const gint tpgt,const gchar * address,const gint port,const gchar * iface,GVariant * params,gchar ** errorstr)268 iscsi_login (UDisksLinuxModuleISCSI *module,
269 const gchar *name,
270 const gint tpgt,
271 const gchar *address,
272 const gint port,
273 const gchar *iface,
274 GVariant *params,
275 gchar **errorstr)
276 {
277 struct libiscsi_context *ctx;
278 struct libiscsi_auth_info auth_info = {0,};
279 struct libiscsi_node node = {0,};
280 GVariant *params_without_chap;
281 const gchar *username = NULL;
282 const gchar *password = NULL;
283 const gchar *reverse_username = NULL;
284 const gchar *reverse_password = NULL;
285 gint err;
286
287 g_return_val_if_fail (UDISKS_IS_LINUX_MODULE_ISCSI (module), 1);
288
289 /* Optional data for CHAP authentication. We pop these parameters from the
290 * dictionary; it then contains only iSCSI node parameters. */
291 params_without_chap = iscsi_params_pop_chap_data (params,
292 &username,
293 &password,
294 &reverse_username,
295 &reverse_password);
296
297 /* Prepare authentication data */
298 iscsi_make_auth_info (&auth_info,
299 username,
300 password,
301 reverse_username,
302 reverse_password);
303
304 /* Create iscsi node. */
305 iscsi_make_node (&node, name, tpgt, address, port, iface);
306
307 /* Get iscsi context. */
308 ctx = udisks_linux_module_iscsi_get_libiscsi_context (module);
309
310 /* Login */
311 err = iscsi_perform_login_action (module,
312 ACTION_LOGIN,
313 &node,
314 &auth_info,
315 errorstr);
316
317 if (err == 0 && params)
318 {
319 /* Update node parameters. */
320 err = iscsi_node_set_parameters (ctx, &node, params_without_chap);
321 }
322
323 g_variant_unref (params_without_chap);
324
325 return err;
326 }
327
328 gint
iscsi_logout(UDisksLinuxModuleISCSI * module,const gchar * name,const gint tpgt,const gchar * address,const gint port,const gchar * iface,GVariant * params,gchar ** errorstr)329 iscsi_logout (UDisksLinuxModuleISCSI *module,
330 const gchar *name,
331 const gint tpgt,
332 const gchar *address,
333 const gint port,
334 const gchar *iface,
335 GVariant *params,
336 gchar **errorstr)
337 {
338 struct libiscsi_context *ctx;
339 struct libiscsi_node node = {0,};
340 gint err;
341
342 g_return_val_if_fail (UDISKS_IS_LINUX_MODULE_ISCSI (module), 1);
343
344 /* Create iscsi node. */
345 iscsi_make_node (&node, name, tpgt, address, port, iface);
346
347 /* Get iscsi context. */
348 ctx = udisks_linux_module_iscsi_get_libiscsi_context (module);
349
350 /* Logout */
351 err = iscsi_perform_login_action (module,
352 ACTION_LOGOUT,
353 &node,
354 NULL,
355 errorstr);
356
357 if (err == 0 && params)
358 {
359 /* Update node parameters. */
360 err = iscsi_node_set_parameters (ctx, &node, params);
361
362 }
363
364 return err;
365 }
366
367 gint
iscsi_discover_send_targets(UDisksLinuxModuleISCSI * module,const gchar * address,const guint16 port,GVariant * params,GVariant ** nodes,gint * nodes_cnt,gchar ** errorstr)368 iscsi_discover_send_targets (UDisksLinuxModuleISCSI *module,
369 const gchar *address,
370 const guint16 port,
371 GVariant *params,
372 GVariant **nodes,
373 gint *nodes_cnt,
374 gchar **errorstr)
375 {
376 struct libiscsi_context *ctx;
377 struct libiscsi_auth_info auth_info = {0,};
378 struct libiscsi_node *found_nodes;
379 const gchar *username = NULL;
380 const gchar *password = NULL;
381 const gchar *reverse_username = NULL;
382 const gchar *reverse_password = NULL;
383 gint err;
384
385 g_return_val_if_fail (UDISKS_IS_LINUX_MODULE_ISCSI (module), 1);
386
387 ctx = udisks_linux_module_iscsi_get_libiscsi_context (module);
388
389 /* Optional data for CHAP authentication. */
390 iscsi_params_get_chap_data (params,
391 &username,
392 &password,
393 &reverse_username,
394 &reverse_password);
395
396 /* Prepare authentication data */
397 iscsi_make_auth_info (&auth_info,
398 username,
399 password,
400 reverse_username,
401 reverse_password);
402
403 /* Discovery */
404 err = libiscsi_discover_sendtargets (ctx,
405 address,
406 port,
407 &auth_info,
408 nodes_cnt,
409 &found_nodes);
410
411 if (err == 0)
412 *nodes = iscsi_libiscsi_nodes_to_gvariant (found_nodes, *nodes_cnt);
413 else if (errorstr)
414 *errorstr = g_strdup (libiscsi_get_error_string (ctx));
415
416 /* Release the resources */
417 iscsi_libiscsi_nodes_free (found_nodes);
418
419 return err;
420 }
421
422 GVariant *
iscsi_libiscsi_nodes_to_gvariant(const struct libiscsi_node * nodes,const gint nodes_cnt)423 iscsi_libiscsi_nodes_to_gvariant (const struct libiscsi_node *nodes,
424 const gint nodes_cnt)
425 {
426 gint i;
427 GVariantBuilder builder;
428
429 g_variant_builder_init (&builder, G_VARIANT_TYPE (iscsi_nodes_fmt));
430 for (i = 0; i < nodes_cnt; ++i)
431 {
432 g_variant_builder_add (&builder,
433 iscsi_node_fmt,
434 nodes[i].name,
435 nodes[i].tpgt,
436 nodes[i].address,
437 nodes[i].port,
438 nodes[i].iface);
439 }
440 return g_variant_builder_end (&builder);
441 }
442
443 void
iscsi_libiscsi_nodes_free(const struct libiscsi_node * nodes)444 iscsi_libiscsi_nodes_free (const struct libiscsi_node *nodes)
445 {
446 g_free ((gpointer) nodes);
447 }
448
449 UDisksError
iscsi_error_to_udisks_error(const gint err)450 iscsi_error_to_udisks_error (const gint err)
451 {
452 switch (err)
453 {
454 case ISCSI_ERR_TRANS: /* 4 */
455 return UDISKS_ERROR_ISCSI_TRANSPORT_FAILED;
456 case ISCSI_ERR_LOGIN: /* 5 */
457 return UDISKS_ERROR_ISCSI_LOGIN_FAILED;
458 case ISCSI_ERR_IDBM: /* 6 */
459 return UDISKS_ERROR_ISCSI_IDMB;
460 case ISCSI_ERR_LOGOUT: /* 10 */
461 return UDISKS_ERROR_ISCSI_LOGOUT_FAILED;
462 case ISCSI_ERR_ISCSID_COMM_ERR: /* 18 */
463 return UDISKS_ERROR_ISCSI_DAEMON_TRANSPORT_FAILED;
464 case ISCSI_ERR_FATAL_LOGIN: /* 19 */
465 return UDISKS_ERROR_ISCSI_LOGIN_FATAL;
466 case ISCSI_ERR_ISCSID_NOTCONN: /* 20 */
467 return UDISKS_ERROR_ISCSI_NOT_CONNECTED;
468 case ISCSI_ERR_NO_OBJS_FOUND: /* 21 */
469 return UDISKS_ERROR_ISCSI_NO_OBJECTS_FOUND;
470 case ISCSI_ERR_HOST_NOT_FOUND: /* 23 */
471 return UDISKS_ERROR_ISCSI_HOST_NOT_FOUND;
472 case ISCSI_ERR_LOGIN_AUTH_FAILED: /* 24 */
473 return UDISKS_ERROR_ISCSI_LOGIN_AUTH_FAILED;
474 case ISCSI_ERR_UNKNOWN_DISCOVERY_TYPE: /* 30 */
475 return UDISKS_ERROR_ISCSI_UNKNOWN_DISCOVERY_TYPE;
476
477 default:
478 return UDISKS_ERROR_FAILED;
479 }
480 }
481
482 UDisksObject *
wait_for_iscsi_object(UDisksDaemon * daemon,gpointer user_data)483 wait_for_iscsi_object (UDisksDaemon *daemon,
484 gpointer user_data)
485 {
486 const gchar *device_iqn = user_data;
487 UDisksObject *ret = NULL;
488 GList *objects, *l;
489 const gchar *const *symlinks = NULL;
490
491 objects = udisks_daemon_get_objects (daemon);
492 for (l = objects; l != NULL; l = l->next)
493 {
494 UDisksObject *object = UDISKS_OBJECT (l->data);
495 UDisksBlock *block;
496
497 block = udisks_object_peek_block (object);
498 if (block != NULL)
499 {
500 symlinks = udisks_block_get_symlinks (UDISKS_BLOCK (block));
501 if (symlinks != NULL)
502 for (guint n = 0; symlinks[n] != NULL; n++)
503 if (g_str_has_prefix (symlinks[n], "/dev/disk/by-path/") &&
504 strstr (symlinks[n], device_iqn) != NULL)
505 {
506 ret = g_object_ref (object);
507 goto out;
508 }
509 }
510 }
511
512 out:
513 g_list_free_full (objects, g_object_unref);
514 return ret;
515 }
516
517 UDisksObject *
wait_for_iscsi_session_object(UDisksDaemon * daemon,gpointer user_data)518 wait_for_iscsi_session_object (UDisksDaemon *daemon,
519 gpointer user_data)
520 {
521 const gchar *device_iqn = user_data;
522 UDisksObject *ret = NULL;
523 GList *objects, *l;
524
525 objects = udisks_daemon_get_objects (daemon);
526 for (l = objects; l != NULL; l = l->next)
527 {
528 UDisksObject *object = UDISKS_OBJECT (l->data);
529 UDisksISCSISession *session;
530
531 session = udisks_object_peek_iscsi_session (object);
532 if (session != NULL)
533 {
534 if (g_strcmp0 (udisks_iscsi_session_get_target_name (session), device_iqn) == 0)
535 {
536 ret = g_object_ref (object);
537 goto out;
538 }
539 }
540 }
541
542 out:
543 g_list_free_full (objects, g_object_unref);
544 return ret;
545 }
546