1 /*****************************************************************************
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 *****************************************************************************/
25 #include <vlc_common.h>
26 #include <vlc_dialog.h>
27 #include <vlc_keystore.h>
28 #include <vlc_modules.h>
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
),
40 if (unlikely(p_keystore
== 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
);
49 assert(p_keystore
->pf_store
);
50 assert(p_keystore
->pf_find
);
51 assert(p_keystore
->pf_remove
);
56 #undef vlc_keystore_create
58 vlc_keystore_create(vlc_object_t
*p_parent
)
62 char *modlist
= var_InheritString(p_parent
, "keystore");
63 vlc_keystore
*p_keystore
= keystore_create(p_parent
, modlist
);
70 vlc_keystore_release(vlc_keystore
*p_keystore
)
73 module_unneed(p_keystore
, p_keystore
->p_module
);
75 vlc_object_release(p_keystore
);
79 vlc_keystore_store(vlc_keystore
*p_keystore
,
80 const char * const ppsz_values
[KEY_MAX
],
81 const uint8_t *p_secret
, ssize_t i_secret_len
,
82 const char *psz_label
)
84 assert(p_keystore
&& ppsz_values
&& p_secret
&& i_secret_len
);
86 if (!ppsz_values
[KEY_PROTOCOL
] || !ppsz_values
[KEY_SERVER
])
88 msg_Err(p_keystore
, "invalid store request: "
89 "protocol and server should be valid");
92 if (ppsz_values
[KEY_PORT
])
94 long int i_port
= strtol(ppsz_values
[KEY_PORT
], NULL
, 10);
95 if (i_port
== LONG_MIN
|| i_port
== LONG_MAX
)
97 msg_Err(p_keystore
, "invalid store request: "
98 "port is not valid number");
102 if (i_secret_len
< 0)
103 i_secret_len
= strlen((const char *)p_secret
) + 1;
104 return p_keystore
->pf_store(p_keystore
, ppsz_values
, p_secret
, i_secret_len
,
109 vlc_keystore_find(vlc_keystore
*p_keystore
,
110 const char * const ppsz_values
[KEY_MAX
],
111 vlc_keystore_entry
**pp_entries
)
113 assert(p_keystore
&& ppsz_values
&& pp_entries
);
114 return p_keystore
->pf_find(p_keystore
, ppsz_values
, pp_entries
);
118 vlc_keystore_remove(vlc_keystore
*p_keystore
,
119 const char *const ppsz_values
[KEY_MAX
])
121 assert(p_keystore
&& ppsz_values
);
122 return p_keystore
->pf_remove(p_keystore
, ppsz_values
);
126 vlc_keystore_release_entries(vlc_keystore_entry
*p_entries
, unsigned int i_count
)
128 for (unsigned int i
= 0; i
< i_count
; ++i
)
129 vlc_keystore_release_entry(&p_entries
[i
]);
134 libvlc_InternalKeystoreInit(libvlc_int_t
*p_libvlc
)
136 assert(p_libvlc
!= NULL
);
137 libvlc_priv_t
*p_priv
= libvlc_priv(p_libvlc
);
139 p_priv
->p_memory_keystore
= keystore_create(VLC_OBJECT(p_libvlc
), "memory");
140 return p_priv
->p_memory_keystore
!= NULL
? VLC_SUCCESS
: VLC_EGENERIC
;
144 libvlc_InternalKeystoreClean(libvlc_int_t
*p_libvlc
)
146 assert(p_libvlc
!= NULL
);
147 libvlc_priv_t
*p_priv
= libvlc_priv(p_libvlc
);
149 if (p_priv
->p_memory_keystore
!= NULL
)
151 vlc_keystore_release(p_priv
->p_memory_keystore
);
152 p_priv
->p_memory_keystore
= NULL
;
156 static vlc_keystore
*
157 get_memory_keystore(vlc_object_t
*p_obj
)
159 return libvlc_priv(p_obj
->obj
.libvlc
)->p_memory_keystore
;
162 static vlc_keystore_entry
*
163 find_closest_path(vlc_keystore_entry
*p_entries
, unsigned i_count
,
164 const char *psz_path
)
166 vlc_keystore_entry
*p_match_entry
= NULL
;
167 size_t i_last_pathlen
= 0;
168 char *psz_decoded_path
= vlc_uri_decode_duplicate(psz_path
);
169 if (psz_decoded_path
== NULL
)
172 /* Try to find the entry that has the closest path to psz_url */
173 for (unsigned int i
= 0; i
< i_count
; ++i
)
175 vlc_keystore_entry
*p_entry
= &p_entries
[i
];
176 const char *psz_entry_path
= p_entry
->ppsz_values
[KEY_PATH
];
177 if (psz_entry_path
== NULL
)
179 if (p_match_entry
== NULL
)
180 p_match_entry
= p_entry
;
183 size_t i_entry_pathlen
= strlen(psz_entry_path
);
185 if (strncasecmp(psz_decoded_path
, psz_entry_path
, i_entry_pathlen
) == 0
186 && i_entry_pathlen
> i_last_pathlen
)
188 i_last_pathlen
= i_entry_pathlen
;
189 p_match_entry
= p_entry
;
192 free(psz_decoded_path
);
193 return p_match_entry
;
197 is_credential_valid(vlc_credential
*p_credential
)
199 if (p_credential
->psz_username
&& *p_credential
->psz_username
!= '\0'
200 && p_credential
->psz_password
)
202 p_credential
->psz_password
= NULL
;
207 is_url_valid(const vlc_url_t
*p_url
)
209 return p_url
&& p_url
->psz_protocol
&& p_url
->psz_protocol
[0]
210 && p_url
->psz_host
&& p_url
->psz_host
[0];
213 /* Default port for each protocol */
216 const char * psz_protocol
;
218 } protocol_default_ports
[] = {
227 /* Don't store a port if it's the default one */
229 protocol_set_port(const vlc_url_t
*p_url
, char *psz_port
)
233 if (p_url
->i_port
!= 0 && p_url
->i_port
<= UINT16_MAX
)
234 i_port
= p_url
->i_port
;
237 for (unsigned int i
= 0; i
< sizeof(protocol_default_ports
)
238 / sizeof(*protocol_default_ports
); ++i
)
240 if (strcasecmp(p_url
->psz_protocol
,
241 protocol_default_ports
[i
].psz_protocol
) == 0)
243 i_port
= protocol_default_ports
[i
].i_port
;
250 sprintf(psz_port
, "%" PRIu16
, (uint16_t) i_port
);
257 protocol_is_smb(const vlc_url_t
*p_url
)
259 return strcasecmp(p_url
->psz_protocol
, "smb") == 0;
263 protocol_store_path(const vlc_url_t
*p_url
)
265 return p_url
->psz_path
266 && (strncasecmp(p_url
->psz_protocol
, "http", 4) == 0
267 || strcasecmp(p_url
->psz_protocol
, "rtsp") == 0
268 || protocol_is_smb(p_url
));
271 /* Split domain;user in userinfo */
273 smb_split_domain(vlc_credential
*p_credential
)
275 char *psz_delim
= strchr(p_credential
->psz_username
, ';');
278 size_t i_len
= psz_delim
- p_credential
->psz_username
;
281 free(p_credential
->psz_split_domain
);
282 p_credential
->psz_split_domain
=
283 strndup(p_credential
->psz_username
, i_len
);
284 p_credential
->psz_realm
= p_credential
->psz_split_domain
;
286 p_credential
->psz_username
= psz_delim
+ 1;
291 credential_find_keystore(vlc_credential
*p_credential
, vlc_keystore
*p_keystore
)
293 const vlc_url_t
*p_url
= p_credential
->p_url
;
295 const char *ppsz_values
[KEY_MAX
] = { 0 };
296 ppsz_values
[KEY_PROTOCOL
] = p_url
->psz_protocol
;
297 ppsz_values
[KEY_USER
] = p_credential
->psz_username
;
298 ppsz_values
[KEY_SERVER
] = p_url
->psz_host
;
299 /* don't try to match with the path */
300 ppsz_values
[KEY_REALM
] = p_credential
->psz_realm
;
301 ppsz_values
[KEY_AUTHTYPE
] = p_credential
->psz_authtype
;
303 if (protocol_set_port(p_url
, psz_port
))
304 ppsz_values
[KEY_PORT
] = psz_port
;
306 vlc_keystore_entry
*p_entries
;
307 unsigned int i_entries_count
;
308 i_entries_count
= vlc_keystore_find(p_keystore
, ppsz_values
, &p_entries
);
310 /* Remove last entries after vlc_keystore_find call since
311 * p_credential->psz_username (default username) can be a pointer to an
313 if (p_credential
->i_entries_count
> 0)
315 vlc_keystore_release_entries(p_credential
->p_entries
,
316 p_credential
->i_entries_count
);
317 p_credential
->psz_username
= NULL
;
319 p_credential
->p_entries
= p_entries
;
320 p_credential
->i_entries_count
= i_entries_count
;
322 if (p_credential
->i_entries_count
> 0)
324 vlc_keystore_entry
*p_entry
;
326 if (protocol_store_path(p_url
))
327 p_entry
= find_closest_path(p_credential
->p_entries
,
328 p_credential
->i_entries_count
,
331 p_entry
= &p_credential
->p_entries
[0];
333 if (!p_entry
|| p_entry
->p_secret
[p_entry
->i_secret_len
- 1] != '\0')
335 vlc_keystore_release_entries(p_credential
->p_entries
,
336 p_credential
->i_entries_count
);
337 p_credential
->i_entries_count
= 0;
341 p_credential
->psz_password
= (const char *)p_entry
->p_secret
;
342 p_credential
->psz_username
= p_entry
->ppsz_values
[KEY_USER
];
343 p_credential
->psz_realm
= p_entry
->ppsz_values
[KEY_REALM
];
344 p_credential
->psz_authtype
= p_entry
->ppsz_values
[KEY_AUTHTYPE
];
345 p_credential
->b_from_keystore
= true;
351 vlc_credential_init(vlc_credential
*p_credential
, const vlc_url_t
*p_url
)
353 assert(p_credential
);
355 memset(p_credential
, 0, sizeof(*p_credential
));
356 p_credential
->i_get_order
= GET_FROM_URL
;
357 p_credential
->p_url
= p_url
;
361 vlc_credential_clean(vlc_credential
*p_credential
)
363 if (p_credential
->i_entries_count
> 0)
364 vlc_keystore_release_entries(p_credential
->p_entries
,
365 p_credential
->i_entries_count
);
366 if (p_credential
->p_keystore
)
367 vlc_keystore_release(p_credential
->p_keystore
);
369 free(p_credential
->psz_split_domain
);
370 free(p_credential
->psz_var_username
);
371 free(p_credential
->psz_var_password
);
372 free(p_credential
->psz_dialog_username
);
373 free(p_credential
->psz_dialog_password
);
376 #undef vlc_credential_get
378 vlc_credential_get(vlc_credential
*p_credential
, vlc_object_t
*p_parent
,
379 const char *psz_option_username
,
380 const char *psz_option_password
,
381 const char *psz_dialog_title
,
382 const char *psz_dialog_fmt
, ...)
384 assert(p_credential
&& p_parent
);
385 const vlc_url_t
*p_url
= p_credential
->p_url
;
387 if (!is_url_valid(p_url
))
389 msg_Err(p_parent
, "vlc_credential_get: invalid url");
393 p_credential
->b_from_keystore
= false;
394 /* Don't set username to NULL, we may want to use the last one set */
395 p_credential
->psz_password
= NULL
;
397 while (!is_credential_valid(p_credential
))
399 /* First, fetch credential from URL (if any).
400 * Secondly, fetch credential from VLC Options (if any).
401 * Thirdly, fetch credential from keystore (if any) using user and realm
402 * previously set by the caller, the URL or by VLC Options.
403 * Finally, fetch credential from the dialog (if any). This last will be
404 * repeated until user cancel the dialog. */
406 switch (p_credential
->i_get_order
)
409 p_credential
->psz_username
= p_url
->psz_username
;
410 p_credential
->psz_password
= p_url
->psz_password
;
412 if (p_credential
->psz_password
)
413 msg_Warn(p_parent
, "Password in a URI is DEPRECATED");
415 if (p_url
->psz_username
&& protocol_is_smb(p_url
))
416 smb_split_domain(p_credential
);
417 p_credential
->i_get_order
++;
420 case GET_FROM_OPTION
:
421 free(p_credential
->psz_var_username
);
422 free(p_credential
->psz_var_password
);
423 p_credential
->psz_var_username
=
424 p_credential
->psz_var_password
= NULL
;
426 if (psz_option_username
)
427 p_credential
->psz_var_username
=
428 var_InheritString(p_parent
, psz_option_username
);
429 if (psz_option_password
)
430 p_credential
->psz_var_password
=
431 var_InheritString(p_parent
, psz_option_password
);
433 if (p_credential
->psz_var_username
)
434 p_credential
->psz_username
= p_credential
->psz_var_username
;
435 if (p_credential
->psz_var_password
)
436 p_credential
->psz_password
= p_credential
->psz_var_password
;
438 p_credential
->i_get_order
++;
441 case GET_FROM_MEMORY_KEYSTORE
:
443 if (!psz_dialog_title
|| !psz_dialog_fmt
)
446 vlc_keystore
*p_keystore
= get_memory_keystore(p_parent
);
447 if (p_keystore
!= NULL
)
448 credential_find_keystore(p_credential
, p_keystore
);
449 p_credential
->i_get_order
++;
453 case GET_FROM_KEYSTORE
:
454 if (!psz_dialog_title
|| !psz_dialog_fmt
)
457 if (p_credential
->p_keystore
== NULL
)
458 p_credential
->p_keystore
= vlc_keystore_create(p_parent
);
459 if (p_credential
->p_keystore
!= NULL
)
460 credential_find_keystore(p_credential
, p_credential
->p_keystore
);
462 p_credential
->i_get_order
++;
466 case GET_FROM_DIALOG
:
467 if (!psz_dialog_title
|| !psz_dialog_fmt
)
469 char *psz_dialog_username
= NULL
;
470 char *psz_dialog_password
= NULL
;
472 va_start(ap
, psz_dialog_fmt
);
473 bool *p_store
= p_credential
->p_keystore
!= NULL
?
474 &p_credential
->b_store
: NULL
;
476 vlc_dialog_wait_login_va(p_parent
,
477 &psz_dialog_username
,
478 &psz_dialog_password
, p_store
,
479 p_credential
->psz_username
,
480 psz_dialog_title
, psz_dialog_fmt
, ap
);
483 /* Free previous dialog strings after vlc_dialog_wait_login_va call
484 * since p_credential->psz_username (default username) can be a
485 * pointer to p_credential->psz_dialog_username */
486 free(p_credential
->psz_dialog_username
);
487 free(p_credential
->psz_dialog_password
);
488 p_credential
->psz_dialog_username
= psz_dialog_username
;
489 p_credential
->psz_dialog_password
= psz_dialog_password
;
493 p_credential
->psz_username
= p_credential
->psz_password
= NULL
;
497 p_credential
->psz_username
= p_credential
->psz_dialog_username
;
498 p_credential
->psz_password
= p_credential
->psz_dialog_password
;
500 if (protocol_is_smb(p_url
))
501 smb_split_domain(p_credential
);
506 return is_credential_valid(p_credential
);
509 #undef vlc_credential_store
511 vlc_credential_store(vlc_credential
*p_credential
, vlc_object_t
*p_parent
)
513 if (!is_credential_valid(p_credential
))
515 /* Don't need to store again */
516 if (p_credential
->b_from_keystore
)
517 return p_credential
->b_from_keystore
;
519 vlc_keystore
*p_keystore
;
520 if (p_credential
->b_store
)
522 /* Store in permanent keystore */
523 assert(p_credential
->p_keystore
!= NULL
);
524 p_keystore
= p_credential
->p_keystore
;
528 /* Store in memory keystore */
529 p_keystore
= get_memory_keystore(p_parent
);
531 if (p_keystore
== NULL
)
534 const vlc_url_t
*p_url
= p_credential
->p_url
;
536 char *psz_path
= NULL
;
537 if (protocol_store_path(p_url
)
538 && (psz_path
= vlc_uri_decode_duplicate(p_url
->psz_path
)) != NULL
)
541 if (protocol_is_smb(p_url
))
543 /* Remove all characters after the first slash (store the share but
545 p_slash
= strchr(psz_path
+ 1, '/');
549 /* Remove all characters after the last slash (store the path
550 * without the filename) */
551 p_slash
= strrchr(psz_path
+ 1, '/');
553 if (p_slash
&& psz_path
!= p_slash
)
557 const char *ppsz_values
[KEY_MAX
] = { 0 };
558 ppsz_values
[KEY_PROTOCOL
] = p_url
->psz_protocol
;
559 ppsz_values
[KEY_USER
] = p_credential
->psz_username
;
560 ppsz_values
[KEY_SERVER
] = p_url
->psz_host
;
561 ppsz_values
[KEY_PATH
] = psz_path
;
562 ppsz_values
[KEY_REALM
] = p_credential
->psz_realm
;
563 ppsz_values
[KEY_AUTHTYPE
] = p_credential
->psz_authtype
;
566 if (protocol_set_port(p_url
, psz_port
))
567 ppsz_values
[KEY_PORT
] = psz_port
;
570 if (asprintf(&psz_label
, "LibVLC password for %s://%s%s",
571 p_url
->psz_protocol
, p_url
->psz_host
,
572 psz_path
? psz_path
: "") == -1)
578 const uint8_t *p_password
= (const uint8_t *)
579 (p_credential
->psz_password
!= NULL
? p_credential
->psz_password
: "");
581 bool b_ret
= vlc_keystore_store(p_keystore
, ppsz_values
, p_password
,
582 -1, psz_label
) == VLC_SUCCESS
;