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", &param_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