1 | /*************************************************************************** |
2 | * _ _ ____ _ |
3 | * Project ___| | | | _ \| | |
4 | * / __| | | | |_) | | |
5 | * | (__| |_| | _ <| |___ |
6 | * \___|\___/|_| \_\_____| |
7 | * |
8 | * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. |
9 | * |
10 | * This software is licensed as described in the file COPYING, which |
11 | * you should have received as part of this distribution. The terms |
12 | * are also available at https://curl.haxx.se/docs/copyright.html. |
13 | * |
14 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
15 | * copies of the Software, and permit persons to whom the Software is |
16 | * furnished to do so, under the terms of the COPYING file. |
17 | * |
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
19 | * KIND, either express or implied. |
20 | * |
21 | ***************************************************************************/ |
22 | #include "tool_setup.h" |
23 | |
24 | #include "strcase.h" |
25 | |
26 | #define ENABLE_CURLX_PRINTF |
27 | /* use our own printf() functions */ |
28 | #include "curlx.h" |
29 | |
30 | #include "tool_cfgable.h" |
31 | #include "tool_getparam.h" |
32 | #include "tool_getpass.h" |
33 | #include "tool_homedir.h" |
34 | #include "tool_msgs.h" |
35 | #include "tool_paramhlp.h" |
36 | #include "tool_version.h" |
37 | |
38 | #include "memdebug.h" /* keep this as LAST include */ |
39 | |
40 | struct getout *new_getout(struct OperationConfig *config) |
41 | { |
42 | struct getout *node = calloc(1, sizeof(struct getout)); |
43 | struct getout *last = config->url_last; |
44 | if(node) { |
45 | /* append this new node last in the list */ |
46 | if(last) |
47 | last->next = node; |
48 | else |
49 | config->url_list = node; /* first node */ |
50 | |
51 | /* move the last pointer */ |
52 | config->url_last = node; |
53 | |
54 | node->flags = config->default_node_flags; |
55 | } |
56 | return node; |
57 | } |
58 | |
59 | ParameterError file2string(char **bufp, FILE *file) |
60 | { |
61 | char *string = NULL; |
62 | if(file) { |
63 | char *ptr; |
64 | size_t alloc = 512; |
65 | size_t alloc_needed; |
66 | char buffer[256]; |
67 | size_t stringlen = 0; |
68 | string = malloc(alloc); |
69 | if(!string) |
70 | return PARAM_NO_MEM; |
71 | |
72 | while(fgets(buffer, sizeof(buffer), file)) { |
73 | size_t buflen; |
74 | ptr = strchr(buffer, '\r'); |
75 | if(ptr) |
76 | *ptr = '\0'; |
77 | ptr = strchr(buffer, '\n'); |
78 | if(ptr) |
79 | *ptr = '\0'; |
80 | buflen = strlen(buffer); |
81 | alloc_needed = stringlen + buflen + 1; |
82 | if(alloc < alloc_needed) { |
83 | #if SIZEOF_SIZE_T < 8 |
84 | if(alloc >= (size_t)SIZE_T_MAX/2) { |
85 | Curl_safefree(string); |
86 | return PARAM_NO_MEM; |
87 | } |
88 | #endif |
89 | /* doubling is enough since the string to add is always max 256 bytes |
90 | and the alloc size start at 512 */ |
91 | alloc *= 2; |
92 | ptr = realloc(string, alloc); |
93 | if(!ptr) { |
94 | Curl_safefree(string); |
95 | return PARAM_NO_MEM; |
96 | } |
97 | string = ptr; |
98 | } |
99 | strcpy(string + stringlen, buffer); |
100 | stringlen += buflen; |
101 | } |
102 | } |
103 | *bufp = string; |
104 | return PARAM_OK; |
105 | } |
106 | |
107 | ParameterError file2memory(char **bufp, size_t *size, FILE *file) |
108 | { |
109 | char *newbuf; |
110 | char *buffer = NULL; |
111 | size_t nused = 0; |
112 | |
113 | if(file) { |
114 | size_t nread; |
115 | size_t alloc = 512; |
116 | do { |
117 | if(!buffer || (alloc == nused)) { |
118 | /* size_t overflow detection for huge files */ |
119 | if(alloc + 1 > ((size_t)-1)/2) { |
120 | Curl_safefree(buffer); |
121 | return PARAM_NO_MEM; |
122 | } |
123 | alloc *= 2; |
124 | /* allocate an extra char, reserved space, for null termination */ |
125 | newbuf = realloc(buffer, alloc + 1); |
126 | if(!newbuf) { |
127 | Curl_safefree(buffer); |
128 | return PARAM_NO_MEM; |
129 | } |
130 | buffer = newbuf; |
131 | } |
132 | nread = fread(buffer + nused, 1, alloc-nused, file); |
133 | nused += nread; |
134 | } while(nread); |
135 | /* null terminate the buffer in case it's used as a string later */ |
136 | buffer[nused] = '\0'; |
137 | /* free trailing slack space, if possible */ |
138 | if(alloc != nused) { |
139 | newbuf = realloc(buffer, nused + 1); |
140 | if(!newbuf) { |
141 | Curl_safefree(buffer); |
142 | return PARAM_NO_MEM; |
143 | } |
144 | buffer = newbuf; |
145 | } |
146 | /* discard buffer if nothing was read */ |
147 | if(!nused) { |
148 | Curl_safefree(buffer); /* no string */ |
149 | } |
150 | } |
151 | *size = nused; |
152 | *bufp = buffer; |
153 | return PARAM_OK; |
154 | } |
155 | |
156 | void cleanarg(char *str) |
157 | { |
158 | #ifdef HAVE_WRITABLE_ARGV |
159 | /* now that GetStr has copied the contents of nextarg, wipe the next |
160 | * argument out so that the username:password isn't displayed in the |
161 | * system process list */ |
162 | if(str) { |
163 | size_t len = strlen(str); |
164 | memset(str, ' ', len); |
165 | } |
166 | #else |
167 | (void)str; |
168 | #endif |
169 | } |
170 | |
171 | /* |
172 | * Parse the string and write the long in the given address. Return PARAM_OK |
173 | * on success, otherwise a parameter specific error enum. |
174 | * |
175 | * Since this function gets called with the 'nextarg' pointer from within the |
176 | * getparameter a lot, we must check it for NULL before accessing the str |
177 | * data. |
178 | */ |
179 | |
180 | ParameterError str2num(long *val, const char *str) |
181 | { |
182 | if(str) { |
183 | char *endptr; |
184 | long num; |
185 | errno = 0; |
186 | num = strtol(str, &endptr, 10); |
187 | if(errno == ERANGE) |
188 | return PARAM_NUMBER_TOO_LARGE; |
189 | if((endptr != str) && (endptr == str + strlen(str))) { |
190 | *val = num; |
191 | return PARAM_OK; /* Ok */ |
192 | } |
193 | } |
194 | return PARAM_BAD_NUMERIC; /* badness */ |
195 | } |
196 | |
197 | /* |
198 | * Parse the string and write the long in the given address. Return PARAM_OK |
199 | * on success, otherwise a parameter error enum. ONLY ACCEPTS POSITIVE NUMBERS! |
200 | * |
201 | * Since this function gets called with the 'nextarg' pointer from within the |
202 | * getparameter a lot, we must check it for NULL before accessing the str |
203 | * data. |
204 | */ |
205 | |
206 | ParameterError str2unum(long *val, const char *str) |
207 | { |
208 | ParameterError result = str2num(val, str); |
209 | if(result != PARAM_OK) |
210 | return result; |
211 | if(*val < 0) |
212 | return PARAM_NEGATIVE_NUMERIC; |
213 | |
214 | return PARAM_OK; |
215 | } |
216 | |
217 | /* |
218 | * Parse the string and write the long in the given address if it is below the |
219 | * maximum allowed value. Return PARAM_OK on success, otherwise a parameter |
220 | * error enum. ONLY ACCEPTS POSITIVE NUMBERS! |
221 | * |
222 | * Since this function gets called with the 'nextarg' pointer from within the |
223 | * getparameter a lot, we must check it for NULL before accessing the str |
224 | * data. |
225 | */ |
226 | |
227 | ParameterError str2unummax(long *val, const char *str, long max) |
228 | { |
229 | ParameterError result = str2unum(val, str); |
230 | if(result != PARAM_OK) |
231 | return result; |
232 | if(*val > max) |
233 | return PARAM_NUMBER_TOO_LARGE; |
234 | |
235 | return PARAM_OK; |
236 | } |
237 | |
238 | |
239 | /* |
240 | * Parse the string and write the double in the given address. Return PARAM_OK |
241 | * on success, otherwise a parameter specific error enum. |
242 | * |
243 | * The 'max' argument is the maximum value allowed, as the numbers are often |
244 | * multiplied when later used. |
245 | * |
246 | * Since this function gets called with the 'nextarg' pointer from within the |
247 | * getparameter a lot, we must check it for NULL before accessing the str |
248 | * data. |
249 | */ |
250 | |
251 | static ParameterError str2double(double *val, const char *str, long max) |
252 | { |
253 | if(str) { |
254 | char *endptr; |
255 | double num; |
256 | errno = 0; |
257 | num = strtod(str, &endptr); |
258 | if(errno == ERANGE) |
259 | return PARAM_NUMBER_TOO_LARGE; |
260 | if(num > max) { |
261 | /* too large */ |
262 | return PARAM_NUMBER_TOO_LARGE; |
263 | } |
264 | if((endptr != str) && (endptr == str + strlen(str))) { |
265 | *val = num; |
266 | return PARAM_OK; /* Ok */ |
267 | } |
268 | } |
269 | return PARAM_BAD_NUMERIC; /* badness */ |
270 | } |
271 | |
272 | /* |
273 | * Parse the string and write the double in the given address. Return PARAM_OK |
274 | * on success, otherwise a parameter error enum. ONLY ACCEPTS POSITIVE NUMBERS! |
275 | * |
276 | * The 'max' argument is the maximum value allowed, as the numbers are often |
277 | * multiplied when later used. |
278 | * |
279 | * Since this function gets called with the 'nextarg' pointer from within the |
280 | * getparameter a lot, we must check it for NULL before accessing the str |
281 | * data. |
282 | */ |
283 | |
284 | ParameterError str2udouble(double *valp, const char *str, long max) |
285 | { |
286 | double value; |
287 | ParameterError result = str2double(&value, str, max); |
288 | if(result != PARAM_OK) |
289 | return result; |
290 | if(value < 0) |
291 | return PARAM_NEGATIVE_NUMERIC; |
292 | |
293 | *valp = value; |
294 | return PARAM_OK; |
295 | } |
296 | |
297 | /* |
298 | * Parse the string and modify the long in the given address. Return |
299 | * non-zero on failure, zero on success. |
300 | * |
301 | * The string is a list of protocols |
302 | * |
303 | * Since this function gets called with the 'nextarg' pointer from within the |
304 | * getparameter a lot, we must check it for NULL before accessing the str |
305 | * data. |
306 | */ |
307 | |
308 | long proto2num(struct OperationConfig *config, long *val, const char *str) |
309 | { |
310 | char *buffer; |
311 | const char *sep = "," ; |
312 | char *token; |
313 | |
314 | static struct sprotos { |
315 | const char *name; |
316 | long bit; |
317 | } const protos[] = { |
318 | { "all" , CURLPROTO_ALL }, |
319 | { "http" , CURLPROTO_HTTP }, |
320 | { "https" , CURLPROTO_HTTPS }, |
321 | { "ftp" , CURLPROTO_FTP }, |
322 | { "ftps" , CURLPROTO_FTPS }, |
323 | { "scp" , CURLPROTO_SCP }, |
324 | { "sftp" , CURLPROTO_SFTP }, |
325 | { "telnet" , CURLPROTO_TELNET }, |
326 | { "ldap" , CURLPROTO_LDAP }, |
327 | { "ldaps" , CURLPROTO_LDAPS }, |
328 | { "dict" , CURLPROTO_DICT }, |
329 | { "file" , CURLPROTO_FILE }, |
330 | { "tftp" , CURLPROTO_TFTP }, |
331 | { "imap" , CURLPROTO_IMAP }, |
332 | { "imaps" , CURLPROTO_IMAPS }, |
333 | { "pop3" , CURLPROTO_POP3 }, |
334 | { "pop3s" , CURLPROTO_POP3S }, |
335 | { "smtp" , CURLPROTO_SMTP }, |
336 | { "smtps" , CURLPROTO_SMTPS }, |
337 | { "rtsp" , CURLPROTO_RTSP }, |
338 | { "gopher" , CURLPROTO_GOPHER }, |
339 | { "smb" , CURLPROTO_SMB }, |
340 | { "smbs" , CURLPROTO_SMBS }, |
341 | { NULL, 0 } |
342 | }; |
343 | |
344 | if(!str) |
345 | return 1; |
346 | |
347 | buffer = strdup(str); /* because strtok corrupts it */ |
348 | if(!buffer) |
349 | return 1; |
350 | |
351 | /* Allow strtok() here since this isn't used threaded */ |
352 | /* !checksrc! disable BANNEDFUNC 2 */ |
353 | for(token = strtok(buffer, sep); |
354 | token; |
355 | token = strtok(NULL, sep)) { |
356 | enum e_action { allow, deny, set } action = allow; |
357 | |
358 | struct sprotos const *pp; |
359 | |
360 | /* Process token modifiers */ |
361 | while(!ISALNUM(*token)) { /* may be NULL if token is all modifiers */ |
362 | switch (*token++) { |
363 | case '=': |
364 | action = set; |
365 | break; |
366 | case '-': |
367 | action = deny; |
368 | break; |
369 | case '+': |
370 | action = allow; |
371 | break; |
372 | default: /* Includes case of terminating NULL */ |
373 | Curl_safefree(buffer); |
374 | return 1; |
375 | } |
376 | } |
377 | |
378 | for(pp = protos; pp->name; pp++) { |
379 | if(curl_strequal(token, pp->name)) { |
380 | switch(action) { |
381 | case deny: |
382 | *val &= ~(pp->bit); |
383 | break; |
384 | case allow: |
385 | *val |= pp->bit; |
386 | break; |
387 | case set: |
388 | *val = pp->bit; |
389 | break; |
390 | } |
391 | break; |
392 | } |
393 | } |
394 | |
395 | if(!(pp->name)) { /* unknown protocol */ |
396 | /* If they have specified only this protocol, we say treat it as |
397 | if no protocols are allowed */ |
398 | if(action == set) |
399 | *val = 0; |
400 | warnf(config->global, "unrecognized protocol '%s'\n" , token); |
401 | } |
402 | } |
403 | Curl_safefree(buffer); |
404 | return 0; |
405 | } |
406 | |
407 | /** |
408 | * Check if the given string is a protocol supported by libcurl |
409 | * |
410 | * @param str the protocol name |
411 | * @return PARAM_OK protocol supported |
412 | * @return PARAM_LIBCURL_UNSUPPORTED_PROTOCOL protocol not supported |
413 | * @return PARAM_REQUIRES_PARAMETER missing parameter |
414 | */ |
415 | int check_protocol(const char *str) |
416 | { |
417 | const char * const *pp; |
418 | const curl_version_info_data *curlinfo = curl_version_info(CURLVERSION_NOW); |
419 | if(!str) |
420 | return PARAM_REQUIRES_PARAMETER; |
421 | for(pp = curlinfo->protocols; *pp; pp++) { |
422 | if(curl_strequal(*pp, str)) |
423 | return PARAM_OK; |
424 | } |
425 | return PARAM_LIBCURL_UNSUPPORTED_PROTOCOL; |
426 | } |
427 | |
428 | /** |
429 | * Parses the given string looking for an offset (which may be a |
430 | * larger-than-integer value). The offset CANNOT be negative! |
431 | * |
432 | * @param val the offset to populate |
433 | * @param str the buffer containing the offset |
434 | * @return PARAM_OK if successful, a parameter specific error enum if failure. |
435 | */ |
436 | ParameterError str2offset(curl_off_t *val, const char *str) |
437 | { |
438 | char *endptr; |
439 | if(str[0] == '-') |
440 | /* offsets aren't negative, this indicates weird input */ |
441 | return PARAM_NEGATIVE_NUMERIC; |
442 | |
443 | #if(SIZEOF_CURL_OFF_T > SIZEOF_LONG) |
444 | { |
445 | CURLofft offt = curlx_strtoofft(str, &endptr, 0, val); |
446 | if(CURL_OFFT_FLOW == offt) |
447 | return PARAM_NUMBER_TOO_LARGE; |
448 | else if(CURL_OFFT_INVAL == offt) |
449 | return PARAM_BAD_NUMERIC; |
450 | } |
451 | #else |
452 | errno = 0; |
453 | *val = strtol(str, &endptr, 0); |
454 | if((*val == LONG_MIN || *val == LONG_MAX) && errno == ERANGE) |
455 | return PARAM_NUMBER_TOO_LARGE; |
456 | #endif |
457 | if((endptr != str) && (endptr == str + strlen(str))) |
458 | return PARAM_OK; |
459 | |
460 | return PARAM_BAD_NUMERIC; |
461 | } |
462 | |
463 | static CURLcode checkpasswd(const char *kind, /* for what purpose */ |
464 | const size_t i, /* operation index */ |
465 | const bool last, /* TRUE if last operation */ |
466 | char **userpwd) /* pointer to allocated string */ |
467 | { |
468 | char *psep; |
469 | char *osep; |
470 | |
471 | if(!*userpwd) |
472 | return CURLE_OK; |
473 | |
474 | /* Attempt to find the password separator */ |
475 | psep = strchr(*userpwd, ':'); |
476 | |
477 | /* Attempt to find the options separator */ |
478 | osep = strchr(*userpwd, ';'); |
479 | |
480 | if(!psep && **userpwd != ';') { |
481 | /* no password present, prompt for one */ |
482 | char passwd[256] = "" ; |
483 | char prompt[256]; |
484 | size_t passwdlen; |
485 | size_t userlen = strlen(*userpwd); |
486 | char *passptr; |
487 | |
488 | if(osep) |
489 | *osep = '\0'; |
490 | |
491 | /* build a nice-looking prompt */ |
492 | if(!i && last) |
493 | curlx_msnprintf(prompt, sizeof(prompt), |
494 | "Enter %s password for user '%s':" , |
495 | kind, *userpwd); |
496 | else |
497 | curlx_msnprintf(prompt, sizeof(prompt), |
498 | "Enter %s password for user '%s' on URL #%zu:" , |
499 | kind, *userpwd, i + 1); |
500 | |
501 | /* get password */ |
502 | getpass_r(prompt, passwd, sizeof(passwd)); |
503 | passwdlen = strlen(passwd); |
504 | |
505 | if(osep) |
506 | *osep = ';'; |
507 | |
508 | /* extend the allocated memory area to fit the password too */ |
509 | passptr = realloc(*userpwd, |
510 | passwdlen + 1 + /* an extra for the colon */ |
511 | userlen + 1); /* an extra for the zero */ |
512 | if(!passptr) |
513 | return CURLE_OUT_OF_MEMORY; |
514 | |
515 | /* append the password separated with a colon */ |
516 | passptr[userlen] = ':'; |
517 | memcpy(&passptr[userlen + 1], passwd, passwdlen + 1); |
518 | *userpwd = passptr; |
519 | } |
520 | |
521 | return CURLE_OK; |
522 | } |
523 | |
524 | ParameterError add2list(struct curl_slist **list, const char *ptr) |
525 | { |
526 | struct curl_slist *newlist = curl_slist_append(*list, ptr); |
527 | if(newlist) |
528 | *list = newlist; |
529 | else |
530 | return PARAM_NO_MEM; |
531 | |
532 | return PARAM_OK; |
533 | } |
534 | |
535 | int ftpfilemethod(struct OperationConfig *config, const char *str) |
536 | { |
537 | if(curl_strequal("singlecwd" , str)) |
538 | return CURLFTPMETHOD_SINGLECWD; |
539 | if(curl_strequal("nocwd" , str)) |
540 | return CURLFTPMETHOD_NOCWD; |
541 | if(curl_strequal("multicwd" , str)) |
542 | return CURLFTPMETHOD_MULTICWD; |
543 | |
544 | warnf(config->global, "unrecognized ftp file method '%s', using default\n" , |
545 | str); |
546 | |
547 | return CURLFTPMETHOD_MULTICWD; |
548 | } |
549 | |
550 | int ftpcccmethod(struct OperationConfig *config, const char *str) |
551 | { |
552 | if(curl_strequal("passive" , str)) |
553 | return CURLFTPSSL_CCC_PASSIVE; |
554 | if(curl_strequal("active" , str)) |
555 | return CURLFTPSSL_CCC_ACTIVE; |
556 | |
557 | warnf(config->global, "unrecognized ftp CCC method '%s', using default\n" , |
558 | str); |
559 | |
560 | return CURLFTPSSL_CCC_PASSIVE; |
561 | } |
562 | |
563 | long delegation(struct OperationConfig *config, char *str) |
564 | { |
565 | if(curl_strequal("none" , str)) |
566 | return CURLGSSAPI_DELEGATION_NONE; |
567 | if(curl_strequal("policy" , str)) |
568 | return CURLGSSAPI_DELEGATION_POLICY_FLAG; |
569 | if(curl_strequal("always" , str)) |
570 | return CURLGSSAPI_DELEGATION_FLAG; |
571 | |
572 | warnf(config->global, "unrecognized delegation method '%s', using none\n" , |
573 | str); |
574 | |
575 | return CURLGSSAPI_DELEGATION_NONE; |
576 | } |
577 | |
578 | /* |
579 | * my_useragent: returns allocated string with default user agent |
580 | */ |
581 | static char *my_useragent(void) |
582 | { |
583 | return strdup(CURL_NAME "/" CURL_VERSION); |
584 | } |
585 | |
586 | CURLcode get_args(struct OperationConfig *config, const size_t i) |
587 | { |
588 | CURLcode result = CURLE_OK; |
589 | bool last = (config->next ? FALSE : TRUE); |
590 | |
591 | /* Check we have a password for the given host user */ |
592 | if(config->userpwd && !config->oauth_bearer) { |
593 | result = checkpasswd("host" , i, last, &config->userpwd); |
594 | if(result) |
595 | return result; |
596 | } |
597 | |
598 | /* Check we have a password for the given proxy user */ |
599 | if(config->proxyuserpwd) { |
600 | result = checkpasswd("proxy" , i, last, &config->proxyuserpwd); |
601 | if(result) |
602 | return result; |
603 | } |
604 | |
605 | /* Check we have a user agent */ |
606 | if(!config->useragent) { |
607 | config->useragent = my_useragent(); |
608 | if(!config->useragent) { |
609 | helpf(config->global->errors, "out of memory\n" ); |
610 | result = CURLE_OUT_OF_MEMORY; |
611 | } |
612 | } |
613 | |
614 | return result; |
615 | } |
616 | |
617 | /* |
618 | * Parse the string and modify ssl_version in the val argument. Return PARAM_OK |
619 | * on success, otherwise a parameter error enum. ONLY ACCEPTS POSITIVE NUMBERS! |
620 | * |
621 | * Since this function gets called with the 'nextarg' pointer from within the |
622 | * getparameter a lot, we must check it for NULL before accessing the str |
623 | * data. |
624 | */ |
625 | |
626 | ParameterError str2tls_max(long *val, const char *str) |
627 | { |
628 | static struct s_tls_max { |
629 | const char *tls_max_str; |
630 | long tls_max; |
631 | } const tls_max_array[] = { |
632 | { "default" , CURL_SSLVERSION_MAX_DEFAULT }, |
633 | { "1.0" , CURL_SSLVERSION_MAX_TLSv1_0 }, |
634 | { "1.1" , CURL_SSLVERSION_MAX_TLSv1_1 }, |
635 | { "1.2" , CURL_SSLVERSION_MAX_TLSv1_2 }, |
636 | { "1.3" , CURL_SSLVERSION_MAX_TLSv1_3 } |
637 | }; |
638 | size_t i = 0; |
639 | if(!str) |
640 | return PARAM_REQUIRES_PARAMETER; |
641 | for(i = 0; i < sizeof(tls_max_array)/sizeof(tls_max_array[0]); i++) { |
642 | if(!strcmp(str, tls_max_array[i].tls_max_str)) { |
643 | *val = tls_max_array[i].tls_max; |
644 | return PARAM_OK; |
645 | } |
646 | } |
647 | return PARAM_BAD_USE; |
648 | } |
649 | |