]> git.ipfire.org Git - thirdparty/cups.git/blame - cgi-bin/var.c
CVE-2018-4700: Linux session cookies used a predictable random number seed.
[thirdparty/cups.git] / cgi-bin / var.c
CommitLineData
ef416fc2 1/*
7e86f2f6 2 * CGI form variable and array functions for CUPS.
ef416fc2 3 *
fab4b71e 4 * Copyright 2007-2015 by Apple Inc.
7e86f2f6 5 * Copyright 1997-2005 by Easy Software Products.
ef416fc2 6 *
e3101897 7 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
7e86f2f6
MS
8 */
9
10/*
11 * Include necessary headers...
ef416fc2 12 */
13
14/*#define DEBUG*/
15#include "cgi-private.h"
f8b3a85b 16#include <cups/http.h>
f8b3a85b
MS
17
18
19/*
20 * Session ID name
21 */
22
23#define CUPS_SID "org.cups.sid"
ef416fc2 24
25
26/*
27 * Data structure to hold all the CGI form variables and arrays...
28 */
29
30typedef struct /**** Form variable structure ****/
31{
32 const char *name; /* Name of variable */
33 int nvalues, /* Number of values */
34 avalues; /* Number of values allocated */
35 const char **values; /* Value(s) of variable */
36} _cgi_var_t;
37
38
39/*
40 * Local globals...
41 */
42
f8b3a85b
MS
43static int num_cookies = 0;/* Number of cookies */
44static cups_option_t *cookies = NULL;/* Cookies */
ef416fc2 45static int form_count = 0, /* Form variable count */
46 form_alloc = 0; /* Number of variables allocated */
47static _cgi_var_t *form_vars = NULL;
48 /* Form variables */
49static cgi_file_t *form_file = NULL;
50 /* Uploaded file */
51
52
53/*
54 * Local functions...
55 */
56
57static void cgi_add_variable(const char *name, int element,
58 const char *value);
59static int cgi_compare_variables(const _cgi_var_t *v1,
60 const _cgi_var_t *v2);
61static _cgi_var_t *cgi_find_variable(const char *name);
f8b3a85b 62static void cgi_initialize_cookies(void);
ef416fc2 63static int cgi_initialize_get(void);
64static int cgi_initialize_multipart(const char *boundary);
65static int cgi_initialize_post(void);
66static int cgi_initialize_string(const char *data);
67static const char *cgi_passwd(const char *prompt);
f8b3a85b 68static const char *cgi_set_sid(void);
ef416fc2 69static void cgi_sort_variables(void);
70static void cgi_unlink_file(void);
71
72
73/*
74 * 'cgiCheckVariables()' - Check for the presence of "required" variables.
75 *
76 * Names may be separated by spaces and/or commas.
77 */
78
79int /* O - 1 if all variables present, 0 otherwise */
80cgiCheckVariables(const char *names) /* I - Variables to look for */
81{
82 char name[255], /* Current variable name */
83 *s; /* Pointer in string */
84 const char *val; /* Value of variable */
85 int element; /* Array element number */
86
87
88 if (names == NULL)
89 return (1);
90
91 while (*names != '\0')
92 {
93 while (*names == ' ' || *names == ',')
94 names ++;
95
96 for (s = name; *names != '\0' && *names != ' ' && *names != ','; s ++, names ++)
97 *s = *names;
98
99 *s = 0;
100 if (name[0] == '\0')
101 break;
102
103 if ((s = strrchr(name, '-')) != NULL)
104 {
105 *s = '\0';
106 element = atoi(s + 1) - 1;
107 val = cgiGetArray(name, element);
108 }
109 else
110 val = cgiGetVariable(name);
111
112 if (val == NULL)
113 return (0);
114
115 if (*val == '\0')
116 return (0); /* Can't be blank, either! */
117 }
118
119 return (1);
120}
121
122
ef55b745
MS
123/*
124 * 'cgiClearVariables()' - Clear all form variables.
125 */
126
127void
128cgiClearVariables(void)
129{
130 int i, j; /* Looping vars */
131 _cgi_var_t *v; /* Current variable */
132
133
82cc1f9a
MS
134 fputs("DEBUG: cgiClearVariables called.\n", stderr);
135
ef55b745
MS
136 for (v = form_vars, i = form_count; i > 0; v ++, i --)
137 {
138 _cupsStrFree(v->name);
139 for (j = 0; j < v->nvalues; j ++)
140 if (v->values[j])
141 _cupsStrFree(v->values[j]);
142 }
143
144 form_count = 0;
145
146 cgi_unlink_file();
147}
148
149
ef416fc2 150/*
f8b3a85b 151 * 'cgiGetArray()' - Get an element from a form array.
ef416fc2 152 */
153
154const char * /* O - Element value or NULL */
155cgiGetArray(const char *name, /* I - Name of array variable */
156 int element) /* I - Element number (0 to N) */
157{
158 _cgi_var_t *var; /* Pointer to variable */
159
160
161 if ((var = cgi_find_variable(name)) == NULL)
162 return (NULL);
163
ef416fc2 164 if (element < 0 || element >= var->nvalues)
165 return (NULL);
166
ef55b745 167 return (_cupsStrRetain(var->values[element]));
ef416fc2 168}
169
170
f8b3a85b
MS
171/*
172 * 'cgiGetCookie()' - Get a cookie value.
173 */
174
175const char * /* O - Value or NULL */
176cgiGetCookie(const char *name) /* I - Name of cookie */
177{
178 return (cupsGetOption(name, num_cookies, cookies));
179}
180
181
ef416fc2 182/*
183 * 'cgiGetFile()' - Get the file (if any) that was submitted in the form.
184 */
185
186const cgi_file_t * /* O - Attached file or NULL */
187cgiGetFile(void)
188{
189 return (form_file);
190}
191
192
193/*
194 * 'cgiGetSize()' - Get the size of a form array value.
195 */
196
197int /* O - Number of elements */
198cgiGetSize(const char *name) /* I - Name of variable */
199{
200 _cgi_var_t *var; /* Pointer to variable */
201
202
203 if ((var = cgi_find_variable(name)) == NULL)
204 return (0);
205
206 return (var->nvalues);
207}
208
209
210/*
f8b3a85b 211 * 'cgiGetVariable()' - Get a CGI variable from the database.
ef416fc2 212 *
213 * Returns NULL if the variable doesn't exist. If the variable is an
f8b3a85b 214 * array of values, returns the last element.
ef416fc2 215 */
216
217const char * /* O - Value of variable */
218cgiGetVariable(const char *name) /* I - Name of variable */
219{
220 const _cgi_var_t *var; /* Returned variable */
221
222
223 var = cgi_find_variable(name);
224
ef55b745 225 return ((var == NULL) ? NULL : _cupsStrRetain(var->values[var->nvalues - 1]));
ef416fc2 226}
227
228
229/*
f8b3a85b 230 * 'cgiInitialize()' - Initialize the CGI variable "database".
ef416fc2 231 */
232
233int /* O - Non-zero if there was form data */
234cgiInitialize(void)
235{
f8b3a85b
MS
236 const char *method, /* Form posting method */
237 *content_type, /* Content-Type of post data */
238 *cups_sid_cookie, /* SID cookie */
239 *cups_sid_form; /* SID form variable */
ef416fc2 240
241
242 /*
243 * Setup a password callback for authentication...
244 */
245
246 cupsSetPasswordCB(cgi_passwd);
247
80ca4592 248 /*
249 * Set the locale so that times, etc. are formatted properly...
250 */
251
252 setlocale(LC_ALL, "");
253
ef416fc2 254#ifdef DEBUG
255 /*
256 * Disable output buffering to find bugs...
257 */
258
259 setbuf(stdout, NULL);
ef416fc2 260#endif /* DEBUG */
261
f8b3a85b
MS
262 /*
263 * Get cookies...
264 */
265
266 cgi_initialize_cookies();
267
268 if ((cups_sid_cookie = cgiGetCookie(CUPS_SID)) == NULL)
269 {
270 fputs("DEBUG: " CUPS_SID " cookie not found, initializing!\n", stderr);
271 cups_sid_cookie = cgi_set_sid();
272 }
273
274 fprintf(stderr, "DEBUG: " CUPS_SID " cookie is \"%s\"\n", cups_sid_cookie);
275
ef416fc2 276 /*
277 * Get the request method (GET or POST)...
278 */
279
280 method = getenv("REQUEST_METHOD");
281 content_type = getenv("CONTENT_TYPE");
282 if (!method)
283 return (0);
284
285 /*
286 * Grab form data from the corresponding location...
287 */
288
88f9aafc 289 if (!_cups_strcasecmp(method, "GET"))
ef416fc2 290 return (cgi_initialize_get());
88f9aafc 291 else if (!_cups_strcasecmp(method, "POST") && content_type)
ef416fc2 292 {
293 const char *boundary = strstr(content_type, "boundary=");
294
295 if (boundary)
296 boundary += 9;
297
298 if (content_type && !strncmp(content_type, "multipart/form-data; ", 21))
f8b3a85b
MS
299 {
300 if (!cgi_initialize_multipart(boundary))
301 return (0);
302 }
303 else if (!cgi_initialize_post())
304 return (0);
305
306 if ((cups_sid_form = cgiGetVariable(CUPS_SID)) == NULL ||
307 strcmp(cups_sid_cookie, cups_sid_form))
308 {
309 if (cups_sid_form)
310 fprintf(stderr, "DEBUG: " CUPS_SID " form variable is \"%s\"\n",
311 cups_sid_form);
312 else
313 fputs("DEBUG: " CUPS_SID " form variable is not present.\n", stderr);
314
315 cgiClearVariables();
316 return (0);
317 }
ef416fc2 318 else
f8b3a85b 319 return (1);
ef416fc2 320 }
321 else
322 return (0);
323}
324
325
326/*
327 * 'cgiIsPOST()' - Determine whether this page was POSTed.
328 */
329
330int /* O - 1 if POST, 0 if GET */
331cgiIsPOST(void)
332{
333 const char *method; /* REQUEST_METHOD environment variable */
334
335
336 if ((method = getenv("REQUEST_METHOD")) == NULL)
337 return (0);
338 else
339 return (!strcmp(method, "POST"));
340}
341
342
343/*
344 * 'cgiSetArray()' - Set array element N to the specified string.
345 *
346 * If the variable array is smaller than (element + 1), the intervening
347 * elements are set to NULL.
348 */
349
350void
351cgiSetArray(const char *name, /* I - Name of variable */
352 int element, /* I - Element number (0 to N) */
353 const char *value) /* I - Value of variable */
354{
355 int i; /* Looping var */
356 _cgi_var_t *var; /* Returned variable */
357
358
359 if (name == NULL || value == NULL || element < 0 || element > 100000)
360 return;
361
82cc1f9a
MS
362 fprintf(stderr, "DEBUG: cgiSetArray: %s[%d]=\"%s\"\n", name, element, value);
363
ef416fc2 364 if ((var = cgi_find_variable(name)) == NULL)
365 {
366 cgi_add_variable(name, element, value);
367 cgi_sort_variables();
368 }
369 else
370 {
371 if (element >= var->avalues)
372 {
91c84a35
MS
373 const char **temp; /* Temporary pointer */
374
375 temp = (const char **)realloc((void *)(var->values),
7e86f2f6 376 sizeof(char *) * (size_t)(element + 16));
91c84a35
MS
377 if (!temp)
378 return;
379
ef416fc2 380 var->avalues = element + 16;
91c84a35 381 var->values = temp;
ef416fc2 382 }
383
384 if (element >= var->nvalues)
385 {
386 for (i = var->nvalues; i < element; i ++)
387 var->values[i] = NULL;
388
389 var->nvalues = element + 1;
390 }
391 else if (var->values[element])
ef55b745 392 _cupsStrFree((char *)var->values[element]);
ef416fc2 393
ef55b745 394 var->values[element] = _cupsStrAlloc(value);
ef416fc2 395 }
396}
397
398
f8b3a85b
MS
399/*
400 * 'cgiSetCookie()' - Set a cookie value.
401 */
402
403void
404cgiSetCookie(const char *name, /* I - Name */
405 const char *value, /* I - Value */
406 const char *path, /* I - Path (typically "/") */
407 const char *domain, /* I - Domain name */
408 time_t expires, /* I - Expiration date (0 for session) */
409 int secure) /* I - Require SSL */
410{
411 num_cookies = cupsAddOption(name, value, num_cookies, &cookies);
412
413 printf("Set-Cookie: %s=%s;", name, value);
414 if (path)
85dda01c 415 printf(" path=%s;", path);
f8b3a85b 416 if (domain)
85dda01c 417 printf(" domain=%s;", domain);
f8b3a85b
MS
418 if (expires)
419 {
420 char date[256]; /* Date string */
421
85dda01c 422 printf(" expires=%s;", httpGetDateString2(expires, date, sizeof(date)));
f8b3a85b
MS
423 }
424 if (secure)
6c2b2b19 425 puts(" httponly; secure;");
f8b3a85b 426 else
6c2b2b19 427 puts(" httponly;");
f8b3a85b
MS
428}
429
430
ef416fc2 431/*
432 * 'cgiSetSize()' - Set the array size.
433 */
434
435void
436cgiSetSize(const char *name, /* I - Name of variable */
437 int size) /* I - Number of elements (0 to N) */
438{
439 int i; /* Looping var */
440 _cgi_var_t *var; /* Returned variable */
441
442
443 if (name == NULL || size < 0 || size > 100000)
444 return;
445
446 if ((var = cgi_find_variable(name)) == NULL)
447 return;
448
449 if (size >= var->avalues)
450 {
91c84a35
MS
451 const char **temp; /* Temporary pointer */
452
453 temp = (const char **)realloc((void *)(var->values),
7e86f2f6 454 sizeof(char *) * (size_t)(size + 16));
91c84a35
MS
455 if (!temp)
456 return;
457
ef416fc2 458 var->avalues = size + 16;
91c84a35 459 var->values = temp;
ef416fc2 460 }
461
462 if (size > var->nvalues)
463 {
464 for (i = var->nvalues; i < size; i ++)
465 var->values[i] = NULL;
466 }
467 else if (size < var->nvalues)
468 {
469 for (i = size; i < var->nvalues; i ++)
470 if (var->values[i])
ef55b745 471 _cupsStrFree((void *)(var->values[i]));
ef416fc2 472 }
473
474 var->nvalues = size;
475}
476
477
478/*
f8b3a85b 479 * 'cgiSetVariable()' - Set a CGI variable in the database.
ef416fc2 480 *
481 * If the variable is an array, this truncates the array to a single element.
482 */
483
484void
485cgiSetVariable(const char *name, /* I - Name of variable */
486 const char *value) /* I - Value of variable */
487{
488 int i; /* Looping var */
489 _cgi_var_t *var; /* Returned variable */
490
491
492 if (name == NULL || value == NULL)
493 return;
494
82cc1f9a
MS
495 fprintf(stderr, "cgiSetVariable: %s=\"%s\"\n", name, value);
496
ef416fc2 497 if ((var = cgi_find_variable(name)) == NULL)
498 {
499 cgi_add_variable(name, 0, value);
500 cgi_sort_variables();
501 }
502 else
503 {
504 for (i = 0; i < var->nvalues; i ++)
505 if (var->values[i])
ef55b745 506 _cupsStrFree((char *)var->values[i]);
ef416fc2 507
ef55b745 508 var->values[0] = _cupsStrAlloc(value);
ef416fc2 509 var->nvalues = 1;
510 }
511}
512
513
514/*
515 * 'cgi_add_variable()' - Add a form variable.
516 */
517
518static void
519cgi_add_variable(const char *name, /* I - Variable name */
520 int element, /* I - Array element number */
521 const char *value) /* I - Variable value */
522{
91c84a35 523 _cgi_var_t *var; /* New variable */
ef416fc2 524
525
526 if (name == NULL || value == NULL || element < 0 || element > 100000)
527 return;
528
ef416fc2 529 if (form_count >= form_alloc)
530 {
91c84a35
MS
531 _cgi_var_t *temp_vars; /* Temporary form pointer */
532
533
ef416fc2 534 if (form_alloc == 0)
91c84a35 535 temp_vars = malloc(sizeof(_cgi_var_t) * 16);
ef416fc2 536 else
7e86f2f6 537 temp_vars = realloc(form_vars, (size_t)(form_alloc + 16) * sizeof(_cgi_var_t));
91c84a35
MS
538
539 if (!temp_vars)
540 return;
ef416fc2 541
91c84a35 542 form_vars = temp_vars;
ef416fc2 543 form_alloc += 16;
544 }
545
91c84a35
MS
546 var = form_vars + form_count;
547
7e86f2f6 548 if ((var->values = calloc((size_t)element + 1, sizeof(char *))) == NULL)
91c84a35
MS
549 return;
550
ef55b745 551 var->name = _cupsStrAlloc(name);
ef416fc2 552 var->nvalues = element + 1;
553 var->avalues = element + 1;
ef55b745 554 var->values[element] = _cupsStrAlloc(value);
ef416fc2 555
556 form_count ++;
557}
558
559
560/*
561 * 'cgi_compare_variables()' - Compare two variables.
562 */
563
564static int /* O - Result of comparison */
565cgi_compare_variables(
566 const _cgi_var_t *v1, /* I - First variable */
567 const _cgi_var_t *v2) /* I - Second variable */
568{
88f9aafc 569 return (_cups_strcasecmp(v1->name, v2->name));
ef416fc2 570}
571
572
573/*
f8b3a85b 574 * 'cgi_find_variable()' - Find a variable.
ef416fc2 575 */
576
577static _cgi_var_t * /* O - Variable pointer or NULL */
578cgi_find_variable(const char *name) /* I - Name of variable */
579{
580 _cgi_var_t key; /* Search key */
581
582
583 if (form_count < 1 || name == NULL)
584 return (NULL);
585
586 key.name = name;
587
7e86f2f6 588 return ((_cgi_var_t *)bsearch(&key, form_vars, (size_t)form_count, sizeof(_cgi_var_t),
ef416fc2 589 (int (*)(const void *, const void *))cgi_compare_variables));
590}
591
592
f8b3a85b
MS
593/*
594 * 'cgi_initialize_cookies()' - Initialize cookies.
595 */
596
597static void
598cgi_initialize_cookies(void)
599{
600 const char *cookie; /* HTTP_COOKIE environment variable */
601 char name[128], /* Name string */
602 value[512], /* Value string */
603 *ptr; /* Pointer into name/value */
604
605
606 if ((cookie = getenv("HTTP_COOKIE")) == NULL)
607 return;
608
609 while (*cookie)
610 {
fab4b71e
MS
611 int skip = 0; /* Skip this cookie? */
612
f8b3a85b
MS
613 /*
614 * Skip leading whitespace...
615 */
616
617 while (isspace(*cookie & 255))
618 cookie ++;
619 if (!*cookie)
620 break;
621
622 /*
623 * Copy the name...
624 */
625
626 for (ptr = name; *cookie && *cookie != '=';)
627 if (ptr < (name + sizeof(name) - 1))
fab4b71e 628 {
f8b3a85b 629 *ptr++ = *cookie++;
fab4b71e 630 }
f8b3a85b 631 else
fab4b71e
MS
632 {
633 skip = 1;
634 cookie ++;
635 }
f8b3a85b
MS
636
637 if (*cookie != '=')
638 break;
639
640 *ptr = '\0';
641 cookie ++;
642
643 /*
644 * Then the value...
645 */
646
647 if (*cookie == '\"')
648 {
649 for (cookie ++, ptr = value; *cookie && *cookie != '\"';)
650 if (ptr < (value + sizeof(value) - 1))
fab4b71e 651 {
f8b3a85b 652 *ptr++ = *cookie++;
fab4b71e 653 }
f8b3a85b 654 else
fab4b71e
MS
655 {
656 skip = 1;
657 cookie ++;
658 }
f8b3a85b
MS
659
660 if (*cookie == '\"')
661 cookie ++;
fab4b71e
MS
662 else
663 skip = 1;
f8b3a85b
MS
664 }
665 else
666 {
667 for (ptr = value; *cookie && *cookie != ';';)
668 if (ptr < (value + sizeof(value) - 1))
fab4b71e 669 {
f8b3a85b 670 *ptr++ = *cookie++;
fab4b71e 671 }
f8b3a85b 672 else
fab4b71e
MS
673 {
674 skip = 1;
675 cookie ++;
676 }
f8b3a85b
MS
677 }
678
679 if (*cookie == ';')
680 cookie ++;
681 else if (*cookie)
fab4b71e 682 skip = 1;
f8b3a85b
MS
683
684 *ptr = '\0';
685
686 /*
687 * Then add the cookie to an array as long as the name doesn't start with
688 * "$"...
689 */
690
fab4b71e 691 if (name[0] != '$' && !skip)
f8b3a85b
MS
692 num_cookies = cupsAddOption(name, value, num_cookies, &cookies);
693 }
694}
695
696
ef416fc2 697/*
698 * 'cgi_initialize_get()' - Initialize form variables using the GET method.
699 */
700
701static int /* O - 1 if form data read */
702cgi_initialize_get(void)
703{
704 char *data; /* Pointer to form data string */
705
706
ef416fc2 707 /*
708 * Check to see if there is anything for us to read...
709 */
710
711 data = getenv("QUERY_STRING");
712 if (data == NULL || strlen(data) == 0)
713 return (0);
714
715 /*
716 * Parse it out and return...
717 */
718
719 return (cgi_initialize_string(data));
720}
721
722
723/*
f8b3a85b
MS
724 * 'cgi_initialize_multipart()' - Initialize variables and file using the POST
725 * method.
ef416fc2 726 *
727 * TODO: Update to support files > 2GB.
728 */
729
730static int /* O - 1 if form data was read */
731cgi_initialize_multipart(
732 const char *boundary) /* I - Boundary string */
733{
734 char line[10240], /* MIME header line */
735 name[1024], /* Form variable name */
736 filename[1024], /* Form filename */
737 mimetype[1024], /* MIME media type */
738 bstring[256], /* Boundary string to look for */
739 *ptr, /* Pointer into name/filename */
740 *end; /* End of buffer */
741 int ch, /* Character from file */
7e86f2f6
MS
742 fd; /* Temporary file descriptor */
743 size_t blen; /* Length of boundary string */
ef416fc2 744
745
ef416fc2 746 /*
747 * Read multipart form data until we run out...
748 */
749
750 name[0] = '\0';
751 filename[0] = '\0';
752 mimetype[0] = '\0';
753
754 snprintf(bstring, sizeof(bstring), "\r\n--%s", boundary);
755 blen = strlen(bstring);
756
757 while (fgets(line, sizeof(line), stdin))
758 {
759 if (!strcmp(line, "\r\n"))
760 {
761 /*
762 * End of headers, grab value...
763 */
764
765 if (filename[0])
766 {
767 /*
768 * Read an embedded file...
769 */
770
771 if (form_file)
772 {
773 /*
774 * Remove previous file...
775 */
776
777 cgi_unlink_file();
778 }
779
780 /*
781 * Allocate memory for the new file...
782 */
783
784 if ((form_file = calloc(1, sizeof(cgi_file_t))) == NULL)
785 return (0);
786
787 form_file->name = strdup(name);
788 form_file->filename = strdup(filename);
789 form_file->mimetype = strdup(mimetype);
790
791 fd = cupsTempFd(form_file->tempfile, sizeof(form_file->tempfile));
792
793 if (fd < 0)
794 return (0);
795
796 atexit(cgi_unlink_file);
797
798 /*
799 * Copy file data to the temp file...
800 */
88f9aafc 801
ef416fc2 802 ptr = line;
803
804 while ((ch = getchar()) != EOF)
805 {
7e86f2f6 806 *ptr++ = (char)ch;
ef416fc2 807
7e86f2f6 808 if ((size_t)(ptr - line) >= blen && !memcmp(ptr - blen, bstring, blen))
ef416fc2 809 {
810 ptr -= blen;
811 break;
812 }
813
7e86f2f6 814 if ((ptr - line - (int)blen) >= 8192)
ef416fc2 815 {
816 /*
817 * Write out the first 8k of the buffer...
818 */
819
820 write(fd, line, 8192);
07623986 821 memmove(line, line + 8192, (size_t)(ptr - line - 8192));
ef416fc2 822 ptr -= 8192;
823 }
824 }
825
826 /*
827 * Write the rest of the data and close the temp file...
828 */
829
830 if (ptr > line)
7e86f2f6 831 write(fd, line, (size_t)(ptr - line));
ef416fc2 832
833 close(fd);
834 }
835 else
836 {
837 /*
838 * Just get a form variable; the current code only handles
839 * form values up to 10k in size...
840 */
841
842 ptr = line;
843 end = line + sizeof(line) - 1;
844
845 while ((ch = getchar()) != EOF)
846 {
847 if (ptr < end)
7e86f2f6 848 *ptr++ = (char)ch;
ef416fc2 849
7e86f2f6 850 if ((size_t)(ptr - line) >= blen && !memcmp(ptr - blen, bstring, blen))
ef416fc2 851 {
852 ptr -= blen;
853 break;
854 }
855 }
856
857 *ptr = '\0';
858
859 /*
860 * Set the form variable...
861 */
862
863 if ((ptr = strrchr(name, '-')) != NULL && isdigit(ptr[1] & 255))
864 {
865 /*
866 * Set a specific index in the array...
867 */
868
869 *ptr++ = '\0';
870 if (line[0])
871 cgiSetArray(name, atoi(ptr) - 1, line);
872 }
873 else if (cgiGetVariable(name))
874 {
875 /*
876 * Add another element in the array...
877 */
878
879 cgiSetArray(name, cgiGetSize(name), line);
880 }
881 else
882 {
883 /*
884 * Just set the line...
885 */
886
887 cgiSetVariable(name, line);
888 }
889 }
890
891 /*
892 * Read the rest of the current line...
893 */
894
895 fgets(line, sizeof(line), stdin);
896
897 /*
898 * Clear the state vars...
899 */
900
901 name[0] = '\0';
902 filename[0] = '\0';
903 mimetype[0] = '\0';
904 }
88f9aafc 905 else if (!_cups_strncasecmp(line, "Content-Disposition:", 20))
ef416fc2 906 {
907 if ((ptr = strstr(line + 20, " name=\"")) != NULL)
908 {
909 strlcpy(name, ptr + 7, sizeof(name));
910
911 if ((ptr = strchr(name, '\"')) != NULL)
912 *ptr = '\0';
913 }
914
915 if ((ptr = strstr(line + 20, " filename=\"")) != NULL)
916 {
917 strlcpy(filename, ptr + 11, sizeof(filename));
918
919 if ((ptr = strchr(filename, '\"')) != NULL)
920 *ptr = '\0';
921 }
922 }
88f9aafc 923 else if (!_cups_strncasecmp(line, "Content-Type:", 13))
ef416fc2 924 {
925 for (ptr = line + 13; isspace(*ptr & 255); ptr ++);
926
927 strlcpy(mimetype, ptr, sizeof(mimetype));
928
929 for (ptr = mimetype + strlen(mimetype) - 1;
930 ptr > mimetype && isspace(*ptr & 255);
931 *ptr-- = '\0');
932 }
933 }
934
935 /*
936 * Return 1 for "form data found"...
937 */
938
939 return (1);
940}
941
942
943/*
944 * 'cgi_initialize_post()' - Initialize variables using the POST method.
945 */
946
947static int /* O - 1 if form data was read */
948cgi_initialize_post(void)
949{
7e86f2f6
MS
950 char *content_length, /* Length of input data (string) */
951 *data; /* Pointer to form data string */
952 size_t length, /* Length of input data */
953 tbytes; /* Total number of bytes read */
954 ssize_t nbytes; /* Number of bytes read this read() */
955 int status; /* Return status */
ef416fc2 956
957
ef416fc2 958 /*
959 * Check to see if there is anything for us to read...
960 */
961
962 content_length = getenv("CONTENT_LENGTH");
963 if (content_length == NULL || atoi(content_length) <= 0)
964 return (0);
965
966 /*
967 * Get the length of the input stream and allocate a buffer for it...
968 */
969
7e86f2f6 970 length = (size_t)strtol(content_length, NULL, 10);
ef416fc2 971 data = malloc(length + 1);
972
973 if (data == NULL)
974 return (0);
975
976 /*
977 * Read the data into the buffer...
978 */
979
7e86f2f6
MS
980 for (tbytes = 0; tbytes < length; tbytes += (size_t)nbytes)
981 if ((nbytes = read(0, data + tbytes, (size_t)(length - tbytes))) < 0)
080811b1 982 {
ef416fc2 983 if (errno != EAGAIN)
984 {
985 free(data);
986 return (0);
987 }
080811b1
MS
988 else
989 nbytes = 0;
990 }
f11a948a
MS
991 else if (nbytes == 0)
992 {
993 /*
994 * CUPS STR #3176: OpenBSD: Early end-of-file on POST data causes 100% CPU
995 *
996 * This should never happen, but does on OpenBSD. If we see early end-of-
997 * file, treat this as an error and process no data.
998 */
999
1000 free(data);
1001 return (0);
1002 }
ef416fc2 1003
1004 data[length] = '\0';
1005
1006 /*
1007 * Parse it out...
1008 */
1009
1010 status = cgi_initialize_string(data);
1011
1012 /*
1013 * Free the data and return...
1014 */
1015
1016 free(data);
1017
1018 return (status);
1019}
1020
1021
1022/*
1023 * 'cgi_initialize_string()' - Initialize form variables from a string.
1024 */
1025
1026static int /* O - 1 if form data was processed */
1027cgi_initialize_string(const char *data) /* I - Form data string */
1028{
1029 int done; /* True if we're done reading a form variable */
1030 char *s, /* Pointer to current form string */
1031 ch, /* Temporary character */
1032 name[255], /* Name of form variable */
f8b3a85b 1033 value[65536]; /* Variable value */
ef416fc2 1034
1035
1036 /*
1037 * Check input...
1038 */
1039
1040 if (data == NULL)
1041 return (0);
1042
1043 /*
1044 * Loop until we've read all the form data...
1045 */
1046
1047 while (*data != '\0')
1048 {
1049 /*
1050 * Get the variable name...
1051 */
1052
1053 for (s = name; *data != '\0'; data ++)
1054 if (*data == '=')
1055 break;
1056 else if (*data >= ' ' && s < (name + sizeof(name) - 1))
1057 *s++ = *data;
1058
1059 *s = '\0';
1060 if (*data == '=')
1061 data ++;
1062 else
1063 return (0);
1064
1065 /*
1066 * Read the variable value...
1067 */
1068
1069 for (s = value, done = 0; !done && *data != '\0'; data ++)
1070 switch (*data)
1071 {
1072 case '&' : /* End of data... */
1073 done = 1;
1074 break;
1075
1076 case '+' : /* Escaped space character */
1077 if (s < (value + sizeof(value) - 1))
1078 *s++ = ' ';
1079 break;
1080
1081 case '%' : /* Escaped control character */
1082 /*
1083 * Read the hex code...
1084 */
1085
c7017ecc
MS
1086 if (!isxdigit(data[1] & 255) || !isxdigit(data[2] & 255))
1087 return (0);
1088
ef416fc2 1089 if (s < (value + sizeof(value) - 1))
1090 {
1091 data ++;
1092 ch = *data - '0';
1093 if (ch > 9)
1094 ch -= 7;
7e86f2f6 1095 *s = (char)(ch << 4);
ef416fc2 1096
1097 data ++;
1098 ch = *data - '0';
1099 if (ch > 9)
1100 ch -= 7;
1101 *s++ |= ch;
1102 }
1103 else
1104 data += 2;
1105 break;
1106
1107 default : /* Other characters come straight through */
1108 if (*data >= ' ' && s < (value + sizeof(value) - 1))
1109 *s++ = *data;
1110 break;
1111 }
1112
1113 *s = '\0'; /* nul terminate the string */
1114
1115 /*
1116 * Remove trailing whitespace...
1117 */
1118
1119 if (s > value)
1120 s --;
1121
58dc1933 1122 while (s >= value && isspace(*s & 255))
ef416fc2 1123 *s-- = '\0';
1124
1125 /*
1126 * Add the string to the variable "database"...
1127 */
1128
1129 if ((s = strrchr(name, '-')) != NULL && isdigit(s[1] & 255))
1130 {
1131 *s++ = '\0';
1132 if (value[0])
1133 cgiSetArray(name, atoi(s) - 1, value);
1134 }
1135 else if (cgiGetVariable(name) != NULL)
1136 cgiSetArray(name, cgiGetSize(name), value);
1137 else
1138 cgiSetVariable(name, value);
1139 }
1140
1141 return (1);
1142}
1143
1144
1145/*
1146 * 'cgi_passwd()' - Catch authentication requests and notify the server.
1147 *
1148 * This function sends a Status header and exits, forcing authentication
1149 * for this request.
1150 */
1151
1152static const char * /* O - NULL (no return) */
1153cgi_passwd(const char *prompt) /* I - Prompt (not used) */
1154{
1155 (void)prompt;
1156
f301802f 1157 fprintf(stderr, "DEBUG: cgi_passwd(prompt=\"%s\") called!\n",
1158 prompt ? prompt : "(null)");
ef416fc2 1159
1160 /*
1161 * Send a 401 (unauthorized) status to the server, so it can notify
1162 * the client that authentication is required.
1163 */
1164
1165 puts("Status: 401\n");
1166 exit(0);
1167
1168 /*
1169 * This code is never executed, but is present to satisfy the compiler.
1170 */
1171
1172 return (NULL);
1173}
1174
1175
f8b3a85b
MS
1176/*
1177 * 'cgi_set_sid()' - Set the CUPS session ID.
1178 */
1179
1180static const char * /* O - New session ID */
1181cgi_set_sid(void)
1182{
1183 char buffer[512], /* SID data */
1184 sid[33]; /* SID string */
f8b3a85b
MS
1185 unsigned char sum[16]; /* MD5 sum */
1186 const char *remote_addr, /* REMOTE_ADDR */
1187 *server_name, /* SERVER_NAME */
1188 *server_port; /* SERVER_PORT */
b9ff93ce 1189 struct timeval curtime; /* Current time */
f8b3a85b
MS
1190
1191
1192 if ((remote_addr = getenv("REMOTE_ADDR")) == NULL)
1193 remote_addr = "REMOTE_ADDR";
1194 if ((server_name = getenv("SERVER_NAME")) == NULL)
1195 server_name = "SERVER_NAME";
1196 if ((server_port = getenv("SERVER_PORT")) == NULL)
1197 server_port = "SERVER_PORT";
1198
b9ff93ce
MS
1199 gettimeofday(&curtime, NULL);
1200 CUPS_SRAND(curtime.tv_sec + curtime.tv_usec);
f8b3a85b
MS
1201 snprintf(buffer, sizeof(buffer), "%s:%s:%s:%02X%02X%02X%02X%02X%02X%02X%02X",
1202 remote_addr, server_name, server_port,
1203 (unsigned)CUPS_RAND() & 255, (unsigned)CUPS_RAND() & 255,
1204 (unsigned)CUPS_RAND() & 255, (unsigned)CUPS_RAND() & 255,
1205 (unsigned)CUPS_RAND() & 255, (unsigned)CUPS_RAND() & 255,
1206 (unsigned)CUPS_RAND() & 255, (unsigned)CUPS_RAND() & 255);
7ec11630 1207 cupsHashData("md5", (unsigned char *)buffer, strlen(buffer), sum, sizeof(sum));
88f9aafc 1208
7ec11630 1209 cgiSetCookie(CUPS_SID, cupsHashString(sum, sizeof(sum), sid, sizeof(sid)), "/", NULL, 0, 0);
f8b3a85b
MS
1210
1211 return (cupsGetOption(CUPS_SID, num_cookies, cookies));
1212}
1213
1214
ef416fc2 1215/*
1216 * 'cgi_sort_variables()' - Sort all form variables for faster lookup.
1217 */
1218
1219static void
1220cgi_sort_variables(void)
1221{
ef416fc2 1222 if (form_count < 2)
1223 return;
1224
7e86f2f6 1225 qsort(form_vars, (size_t)form_count, sizeof(_cgi_var_t),
ef416fc2 1226 (int (*)(const void *, const void *))cgi_compare_variables);
ef416fc2 1227}
1228
1229
1230/*
1231 * 'cgi_unlink_file()' - Remove the uploaded form.
1232 */
1233
1234static void
1235cgi_unlink_file(void)
1236{
1237 if (form_file)
1238 {
1239 /*
1240 * Remove the temporary file...
1241 */
1242
1243 unlink(form_file->tempfile);
1244
1245 /*
1246 * Free memory used...
1247 */
1248
1249 free(form_file->name);
1250 free(form_file->filename);
1251 free(form_file->mimetype);
1252 free(form_file);
1253
1254 form_file = NULL;
1255 }
1256}