File: | process.c |
Location: | line 1501, column 6 |
Description: | Potential leak of memory pointed to by 'listtail' |
1 | /* | |||||||
2 | ||||||||
3 | Copyright 1989, 1998 The Open Group | |||||||
4 | ||||||||
5 | Permission to use, copy, modify, distribute, and sell this software and its | |||||||
6 | documentation for any purpose is hereby granted without fee, provided that | |||||||
7 | the above copyright notice appear in all copies and that both that | |||||||
8 | copyright notice and this permission notice appear in supporting | |||||||
9 | documentation. | |||||||
10 | ||||||||
11 | The above copyright notice and this permission notice shall be included | |||||||
12 | in all copies or substantial portions of the Software. | |||||||
13 | ||||||||
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |||||||
15 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |||||||
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |||||||
17 | IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR | |||||||
18 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |||||||
19 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |||||||
20 | OTHER DEALINGS IN THE SOFTWARE. | |||||||
21 | ||||||||
22 | Except as contained in this notice, the name of The Open Group shall | |||||||
23 | not be used in advertising or otherwise to promote the sale, use or | |||||||
24 | other dealings in this Software without prior written authorization | |||||||
25 | from The Open Group. | |||||||
26 | ||||||||
27 | */ | |||||||
28 | ||||||||
29 | /* | |||||||
30 | * Author: Jim Fulton, MIT X Consortium | |||||||
31 | */ | |||||||
32 | ||||||||
33 | #ifdef HAVE_CONFIG_H1 | |||||||
34 | #include "config.h" | |||||||
35 | #endif | |||||||
36 | ||||||||
37 | #include "xauth.h" | |||||||
38 | #include <ctype.h> | |||||||
39 | #include <errno(*__error()).h> | |||||||
40 | #include <sys/stat.h> | |||||||
41 | #ifndef WIN32 | |||||||
42 | #include <sys/socket.h> | |||||||
43 | #else | |||||||
44 | #include <X11/Xwinsock.h> | |||||||
45 | #endif | |||||||
46 | ||||||||
47 | #include <signal.h> | |||||||
48 | #include <X11/X.h> /* for Family constants */ | |||||||
49 | ||||||||
50 | #include <X11/Xlib.h> | |||||||
51 | #include <X11/extensions/security.h> | |||||||
52 | ||||||||
53 | #ifndef DEFAULT_PROTOCOL_ABBREV"." /* to make add command easier */ | |||||||
54 | #define DEFAULT_PROTOCOL_ABBREV"." "." | |||||||
55 | #endif | |||||||
56 | #ifndef DEFAULT_PROTOCOL"MIT-MAGIC-COOKIE-1" /* for protocol abbreviation */ | |||||||
57 | #define DEFAULT_PROTOCOL"MIT-MAGIC-COOKIE-1" "MIT-MAGIC-COOKIE-1" | |||||||
58 | #endif | |||||||
59 | ||||||||
60 | #define SECURERPC"SUN-DES-1" "SUN-DES-1" | |||||||
61 | #define K5AUTH"MIT-KERBEROS-5" "MIT-KERBEROS-5" | |||||||
62 | ||||||||
63 | #define XAUTH_DEFAULT_RETRIES10 10 /* number of competitors we expect */ | |||||||
64 | #define XAUTH_DEFAULT_TIMEOUT2 2 /* in seconds, be quick */ | |||||||
65 | #define XAUTH_DEFAULT_DEADTIME600L 600L /* 10 minutes in seconds */ | |||||||
66 | ||||||||
67 | typedef struct _AuthList { /* linked list of entries */ | |||||||
68 | struct _AuthList *next; | |||||||
69 | Xauth *auth; | |||||||
70 | } AuthList; | |||||||
71 | ||||||||
72 | typedef int (*ProcessFunc)(const char *, int, int, const char**); | |||||||
73 | ||||||||
74 | #define add_to_list(h,t,e){if (t) (t)->next = (e); else (h) = (e); (t) = (e);} {if (t) (t)->next = (e); else (h) = (e); (t) = (e);} | |||||||
75 | ||||||||
76 | typedef struct _CommandTable { /* commands that are understood */ | |||||||
77 | const char *name; /* full name */ | |||||||
78 | int minlen; /* unique prefix */ | |||||||
79 | int maxlen; /* strlen(name) */ | |||||||
80 | ProcessFunc processfunc; /* handler */ | |||||||
81 | const char *helptext; /* what to print for help */ | |||||||
82 | } CommandTable; | |||||||
83 | ||||||||
84 | struct _extract_data { /* for iterating */ | |||||||
85 | FILE *fp; /* input source */ | |||||||
86 | const char *filename; /* name of input */ | |||||||
87 | Boolint used_stdout; /* whether or not need to close */ | |||||||
88 | Boolint numeric; /* format in which to write */ | |||||||
89 | int nwritten; /* number of entries written */ | |||||||
90 | const char *cmd; /* for error messages */ | |||||||
91 | }; | |||||||
92 | ||||||||
93 | struct _list_data { /* for iterating */ | |||||||
94 | FILE *fp; /* output file */ | |||||||
95 | Boolint numeric; /* format in which to write */ | |||||||
96 | }; | |||||||
97 | ||||||||
98 | ||||||||
99 | /* | |||||||
100 | * private data | |||||||
101 | */ | |||||||
102 | static const char *stdin_filename = "(stdin)"; /* for messages */ | |||||||
103 | static const char *stdout_filename = "(stdout)"; /* for messages */ | |||||||
104 | static const char *Yes = "yes"; /* for messages */ | |||||||
105 | static const char *No = "no"; /* for messages */ | |||||||
106 | ||||||||
107 | static int do_help ( const char *inputfilename, int lineno, int argc, const char **argv ); | |||||||
108 | static int do_questionmark ( const char *inputfilename, int lineno, int argc, const char **argv ); | |||||||
109 | static int do_list ( const char *inputfilename, int lineno, int argc, const char **argv ); | |||||||
110 | static int do_merge ( const char *inputfilename, int lineno, int argc, const char **argv ); | |||||||
111 | static int do_extract ( const char *inputfilename, int lineno, int argc, const char **argv ); | |||||||
112 | static int do_add ( const char *inputfilename, int lineno, int argc, const char **argv ); | |||||||
113 | static int do_remove ( const char *inputfilename, int lineno, int argc, const char **argv ); | |||||||
114 | static int do_info ( const char *inputfilename, int lineno, int argc, const char **argv ); | |||||||
115 | static int do_exit ( const char *inputfilename, int lineno, int argc, const char **argv ); | |||||||
116 | static int do_quit ( const char *inputfilename, int lineno, int argc, const char **argv ); | |||||||
117 | static int do_source ( const char *inputfilename, int lineno, int argc, const char **argv ); | |||||||
118 | static int do_generate ( const char *inputfilename, int lineno, int argc, const char **argv ); | |||||||
119 | static int do_version ( const char *inputfilename, int lineno, int argc, const char **argv ); | |||||||
120 | ||||||||
121 | static CommandTable command_table[] = { /* table of known commands */ | |||||||
122 | { "add", 2, 3, do_add, | |||||||
123 | "add dpyname protoname hexkey add entry" }, | |||||||
124 | { "exit", 3, 4, do_exit, | |||||||
125 | "exit save changes and exit program" }, | |||||||
126 | { "extract", 3, 7, do_extract, | |||||||
127 | "extract filename dpyname... extract entries into file" }, | |||||||
128 | { "help", 1, 4, do_help, | |||||||
129 | "help [topic] print help" }, | |||||||
130 | { "info", 1, 4, do_info, | |||||||
131 | "info print information about entries" }, | |||||||
132 | { "list", 1, 4, do_list, | |||||||
133 | "list [dpyname...] list entries" }, | |||||||
134 | { "merge", 1, 5, do_merge, | |||||||
135 | "merge filename... merge entries from files" }, | |||||||
136 | { "nextract", 2, 8, do_extract, | |||||||
137 | "nextract filename dpyname... numerically extract entries" }, | |||||||
138 | { "nlist", 2, 5, do_list, | |||||||
139 | "nlist [dpyname...] numerically list entries" }, | |||||||
140 | { "nmerge", 2, 6, do_merge, | |||||||
141 | "nmerge filename... numerically merge entries" }, | |||||||
142 | { "quit", 1, 4, do_quit, | |||||||
143 | "quit abort changes and exit program" }, | |||||||
144 | { "remove", 1, 6, do_remove, | |||||||
145 | "remove dpyname... remove entries" }, | |||||||
146 | { "source", 1, 6, do_source, | |||||||
147 | "source filename read commands from file" }, | |||||||
148 | { "version", 1, 7, do_version, | |||||||
149 | "version show version number of xauth" }, | |||||||
150 | { "?", 1, 1, do_questionmark, | |||||||
151 | "? list available commands" }, | |||||||
152 | { "generate", 1, 8, do_generate, | |||||||
153 | "generate dpyname protoname [options] use server to generate entry\n" | |||||||
154 | " options are:\n" | |||||||
155 | " timeout n authorization expiration time in seconds\n" | |||||||
156 | " trusted clients using this entry are trusted\n" | |||||||
157 | " untrusted clients using this entry are untrusted\n" | |||||||
158 | " group n clients using this entry belong to application group n\n" | |||||||
159 | " data hexkey auth protocol specific data needed to generate the entry\n" | |||||||
160 | }, | |||||||
161 | { NULL((void*)0), 0, 0, NULL((void*)0), NULL((void*)0) }, | |||||||
162 | }; | |||||||
163 | ||||||||
164 | #define COMMAND_NAMES_PADDED_WIDTH10 10 /* wider than anything above */ | |||||||
165 | ||||||||
166 | ||||||||
167 | static Boolint okay_to_use_stdin = True1; /* set to false after using */ | |||||||
168 | ||||||||
169 | static const char *hex_table[] = { /* for printing hex digits */ | |||||||
170 | "00", "01", "02", "03", "04", "05", "06", "07", | |||||||
171 | "08", "09", "0a", "0b", "0c", "0d", "0e", "0f", | |||||||
172 | "10", "11", "12", "13", "14", "15", "16", "17", | |||||||
173 | "18", "19", "1a", "1b", "1c", "1d", "1e", "1f", | |||||||
174 | "20", "21", "22", "23", "24", "25", "26", "27", | |||||||
175 | "28", "29", "2a", "2b", "2c", "2d", "2e", "2f", | |||||||
176 | "30", "31", "32", "33", "34", "35", "36", "37", | |||||||
177 | "38", "39", "3a", "3b", "3c", "3d", "3e", "3f", | |||||||
178 | "40", "41", "42", "43", "44", "45", "46", "47", | |||||||
179 | "48", "49", "4a", "4b", "4c", "4d", "4e", "4f", | |||||||
180 | "50", "51", "52", "53", "54", "55", "56", "57", | |||||||
181 | "58", "59", "5a", "5b", "5c", "5d", "5e", "5f", | |||||||
182 | "60", "61", "62", "63", "64", "65", "66", "67", | |||||||
183 | "68", "69", "6a", "6b", "6c", "6d", "6e", "6f", | |||||||
184 | "70", "71", "72", "73", "74", "75", "76", "77", | |||||||
185 | "78", "79", "7a", "7b", "7c", "7d", "7e", "7f", | |||||||
186 | "80", "81", "82", "83", "84", "85", "86", "87", | |||||||
187 | "88", "89", "8a", "8b", "8c", "8d", "8e", "8f", | |||||||
188 | "90", "91", "92", "93", "94", "95", "96", "97", | |||||||
189 | "98", "99", "9a", "9b", "9c", "9d", "9e", "9f", | |||||||
190 | "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", | |||||||
191 | "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af", | |||||||
192 | "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", | |||||||
193 | "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf", | |||||||
194 | "c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", | |||||||
195 | "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf", | |||||||
196 | "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", | |||||||
197 | "d8", "d9", "da", "db", "dc", "dd", "de", "df", | |||||||
198 | "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", | |||||||
199 | "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef", | |||||||
200 | "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", | |||||||
201 | "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff", | |||||||
202 | }; | |||||||
203 | ||||||||
204 | static unsigned int hexvalues[256]; /* for parsing hex input */ | |||||||
205 | ||||||||
206 | static int original_umask = 0; /* for restoring */ | |||||||
207 | ||||||||
208 | ||||||||
209 | /* | |||||||
210 | * private utility procedures | |||||||
211 | */ | |||||||
212 | ||||||||
213 | static void | |||||||
214 | prefix(const char *fn, int n) | |||||||
215 | { | |||||||
216 | fprintf (stderr__stderrp, "%s: %s:%d: ", ProgramName, fn, n); | |||||||
217 | } | |||||||
218 | ||||||||
219 | static void | |||||||
220 | baddisplayname(const char *dpy, const char *cmd) | |||||||
221 | { | |||||||
222 | fprintf (stderr__stderrp, "bad display name \"%s\" in \"%s\" command\n", | |||||||
223 | dpy, cmd); | |||||||
224 | } | |||||||
225 | ||||||||
226 | static void | |||||||
227 | badcommandline(const char *cmd) | |||||||
228 | { | |||||||
229 | fprintf (stderr__stderrp, "bad \"%s\" command line\n", cmd); | |||||||
230 | } | |||||||
231 | ||||||||
232 | static char * | |||||||
233 | skip_space(register char *s) | |||||||
234 | { | |||||||
235 | if (!s) return NULL((void*)0); | |||||||
236 | ||||||||
237 | for ( ; *s && isascii(*s) && isspace(*s); s++) | |||||||
238 | ; | |||||||
239 | return s; | |||||||
240 | } | |||||||
241 | ||||||||
242 | ||||||||
243 | static char * | |||||||
244 | skip_nonspace(register char *s) | |||||||
245 | { | |||||||
246 | if (!s) return NULL((void*)0); | |||||||
247 | ||||||||
248 | /* put quoting into loop if need be */ | |||||||
249 | for ( ; *s && isascii(*s) && !isspace(*s); s++) | |||||||
250 | ; | |||||||
251 | return s; | |||||||
252 | } | |||||||
253 | ||||||||
254 | static const char ** | |||||||
255 | split_into_words(char *src, int *argcp) /* argvify string */ | |||||||
256 | { | |||||||
257 | char *jword; | |||||||
258 | char savec; | |||||||
259 | const char **argv; | |||||||
260 | int cur, total; | |||||||
261 | ||||||||
262 | *argcp = 0; | |||||||
263 | #define WORDSTOALLOC4 4 /* most lines are short */ | |||||||
264 | argv = malloc (WORDSTOALLOC4 * sizeof (char *)); | |||||||
265 | if (!argv) return NULL((void*)0); | |||||||
266 | cur = 0; | |||||||
267 | total = WORDSTOALLOC4; | |||||||
268 | ||||||||
269 | /* | |||||||
270 | * split the line up into separate, nul-terminated tokens; the last | |||||||
271 | * "token" will point to the empty string so that it can be bashed into | |||||||
272 | * a null pointer. | |||||||
273 | */ | |||||||
274 | ||||||||
275 | do { | |||||||
276 | jword = skip_space (src); | |||||||
277 | src = skip_nonspace (jword); | |||||||
278 | savec = *src; | |||||||
279 | *src = '\0'; | |||||||
280 | if (cur == total) { | |||||||
281 | total += WORDSTOALLOC4; | |||||||
282 | argv = realloc (argv, total * sizeof (char *)); | |||||||
283 | if (!argv) return NULL((void*)0); | |||||||
284 | } | |||||||
285 | argv[cur++] = jword; | |||||||
286 | if (savec) src++; /* if not last on line advance */ | |||||||
287 | } while (jword != src); | |||||||
288 | ||||||||
289 | argv[--cur] = NULL((void*)0); /* smash empty token to end list */ | |||||||
290 | *argcp = cur; | |||||||
291 | return argv; | |||||||
292 | } | |||||||
293 | ||||||||
294 | ||||||||
295 | static FILE * | |||||||
296 | open_file(const char **filenamep, | |||||||
297 | const char *mode, | |||||||
298 | Boolint *usedstdp, | |||||||
299 | const char *srcfn, | |||||||
300 | int srcln, | |||||||
301 | const char *cmd) | |||||||
302 | { | |||||||
303 | FILE *fp; | |||||||
304 | ||||||||
305 | if (strcmp (*filenamep, "-") == 0) { | |||||||
306 | *usedstdp = True1; | |||||||
307 | /* select std descriptor to use */ | |||||||
308 | if (mode[0] == 'r') { | |||||||
309 | if (okay_to_use_stdin) { | |||||||
310 | okay_to_use_stdin = False0; | |||||||
311 | *filenamep = stdin_filename; | |||||||
312 | return stdin__stdinp; | |||||||
313 | } else { | |||||||
314 | prefix (srcfn, srcln); | |||||||
315 | fprintf (stderr__stderrp, "%s: stdin already in use\n", cmd); | |||||||
316 | return NULL((void*)0); | |||||||
317 | } | |||||||
318 | } else { | |||||||
319 | *filenamep = stdout_filename; | |||||||
320 | return stdout__stdoutp; /* always okay to use stdout */ | |||||||
321 | } | |||||||
322 | } | |||||||
323 | ||||||||
324 | fp = fopen (*filenamep, mode); | |||||||
325 | if (!fp) { | |||||||
326 | prefix (srcfn, srcln); | |||||||
327 | fprintf (stderr__stderrp, "%s: unable to open file %s\n", cmd, *filenamep); | |||||||
328 | } | |||||||
329 | return fp; | |||||||
330 | } | |||||||
331 | ||||||||
332 | static int | |||||||
333 | getinput(FILE *fp) | |||||||
334 | { | |||||||
335 | register int c; | |||||||
336 | ||||||||
337 | while ((c = getc (fp)) != EOF(-1) && isascii(c) && c != '\n' && isspace(c)) ; | |||||||
338 | return c; | |||||||
339 | } | |||||||
340 | ||||||||
341 | static int | |||||||
342 | get_short(FILE *fp, unsigned short *sp) /* for reading numeric input */ | |||||||
343 | { | |||||||
344 | int c; | |||||||
345 | int i; | |||||||
346 | unsigned short us = 0; | |||||||
347 | ||||||||
348 | /* | |||||||
349 | * read family: written with %04x | |||||||
350 | */ | |||||||
351 | for (i = 0; i < 4; i++) { | |||||||
352 | switch (c = getinput (fp)) { | |||||||
353 | case EOF(-1): | |||||||
354 | case '\n': | |||||||
355 | return 0; | |||||||
356 | } | |||||||
357 | if (c < 0 || c > 255) return 0; | |||||||
358 | us = (us * 16) + hexvalues[c]; /* since msb */ | |||||||
359 | } | |||||||
360 | *sp = us; | |||||||
361 | return 1; | |||||||
362 | } | |||||||
363 | ||||||||
364 | static int | |||||||
365 | get_bytes(FILE *fp, unsigned int n, char **ptr) /* for reading numeric input */ | |||||||
366 | { | |||||||
367 | char *s; | |||||||
368 | register char *cp; | |||||||
369 | int c1, c2; | |||||||
370 | ||||||||
371 | cp = s = malloc (n); | |||||||
372 | if (!cp) return 0; | |||||||
373 | ||||||||
374 | while (n > 0) { | |||||||
375 | if ((c1 = getinput (fp)) == EOF(-1) || c1 == '\n' || | |||||||
376 | (c2 = getinput (fp)) == EOF(-1) || c2 == '\n') { | |||||||
377 | free (s); | |||||||
378 | return 0; | |||||||
379 | } | |||||||
380 | *cp = (char) ((hexvalues[c1] * 16) + hexvalues[c2]); | |||||||
381 | cp++; | |||||||
382 | n--; | |||||||
383 | } | |||||||
384 | ||||||||
385 | *ptr = s; | |||||||
386 | return 1; | |||||||
387 | } | |||||||
388 | ||||||||
389 | ||||||||
390 | static Xauth * | |||||||
391 | read_numeric(FILE *fp) | |||||||
392 | { | |||||||
393 | Xauth *auth; | |||||||
394 | ||||||||
395 | auth = (Xauth *) malloc (sizeof (Xauth)); | |||||||
396 | if (!auth) goto bad; | |||||||
397 | auth->family = 0; | |||||||
398 | auth->address = NULL((void*)0); | |||||||
399 | auth->address_length = 0; | |||||||
400 | auth->number = NULL((void*)0); | |||||||
401 | auth->number_length = 0; | |||||||
402 | auth->name = NULL((void*)0); | |||||||
403 | auth->name_length = 0; | |||||||
404 | auth->data = NULL((void*)0); | |||||||
405 | auth->data_length = 0; | |||||||
406 | ||||||||
407 | if (!get_short (fp, (unsigned short *) &auth->family)) | |||||||
408 | goto bad; | |||||||
409 | if (!get_short (fp, (unsigned short *) &auth->address_length)) | |||||||
410 | goto bad; | |||||||
411 | if (!get_bytes (fp, (unsigned int) auth->address_length, &auth->address)) | |||||||
412 | goto bad; | |||||||
413 | if (!get_short (fp, (unsigned short *) &auth->number_length)) | |||||||
414 | goto bad; | |||||||
415 | if (!get_bytes (fp, (unsigned int) auth->number_length, &auth->number)) | |||||||
416 | goto bad; | |||||||
417 | if (!get_short (fp, (unsigned short *) &auth->name_length)) | |||||||
418 | goto bad; | |||||||
419 | if (!get_bytes (fp, (unsigned int) auth->name_length, &auth->name)) | |||||||
420 | goto bad; | |||||||
421 | if (!get_short (fp, (unsigned short *) &auth->data_length)) | |||||||
422 | goto bad; | |||||||
423 | if (!get_bytes (fp, (unsigned int) auth->data_length, &auth->data)) | |||||||
424 | goto bad; | |||||||
425 | ||||||||
426 | switch (getinput (fp)) { /* get end of line */ | |||||||
427 | case EOF(-1): | |||||||
428 | case '\n': | |||||||
429 | return auth; | |||||||
430 | } | |||||||
431 | ||||||||
432 | bad: | |||||||
433 | if (auth) XauDisposeAuth (auth); /* won't free null pointers */ | |||||||
434 | return NULL((void*)0); | |||||||
435 | } | |||||||
436 | ||||||||
437 | typedef Xauth *(*ReadFunc)(FILE *); | |||||||
438 | ||||||||
439 | static int | |||||||
440 | read_auth_entries(FILE *fp, Boolint numeric, AuthList **headp, AuthList **tailp) | |||||||
441 | { | |||||||
442 | ReadFunc readfunc = (numeric ? read_numeric : XauReadAuth); | |||||||
443 | Xauth *auth; | |||||||
444 | AuthList *head, *tail; | |||||||
445 | int n; | |||||||
446 | ||||||||
447 | head = tail = NULL((void*)0); | |||||||
448 | n = 0; | |||||||
449 | /* put all records into linked list */ | |||||||
450 | while ((auth = ((*readfunc) (fp))) != NULL((void*)0)) { | |||||||
451 | AuthList *l = (AuthList *) malloc (sizeof (AuthList)); | |||||||
452 | if (!l) { | |||||||
453 | fprintf (stderr__stderrp, | |||||||
454 | "%s: unable to alloc entry reading auth file\n", | |||||||
455 | ProgramName); | |||||||
456 | exit (1); | |||||||
457 | } | |||||||
458 | l->next = NULL((void*)0); | |||||||
459 | l->auth = auth; | |||||||
460 | if (tail) /* if not first time through append */ | |||||||
461 | tail->next = l; | |||||||
462 | else | |||||||
463 | head = l; /* first time through, so assign */ | |||||||
464 | tail = l; | |||||||
465 | n++; | |||||||
466 | } | |||||||
467 | *headp = head; | |||||||
468 | *tailp = tail; | |||||||
469 | return n; | |||||||
470 | } | |||||||
471 | ||||||||
472 | static Boolint | |||||||
473 | get_displayname_auth(const char *displayname, AuthList **authl) | |||||||
474 | { | |||||||
475 | int family; | |||||||
476 | char *host = NULL((void*)0), *rest = NULL((void*)0); | |||||||
477 | int dpynum, scrnum; | |||||||
478 | char *cp; | |||||||
479 | int prelen = 0; | |||||||
480 | struct addrlist *addrlist_head, *addrlist_cur; | |||||||
481 | AuthList *authl_cur = NULL((void*)0); | |||||||
482 | ||||||||
483 | *authl = NULL((void*)0); | |||||||
484 | /* | |||||||
485 | * check to see if the display name is of the form "host/unix:" | |||||||
486 | * which is how the list routine prints out local connections | |||||||
487 | */ | |||||||
488 | cp = strchr(displayname, '/'); | |||||||
489 | if (cp && strncmp (cp, "/unix:", 6) == 0) | |||||||
490 | prelen = (cp - displayname); | |||||||
491 | ||||||||
492 | if (!parse_displayname (displayname + ((prelen > 0) ? prelen + 1 : 0), | |||||||
493 | &family, &host, &dpynum, &scrnum, &rest)) { | |||||||
494 | return False0; | |||||||
495 | } | |||||||
496 | ||||||||
497 | addrlist_head = get_address_info(family, displayname, prelen, host); | |||||||
498 | if (addrlist_head) { | |||||||
499 | char buf[40]; /* want to hold largest display num */ | |||||||
500 | unsigned short dpylen; | |||||||
501 | ||||||||
502 | buf[0] = '\0'; | |||||||
503 | sprintf (buf, "%d", dpynum)__builtin___sprintf_chk (buf, 0, __builtin_object_size (buf, 2 > 1 ? 1 : 0), "%d", dpynum); | |||||||
504 | dpylen = strlen (buf); | |||||||
505 | if (dpylen > 0) { | |||||||
506 | for (addrlist_cur = addrlist_head; addrlist_cur != NULL((void*)0); | |||||||
507 | addrlist_cur = addrlist_cur->next) { | |||||||
508 | AuthList *newal = malloc(sizeof(AuthList)); | |||||||
509 | Xauth *auth = malloc(sizeof(Xauth)); | |||||||
510 | ||||||||
511 | if ((newal == NULL((void*)0)) || (auth == NULL((void*)0))) { | |||||||
512 | if (newal != NULL((void*)0)) free(newal); | |||||||
513 | if (auth != NULL((void*)0)) free(auth); | |||||||
514 | break; | |||||||
515 | } | |||||||
516 | ||||||||
517 | if (authl_cur == NULL((void*)0)) { | |||||||
518 | *authl = authl_cur = newal; | |||||||
519 | } else { | |||||||
520 | authl_cur->next = newal; | |||||||
521 | authl_cur = newal; | |||||||
522 | } | |||||||
523 | ||||||||
524 | newal->next = NULL((void*)0); | |||||||
525 | newal->auth = auth; | |||||||
526 | ||||||||
527 | auth->family = addrlist_cur->family; | |||||||
528 | auth->address = addrlist_cur->address; | |||||||
529 | auth->address_length = addrlist_cur->len; | |||||||
530 | auth->number = copystring(buf, dpylen); | |||||||
531 | auth->number_length = dpylen; | |||||||
532 | auth->name = NULL((void*)0); | |||||||
533 | auth->name_length = 0; | |||||||
534 | auth->data = NULL((void*)0); | |||||||
535 | auth->data_length = 0; | |||||||
536 | } | |||||||
537 | } | |||||||
538 | } | |||||||
539 | ||||||||
540 | if (host) free (host); | |||||||
541 | if (rest) free (rest); | |||||||
542 | ||||||||
543 | if (*authl != NULL((void*)0)) { | |||||||
544 | return True1; | |||||||
545 | } else { | |||||||
546 | return False0; | |||||||
547 | } | |||||||
548 | } | |||||||
549 | ||||||||
550 | static int | |||||||
551 | cvthexkey(const char *hexstr, char **ptrp) /* turn hex key string into octets */ | |||||||
552 | { | |||||||
553 | int i; | |||||||
554 | int len = 0; | |||||||
555 | char *retval; | |||||||
556 | const char *s; | |||||||
557 | unsigned char *us; | |||||||
558 | char c; | |||||||
559 | char savec = '\0'; | |||||||
560 | ||||||||
561 | /* count */ | |||||||
562 | for (s = hexstr; *s; s++) { | |||||||
563 | if (!isascii(*s)) return -1; | |||||||
564 | if (isspace(*s)) continue; | |||||||
565 | if (!isxdigit(*s)) return -1; | |||||||
566 | len++; | |||||||
567 | } | |||||||
568 | ||||||||
569 | /* if 0 or odd, then there was an error */ | |||||||
570 | if (len == 0 || (len & 1) == 1) return -1; | |||||||
571 | ||||||||
572 | ||||||||
573 | /* now we know that the input is good */ | |||||||
574 | len >>= 1; | |||||||
575 | retval = malloc (len); | |||||||
576 | if (!retval) { | |||||||
577 | fprintf (stderr__stderrp, "%s: unable to allocate %d bytes for hexkey\n", | |||||||
578 | ProgramName, len); | |||||||
579 | return -1; | |||||||
580 | } | |||||||
581 | ||||||||
582 | for (us = (unsigned char *) retval, i = len; i > 0; hexstr++) { | |||||||
583 | c = *hexstr; | |||||||
584 | if (isspace(c)) continue; /* already know it is ascii */ | |||||||
585 | if (isupper(c)) | |||||||
586 | c = tolower(c); | |||||||
587 | if (savec) { | |||||||
588 | #define atoh(c) ((c) - (((c) >= '0' && (c) <= '9') ? '0' : ('a'-10))) | |||||||
589 | *us = (unsigned char)((atoh(savec) << 4) + atoh(c)); | |||||||
590 | #undef atoh | |||||||
591 | savec = 0; /* ready for next character */ | |||||||
592 | us++; | |||||||
593 | i--; | |||||||
594 | } else { | |||||||
595 | savec = c; | |||||||
596 | } | |||||||
597 | } | |||||||
598 | *ptrp = retval; | |||||||
599 | return len; | |||||||
600 | } | |||||||
601 | ||||||||
602 | static int | |||||||
603 | dispatch_command(const char *inputfilename, | |||||||
604 | int lineno, | |||||||
605 | int argc, | |||||||
606 | const char **argv, | |||||||
607 | CommandTable *tab, | |||||||
608 | int *statusp) | |||||||
609 | { | |||||||
610 | CommandTable *ct; | |||||||
611 | const char *cmd; | |||||||
612 | int n; | |||||||
613 | /* scan table for command */ | |||||||
614 | cmd = argv[0]; | |||||||
615 | n = strlen (cmd); | |||||||
616 | for (ct = tab; ct->name; ct++) { | |||||||
617 | /* look for unique prefix */ | |||||||
618 | if (n >= ct->minlen && n <= ct->maxlen && | |||||||
619 | strncmp (cmd, ct->name, n) == 0) { | |||||||
620 | *statusp = (*(ct->processfunc))(inputfilename, lineno, argc, argv); | |||||||
621 | return 1; | |||||||
622 | } | |||||||
623 | } | |||||||
624 | ||||||||
625 | *statusp = 1; | |||||||
626 | return 0; | |||||||
627 | } | |||||||
628 | ||||||||
629 | ||||||||
630 | static AuthList *xauth_head = NULL((void*)0); /* list of auth entries */ | |||||||
631 | static Boolint xauth_existed = False0; /* if was present at initialize */ | |||||||
632 | static Boolint xauth_modified = False0; /* if added, removed, or merged */ | |||||||
633 | static Boolint xauth_allowed = True1; /* if allowed to write auth file */ | |||||||
634 | static Boolint xauth_locked = False0; /* if has been locked */ | |||||||
635 | static const char *xauth_filename = NULL((void*)0); | |||||||
636 | static volatile Boolint dieing = False0; | |||||||
637 | ||||||||
638 | ||||||||
639 | /* poor man's puts(), for under signal handlers, | |||||||
640 | extended to ignore warn_unused_result */ | |||||||
641 | #define WRITES(fd, S){if(write((fd), (S), strlen((S))));} {if(write((fd), (S), strlen((S))));} | |||||||
642 | ||||||||
643 | /* ARGSUSED */ | |||||||
644 | _X_NORETURN__attribute((noreturn)) | |||||||
645 | static void | |||||||
646 | die(int sig) | |||||||
647 | { | |||||||
648 | dieing = True1; | |||||||
649 | _exit (auth_finalize ()); | |||||||
650 | /* NOTREACHED */ | |||||||
651 | } | |||||||
652 | ||||||||
653 | _X_NORETURN__attribute((noreturn)) | |||||||
654 | static void | |||||||
655 | catchsig(int sig) | |||||||
656 | { | |||||||
657 | #ifdef SYSV | |||||||
658 | if (sig > 0) signal (sig, die); /* re-establish signal handler */ | |||||||
659 | #endif | |||||||
660 | /* | |||||||
661 | * fileno() might not be reentrant, avoid it if possible, and use | |||||||
662 | * stderr instead of stdout | |||||||
663 | */ | |||||||
664 | #ifdef STDERR_FILENO2 | |||||||
665 | if (verbose && xauth_modified) WRITES(STDERR_FILENO, "\r\n"){if(write((2), ("\r\n"), strlen(("\r\n"))));}; | |||||||
666 | #else | |||||||
667 | if (verbose && xauth_modified) WRITES(fileno(stderr), "\r\n"){if(write((fileno(__stderrp)), ("\r\n"), strlen(("\r\n"))));}; | |||||||
668 | #endif | |||||||
669 | die (sig); | |||||||
670 | /* NOTREACHED */ | |||||||
671 | } | |||||||
672 | ||||||||
673 | static void | |||||||
674 | register_signals(void) | |||||||
675 | { | |||||||
676 | signal (SIGINT2, catchsig); | |||||||
677 | signal (SIGTERM15, catchsig); | |||||||
678 | #ifdef SIGHUP1 | |||||||
679 | signal (SIGHUP1, catchsig); | |||||||
680 | #endif | |||||||
681 | #ifdef SIGPIPE13 | |||||||
682 | signal (SIGPIPE13, catchsig); | |||||||
683 | #endif | |||||||
684 | return; | |||||||
685 | } | |||||||
686 | ||||||||
687 | ||||||||
688 | /* | |||||||
689 | * public procedures for parsing lines of input | |||||||
690 | */ | |||||||
691 | ||||||||
692 | int | |||||||
693 | auth_initialize(const char *authfilename) | |||||||
694 | { | |||||||
695 | int n; | |||||||
696 | AuthList *head, *tail; | |||||||
697 | FILE *authfp; | |||||||
698 | Boolint exists; | |||||||
699 | ||||||||
700 | xauth_filename = authfilename; /* used in cleanup, prevent race with | |||||||
701 | signals */ | |||||||
702 | register_signals (); | |||||||
703 | ||||||||
704 | bzero ((char *) hexvalues, sizeof hexvalues)__builtin___memset_chk ((char *) hexvalues, 0, sizeof hexvalues , __builtin_object_size ((char *) hexvalues, 0)); | |||||||
705 | hexvalues['0'] = 0; | |||||||
706 | hexvalues['1'] = 1; | |||||||
707 | hexvalues['2'] = 2; | |||||||
708 | hexvalues['3'] = 3; | |||||||
709 | hexvalues['4'] = 4; | |||||||
710 | hexvalues['5'] = 5; | |||||||
711 | hexvalues['6'] = 6; | |||||||
712 | hexvalues['7'] = 7; | |||||||
713 | hexvalues['8'] = 8; | |||||||
714 | hexvalues['9'] = 9; | |||||||
715 | hexvalues['a'] = hexvalues['A'] = 0xa; | |||||||
716 | hexvalues['b'] = hexvalues['B'] = 0xb; | |||||||
717 | hexvalues['c'] = hexvalues['C'] = 0xc; | |||||||
718 | hexvalues['d'] = hexvalues['D'] = 0xd; | |||||||
719 | hexvalues['e'] = hexvalues['E'] = 0xe; | |||||||
720 | hexvalues['f'] = hexvalues['F'] = 0xf; | |||||||
721 | ||||||||
722 | if (break_locks && verbose) { | |||||||
723 | printf ("Attempting to break locks on authority file %s\n", | |||||||
724 | authfilename); | |||||||
725 | } | |||||||
726 | ||||||||
727 | if (ignore_locks) { | |||||||
728 | if (break_locks) XauUnlockAuth (authfilename); | |||||||
729 | } else { | |||||||
730 | n = XauLockAuth (authfilename, XAUTH_DEFAULT_RETRIES10, | |||||||
731 | XAUTH_DEFAULT_TIMEOUT2, | |||||||
732 | (break_locks ? 0L : XAUTH_DEFAULT_DEADTIME600L)); | |||||||
733 | if (n != LOCK_SUCCESS0) { | |||||||
734 | const char *reason = "unknown error"; | |||||||
735 | switch (n) { | |||||||
736 | case LOCK_ERROR1: | |||||||
737 | reason = "error"; | |||||||
738 | break; | |||||||
739 | case LOCK_TIMEOUT2: | |||||||
740 | reason = "timeout"; | |||||||
741 | break; | |||||||
742 | } | |||||||
743 | fprintf (stderr__stderrp, "%s: %s in locking authority file %s\n", | |||||||
744 | ProgramName, reason, authfilename); | |||||||
745 | return -1; | |||||||
746 | } else | |||||||
747 | xauth_locked = True1; | |||||||
748 | } | |||||||
749 | ||||||||
750 | /* these checks can only be done reliably after the file is locked */ | |||||||
751 | exists = (access (authfilename, F_OK0) == 0); | |||||||
752 | if (exists && access (authfilename, W_OK(1<<1)) != 0) { | |||||||
753 | fprintf (stderr__stderrp, | |||||||
754 | "%s: %s not writable, changes will be ignored\n", | |||||||
755 | ProgramName, authfilename); | |||||||
756 | xauth_allowed = False0; | |||||||
757 | } | |||||||
758 | ||||||||
759 | original_umask = umask (0077); /* disallow non-owner access */ | |||||||
760 | ||||||||
761 | authfp = fopen (authfilename, "rb"); | |||||||
762 | if (!authfp) { | |||||||
763 | int olderrno = errno(*__error()); | |||||||
764 | ||||||||
765 | /* if file there then error */ | |||||||
766 | if (access (authfilename, F_OK0) == 0) { /* then file does exist! */ | |||||||
767 | errno(*__error()) = olderrno; | |||||||
768 | return -1; | |||||||
769 | } /* else ignore it */ | |||||||
770 | fprintf (stderr__stderrp, | |||||||
771 | "%s: file %s does not exist\n", | |||||||
772 | ProgramName, authfilename); | |||||||
773 | } else { | |||||||
774 | xauth_existed = True1; | |||||||
775 | n = read_auth_entries (authfp, False0, &head, &tail); | |||||||
776 | (void) fclose (authfp); | |||||||
777 | if (n < 0) { | |||||||
778 | fprintf (stderr__stderrp, | |||||||
779 | "%s: unable to read auth entries from file \"%s\"\n", | |||||||
780 | ProgramName, authfilename); | |||||||
781 | return -1; | |||||||
782 | } | |||||||
783 | xauth_head = head; | |||||||
784 | } | |||||||
785 | ||||||||
786 | xauth_filename = strdup(authfilename); | |||||||
787 | if (!xauth_filename) { | |||||||
788 | fprintf(stderr__stderrp,"cannot allocate memory\n"); | |||||||
789 | return -1; | |||||||
790 | } | |||||||
791 | ||||||||
792 | xauth_modified = False0; | |||||||
793 | ||||||||
794 | if (verbose) { | |||||||
795 | printf ("%s authority file %s\n", | |||||||
796 | ignore_locks ? "Ignoring locks on" : "Using", authfilename); | |||||||
797 | } | |||||||
798 | return 0; | |||||||
799 | } | |||||||
800 | ||||||||
801 | static int | |||||||
802 | write_auth_file(char *tmp_nam) | |||||||
803 | { | |||||||
804 | FILE *fp = NULL((void*)0); | |||||||
805 | int fd; | |||||||
806 | AuthList *list; | |||||||
807 | ||||||||
808 | /* | |||||||
809 | * xdm and auth spec assumes auth file is 12 or fewer characters | |||||||
810 | */ | |||||||
811 | strcpy (tmp_nam, xauth_filename)__builtin___strcpy_chk (tmp_nam, xauth_filename, __builtin_object_size (tmp_nam, 2 > 1 ? 1 : 0)); | |||||||
812 | strcat (tmp_nam, "-n")__builtin___strcat_chk (tmp_nam, "-n", __builtin_object_size ( tmp_nam, 2 > 1 ? 1 : 0)); /* for new */ | |||||||
813 | (void) unlink (tmp_nam); | |||||||
814 | /* CPhipps 2000/02/12 - fix file unlink/fopen race */ | |||||||
815 | fd = open(tmp_nam, O_WRONLY0x0001 | O_CREAT0x0200 | O_EXCL0x0800, 0600); | |||||||
816 | if (fd != -1) fp = fdopen (fd, "wb"); | |||||||
817 | if (!fp) { | |||||||
818 | if (fd != -1) close(fd); | |||||||
819 | fprintf (stderr__stderrp, "%s: unable to open tmp file \"%s\"\n", | |||||||
820 | ProgramName, tmp_nam); | |||||||
821 | return -1; | |||||||
822 | } | |||||||
823 | ||||||||
824 | /* | |||||||
825 | * Write MIT-MAGIC-COOKIE-1 first, because R4 Xlib knows | |||||||
826 | * only that and uses the first authorization it finds. | |||||||
827 | */ | |||||||
828 | for (list = xauth_head; list; list = list->next) { | |||||||
829 | if (list->auth->name_length == 18 | |||||||
830 | && strncmp(list->auth->name, "MIT-MAGIC-COOKIE-1", 18) == 0) { | |||||||
831 | if (!XauWriteAuth(fp, list->auth)) { | |||||||
832 | (void) fclose(fp); | |||||||
833 | return -1; | |||||||
834 | } | |||||||
835 | } | |||||||
836 | } | |||||||
837 | for (list = xauth_head; list; list = list->next) { | |||||||
838 | if (list->auth->name_length != 18 | |||||||
839 | || strncmp(list->auth->name, "MIT-MAGIC-COOKIE-1", 18) != 0) { | |||||||
840 | if (!XauWriteAuth(fp, list->auth)) { | |||||||
841 | (void) fclose(fp); | |||||||
842 | return -1; | |||||||
843 | } | |||||||
844 | } | |||||||
845 | } | |||||||
846 | ||||||||
847 | if (fclose(fp)) { | |||||||
848 | return -1; | |||||||
849 | } | |||||||
850 | ||||||||
851 | return 0; | |||||||
852 | } | |||||||
853 | ||||||||
854 | int | |||||||
855 | auth_finalize(void) | |||||||
856 | { | |||||||
857 | char temp_name[1024]; /* large filename size */ | |||||||
858 | ||||||||
859 | if (xauth_modified) { | |||||||
860 | if (dieing) { | |||||||
861 | if (verbose) { | |||||||
862 | /* | |||||||
863 | * called from a signal handler -- printf is *not* reentrant; also | |||||||
864 | * fileno() might not be reentrant, avoid it if possible, and use | |||||||
865 | * stderr instead of stdout | |||||||
866 | */ | |||||||
867 | #ifdef STDERR_FILENO2 | |||||||
868 | WRITES(STDERR_FILENO, "\nAborting changes to authority file "){if(write((2), ("\nAborting changes to authority file "), strlen (("\nAborting changes to authority file "))));}; | |||||||
869 | WRITES(STDERR_FILENO, xauth_filename){if(write((2), (xauth_filename), strlen((xauth_filename))));}; | |||||||
870 | WRITES(STDERR_FILENO, "\n"){if(write((2), ("\n"), strlen(("\n"))));}; | |||||||
871 | #else | |||||||
872 | WRITES(fileno(stderr), "\nAborting changes to authority file "){if(write((fileno(__stderrp)), ("\nAborting changes to authority file " ), strlen(("\nAborting changes to authority file "))));}; | |||||||
873 | WRITES(fileno(stderr), xauth_filename){if(write((fileno(__stderrp)), (xauth_filename), strlen((xauth_filename ))));}; | |||||||
874 | WRITES(fileno(stderr), "\n"){if(write((fileno(__stderrp)), ("\n"), strlen(("\n"))));}; | |||||||
875 | #endif | |||||||
876 | } | |||||||
877 | } else if (!xauth_allowed) { | |||||||
878 | fprintf (stderr__stderrp, | |||||||
879 | "%s: %s not writable, changes ignored\n", | |||||||
880 | ProgramName, xauth_filename); | |||||||
881 | } else { | |||||||
882 | if (verbose) { | |||||||
883 | printf ("%s authority file %s\n", | |||||||
884 | ignore_locks ? "Ignoring locks and writing" : | |||||||
885 | "Writing", xauth_filename); | |||||||
886 | } | |||||||
887 | temp_name[0] = '\0'; | |||||||
888 | if (write_auth_file (temp_name) == -1) { | |||||||
889 | fprintf (stderr__stderrp, | |||||||
890 | "%s: unable to write authority file %s\n", | |||||||
891 | ProgramName, temp_name); | |||||||
892 | } else { | |||||||
893 | (void) unlink (xauth_filename); | |||||||
894 | #if defined(WIN32) || defined(__UNIXOS2__) | |||||||
895 | if (rename(temp_name, xauth_filename) == -1) | |||||||
896 | #else | |||||||
897 | /* Attempt to rename() if link() fails, since this may be on a FS that does not support hard links */ | |||||||
898 | if (link (temp_name, xauth_filename) == -1 && rename(temp_name, xauth_filename) == -1) | |||||||
899 | #endif | |||||||
900 | { | |||||||
901 | fprintf (stderr__stderrp, | |||||||
902 | "%s: unable to link authority file %s, use %s\n", | |||||||
903 | ProgramName, xauth_filename, temp_name); | |||||||
904 | } else { | |||||||
905 | (void) unlink (temp_name); | |||||||
906 | } | |||||||
907 | } | |||||||
908 | } | |||||||
909 | } | |||||||
910 | ||||||||
911 | if (xauth_locked) { | |||||||
912 | XauUnlockAuth (xauth_filename); | |||||||
913 | } | |||||||
914 | (void) umask (original_umask); | |||||||
915 | return 0; | |||||||
916 | } | |||||||
917 | ||||||||
918 | int | |||||||
919 | process_command(const char *inputfilename, int lineno, int argc, const char **argv) | |||||||
920 | { | |||||||
921 | int status; | |||||||
922 | ||||||||
923 | if (argc < 1 || !argv || !argv[0]) return 1; | |||||||
924 | ||||||||
925 | if (dispatch_command (inputfilename, lineno, argc, argv, | |||||||
926 | command_table, &status)) | |||||||
927 | return status; | |||||||
928 | ||||||||
929 | prefix (inputfilename, lineno); | |||||||
930 | fprintf (stderr__stderrp, "unknown command \"%s\"\n", argv[0]); | |||||||
931 | return 1; | |||||||
932 | } | |||||||
933 | ||||||||
934 | ||||||||
935 | /* | |||||||
936 | * utility routines | |||||||
937 | */ | |||||||
938 | ||||||||
939 | static char * | |||||||
940 | bintohex(unsigned int len, const char *bindata) | |||||||
941 | { | |||||||
942 | char *hexdata, *starthex; | |||||||
943 | ||||||||
944 | /* two chars per byte, plus null termination */ | |||||||
945 | starthex = hexdata = (char *)malloc(2*len + 1); | |||||||
946 | if (!hexdata) | |||||||
947 | return NULL((void*)0); | |||||||
948 | ||||||||
949 | for (; len > 0; len--, bindata++) { | |||||||
950 | register const char *s = hex_table[(unsigned char)*bindata]; | |||||||
951 | *hexdata++ = s[0]; | |||||||
952 | *hexdata++ = s[1]; | |||||||
953 | } | |||||||
954 | *hexdata = '\0'; | |||||||
955 | return starthex; | |||||||
956 | } | |||||||
957 | ||||||||
958 | static void | |||||||
959 | fprintfhex(register FILE *fp, int len, char *cp) | |||||||
960 | { | |||||||
961 | char *hex; | |||||||
962 | ||||||||
963 | hex = bintohex(len, cp); | |||||||
964 | fprintf(fp, "%s", hex); | |||||||
965 | free(hex); | |||||||
966 | } | |||||||
967 | ||||||||
968 | static int | |||||||
969 | dump_numeric(register FILE *fp, register Xauth *auth) | |||||||
970 | { | |||||||
971 | fprintf (fp, "%04x", auth->family); /* unsigned short */ | |||||||
972 | fprintf (fp, " %04x ", auth->address_length); /* short */ | |||||||
973 | fprintfhex (fp, auth->address_length, auth->address); | |||||||
974 | fprintf (fp, " %04x ", auth->number_length); /* short */ | |||||||
975 | fprintfhex (fp, auth->number_length, auth->number); | |||||||
976 | fprintf (fp, " %04x ", auth->name_length); /* short */ | |||||||
977 | fprintfhex (fp, auth->name_length, auth->name); | |||||||
978 | fprintf (fp, " %04x ", auth->data_length); /* short */ | |||||||
979 | fprintfhex (fp, auth->data_length, auth->data); | |||||||
980 | putc ('\n', fp); | |||||||
981 | return 1; | |||||||
982 | } | |||||||
983 | ||||||||
984 | /* ARGSUSED */ | |||||||
985 | static int | |||||||
986 | dump_entry(const char *inputfilename, int lineno, Xauth *auth, char *data) | |||||||
987 | { | |||||||
988 | struct _list_data *ld = (struct _list_data *) data; | |||||||
989 | FILE *fp = ld->fp; | |||||||
990 | ||||||||
991 | if (ld->numeric) { | |||||||
992 | dump_numeric (fp, auth); | |||||||
993 | } else { | |||||||
994 | const char *dpyname = NULL((void*)0); | |||||||
995 | ||||||||
996 | switch (auth->family) { | |||||||
997 | case FamilyLocal(256): | |||||||
998 | fwrite (auth->address, sizeof (char), auth->address_length, fp); | |||||||
999 | fprintf (fp, "/unix"); | |||||||
1000 | break; | |||||||
1001 | case FamilyInternet0: | |||||||
1002 | #if defined(IPv61) && defined(AF_INET630) | |||||||
1003 | case FamilyInternet66: | |||||||
1004 | #endif | |||||||
1005 | case FamilyDECnet1: | |||||||
1006 | dpyname = get_hostname (auth); | |||||||
1007 | if (dpyname) { | |||||||
1008 | fprintf (fp, "%s", dpyname); | |||||||
1009 | break; | |||||||
1010 | } | |||||||
1011 | /* else fall through to default */ | |||||||
1012 | default: | |||||||
1013 | fprintf (fp, "#%04x#", auth->family); | |||||||
1014 | fprintfhex (fp, auth->address_length, auth->address); | |||||||
1015 | putc ('#', fp); | |||||||
1016 | } | |||||||
1017 | putc (':', fp); | |||||||
1018 | fwrite (auth->number, sizeof (char), auth->number_length, fp); | |||||||
1019 | putc (' ', fp); | |||||||
1020 | putc (' ', fp); | |||||||
1021 | fwrite (auth->name, sizeof (char), auth->name_length, fp); | |||||||
1022 | putc (' ', fp); | |||||||
1023 | putc (' ', fp); | |||||||
1024 | if (!strncmp(auth->name, SECURERPC"SUN-DES-1", auth->name_length) || | |||||||
1025 | !strncmp(auth->name, K5AUTH"MIT-KERBEROS-5", auth->name_length)) | |||||||
1026 | fwrite (auth->data, sizeof (char), auth->data_length, fp); | |||||||
1027 | else | |||||||
1028 | fprintfhex (fp, auth->data_length, auth->data); | |||||||
1029 | putc ('\n', fp); | |||||||
1030 | } | |||||||
1031 | return 0; | |||||||
1032 | } | |||||||
1033 | ||||||||
1034 | static int | |||||||
1035 | extract_entry(const char *inputfilename, int lineno, Xauth *auth, char *data) | |||||||
1036 | { | |||||||
1037 | struct _extract_data *ed = (struct _extract_data *) data; | |||||||
1038 | ||||||||
1039 | if (!ed->fp) { | |||||||
1040 | ed->fp = open_file (&ed->filename, | |||||||
1041 | ed->numeric ? "w" : "wb", | |||||||
1042 | &ed->used_stdout, | |||||||
1043 | inputfilename, lineno, ed->cmd); | |||||||
1044 | if (!ed->fp) { | |||||||
1045 | prefix (inputfilename, lineno); | |||||||
1046 | fprintf (stderr__stderrp, | |||||||
1047 | "unable to open extraction file \"%s\"\n", | |||||||
1048 | ed->filename); | |||||||
1049 | return -1; | |||||||
1050 | } | |||||||
1051 | } | |||||||
1052 | (*(ed->numeric ? dump_numeric : XauWriteAuth)) (ed->fp, auth); | |||||||
1053 | ed->nwritten++; | |||||||
1054 | ||||||||
1055 | return 0; | |||||||
1056 | } | |||||||
1057 | ||||||||
1058 | ||||||||
1059 | static int | |||||||
1060 | eq_auth(Xauth *a, Xauth *b) | |||||||
1061 | { | |||||||
1062 | return((a->family == b->family && | |||||||
1063 | a->address_length == b->address_length && | |||||||
1064 | a->number_length == b->number_length && | |||||||
1065 | a->name_length == b->name_length && | |||||||
1066 | a->data_length == b->data_length && | |||||||
1067 | memcmp(a->address, b->address, a->address_length) == 0 && | |||||||
1068 | memcmp(a->number, b->number, a->number_length) == 0 && | |||||||
1069 | memcmp(a->name, b->name, a->name_length) == 0 && | |||||||
1070 | memcmp(a->data, b->data, a->data_length) == 0) ? 1 : 0); | |||||||
1071 | } | |||||||
1072 | ||||||||
1073 | static int | |||||||
1074 | match_auth_dpy(register Xauth *a, register Xauth *b) | |||||||
1075 | { | |||||||
1076 | if (a->family != FamilyWild(65535) && b->family != FamilyWild(65535)) { | |||||||
1077 | /* Both "a" and "b" are not FamilyWild, they are "normal" families. */ | |||||||
1078 | ||||||||
1079 | /* Make sure, that both families match: */ | |||||||
1080 | if (a->family != b->family) | |||||||
1081 | return 0; | |||||||
1082 | ||||||||
1083 | /* By looking at 'man Xsecurity' and the code in | |||||||
1084 | * GetAuthByAddr() and XauGetBestAuthByAddr() in libXau, we | |||||||
1085 | * decided, that the address is only relevant for "normal" | |||||||
1086 | * families and therefore should be ignored for | |||||||
1087 | * "FamilyWild". */ | |||||||
1088 | if (a->address_length != b->address_length || | |||||||
1089 | memcmp(a->address, b->address, a->address_length) != 0) | |||||||
1090 | return 0; | |||||||
1091 | } | |||||||
1092 | ||||||||
1093 | if (a->number_length != 0 && b->number_length != 0) { | |||||||
1094 | /* Both "a" and "b" have a number, make sure they match: */ | |||||||
1095 | if (a->number_length != b->number_length || | |||||||
1096 | memcmp(a->number, b->number, a->number_length) != 0) | |||||||
1097 | return 0; | |||||||
1098 | } | |||||||
1099 | ||||||||
1100 | return 1; | |||||||
1101 | } | |||||||
1102 | ||||||||
1103 | /* return non-zero iff display and authorization type are the same */ | |||||||
1104 | ||||||||
1105 | static int | |||||||
1106 | match_auth(register Xauth *a, register Xauth *b) | |||||||
1107 | { | |||||||
1108 | return ((match_auth_dpy(a, b) | |||||||
1109 | && a->name_length == b->name_length | |||||||
1110 | && memcmp(a->name, b->name, a->name_length) == 0) ? 1 : 0); | |||||||
1111 | } | |||||||
1112 | ||||||||
1113 | ||||||||
1114 | static int | |||||||
1115 | merge_entries(AuthList **firstp, AuthList *second, int *nnewp, int *nreplp) | |||||||
1116 | { | |||||||
1117 | AuthList *a, *b, *first, *tail; | |||||||
1118 | int n = 0, nnew = 0, nrepl = 0; | |||||||
1119 | ||||||||
1120 | if (!second) return 0; | |||||||
1121 | ||||||||
1122 | if (!*firstp) { /* if nothing to merge into */ | |||||||
1123 | *firstp = second; | |||||||
1124 | for (tail = *firstp, n = 1; tail->next; n++, tail = tail->next) ; | |||||||
1125 | *nnewp = n; | |||||||
1126 | *nreplp = 0; | |||||||
1127 | return n; | |||||||
1128 | } | |||||||
1129 | ||||||||
1130 | first = *firstp; | |||||||
1131 | /* | |||||||
1132 | * find end of first list and stick second list on it | |||||||
1133 | */ | |||||||
1134 | for (tail = first; tail->next; tail = tail->next) ; | |||||||
1135 | tail->next = second; | |||||||
1136 | ||||||||
1137 | /* | |||||||
1138 | * run down list freeing duplicate entries; if an entry is okay, then | |||||||
1139 | * bump the tail up to include it, otherwise, cut the entry out of | |||||||
1140 | * the chain. | |||||||
1141 | */ | |||||||
1142 | for (b = second; b; ) { | |||||||
1143 | AuthList *next = b->next; /* in case we free it */ | |||||||
1144 | ||||||||
1145 | a = first; | |||||||
1146 | for (;;) { | |||||||
1147 | if (match_auth (a->auth, b->auth)) { /* found a duplicate */ | |||||||
1148 | AuthList tmp; /* swap it in for old one */ | |||||||
1149 | tmp = *a; | |||||||
1150 | *a = *b; | |||||||
1151 | *b = tmp; | |||||||
1152 | a->next = b->next; | |||||||
1153 | XauDisposeAuth (b->auth); | |||||||
1154 | free ((char *) b); | |||||||
1155 | b = NULL((void*)0); | |||||||
1156 | tail->next = next; | |||||||
1157 | nrepl++; | |||||||
1158 | nnew--; | |||||||
1159 | break; | |||||||
1160 | } | |||||||
1161 | if (a == tail) break; /* if have looked at left side */ | |||||||
1162 | a = a->next; | |||||||
1163 | } | |||||||
1164 | if (b) { /* if we didn't remove it */ | |||||||
1165 | tail = b; /* bump end of first list */ | |||||||
1166 | } | |||||||
1167 | b = next; | |||||||
1168 | n++; | |||||||
1169 | nnew++; | |||||||
1170 | } | |||||||
1171 | ||||||||
1172 | *nnewp = nnew; | |||||||
1173 | *nreplp = nrepl; | |||||||
1174 | return n; | |||||||
1175 | ||||||||
1176 | } | |||||||
1177 | ||||||||
1178 | static Xauth * | |||||||
1179 | copyAuth(Xauth *auth) | |||||||
1180 | { | |||||||
1181 | Xauth *a; | |||||||
1182 | ||||||||
1183 | a = (Xauth *)malloc(sizeof(Xauth)); | |||||||
1184 | if (a == NULL((void*)0)) { | |||||||
1185 | return NULL((void*)0); | |||||||
1186 | } | |||||||
1187 | memset(a, 0, sizeof(Xauth))__builtin___memset_chk (a, 0, sizeof(Xauth), __builtin_object_size (a, 0)); | |||||||
1188 | a->family = auth->family; | |||||||
1189 | if (auth->address_length != 0) { | |||||||
1190 | a->address = malloc(auth->address_length); | |||||||
1191 | if (a->address == NULL((void*)0)) { | |||||||
1192 | free(a); | |||||||
1193 | return NULL((void*)0); | |||||||
1194 | } | |||||||
1195 | memcpy(a->address, auth->address, auth->address_length)__builtin___memcpy_chk (a->address, auth->address, auth ->address_length, __builtin_object_size (a->address, 0) ); | |||||||
1196 | a->address_length = auth->address_length; | |||||||
1197 | } | |||||||
1198 | if (auth->number_length != 0) { | |||||||
1199 | a->number = malloc(auth->number_length); | |||||||
1200 | if (a->number == NULL((void*)0)) { | |||||||
1201 | free(a->address); | |||||||
1202 | free(a); | |||||||
1203 | return NULL((void*)0); | |||||||
1204 | } | |||||||
1205 | memcpy(a->number, auth->number, auth->number_length)__builtin___memcpy_chk (a->number, auth->number, auth-> number_length, __builtin_object_size (a->number, 0)); | |||||||
1206 | a->number_length = auth->number_length; | |||||||
1207 | } | |||||||
1208 | if (auth->name_length != 0) { | |||||||
1209 | a->name = malloc(auth->name_length); | |||||||
1210 | if (a->name == NULL((void*)0)) { | |||||||
1211 | free(a->address); | |||||||
1212 | free(a->number); | |||||||
1213 | free(a); | |||||||
1214 | return NULL((void*)0); | |||||||
1215 | } | |||||||
1216 | memcpy(a->name, auth->name, auth->name_length)__builtin___memcpy_chk (a->name, auth->name, auth->name_length , __builtin_object_size (a->name, 0)); | |||||||
1217 | a->name_length = auth->name_length; | |||||||
1218 | } | |||||||
1219 | if (auth->data_length != 0) { | |||||||
1220 | a->data = malloc(auth->data_length); | |||||||
1221 | if (a->data == NULL((void*)0)) { | |||||||
1222 | free(a->address); | |||||||
1223 | free(a->number); | |||||||
1224 | free(a->name); | |||||||
1225 | free(a); | |||||||
1226 | return NULL((void*)0); | |||||||
1227 | } | |||||||
1228 | memcpy(a->data, auth->data, auth->data_length)__builtin___memcpy_chk (a->data, auth->data, auth->data_length , __builtin_object_size (a->data, 0)); | |||||||
1229 | a->data_length = auth->data_length; | |||||||
1230 | } | |||||||
1231 | return a; | |||||||
1232 | } | |||||||
1233 | ||||||||
1234 | typedef int (*YesNoFunc)(const char *, int, Xauth *, char *); | |||||||
1235 | ||||||||
1236 | static int | |||||||
1237 | iterdpy (const char *inputfilename, int lineno, int start, | |||||||
1238 | int argc, const char *argv[], | |||||||
1239 | YesNoFunc yfunc, YesNoFunc nfunc, char *data) | |||||||
1240 | { | |||||||
1241 | int i; | |||||||
1242 | int status; | |||||||
1243 | int errors = 0; | |||||||
1244 | Xauth *tmp_auth; | |||||||
1245 | AuthList *proto_head, *proto; | |||||||
1246 | AuthList *l, *next; | |||||||
1247 | ||||||||
1248 | /* | |||||||
1249 | * iterate | |||||||
1250 | */ | |||||||
1251 | for (i = start; i < argc; i++) { | |||||||
1252 | const char *displayname = argv[i]; | |||||||
1253 | if (!get_displayname_auth (displayname, &proto_head)) { | |||||||
1254 | prefix (inputfilename, lineno); | |||||||
1255 | baddisplayname (displayname, argv[0]); | |||||||
1256 | errors++; | |||||||
1257 | continue; | |||||||
1258 | } | |||||||
1259 | status = 0; | |||||||
1260 | for (l = xauth_head; l; l = next) { | |||||||
1261 | Boolint matched = False0; | |||||||
1262 | ||||||||
1263 | /* l may be freed by remove_entry below. so save its contents */ | |||||||
1264 | next = l->next; | |||||||
1265 | tmp_auth = copyAuth(l->auth); | |||||||
1266 | for (proto = proto_head; proto; proto = proto->next) { | |||||||
1267 | if (match_auth_dpy (proto->auth, tmp_auth)) { | |||||||
1268 | matched = True1; | |||||||
1269 | if (yfunc) { | |||||||
1270 | status = (*yfunc) (inputfilename, lineno, | |||||||
1271 | tmp_auth, data); | |||||||
1272 | if (status < 0) break; | |||||||
1273 | } | |||||||
1274 | } | |||||||
1275 | } | |||||||
1276 | XauDisposeAuth(tmp_auth); | |||||||
1277 | if (matched == False0) { | |||||||
1278 | if (nfunc) { | |||||||
1279 | status = (*nfunc) (inputfilename, lineno, | |||||||
1280 | l->auth, data); | |||||||
1281 | } | |||||||
1282 | } | |||||||
1283 | if (status < 0) break; | |||||||
1284 | } | |||||||
1285 | for (proto = proto_head; proto ; proto = next) { | |||||||
1286 | next = proto->next; | |||||||
1287 | if (proto->auth->address) free (proto->auth->address); | |||||||
1288 | if (proto->auth->number) free (proto->auth->number); | |||||||
1289 | free (proto->auth); | |||||||
1290 | free (proto); | |||||||
1291 | } | |||||||
1292 | if (status < 0) { | |||||||
1293 | errors -= status; /* since status is negative */ | |||||||
1294 | break; | |||||||
1295 | } | |||||||
1296 | } | |||||||
1297 | ||||||||
1298 | return errors; | |||||||
1299 | } | |||||||
1300 | ||||||||
1301 | /* ARGSUSED */ | |||||||
1302 | static int | |||||||
1303 | remove_entry(const char *inputfilename, int lineno, Xauth *auth, char *data) | |||||||
1304 | { | |||||||
1305 | int *nremovedp = (int *) data; | |||||||
1306 | AuthList **listp = &xauth_head; | |||||||
1307 | AuthList *list; | |||||||
1308 | ||||||||
1309 | /* | |||||||
1310 | * unlink the auth we were asked to | |||||||
1311 | */ | |||||||
1312 | while (!eq_auth((list = *listp)->auth, auth)) { | |||||||
1313 | listp = &list->next; | |||||||
1314 | if (!*listp) | |||||||
1315 | return 0; | |||||||
1316 | } | |||||||
1317 | *listp = list->next; | |||||||
1318 | XauDisposeAuth (list->auth); /* free the auth */ | |||||||
1319 | free (list); /* free the link */ | |||||||
1320 | xauth_modified = True1; | |||||||
1321 | (*nremovedp)++; | |||||||
1322 | return 1; | |||||||
1323 | } | |||||||
1324 | ||||||||
1325 | /* | |||||||
1326 | * action routines | |||||||
1327 | */ | |||||||
1328 | ||||||||
1329 | /* | |||||||
1330 | * help | |||||||
1331 | */ | |||||||
1332 | int | |||||||
1333 | print_help(FILE *fp, const char *cmd, const char *line_prefix) | |||||||
1334 | { | |||||||
1335 | CommandTable *ct; | |||||||
1336 | int n = 0; | |||||||
1337 | ||||||||
1338 | if (!line_prefix) line_prefix = ""; | |||||||
1339 | ||||||||
1340 | if (!cmd) { /* if no cmd, print all help */ | |||||||
1341 | for (ct = command_table; ct->name; ct++) { | |||||||
1342 | fprintf (fp, "%s%s\n", line_prefix, ct->helptext); | |||||||
1343 | n++; | |||||||
1344 | } | |||||||
1345 | } else { | |||||||
1346 | int len = strlen (cmd); | |||||||
1347 | for (ct = command_table; ct->name; ct++) { | |||||||
1348 | if (strncmp (cmd, ct->name, len) == 0) { | |||||||
1349 | fprintf (fp, "%s%s\n", line_prefix, ct->helptext); | |||||||
1350 | n++; | |||||||
1351 | } | |||||||
1352 | } | |||||||
1353 | } | |||||||
1354 | ||||||||
1355 | return n; | |||||||
1356 | } | |||||||
1357 | ||||||||
1358 | static int | |||||||
1359 | do_help(const char *inputfilename, int lineno, int argc, const char **argv) | |||||||
1360 | { | |||||||
1361 | const char *cmd = (argc > 1 ? argv[1] : NULL((void*)0)); | |||||||
1362 | int n; | |||||||
1363 | ||||||||
1364 | n = print_help (stdout__stdoutp, cmd, " "); /* a nice amount */ | |||||||
1365 | ||||||||
1366 | if (n < 0 || (n == 0 && !cmd)) { | |||||||
1367 | prefix (inputfilename, lineno); | |||||||
1368 | fprintf (stderr__stderrp, "internal error with help"); | |||||||
1369 | if (cmd) { | |||||||
1370 | fprintf (stderr__stderrp, " on command \"%s\"", cmd); | |||||||
1371 | } | |||||||
1372 | fprintf (stderr__stderrp, "\n"); | |||||||
1373 | return 1; | |||||||
1374 | } | |||||||
1375 | ||||||||
1376 | if (n == 0) { | |||||||
1377 | prefix (inputfilename, lineno); | |||||||
1378 | /* already know that cmd is set in this case */ | |||||||
1379 | fprintf (stderr__stderrp, "no help for noexistent command \"%s\"\n", cmd); | |||||||
1380 | } | |||||||
1381 | ||||||||
1382 | return 0; | |||||||
1383 | } | |||||||
1384 | ||||||||
1385 | /* | |||||||
1386 | * questionmark | |||||||
1387 | */ | |||||||
1388 | /* ARGSUSED */ | |||||||
1389 | static int | |||||||
1390 | do_questionmark(const char *inputfilename, int lineno, int argc, const char **argv) | |||||||
1391 | { | |||||||
1392 | CommandTable *ct; | |||||||
1393 | int i; | |||||||
1394 | #define WIDEST_COLUMN72 72 | |||||||
1395 | int col = WIDEST_COLUMN72; | |||||||
1396 | ||||||||
1397 | printf ("Commands:\n"); | |||||||
1398 | for (ct = command_table; ct->name; ct++) { | |||||||
1399 | if ((col + ct->maxlen) > WIDEST_COLUMN72) { | |||||||
1400 | if (ct != command_table) { | |||||||
1401 | putc ('\n', stdout__stdoutp); | |||||||
1402 | } | |||||||
1403 | fputs (" ", stdout__stdoutp); | |||||||
1404 | col = 8; /* length of string above */ | |||||||
1405 | } | |||||||
1406 | fputs (ct->name, stdout__stdoutp); | |||||||
1407 | col += ct->maxlen; | |||||||
1408 | for (i = ct->maxlen; i < COMMAND_NAMES_PADDED_WIDTH10; i++) { | |||||||
1409 | putc (' ', stdout__stdoutp); | |||||||
1410 | col++; | |||||||
1411 | } | |||||||
1412 | } | |||||||
1413 | if (col != 0) { | |||||||
1414 | putc ('\n', stdout__stdoutp); | |||||||
1415 | } | |||||||
1416 | ||||||||
1417 | /* allow bad lines since this is help */ | |||||||
1418 | return 0; | |||||||
1419 | } | |||||||
1420 | ||||||||
1421 | /* | |||||||
1422 | * version | |||||||
1423 | */ | |||||||
1424 | /* ARGSUSED */ | |||||||
1425 | static int | |||||||
1426 | do_version(const char *inputfilename, int lineno, int argc, const char **argv) | |||||||
1427 | { | |||||||
1428 | puts (PACKAGE_VERSION"1.0.9"); | |||||||
1429 | return 0; | |||||||
1430 | } | |||||||
1431 | ||||||||
1432 | /* | |||||||
1433 | * list [displayname ...] | |||||||
1434 | */ | |||||||
1435 | static int | |||||||
1436 | do_list (const char *inputfilename, int lineno, int argc, const char **argv) | |||||||
1437 | { | |||||||
1438 | struct _list_data ld; | |||||||
1439 | ||||||||
1440 | ld.fp = stdout__stdoutp; | |||||||
1441 | ld.numeric = (argv[0][0] == 'n'); | |||||||
1442 | ||||||||
1443 | if (argc == 1) { | |||||||
1444 | register AuthList *l; | |||||||
1445 | ||||||||
1446 | if (xauth_head) { | |||||||
1447 | for (l = xauth_head; l; l = l->next) { | |||||||
1448 | dump_entry (inputfilename, lineno, l->auth, (char *) &ld); | |||||||
1449 | } | |||||||
1450 | } | |||||||
1451 | return 0; | |||||||
1452 | } | |||||||
1453 | ||||||||
1454 | return iterdpy (inputfilename, lineno, 1, argc, argv, | |||||||
1455 | dump_entry, NULL((void*)0), (char *) &ld); | |||||||
1456 | } | |||||||
1457 | ||||||||
1458 | /* | |||||||
1459 | * merge filename [filename ...] | |||||||
1460 | */ | |||||||
1461 | static int | |||||||
1462 | do_merge(const char *inputfilename, int lineno, int argc, const char **argv) | |||||||
1463 | { | |||||||
1464 | int i; | |||||||
1465 | int errors = 0; | |||||||
1466 | AuthList *head, *tail, *listhead, *listtail; | |||||||
1467 | int nentries, nnew, nrepl; | |||||||
1468 | Boolint numeric = False0; | |||||||
1469 | ||||||||
1470 | if (argc < 2) { | |||||||
| ||||||||
1471 | prefix (inputfilename, lineno); | |||||||
1472 | badcommandline (argv[0]); | |||||||
1473 | return 1; | |||||||
1474 | } | |||||||
1475 | ||||||||
1476 | if (argv[0][0] == 'n') numeric = True1; | |||||||
1477 | listhead = listtail = NULL((void*)0); | |||||||
1478 | ||||||||
1479 | for (i = 1; i < argc; i++) { | |||||||
1480 | const char *filename = argv[i]; | |||||||
1481 | FILE *fp; | |||||||
1482 | Boolint used_stdin = False0; | |||||||
1483 | ||||||||
1484 | fp = open_file (&filename, | |||||||
1485 | numeric ? "r" : "rb", | |||||||
1486 | &used_stdin, inputfilename, lineno, | |||||||
1487 | argv[0]); | |||||||
1488 | if (!fp) { | |||||||
1489 | errors++; | |||||||
1490 | continue; | |||||||
1491 | } | |||||||
1492 | ||||||||
1493 | head = tail = NULL((void*)0); | |||||||
1494 | nentries = read_auth_entries (fp, numeric, &head, &tail); | |||||||
1495 | if (nentries == 0) { | |||||||
1496 | prefix (inputfilename, lineno); | |||||||
1497 | fprintf (stderr__stderrp, "unable to read any entries from file \"%s\"\n", | |||||||
1498 | filename); | |||||||
1499 | errors++; | |||||||
1500 | } else { /* link it in */ | |||||||
1501 | add_to_list (listhead, listtail, head){if (listtail) (listtail)->next = (head); else (listhead) = (head); (listtail) = (head);}; | |||||||
| ||||||||
1502 | } | |||||||
1503 | ||||||||
1504 | if (!used_stdin) (void) fclose (fp); | |||||||
1505 | } | |||||||
1506 | ||||||||
1507 | /* | |||||||
1508 | * if we have new entries, merge them in (freeing any duplicates) | |||||||
1509 | */ | |||||||
1510 | if (listhead) { | |||||||
1511 | nentries = merge_entries (&xauth_head, listhead, &nnew, &nrepl); | |||||||
1512 | if (verbose) | |||||||
1513 | printf ("%d entries read in: %d new, %d replacement%s\n", | |||||||
1514 | nentries, nnew, nrepl, nrepl != 1 ? "s" : ""); | |||||||
1515 | if (nentries > 0) xauth_modified = True1; | |||||||
1516 | } | |||||||
1517 | ||||||||
1518 | return 0; | |||||||
1519 | } | |||||||
1520 | ||||||||
1521 | /* | |||||||
1522 | * extract filename displayname [displayname ...] | |||||||
1523 | */ | |||||||
1524 | static int | |||||||
1525 | do_extract(const char *inputfilename, int lineno, int argc, const char **argv) | |||||||
1526 | { | |||||||
1527 | int errors; | |||||||
1528 | struct _extract_data ed; | |||||||
1529 | ||||||||
1530 | if (argc < 3) { | |||||||
1531 | prefix (inputfilename, lineno); | |||||||
1532 | badcommandline (argv[0]); | |||||||
1533 | return 1; | |||||||
1534 | } | |||||||
1535 | ||||||||
1536 | ed.fp = NULL((void*)0); | |||||||
1537 | ed.filename = argv[1]; | |||||||
1538 | ed.used_stdout = False0; | |||||||
1539 | ed.numeric = (argv[0][0] == 'n'); | |||||||
1540 | ed.nwritten = 0; | |||||||
1541 | ed.cmd = argv[0]; | |||||||
1542 | ||||||||
1543 | errors = iterdpy (inputfilename, lineno, 2, argc, argv, | |||||||
1544 | extract_entry, NULL((void*)0), (char *) &ed); | |||||||
1545 | ||||||||
1546 | if (!ed.fp) { | |||||||
1547 | fprintf (stderr__stderrp, | |||||||
1548 | "No matches found, authority file \"%s\" not written\n", | |||||||
1549 | ed.filename); | |||||||
1550 | } else { | |||||||
1551 | if (verbose) { | |||||||
1552 | printf ("%d entries written to \"%s\"\n", | |||||||
1553 | ed.nwritten, ed.filename); | |||||||
1554 | } | |||||||
1555 | if (!ed.used_stdout) { | |||||||
1556 | (void) fclose (ed.fp); | |||||||
1557 | } | |||||||
1558 | } | |||||||
1559 | ||||||||
1560 | return errors; | |||||||
1561 | } | |||||||
1562 | ||||||||
1563 | ||||||||
1564 | /* | |||||||
1565 | * add displayname protocolname hexkey | |||||||
1566 | */ | |||||||
1567 | ||||||||
1568 | static int | |||||||
1569 | do_add(const char *inputfilename, int lineno, int argc, const char **argv) | |||||||
1570 | { | |||||||
1571 | int n, nnew, nrepl; | |||||||
1572 | int len; | |||||||
1573 | const char *dpyname; | |||||||
1574 | const char *protoname; | |||||||
1575 | const char *hexkey; | |||||||
1576 | char *key; | |||||||
1577 | AuthList *list, *list_cur, *list_next; | |||||||
1578 | ||||||||
1579 | if (argc != 4 || !argv[1] || !argv[2] || !argv[3]) { | |||||||
1580 | prefix (inputfilename, lineno); | |||||||
1581 | badcommandline (argv[0]); | |||||||
1582 | return 1; | |||||||
1583 | } | |||||||
1584 | ||||||||
1585 | dpyname = argv[1]; | |||||||
1586 | protoname = argv[2]; | |||||||
1587 | hexkey = argv[3]; | |||||||
1588 | ||||||||
1589 | len = strlen(hexkey); | |||||||
1590 | if (hexkey[0] == '"' && hexkey[len-1] == '"') { | |||||||
1591 | key = malloc(len-1); | |||||||
1592 | strncpy(key, hexkey+1, len-2)__builtin___strncpy_chk (key, hexkey+1, len-2, __builtin_object_size (key, 2 > 1 ? 1 : 0)); | |||||||
1593 | len -= 2; | |||||||
1594 | } else if (!strcmp(protoname, SECURERPC"SUN-DES-1") || | |||||||
1595 | !strcmp(protoname, K5AUTH"MIT-KERBEROS-5")) { | |||||||
1596 | key = malloc(len+1); | |||||||
1597 | strcpy(key, hexkey)__builtin___strcpy_chk (key, hexkey, __builtin_object_size (key , 2 > 1 ? 1 : 0)); | |||||||
1598 | } else { | |||||||
1599 | len = cvthexkey (hexkey, &key); | |||||||
1600 | if (len < 0) { | |||||||
1601 | prefix (inputfilename, lineno); | |||||||
1602 | fprintf (stderr__stderrp, | |||||||
1603 | "key contains odd number of or non-hex characters\n"); | |||||||
1604 | return 1; | |||||||
1605 | } | |||||||
1606 | } | |||||||
1607 | ||||||||
1608 | if (!get_displayname_auth (dpyname, &list)) { | |||||||
1609 | prefix (inputfilename, lineno); | |||||||
1610 | baddisplayname (dpyname, argv[0]); | |||||||
1611 | free (key); | |||||||
1612 | return 1; | |||||||
1613 | } | |||||||
1614 | ||||||||
1615 | /* | |||||||
1616 | * allow an abbreviation for common protocol names | |||||||
1617 | */ | |||||||
1618 | if (strcmp (protoname, DEFAULT_PROTOCOL_ABBREV".") == 0) { | |||||||
1619 | protoname = DEFAULT_PROTOCOL"MIT-MAGIC-COOKIE-1"; | |||||||
1620 | } | |||||||
1621 | ||||||||
1622 | for (list_cur = list; list_cur != NULL((void*)0); list_cur = list_cur->next) { | |||||||
1623 | Xauth *auth = list_cur->auth; | |||||||
1624 | ||||||||
1625 | auth->name_length = strlen (protoname); | |||||||
1626 | auth->name = copystring (protoname, auth->name_length); | |||||||
1627 | if (!auth->name) { | |||||||
1628 | prefix (inputfilename, lineno); | |||||||
1629 | fprintf (stderr__stderrp, "unable to allocate %d character protocol name\n", | |||||||
1630 | auth->name_length); | |||||||
1631 | for (list_cur = list; list_cur != NULL((void*)0); list_cur = list_next) { | |||||||
1632 | list_next = list_cur->next; | |||||||
1633 | XauDisposeAuth(list_cur->auth); | |||||||
1634 | free(list_cur); | |||||||
1635 | } | |||||||
1636 | free (key); | |||||||
1637 | return 1; | |||||||
1638 | } | |||||||
1639 | auth->data_length = len; | |||||||
1640 | auth->data = malloc(len); | |||||||
1641 | if (!auth->data) { | |||||||
1642 | prefix(inputfilename, lineno); | |||||||
1643 | fprintf(stderr__stderrp, "unable to allocate %d bytes for key\n", len); | |||||||
1644 | for (list_cur = list; list_cur != NULL((void*)0); list_cur = list_next) { | |||||||
1645 | list_next = list_cur->next; | |||||||
1646 | XauDisposeAuth(list_cur->auth); | |||||||
1647 | free(list_cur); | |||||||
1648 | } | |||||||
1649 | free(key); | |||||||
1650 | return 1; | |||||||
1651 | } | |||||||
1652 | memcpy(auth->data, key, len)__builtin___memcpy_chk (auth->data, key, len, __builtin_object_size (auth->data, 0)); | |||||||
1653 | } | |||||||
1654 | free(key); | |||||||
1655 | /* | |||||||
1656 | * merge it in; note that merge will deal with allocation | |||||||
1657 | */ | |||||||
1658 | n = merge_entries (&xauth_head, list, &nnew, &nrepl); | |||||||
1659 | if (n <= 0) { | |||||||
1660 | prefix (inputfilename, lineno); | |||||||
1661 | fprintf (stderr__stderrp, "unable to merge in added record\n"); | |||||||
1662 | return 1; | |||||||
1663 | } | |||||||
1664 | ||||||||
1665 | xauth_modified = True1; | |||||||
1666 | return 0; | |||||||
1667 | } | |||||||
1668 | ||||||||
1669 | /* | |||||||
1670 | * remove displayname | |||||||
1671 | */ | |||||||
1672 | static int | |||||||
1673 | do_remove(const char *inputfilename, int lineno, int argc, const char **argv) | |||||||
1674 | { | |||||||
1675 | int nremoved = 0; | |||||||
1676 | int errors; | |||||||
1677 | ||||||||
1678 | if (argc < 2) { | |||||||
1679 | prefix (inputfilename, lineno); | |||||||
1680 | badcommandline (argv[0]); | |||||||
1681 | return 1; | |||||||
1682 | } | |||||||
1683 | ||||||||
1684 | errors = iterdpy (inputfilename, lineno, 1, argc, argv, | |||||||
1685 | remove_entry, NULL((void*)0), (char *) &nremoved); | |||||||
1686 | if (verbose) printf ("%d entries removed\n", nremoved); | |||||||
1687 | return errors; | |||||||
1688 | } | |||||||
1689 | ||||||||
1690 | /* | |||||||
1691 | * info | |||||||
1692 | */ | |||||||
1693 | static int | |||||||
1694 | do_info(const char *inputfilename, int lineno, int argc, const char **argv) | |||||||
1695 | { | |||||||
1696 | int n; | |||||||
1697 | AuthList *l; | |||||||
1698 | ||||||||
1699 | if (argc != 1) { | |||||||
1700 | prefix (inputfilename, lineno); | |||||||
1701 | badcommandline (argv[0]); | |||||||
1702 | return 1; | |||||||
1703 | } | |||||||
1704 | ||||||||
1705 | for (l = xauth_head, n = 0; l; l = l->next, n++) ; | |||||||
1706 | ||||||||
1707 | printf ("Authority file: %s\n", | |||||||
1708 | xauth_filename ? xauth_filename : "(none)"); | |||||||
1709 | printf ("File new: %s\n", xauth_existed ? No : Yes); | |||||||
1710 | printf ("File locked: %s\n", xauth_locked ? No : Yes); | |||||||
1711 | printf ("Number of entries: %d\n", n); | |||||||
1712 | printf ("Changes honored: %s\n", xauth_allowed ? Yes : No); | |||||||
1713 | printf ("Changes made: %s\n", xauth_modified ? Yes : No); | |||||||
1714 | printf ("Current input: %s:%d\n", inputfilename, lineno); | |||||||
1715 | return 0; | |||||||
1716 | } | |||||||
1717 | ||||||||
1718 | ||||||||
1719 | /* | |||||||
1720 | * exit | |||||||
1721 | */ | |||||||
1722 | static Boolint alldone = False0; | |||||||
1723 | ||||||||
1724 | /* ARGSUSED */ | |||||||
1725 | static int | |||||||
1726 | do_exit(const char *inputfilename, int lineno, int argc, const char **argv) | |||||||
1727 | { | |||||||
1728 | /* allow bogus stuff */ | |||||||
1729 | alldone = True1; | |||||||
1730 | return 0; | |||||||
1731 | } | |||||||
1732 | ||||||||
1733 | /* | |||||||
1734 | * quit | |||||||
1735 | */ | |||||||
1736 | /* ARGSUSED */ | |||||||
1737 | static int | |||||||
1738 | do_quit(const char *inputfilename, int lineno, int argc, const char **argv) | |||||||
1739 | { | |||||||
1740 | /* allow bogus stuff */ | |||||||
1741 | die (0); | |||||||
1742 | /* NOTREACHED */ | |||||||
1743 | return -1; /* for picky compilers */ | |||||||
1744 | } | |||||||
1745 | ||||||||
1746 | ||||||||
1747 | /* | |||||||
1748 | * source filename | |||||||
1749 | */ | |||||||
1750 | static int | |||||||
1751 | do_source(const char *inputfilename, int lineno, int argc, const char **argv) | |||||||
1752 | { | |||||||
1753 | const char *script; | |||||||
1754 | char buf[BUFSIZ1024]; | |||||||
1755 | FILE *fp; | |||||||
1756 | Boolint used_stdin = False0; | |||||||
1757 | int len; | |||||||
1758 | int errors = 0, status; | |||||||
1759 | int sublineno = 0; | |||||||
1760 | const char **subargv; | |||||||
1761 | int subargc; | |||||||
1762 | Boolint prompt = False0; /* only true if reading from tty */ | |||||||
1763 | ||||||||
1764 | if (argc != 2 || !argv[1]) { | |||||||
1765 | prefix (inputfilename, lineno); | |||||||
1766 | badcommandline (argv[0]); | |||||||
1767 | return 1; | |||||||
1768 | } | |||||||
1769 | ||||||||
1770 | script = argv[1]; | |||||||
1771 | ||||||||
1772 | fp = open_file (&script, "r", &used_stdin, inputfilename, lineno, argv[0]); | |||||||
1773 | if (!fp) { | |||||||
1774 | return 1; | |||||||
1775 | } | |||||||
1776 | ||||||||
1777 | if (verbose && used_stdin && isatty (fileno (fp))) prompt = True1; | |||||||
1778 | ||||||||
1779 | while (!alldone) { | |||||||
1780 | buf[0] = '\0'; | |||||||
1781 | if (prompt) { | |||||||
1782 | printf ("xauth> "); | |||||||
1783 | fflush (stdout__stdoutp); | |||||||
1784 | } | |||||||
1785 | if (fgets (buf, sizeof buf, fp) == NULL((void*)0)) break; | |||||||
1786 | sublineno++; | |||||||
1787 | len = strlen (buf); | |||||||
1788 | if (len == 0 || buf[0] == '#') continue; | |||||||
1789 | if (buf[len-1] != '\n') { | |||||||
1790 | prefix (script, sublineno); | |||||||
1791 | fprintf (stderr__stderrp, "line too long\n"); | |||||||
1792 | errors++; | |||||||
1793 | break; | |||||||
1794 | } | |||||||
1795 | buf[--len] = '\0'; /* remove new line */ | |||||||
1796 | subargv = (const char **) split_into_words (buf, &subargc); | |||||||
1797 | if (subargv) { | |||||||
1798 | status = process_command (script, sublineno, subargc, subargv); | |||||||
1799 | free (subargv); | |||||||
1800 | errors += status; | |||||||
1801 | } else { | |||||||
1802 | prefix (script, sublineno); | |||||||
1803 | fprintf (stderr__stderrp, "unable to break line into words\n"); | |||||||
1804 | errors++; | |||||||
1805 | } | |||||||
1806 | } | |||||||
1807 | ||||||||
1808 | if (!used_stdin) { | |||||||
1809 | (void) fclose (fp); | |||||||
1810 | } | |||||||
1811 | return errors; | |||||||
1812 | } | |||||||
1813 | ||||||||
1814 | static int x_protocol_error; | |||||||
1815 | static int | |||||||
1816 | catch_x_protocol_error(Display *dpy, XErrorEvent *errevent) | |||||||
1817 | { | |||||||
1818 | char buf[80]; | |||||||
1819 | XGetErrorText(dpy, errevent->error_code, buf, sizeof (buf)); | |||||||
1820 | fprintf(stderr__stderrp, "%s\n", buf); | |||||||
1821 | x_protocol_error = errevent->error_code; | |||||||
1822 | return 1; | |||||||
1823 | } | |||||||
1824 | ||||||||
1825 | /* | |||||||
1826 | * generate | |||||||
1827 | */ | |||||||
1828 | static int | |||||||
1829 | do_generate(const char *inputfilename, int lineno, int argc, const char **argv) | |||||||
1830 | { | |||||||
1831 | const char *displayname; | |||||||
1832 | int major_version, minor_version; | |||||||
1833 | XSecurityAuthorization id_return; | |||||||
1834 | Xauth *auth_in, *auth_return; | |||||||
1835 | XSecurityAuthorizationAttributes attributes; | |||||||
1836 | unsigned long attrmask = 0; | |||||||
1837 | Display *dpy; | |||||||
1838 | int status; | |||||||
1839 | const char *args[4]; | |||||||
1840 | const char *protoname = "."; | |||||||
1841 | int i; | |||||||
1842 | int authdatalen = 0; | |||||||
1843 | const char *hexdata; | |||||||
1844 | char *authdata = NULL((void*)0); | |||||||
1845 | char *hex; | |||||||
1846 | ||||||||
1847 | if (argc < 2 || !argv[1]) { | |||||||
1848 | prefix (inputfilename, lineno); | |||||||
1849 | badcommandline (argv[0]); | |||||||
1850 | return 1; | |||||||
1851 | } | |||||||
1852 | ||||||||
1853 | displayname = argv[1]; | |||||||
1854 | ||||||||
1855 | if (argc > 2) { | |||||||
1856 | protoname = argv[2]; | |||||||
1857 | } | |||||||
1858 | ||||||||
1859 | for (i = 3; i < argc; i++) { | |||||||
1860 | if (0 == strcmp(argv[i], "timeout")) { | |||||||
1861 | if (++i == argc) { | |||||||
1862 | prefix (inputfilename, lineno); | |||||||
1863 | badcommandline (argv[i-1]); | |||||||
1864 | return 1; | |||||||
1865 | } | |||||||
1866 | attributes.timeout = atoi(argv[i]); | |||||||
1867 | attrmask |= XSecurityTimeout(1<<0); | |||||||
1868 | ||||||||
1869 | } else if (0 == strcmp(argv[i], "trusted")) { | |||||||
1870 | attributes.trust_level = XSecurityClientTrusted0; | |||||||
1871 | attrmask |= XSecurityTrustLevel(1<<1); | |||||||
1872 | ||||||||
1873 | } else if (0 == strcmp(argv[i], "untrusted")) { | |||||||
1874 | attributes.trust_level = XSecurityClientUntrusted1; | |||||||
1875 | attrmask |= XSecurityTrustLevel(1<<1); | |||||||
1876 | ||||||||
1877 | } else if (0 == strcmp(argv[i], "group")) { | |||||||
1878 | if (++i == argc) { | |||||||
1879 | prefix (inputfilename, lineno); | |||||||
1880 | badcommandline (argv[i-1]); | |||||||
1881 | return 1; | |||||||
1882 | } | |||||||
1883 | attributes.group = atoi(argv[i]); | |||||||
1884 | attrmask |= XSecurityGroup(1<<2); | |||||||
1885 | ||||||||
1886 | } else if (0 == strcmp(argv[i], "data")) { | |||||||
1887 | if (++i == argc) { | |||||||
1888 | prefix (inputfilename, lineno); | |||||||
1889 | badcommandline (argv[i-1]); | |||||||
1890 | return 1; | |||||||
1891 | } | |||||||
1892 | hexdata = argv[i]; | |||||||
1893 | authdatalen = strlen(hexdata); | |||||||
1894 | if (hexdata[0] == '"' && hexdata[authdatalen-1] == '"') { | |||||||
1895 | authdata = malloc(authdatalen-1); | |||||||
1896 | strncpy(authdata, hexdata+1, authdatalen-2)__builtin___strncpy_chk (authdata, hexdata+1, authdatalen-2, __builtin_object_size (authdata, 2 > 1 ? 1 : 0)); | |||||||
1897 | authdatalen -= 2; | |||||||
1898 | } else { | |||||||
1899 | authdatalen = cvthexkey (hexdata, &authdata); | |||||||
1900 | if (authdatalen < 0) { | |||||||
1901 | prefix (inputfilename, lineno); | |||||||
1902 | fprintf (stderr__stderrp, | |||||||
1903 | "data contains odd number of or non-hex characters\n"); | |||||||
1904 | return 1; | |||||||
1905 | } | |||||||
1906 | } | |||||||
1907 | } else { | |||||||
1908 | prefix (inputfilename, lineno); | |||||||
1909 | badcommandline (argv[i]); | |||||||
1910 | return 1; | |||||||
1911 | } | |||||||
1912 | } | |||||||
1913 | ||||||||
1914 | /* generate authorization using the Security extension */ | |||||||
1915 | ||||||||
1916 | dpy = XOpenDisplay (displayname); | |||||||
1917 | if (!dpy) { | |||||||
1918 | prefix (inputfilename, lineno); | |||||||
1919 | fprintf (stderr__stderrp, "unable to open display \"%s\".\n", displayname); | |||||||
1920 | return 1; | |||||||
1921 | } | |||||||
1922 | ||||||||
1923 | status = XSecurityQueryExtension(dpy, &major_version, &minor_version); | |||||||
1924 | if (!status) | |||||||
1925 | { | |||||||
1926 | prefix (inputfilename, lineno); | |||||||
1927 | fprintf (stderr__stderrp, "couldn't query Security extension on display \"%s\"\n", | |||||||
1928 | displayname); | |||||||
1929 | return 1; | |||||||
1930 | } | |||||||
1931 | ||||||||
1932 | /* fill in input Xauth struct */ | |||||||
1933 | ||||||||
1934 | auth_in = XSecurityAllocXauth(); | |||||||
1935 | if (strcmp (protoname, DEFAULT_PROTOCOL_ABBREV".") == 0) { | |||||||
1936 | auth_in->name = copystring(DEFAULT_PROTOCOL"MIT-MAGIC-COOKIE-1", strlen(DEFAULT_PROTOCOL"MIT-MAGIC-COOKIE-1")); | |||||||
1937 | } | |||||||
1938 | else | |||||||
1939 | auth_in->name = copystring (protoname, strlen(protoname)); | |||||||
1940 | auth_in->name_length = strlen(auth_in->name); | |||||||
1941 | auth_in->data = authdata; | |||||||
1942 | auth_in->data_length = authdatalen; | |||||||
1943 | ||||||||
1944 | x_protocol_error = 0; | |||||||
1945 | XSetErrorHandler(catch_x_protocol_error); | |||||||
1946 | auth_return = XSecurityGenerateAuthorization(dpy, auth_in, attrmask, | |||||||
1947 | &attributes, &id_return); | |||||||
1948 | XSync(dpy, False0); | |||||||
1949 | ||||||||
1950 | if (!auth_return || x_protocol_error) | |||||||
1951 | { | |||||||
1952 | prefix (inputfilename, lineno); | |||||||
1953 | fprintf (stderr__stderrp, "couldn't generate authorization\n"); | |||||||
1954 | return 1; | |||||||
1955 | } | |||||||
1956 | ||||||||
1957 | if (verbose) | |||||||
1958 | printf("authorization id is %ld\n", id_return); | |||||||
1959 | ||||||||
1960 | /* create a fake input line to give to do_add */ | |||||||
1961 | hex = bintohex(auth_return->data_length, auth_return->data); | |||||||
1962 | args[0] = "add"; | |||||||
1963 | args[1] = displayname; | |||||||
1964 | args[2] = auth_in->name; | |||||||
1965 | args[3] = hex; | |||||||
1966 | ||||||||
1967 | status = do_add(inputfilename, lineno, 4, args); | |||||||
1968 | ||||||||
1969 | if (authdata) free(authdata); | |||||||
1970 | XSecurityFreeXauth(auth_in); | |||||||
1971 | XSecurityFreeXauth(auth_return); | |||||||
1972 | free(hex); | |||||||
1973 | XCloseDisplay(dpy); | |||||||
1974 | return status; | |||||||
1975 | } |