"Fossies" - the Fresh Open Source Software Archive 
Member "scponly-20110526/helper.c" (8 Sep 2010, 14005 Bytes) of package /linux/privat/old/scponly-20110526.tgz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
See also the latest
Fossies "Diffs" side-by-side code changes report for "helper.c":
4.8_vs_20110526.
1 /*
2 * helper functions for scponly
3 */
4 #include <stdio.h> /* io */
5 #include <string.h> /* for str* */
6 #include <sys/types.h> /* for stat, getpwuid */
7 #include <sys/stat.h> /* for stat */
8 #include <unistd.h> /* for exit, access, getpwuid, execve, getopt */
9 #include <errno.h> /* for debugging */
10 #include <pwd.h> /* to get username for config parsing */
11 #include <time.h> /* time */
12 #include <libgen.h> /* basename */
13 #include <stdlib.h> /* realloc */
14 #include <syslog.h>
15
16 #include "config.h"
17 #include "scponly.h" /* includes getopt */
18
19 #ifdef HAVE_GLOB
20 #include <glob.h> /* for glob() */
21 #else
22 #ifdef HAVE_WORDEXP
23 #include <wordexp.h> /* for wordexp() */
24 #endif
25 #endif
26
27 #ifdef RSYNC_COMPAT
28 #define RSYNC_ARG_SERVER 0x01
29 #define RSYNC_ARG_EXECUTE 0x02
30 #endif
31
32 #define MAX(x,y) ( ( x > y ) ? x : y )
33 #define MIN(x,y) ( ( x < y ) ? x : y )
34
35 extern int debuglevel;
36 extern char username[MAX_USERNAME];
37 extern char homedir[FILENAME_MAX];
38 extern cmd_t commands[];
39 extern cmd_arg_t dangerous_args[];
40 extern char * allowed_env_vars[];
41 extern char * safeenv[MAX_ENV];
42 extern void (*debug)(int priority, const char* format, ...);
43 extern int (*scponly_getopt_long)( int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex);
44
45 #ifdef HAVE_GETOPT
46 extern char *optarg;
47 extern int optind;
48 #ifdef HAVE_OPTRESET
49 extern int optreset;
50 #endif
51 #endif
52
53 void noop_syslog(int priority, const char* format, ...)
54 {
55 /* purposefully does nothing, used when debuglevel <= 0 */
56 }
57
58 #ifdef UNIX_COMPAT
59 char* solaris_needs_strsep(char** str, char* delims)
60 {
61 char* tmpstr;
62
63 if (*str==NULL) {
64 return NULL;
65 }
66
67 tmpstr=*str;
68 while (**str!='\0') {
69 if (strchr(delims,**str)!=NULL) {
70 **str='\0';
71 (*str)++;
72 return tmpstr;
73 }
74 (*str)++;
75 }
76 *str=NULL;
77 return tmpstr;
78 }
79 #endif
80
81 /*
82 * perform a deep copy of an argument vector, ignoring all vector elements which begin with "--"
83 */
84 char **strip_vector(char **sav)
85 {
86 char **tmpptr=sav;
87 char **dav=(char **)malloc(MAX_ARGC * (sizeof(char *)));
88 int dac=0;
89
90 bzero(dav,sizeof(dav));
91 while (*tmpptr!=NULL)
92 {
93 if(NULL == strbeg(*tmpptr, "--"))
94 dav[dac++]=strdup(*tmpptr);
95 tmpptr++;
96 }
97 return dav;
98 }
99
100 void discard_vector(char **av)
101 {
102 discard_child_vectors(av);
103 free(av);
104 }
105
106 void discard_child_vectors(char **av)
107 {
108 char **tmpptr=av;
109 while (*tmpptr!=NULL)
110 free(*tmpptr++);
111 }
112
113 char *flatten_vector(char **av)
114 {
115 char **tmpptr=av;
116 char *temp=NULL;
117 char *crptr=NULL;
118 char *outbuf=NULL;
119 int len=0,newlen=0;
120
121 while (*tmpptr!=NULL)
122 {
123 if (NULL != (crptr=strchr(*tmpptr, '\n')))
124 {
125 *crptr='\0';
126 }
127 if (outbuf!=NULL)
128 {
129 len = strlen(outbuf);
130 newlen=len + strlen(*tmpptr)+1;
131 }
132 else
133 {
134 len = 0;
135 newlen=strlen(*tmpptr);
136 }
137 if (NULL == (temp = realloc(outbuf, newlen + 1)))
138 {
139 perror("realloc");
140 if (outbuf)
141 free(outbuf);
142 exit(EXIT_FAILURE);
143 }
144 outbuf=temp;
145 temp=NULL;
146 if (len)
147 {
148 outbuf[len]=' ';
149 strcpy(&outbuf[len+1],*tmpptr);
150 }
151 else
152 strcpy(outbuf,*tmpptr);
153 tmpptr++;
154 }
155 return (outbuf);
156 }
157
158 /*
159 * since some programs support invoking other programs for their encryption
160 * (dropins to replace ssh), we must refuse to support these arguments
161 *
162 * RETURN: 1 means reject this command, 0 means it is safe.
163 */
164 int check_dangerous_args(char **av)
165 {
166 cmd_arg_t *cmdarg=dangerous_args;
167 char **tmpptr=av;
168 int ch;
169 int ac=0;
170 int longopt_index = 0;
171 #ifdef RSYNC_COMPAT
172 /*
173 * bitwise flag: 0x01 = server, 0x02 = -e.
174 * Thus 0x03 is allowed and 0x01 is allowed, but 0x02 is not allowed
175 */
176 int rsync_flags = 0;
177 #endif /* RSYNC_COMPAT */
178
179 while (cmdarg != NULL)
180 {
181 if (cmdarg->name == NULL)
182 return 0;
183 if (exact_match(cmdarg->name,av[0]))
184 {
185 /*
186 * the command matches one of our dangerous commands
187 * check the rest of the vector for dangerous command
188 * line arguments
189 *
190 * if the "getoptflag" is set for this command, use getopt
191 * to determine if the flag is present, otherwise
192 * to a string match on each element of the argument vector
193 */
194 if (1 == cmdarg->getoptflag)
195 {
196 debug(LOG_DEBUG, "Using getopt processing for cmd %s\n (%s)", cmdarg->name, logstamp());
197 /*
198 * first count the arguments in the vector
199 */
200 tmpptr=av;
201 while (*tmpptr!=NULL)
202 {
203 tmpptr++;
204 ac++;
205 }
206 /*
207 * now use getopt to look for our problem option
208 */
209 #ifdef HAVE_GETOPT
210 #ifdef HAVE_OPTRESET
211 /*
212 * if we have optreset, use it to reset the global variables
213 */
214 optreset=1;
215 optind=1;
216 #else
217 /*
218 * otherwise, try a glibc-style reset of the global getopt vars
219 */
220 optind=0;
221 #endif /* HAVE_OPTRESET */
222 /*
223 * tell getopt to only be strict if the 'opts' is well defined
224 */
225 opterr=cmdarg->strict;
226 while ((ch = scponly_getopt_long(ac, av, cmdarg->opts, cmdarg->longopts, &longopt_index)) != -1) {
227
228 debug(LOG_DEBUG, "getopt processing returned '%c' (%s)", ch, logstamp());
229
230 #ifdef RSYNC_COMPAT
231 if (exact_match(cmdarg->name, PROG_RSYNC) && (ch == 's' || ch == 'e')) {
232 if (ch == 's')
233 rsync_flags |= RSYNC_ARG_SERVER;
234 else
235 /* -e */
236 rsync_flags |= RSYNC_ARG_EXECUTE;
237 debug(LOG_DEBUG, "rsync_flags are now set to: %0x", rsync_flags);
238 }
239 else
240 #endif /* RSYNC_COMPAT */
241
242 /* if the character is found in badarg, then it's not a permitted option */
243 if (cmdarg->badarg != NULL && (strchr(cmdarg->badarg, ch) != NULL))
244 {
245 syslog(LOG_ERR, "option '%c' or a related long option is not permitted for use with %s (arg was %s) (%s))",
246 ch, cmdarg->name, (optarg!=NULL ? optarg : "<NULL>"), logstamp());
247 return 1;
248 }
249 else if (cmdarg->strict && ch == '?')
250 {
251 syslog(LOG_ERR, "an unrecognized option was encountered while processing cmd %s (arg was %s) (%s))",
252 cmdarg->name, (optarg!=NULL ? optarg : "<NULL>"), logstamp());
253 return 1;
254 }
255 }
256 #ifdef RSYNC_COMPAT
257 /* it's not safe if the execute flag was set and server was not set */
258 if ((rsync_flags & RSYNC_ARG_EXECUTE) != 0 && (rsync_flags & RSYNC_ARG_SERVER) == 0) {
259 syslog(LOG_ERR, "option 'e' is not allowed unless '--server' is also set with cmd %s (%s)",
260 PROG_RSYNC, logstamp());
261 return 1;
262 }
263 #endif /* RSYNC_COMPAT */
264
265 #else /* HAVE_GETOPT */
266 /*
267 * make sure that processing doesn't continue if we can't validate a rsync check
268 * and if the getopt flag is set.
269 */
270 syslog(LOG_ERR, "a getopt() argument check could not be performed for %s, recompile scponly without support for %s or rebuild scponly with getopt", av[0], av[0]);
271 return 1;
272 #endif /* HAVE_GETOPT */
273 }
274 else
275 /*
276 * command does not require getopt processing
277 */
278 {
279 debug(LOG_DEBUG, "Not using getopt processing on cmd %s (%s)", cmdarg->name, logstamp());
280
281 tmpptr=av;
282 tmpptr++;
283 while (*tmpptr!=NULL)
284 {
285 debug(LOG_DEBUG, "Verifying that %s is an allowed option (%s)", *tmpptr, logstamp());
286
287 if(strbeg(*tmpptr, cmdarg->badarg))
288 {
289 syslog(LOG_ERR, "%s is not permitted for use with %s (%s))",
290 cmdarg->badarg, cmdarg->name, logstamp());
291 return 1;
292 }
293 tmpptr++;
294 }
295 }
296 }
297 cmdarg++;
298 }
299 return 0;
300 }
301
302 int valid_arg_vector(char **av)
303 {
304 cmd_t *cmd=commands;
305
306 while (cmd != NULL)
307 {
308 if (cmd->name == NULL)
309 return 0;
310 if (exact_match(cmd->name,av[0]))
311 {
312 if ((cmd->argflag == 0) && (av[1]!=NULL))
313 {
314 return 0;
315 }
316 return 1;
317 }
318 cmd++;
319 }
320 return 0;
321 }
322
323 char *substitute_known_path(char *request)
324 {
325 cmd_t *cmd=commands;
326 char *stripped_req=strdup(basename(request));
327 while (cmd != NULL)
328 {
329 if (cmd->name == NULL)
330 break;
331 if (exact_match(basename(cmd->name),stripped_req))
332 {
333 free(stripped_req); /* discard old pathname */
334 return (strdup(cmd->name));
335 }
336 cmd++;
337 }
338 return (stripped_req);
339 }
340
341 char **build_arg_vector(char *request)
342 {
343 /*
344 * i strdup vector elements so i know they are
345 * mine to free later.
346 */
347 char **ap, *argv[MAX_ARGC], *inputstring, *tmpstring, *freeme;
348 char **ap2,**av=(char **)malloc(MAX_ARGC * (sizeof(char *)));
349
350 ap=argv;
351 freeme=inputstring=strdup(request); /* make a local copy */
352
353 while (ap < &argv[(MAX_ARGC-1)])
354 {
355 if (inputstring && (*inputstring=='"'))
356 {
357 if (NULL != (tmpstring=strchr((inputstring+1),'"')))
358 {
359 *tmpstring++='\0';
360 *ap=(inputstring+1);
361
362 #ifdef UNIX_COMPAT
363 if (solaris_needs_strsep(&tmpstring, WHITE) == NULL)
364 #else
365 if (strsep(&tmpstring, WHITE) == NULL)
366 #endif
367 break;
368 inputstring=tmpstring;
369
370 if (**ap != '\0')
371 ap++;
372 continue;
373 }
374 }
375
376 #ifdef UNIX_COMPAT
377 if ((*ap = solaris_needs_strsep(&inputstring, WHITE)) == NULL)
378 #else
379 if ((*ap = strsep(&inputstring, WHITE)) == NULL)
380 #endif
381 break;
382
383 if (**ap != '\0')
384 ap++;
385 }
386 *ap = NULL;
387 ap=argv;
388 ap2=av;
389 while (*ap != NULL)
390 {
391 *ap2=strdup(*ap);
392 ap2++;
393 ap++;
394 }
395 *ap2 = NULL;
396 free(freeme);
397
398 return (av);
399 }
400
401 char **expand_wildcards(char **av_old)
402 #ifdef HAVE_GLOB
403 {
404 char **av_new=(char **)malloc(MAX_ARGC * (sizeof(char *)));
405 glob_t g;
406 int c_old,c_new,c; /* argument counters */
407 #ifdef UNIX_COMPAT
408 int flags = GLOB_NOCHECK;
409 #else
410 int flags = GLOB_NOCHECK | GLOB_TILDE;
411 #endif
412
413 g.gl_offs = c_new = c_old = 0;
414
415 while(av_old[c_old] != NULL )
416 {
417 if (0 == glob(av_old[c_old++],flags,NULL,&g))
418 {
419 c=0;
420 while((g.gl_pathv[c] != NULL) && (c_new < (MAX_ARGC-1)))
421 av_new[c_new++]=strdup(g.gl_pathv[c++]);
422 globfree(&g);
423 }
424 }
425 av_new[c_new]=NULL;
426 discard_vector(av_old);
427 return av_new;
428 }
429
430 #else
431 #ifdef HAVE_WORDEXP
432 {
433 return NULL;
434 }
435 #endif
436 #endif
437
438 int cntchr(char *buf, char x)
439 {
440 int count=0;
441 while (*buf!=0)
442 if (*buf++==x)
443 count++;
444 return count;
445 }
446
447 char *logstamp ()
448 {
449 /* Time and pid are handled for us by syslog(3). */
450 static char ret_buf[255];
451 static const char bad_ip[10] = "no ip?!";
452 char *ipstring = NULL;
453
454 ipstring = (char *)getenv("SSH_CLIENT");
455 if (!ipstring)
456 ipstring = (char *)getenv("SSH2_CLIENT");
457 if (!ipstring)
458 ipstring = (char *)bad_ip;
459 snprintf(ret_buf, sizeof(ret_buf)-1,
460 "username: %s(%d), IP/port: %s", username, getuid(), ipstring);
461 return ret_buf;
462 }
463
464 /*
465 * if big ends with small, return big without
466 * small in a new buf, else NULL
467 */
468 char *strend (char *big, char *small)
469 {
470 int blen,slen;
471 slen=strlen(small);
472 blen=strlen(big);
473 if ((blen==0) || (slen==0) || (blen < slen))
474 {
475 return NULL;
476 }
477 if (0 == strcmp(&big[(blen-slen)],small))
478 {
479 char *tempbuf=NULL;
480 tempbuf=(char *)malloc(blen-slen+1);
481 if (tempbuf==NULL)
482 {
483 perror("malloc");
484 exit(EXIT_FAILURE);
485 }
486 bzero(tempbuf,(blen-slen+1));
487 strncpy(tempbuf, big, blen-slen);
488 return tempbuf;
489 }
490 return NULL;
491 }
492
493 /*
494 * if big starts with small, return the char after
495 * the last char in small from big. ahem.
496 */
497 char *strbeg(char *big, char *small)
498 {
499 if (strlen(big) <= strlen(small))
500 return NULL;
501 if (0==strncmp(big,small,strlen(small)))
502 return (big+strlen(small));
503 return NULL;
504 }
505
506 /*
507 * if any chars in string dont appear in ALLOWABLE
508 * then fail (return 0)
509 */
510 int valid_chars(char *string)
511 {
512 int count;
513 if ((count=strspn(string,ALLOWABLE))==strlen(string))
514 return 1;
515 else
516 {
517 fprintf (stderr, "invalid characters in scp command!\n");
518 fprintf (stderr, "here:%s\n",string+count);
519 fprintf (stderr, "try using a wildcard to match this file/directory\n");
520 return 0;
521 }
522 }
523
524 /*
525 * retrieves username and home directory from passwd database
526 */
527 int get_uservar(void)
528 {
529 struct passwd *userinfo;
530
531 char *user = (char *)getenv("USER");
532 if (user) {
533 if (NULL==(userinfo=getpwnam(user)))
534 {
535 syslog(LOG_WARNING, "no knowledge of username %s [%s]", user, logstamp());
536 return 0;
537 }
538 if (userinfo->pw_uid != getuid())
539 {
540 syslog(LOG_WARNING, "%s's uid doesn't match getuid(): %d [%s]",
541 user, getuid(), logstamp());
542 return 0;
543 }
544 debug(LOG_DEBUG, "determined USER is \"%s\" from environment", user);
545 } else {
546 if (NULL==(userinfo=getpwuid(getuid())))
547 {
548 syslog (LOG_WARNING, "no knowledge of uid %d [%s]", getuid(), logstamp());
549 return 0;
550 }
551 }
552 debug(
553 LOG_DEBUG,
554 "retrieved home directory of \"%s\" for user \"%s\"",
555 userinfo->pw_dir, userinfo->pw_name
556 );
557 strncpy(username,userinfo->pw_name,MAX_USERNAME);
558 strncpy(homedir,userinfo->pw_dir,FILENAME_MAX);
559 return 1;
560 }
561
562 /*
563 * look through safeenv for the "name" environment variable and replace
564 * it's value with "value".
565 */
566 int replace_env_entry(const char* name, const char* value) {
567
568 char** base = safeenv;
569 char buf[257]; /* make sure I don't overflow */
570 snprintf(buf, 255, "%s=", name);
571 while (*base != NULL) {
572 debug(LOG_DEBUG, "Looking for '%s' in '%s'", buf, *base);
573 if (strbeg(*base, buf) != NULL) {
574 strcpy(*base + strlen(buf), value);
575 debug(LOG_DEBUG, "'%s' env entry now reads '%s'", name, *base);
576 return 0;
577 }
578 base++;
579 }
580 return 1;
581 }
582
583 int mysetenv(const char *name, const char *value) {
584 /* from: http://www.onlamp.com/pub/a/onlamp/excerpt/PUIS3_chap16/index3.html */
585 static char count = 0;
586 char buff[255];
587
588 if (count == 0)
589 /* in case nothing ever gets put in here... */
590 safeenv[0] = NULL;
591 if (count == MAX_ENV)
592 return 0;
593 if (!name || !value)
594 return 0;
595 if (snprintf(buff, sizeof(buff), "%s=%s", name, value) < 0)
596 return 0;
597 if ((safeenv[count] = strdup(buff))) {
598 safeenv[++count] = NULL;
599 return 1;
600 }
601 return 0;
602 }
603
604 void filter_allowed_env_vars() {
605
606 char *p_env;
607 char **p_valid = allowed_env_vars;
608
609 /* check each allowed variable */
610 while (NULL != *p_valid) {
611
612 p_env = (char*)getenv(*p_valid);
613 if (NULL != p_env) {
614 debug(LOG_DEBUG, "Found \"%s\" and setting it to \"%s\"", *p_valid, p_env);
615 if (!mysetenv(*p_valid, p_env))
616 syslog(LOG_ERR, "Unable to set environment var \"%s\" to \"%s\"", *p_valid, p_env);
617 } else {
618 debug(LOG_DEBUG, "Unable to find \"%s\" in the environment", *p_valid);
619 }
620 p_valid++;
621 }
622 }
623
624 /* vim: set noet sw=4 ts=4: */