asx: remove useless test
[vlc.git] / src / misc / keystore.c
blob116849e0427be70d13af55603be48a21eb15c3a9
1 /*****************************************************************************
2 * keystore.c:
3 *****************************************************************************
4 * Copyright (C) 2015-2016 VLC authors, VideoLAN and VideoLabs
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
25 #include <vlc_common.h>
26 #include <vlc_dialog.h>
27 #include <vlc_keystore.h>
28 #include <vlc_modules.h>
29 #include <vlc_url.h>
30 #include <libvlc.h>
32 #include <assert.h>
33 #include <limits.h>
35 static vlc_keystore *
36 keystore_create(vlc_object_t *p_parent, const char *psz_name)
38 vlc_keystore *p_keystore = vlc_custom_create(p_parent, sizeof (*p_keystore),
39 "keystore");
40 if (unlikely(p_keystore == NULL))
41 return NULL;
43 p_keystore->p_module = module_need(p_keystore, "keystore", psz_name, true);
44 if (p_keystore->p_module == NULL)
46 vlc_object_release(p_keystore);
47 return NULL;
49 assert(p_keystore->pf_store);
50 assert(p_keystore->pf_find);
51 assert(p_keystore->pf_remove);
53 return p_keystore;
56 #undef vlc_keystore_create
57 vlc_keystore *
58 vlc_keystore_create(vlc_object_t *p_parent)
60 assert(p_parent);
61 return keystore_create(p_parent, "$keystore");
64 void
65 vlc_keystore_release(vlc_keystore *p_keystore)
67 assert(p_keystore);
68 module_unneed(p_keystore, p_keystore->p_module);
70 vlc_object_release(p_keystore);
73 int
74 vlc_keystore_store(vlc_keystore *p_keystore,
75 const char * const ppsz_values[KEY_MAX],
76 const uint8_t *p_secret, ssize_t i_secret_len,
77 const char *psz_label)
79 assert(p_keystore && ppsz_values && p_secret && i_secret_len);
81 if (!ppsz_values[KEY_PROTOCOL] || !ppsz_values[KEY_SERVER])
83 msg_Err(p_keystore, "invalid store request: "
84 "protocol and server should be valid");
85 return VLC_EGENERIC;
87 if (ppsz_values[KEY_PORT])
89 long int i_port = strtol(ppsz_values[KEY_PORT], NULL, 10);
90 if (i_port == LONG_MIN || i_port == LONG_MAX)
92 msg_Err(p_keystore, "invalid store request: "
93 "port is not valid number");
94 return VLC_EGENERIC;
97 if (i_secret_len < 0)
98 i_secret_len = strlen((const char *)p_secret) + 1;
99 return p_keystore->pf_store(p_keystore, ppsz_values, p_secret, i_secret_len,
100 psz_label);
103 unsigned int
104 vlc_keystore_find(vlc_keystore *p_keystore,
105 const char * const ppsz_values[KEY_MAX],
106 vlc_keystore_entry **pp_entries)
108 assert(p_keystore && ppsz_values && pp_entries);
109 return p_keystore->pf_find(p_keystore, ppsz_values, pp_entries);
112 unsigned int
113 vlc_keystore_remove(vlc_keystore *p_keystore,
114 const char *const ppsz_values[KEY_MAX])
116 assert(p_keystore && ppsz_values);
117 return p_keystore->pf_remove(p_keystore, ppsz_values);
120 void
121 vlc_keystore_release_entries(vlc_keystore_entry *p_entries, unsigned int i_count)
123 for (unsigned int i = 0; i < i_count; ++i)
124 vlc_keystore_release_entry(&p_entries[i]);
125 free(p_entries);
129 libvlc_InternalKeystoreInit(libvlc_int_t *p_libvlc)
131 assert(p_libvlc != NULL);
132 libvlc_priv_t *p_priv = libvlc_priv(p_libvlc);
134 p_priv->p_memory_keystore = keystore_create(VLC_OBJECT(p_libvlc), "memory");
135 return p_priv->p_memory_keystore != NULL ? VLC_SUCCESS : VLC_EGENERIC;
138 void
139 libvlc_InternalKeystoreClean(libvlc_int_t *p_libvlc)
141 assert(p_libvlc != NULL);
142 libvlc_priv_t *p_priv = libvlc_priv(p_libvlc);
144 if (p_priv->p_memory_keystore != NULL)
146 vlc_keystore_release(p_priv->p_memory_keystore);
147 p_priv->p_memory_keystore = NULL;
151 static vlc_keystore *
152 get_memory_keystore(vlc_object_t *p_obj)
154 return libvlc_priv(p_obj->obj.libvlc)->p_memory_keystore;
157 static vlc_keystore_entry *
158 find_closest_path(vlc_keystore_entry *p_entries, unsigned i_count,
159 const char *psz_path)
161 vlc_keystore_entry *p_match_entry = NULL;
162 size_t i_last_pathlen = 0;
163 char *psz_decoded_path = vlc_uri_decode_duplicate(psz_path);
164 if (psz_decoded_path == NULL)
165 return NULL;
167 /* Try to find the entry that has the closest path to psz_url */
168 for (unsigned int i = 0; i < i_count; ++i)
170 vlc_keystore_entry *p_entry = &p_entries[i];
171 const char *psz_entry_path = p_entry->ppsz_values[KEY_PATH];
172 if (psz_entry_path == NULL)
174 if (p_match_entry == NULL)
175 p_match_entry = p_entry;
176 continue;
178 size_t i_entry_pathlen = strlen(psz_entry_path);
180 if (strncasecmp(psz_decoded_path, psz_entry_path, i_entry_pathlen) == 0
181 && i_entry_pathlen > i_last_pathlen)
183 i_last_pathlen = i_entry_pathlen;
184 p_match_entry = p_entry;
187 free(psz_decoded_path);
188 return p_match_entry;
191 static bool
192 is_credential_valid(vlc_credential *p_credential)
194 if (p_credential->psz_username && *p_credential->psz_username != '\0'
195 && p_credential->psz_password)
196 return true;
197 p_credential->psz_password = NULL;
198 return false;
201 static bool
202 is_url_valid(const vlc_url_t *p_url)
204 return p_url && p_url->psz_protocol && p_url->psz_protocol[0]
205 && p_url->psz_host && p_url->psz_host[0];
208 /* Default port for each protocol */
209 static struct
211 const char * psz_protocol;
212 uint16_t i_port;
213 } protocol_default_ports [] = {
214 { "rtsp", 80 },
215 { "http", 80 },
216 { "https", 443 },
217 { "ftp", 21 },
218 { "sftp", 22 },
219 { "smb", 445 },
222 /* Don't store a port if it's the default one */
223 static bool
224 protocol_set_port(const vlc_url_t *p_url, char *psz_port)
226 int i_port = -1;
228 if (p_url->i_port != 0 && p_url->i_port <= UINT16_MAX)
229 i_port = p_url->i_port;
230 else
232 for (unsigned int i = 0; i < sizeof(protocol_default_ports)
233 / sizeof(*protocol_default_ports); ++i)
235 if (strcasecmp(p_url->psz_protocol,
236 protocol_default_ports[i].psz_protocol) == 0)
238 i_port = protocol_default_ports[i].i_port;
239 break;
243 if (i_port != -1)
245 sprintf(psz_port, "%u", (uint16_t) i_port);
246 return true;
248 return false;
251 static bool
252 protocol_is_smb(const vlc_url_t *p_url)
254 return strcasecmp(p_url->psz_protocol, "smb") == 0;
257 static bool
258 protocol_store_path(const vlc_url_t *p_url)
260 return p_url->psz_path
261 && (strncasecmp(p_url->psz_protocol, "http", 4) == 0
262 || strcasecmp(p_url->psz_protocol, "rtsp") == 0
263 || protocol_is_smb(p_url));
266 /* Split domain;user in userinfo */
267 static void
268 smb_split_domain(vlc_credential *p_credential)
270 char *psz_delim = strchr(p_credential->psz_username, ';');
271 if (psz_delim)
273 size_t i_len = psz_delim - p_credential->psz_username;
274 if (i_len > 0)
276 free(p_credential->psz_split_domain);
277 p_credential->psz_split_domain =
278 strndup(p_credential->psz_username, i_len);
279 p_credential->psz_realm = p_credential->psz_split_domain;
281 p_credential->psz_username = psz_delim + 1;
285 static void
286 credential_find_keystore(vlc_credential *p_credential, vlc_keystore *p_keystore)
288 const vlc_url_t *p_url = p_credential->p_url;
290 const char *ppsz_values[KEY_MAX] = { 0 };
291 ppsz_values[KEY_PROTOCOL] = p_url->psz_protocol;
292 ppsz_values[KEY_USER] = p_credential->psz_username;
293 ppsz_values[KEY_SERVER] = p_url->psz_host;
294 /* don't try to match with the path */
295 ppsz_values[KEY_REALM] = p_credential->psz_realm;
296 ppsz_values[KEY_AUTHTYPE] = p_credential->psz_authtype;
297 char psz_port[21];
298 if (protocol_set_port(p_url, psz_port))
299 ppsz_values[KEY_PORT] = psz_port;
301 vlc_keystore_entry *p_entries;
302 unsigned int i_entries_count;
303 i_entries_count = vlc_keystore_find(p_keystore, ppsz_values, &p_entries);
305 /* Remove last entries after vlc_keystore_find call since
306 * p_credential->psz_username (default username) can be a pointer to an
307 * entry */
308 if (p_credential->i_entries_count > 0)
310 vlc_keystore_release_entries(p_credential->p_entries,
311 p_credential->i_entries_count);
312 p_credential->psz_username = NULL;
314 p_credential->p_entries = p_entries;
315 p_credential->i_entries_count = i_entries_count;
317 if (p_credential->i_entries_count > 0)
319 vlc_keystore_entry *p_entry;
321 if (protocol_store_path(p_url))
322 p_entry = find_closest_path(p_credential->p_entries,
323 p_credential->i_entries_count,
324 p_url->psz_path);
325 else
326 p_entry = &p_credential->p_entries[0];
328 if (!p_entry || p_entry->p_secret[p_entry->i_secret_len - 1] != '\0')
330 vlc_keystore_release_entries(p_credential->p_entries,
331 p_credential->i_entries_count);
332 p_credential->i_entries_count = 0;
334 else
336 p_credential->psz_password = (const char *)p_entry->p_secret;
337 p_credential->psz_username = p_entry->ppsz_values[KEY_USER];
338 p_credential->psz_realm = p_entry->ppsz_values[KEY_REALM];
339 p_credential->psz_authtype = p_entry->ppsz_values[KEY_AUTHTYPE];
340 p_credential->b_from_keystore = true;
345 void
346 vlc_credential_init(vlc_credential *p_credential, const vlc_url_t *p_url)
348 assert(p_credential);
350 memset(p_credential, 0, sizeof(*p_credential));
351 p_credential->i_get_order = GET_FROM_URL;
352 p_credential->p_url = p_url;
355 void
356 vlc_credential_clean(vlc_credential *p_credential)
358 if (p_credential->i_entries_count > 0)
359 vlc_keystore_release_entries(p_credential->p_entries,
360 p_credential->i_entries_count);
361 if (p_credential->p_keystore)
362 vlc_keystore_release(p_credential->p_keystore);
364 free(p_credential->psz_split_domain);
365 free(p_credential->psz_var_username);
366 free(p_credential->psz_var_password);
367 free(p_credential->psz_dialog_username);
368 free(p_credential->psz_dialog_password);
371 #undef vlc_credential_get
372 bool
373 vlc_credential_get(vlc_credential *p_credential, vlc_object_t *p_parent,
374 const char *psz_option_username,
375 const char *psz_option_password,
376 const char *psz_dialog_title,
377 const char *psz_dialog_fmt, ...)
379 assert(p_credential && p_parent);
380 const vlc_url_t *p_url = p_credential->p_url;
382 if (!is_url_valid(p_url))
384 msg_Err(p_parent, "vlc_credential_get: invalid url");
385 return false;
388 p_credential->b_from_keystore = false;
389 /* Don't set username to NULL, we may want to use the last one set */
390 p_credential->psz_password = NULL;
392 while (!is_credential_valid(p_credential))
394 /* First, fetch credential from URL (if any).
395 * Secondly, fetch credential from VLC Options (if any).
396 * Thirdly, fetch credential from keystore (if any) using user and realm
397 * previously set by the caller, the URL or by VLC Options.
398 * Finally, fetch credential from the dialog (if any). This last will be
399 * repeated until user cancel the dialog. */
401 switch (p_credential->i_get_order)
403 case GET_FROM_URL:
404 p_credential->psz_username = p_url->psz_username;
405 p_credential->psz_password = p_url->psz_password;
407 if (p_credential->psz_password)
408 msg_Warn(p_parent, "Password in a URI is DEPRECATED");
410 if (p_url->psz_username && protocol_is_smb(p_url))
411 smb_split_domain(p_credential);
412 p_credential->i_get_order++;
413 break;
415 case GET_FROM_OPTION:
416 free(p_credential->psz_var_username);
417 free(p_credential->psz_var_password);
418 p_credential->psz_var_username =
419 p_credential->psz_var_password = NULL;
421 if (psz_option_username)
422 p_credential->psz_var_username =
423 var_InheritString(p_parent, psz_option_username);
424 if (psz_option_password)
425 p_credential->psz_var_password =
426 var_InheritString(p_parent, psz_option_password);
428 if (p_credential->psz_var_username)
429 p_credential->psz_username = p_credential->psz_var_username;
430 if (p_credential->psz_var_password)
431 p_credential->psz_password = p_credential->psz_var_password;
433 p_credential->i_get_order++;
434 break;
436 case GET_FROM_MEMORY_KEYSTORE:
438 if (!psz_dialog_title || !psz_dialog_fmt)
439 return false;
441 vlc_keystore *p_keystore = get_memory_keystore(p_parent);
442 if (p_keystore != NULL)
443 credential_find_keystore(p_credential, p_keystore);
444 p_credential->i_get_order++;
445 break;
448 case GET_FROM_KEYSTORE:
449 if (!psz_dialog_title || !psz_dialog_fmt)
450 return false;
452 if (p_credential->p_keystore == NULL)
453 p_credential->p_keystore = vlc_keystore_create(p_parent);
454 if (p_credential->p_keystore != NULL)
455 credential_find_keystore(p_credential, p_credential->p_keystore);
457 p_credential->i_get_order++;
458 break;
460 default:
461 case GET_FROM_DIALOG:
462 if (!psz_dialog_title || !psz_dialog_fmt)
463 return false;
464 char *psz_dialog_username = NULL;
465 char *psz_dialog_password = NULL;
466 va_list ap;
467 va_start(ap, psz_dialog_fmt);
468 bool *p_store = p_credential->p_keystore != NULL ?
469 &p_credential->b_store : NULL;
470 int i_ret =
471 vlc_dialog_wait_login_va(p_parent,
472 &psz_dialog_username,
473 &psz_dialog_password, p_store,
474 p_credential->psz_username,
475 psz_dialog_title, psz_dialog_fmt, ap);
476 va_end(ap);
478 /* Free previous dialog strings after vlc_dialog_wait_login_va call
479 * since p_credential->psz_username (default username) can be a
480 * pointer to p_credential->psz_dialog_username */
481 free(p_credential->psz_dialog_username);
482 free(p_credential->psz_dialog_password);
483 p_credential->psz_dialog_username = psz_dialog_username;
484 p_credential->psz_dialog_password = psz_dialog_password;
486 if (i_ret != 1)
488 p_credential->psz_username = p_credential->psz_password = NULL;
489 return false;
492 p_credential->psz_username = p_credential->psz_dialog_username;
493 p_credential->psz_password = p_credential->psz_dialog_password;
495 if (protocol_is_smb(p_url))
496 smb_split_domain(p_credential);
498 break;
501 return is_credential_valid(p_credential);
504 #undef vlc_credential_store
505 bool
506 vlc_credential_store(vlc_credential *p_credential, vlc_object_t *p_parent)
508 if (!is_credential_valid(p_credential))
509 return false;
510 /* Don't need to store again */
511 if (p_credential->b_from_keystore)
512 return p_credential->b_from_keystore;
514 vlc_keystore *p_keystore;
515 if (p_credential->b_store)
517 /* Store in permanent keystore */
518 assert(p_credential->p_keystore != NULL);
519 p_keystore = p_credential->p_keystore;
521 else
523 /* Store in memory keystore */
524 p_keystore = get_memory_keystore(p_parent);
526 if (p_keystore == NULL)
527 return false;
529 const vlc_url_t *p_url = p_credential->p_url;
531 char *psz_path = NULL;
532 if (protocol_store_path(p_url)
533 && (psz_path = vlc_uri_decode_duplicate(p_url->psz_path)) != NULL)
535 char *p_slash;
536 if (protocol_is_smb(p_url))
538 /* Remove all characters after the first slash (store the share but
539 * not the path) */
540 p_slash = strchr(psz_path + 1, '/');
542 else
544 /* Remove all characters after the last slash (store the path
545 * without the filename) */
546 p_slash = strrchr(psz_path + 1, '/');
548 if (p_slash && psz_path != p_slash)
549 *p_slash = '\0';
552 const char *ppsz_values[KEY_MAX] = { 0 };
553 ppsz_values[KEY_PROTOCOL] = p_url->psz_protocol;
554 ppsz_values[KEY_USER] = p_credential->psz_username;
555 ppsz_values[KEY_SERVER] = p_url->psz_host;
556 ppsz_values[KEY_PATH] = psz_path;
557 ppsz_values[KEY_REALM] = p_credential->psz_realm;
558 ppsz_values[KEY_AUTHTYPE] = p_credential->psz_authtype;
560 char psz_port[21];
561 if (protocol_set_port(p_url, psz_port))
562 ppsz_values[KEY_PORT] = psz_port;
564 char *psz_label;
565 if (asprintf(&psz_label, "LibVLC password for %s://%s%s",
566 p_url->psz_protocol, p_url->psz_host,
567 psz_path ? psz_path : "") == -1)
569 free(psz_path);
570 return false;
573 const uint8_t *p_password = (const uint8_t *)
574 (p_credential->psz_password != NULL ? p_credential->psz_password : "");
576 bool b_ret = vlc_keystore_store(p_keystore, ppsz_values, p_password,
577 -1, psz_label) == VLC_SUCCESS;
578 free(psz_label);
579 free(psz_path);
580 return b_ret;