Example #1
0
int process_ssh_request(char *request)
{
	char **av, **tmp_av, **tenv;
	char *flat_request,*tmpstring, *tmprequest;
	char bad_winscp3str[] = "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server exec sftp-server";
	int retval;
	int reqlen=strlen(request);
	char **env = NULL;

	debug(LOG_DEBUG, "processing request: \"%s\"\n", request);

	tmprequest=strdup(request);

#ifdef WINSCP_COMPAT			

	bad_winscp3str[57]=10;
	bad_winscp3str[127]=10;
			
	if(strcmp(request,bad_winscp3str)==0)
	{
	    /*
		 * switch out the command to use, winscp wont know the difference
	 	 */
		free(tmprequest);
		tmprequest=strdup(PROG_SFTP_SERVER);
	    syslog(LOG_DEBUG, "winscp3 compat correcting to: \"[%s]\"\n", PROG_SFTP_SERVER);
	}
#endif

	
#ifdef GFTP_COMPAT 
	/*
	 *	gFTP compatibility hack
	 */
	if (NULL != (tmpstring=strbeg(request, "echo -n xsftp ; ")))
	{
		free(tmprequest);
		tmprequest=strdup(tmpstring);
		printf("xsftp");
		fflush(stdout);
	}
#endif

#ifdef RESTRICTIVE_FILENAMES
	/*
	 * we flat out reject special chars
	 */
	if (!valid_chars(tmprequest))
	{
		debug(LOG_DEBUG, "rejected because of invalid chars (%s)", logstamp());
		free(tmprequest);
		return(-1);
	}
#endif

#ifdef WINSCP_COMPAT
	if (strbeg(tmprequest,PROG_CD))
	{
		char *destdir=(char *)malloc(reqlen);
		if (destdir == NULL)
		{
			perror("malloc");
			exit(EXIT_FAILURE);
		}

		/*
		 * well, now that scponly is a persistent shell
		 * i have to maintain a $PWD.  damn.
		 * we're going to INSIST upon a double quote
		 * encapsulated new directory to change to.
		 */
		if ((tmprequest[(reqlen-1)]=='"') && (tmprequest[3]=='"'))
		{
			bzero(destdir,reqlen);
			strncpy(destdir,&tmprequest[4],reqlen-5);
			debug(LOG_INFO, "chdir: %s (%s)", tmprequest, logstamp());
			retval=chdir(destdir);
			free(destdir);
			free(tmprequest);
			return(retval);
		}
		syslog(LOG_ERR, "bogus chdir request: %s (%s)", tmprequest, logstamp());
		free(tmprequest);
		return(-1);
	}
#endif

	/*
	 * convert request string to an arg_vector
	 */
	av = build_arg_vector(tmprequest);

	/*
	 * clean any path info from request and substitute our known pathnames
	 */
	av[0] = substitute_known_path(av[0]);
	
	/*
	 * we only process wildcards for scp commands
	 */
#ifdef ENABLE_WILDCARDS
#ifdef ENABLE_SCP2
	if (exact_match(av[0],PROG_SCP))
		av = expand_wildcards(av);
#endif
#endif

/*
 *	check for a compile time chdir configuration
 */
#ifdef ENABLE_DEFAULT_CHDIR
	if (exact_match(av[0],PROG_SFTP_SERVER))
	{
		syslog(LOG_INFO, "changing initial directory to %s", DEFAULT_CHDIR);
		chdir(DEFAULT_CHDIR);
	}
#endif
	

	flat_request = flatten_vector(av);

	/* 
	 * Use a temp arg vector since getopt will permute the command line arguments
	 * for anything that it does not know about.  If all rsync options are well
	 * defined this isn't necessary.
	 */
	tmp_av = build_arg_vector(flat_request);
	if(check_dangerous_args(tmp_av))
	{
		syslog(LOG_ERR, "requested command (%s) tried to use disallowed argument (%s))", 
			flat_request, logstamp());
		exit(EXIT_FAILURE);
	}
	discard_vector(tmp_av);

	if (valid_arg_vector(av))
	{

/*														   
 * Unison needs the HOME environment variable be set to the directory						  
 * where the .unison directory resides.										
 */														    
#ifdef USE_SAFE_ENVIRONMENT
		safeenv[0] = NULL;
		filter_allowed_env_vars();
		tenv = safeenv;
		if (debuglevel) {
			while (NULL != *tenv) {
				syslog(LOG_DEBUG, "Environment contains \"%s\"", *tenv++);
			}
		}
		env = safeenv;
#endif

#ifdef UNISON_COMPAT
		/* the HOME environment variable should have been set above, but I need to make sure
		 * that it's value as read from the environment is replaced with the actual value
		 * as it exists within the chroot, which is what the applications will expect to see.
		 */
		if (replace_env_entry("HOME",homedir) && (((strlen(homedir) + 6 ) > FILENAME_MAX) || !mysetenv("HOME",homedir)))
		{
			syslog(LOG_ERR, "could not set HOME environment variable (%s)", logstamp());
			exit(EXIT_FAILURE);
		}
		debug(LOG_DEBUG, "set non-chrooted HOME environment variable to %s (%s)", homedir, logstamp());
#endif 
		syslog(LOG_INFO, "running: %s (%s)", flat_request, logstamp());

#ifdef WINSCP_COMPAT
		if (winscp_mode)
		{
			int status=0;
			if (fork() == 0)
				retval=execve(av[0],av,env);
			else
			{
				wait(&status);
				fflush(stdout);
				fflush(stderr);
				discard_vector(av);
#ifdef USE_SAFE_ENVIRONMENT											    
				discard_child_vectors(safeenv);
#endif
				free(flat_request);
				free(tmprequest);
				return(WEXITSTATUS(status));
			}
		}
		else
#endif
		{
			debug(LOG_DEBUG, "about to exec \"%s\" (%s)", av[0], logstamp());
			retval=execve(av[0],av,env);
		}
		syslog(LOG_ERR, "failed: %s with error %s(%u) (%s)", flat_request, strerror(errno), errno, logstamp());
		free(flat_request);
		discard_vector(av);
#ifdef USE_SAFE_ENVIRONMENT
		discard_child_vectors(safeenv);
#endif
#ifdef WINSCP_COMPAT
		if (winscp_mode)
		{
			free(tmprequest);
			return(-1);
		}
		else
#endif 
			exit(errno);
	}

	/*
	 *	reaching this point in the code means the request isnt one of
	 *	our accepted commands
 	 */
	if (debuglevel)
	{
		if (exact_match(flat_request,tmprequest))
			syslog (LOG_ERR, "denied request: %s [%s]", tmprequest, logstamp());
		else
			syslog (LOG_ERR, "denied request: %s (resolved to: %s) [%s]", tmprequest, flat_request, logstamp());
	}
	free(flat_request); 
#ifdef WINSCP_COMPAT
	if (winscp_mode)
	{
		printf ("command not permitted by scponly\n");
		free(tmprequest);
		return(-1);
	}
	else
#endif 
		exit(EXIT_FAILURE);
}
Example #2
0
static readstat_error_t recursive_discard(rdata_sexptype_header_t sexptype_header, rdata_ctx_t *ctx) {
    uint32_t length;
    rdata_sexptype_info_t info;
    rdata_sexptype_info_t prot, tag;
    
    readstat_error_t error = 0;
    int i;

    switch (sexptype_header.type) {
        case RDATA_SEXPTYPE_SYMBOL:
            if ((error = read_sexptype_header(&info, ctx)) != READSTAT_OK)
                goto cleanup;
            
            if ((error = recursive_discard(info.header, ctx)) != READSTAT_OK)
                goto cleanup;
            break;
        case RDATA_PSEUDO_SXP_PERSIST:
        case RDATA_PSEUDO_SXP_NAMESPACE:
        case RDATA_PSEUDO_SXP_PACKAGE:
            if ((error = read_sexptype_header(&info, ctx)) != READSTAT_OK)
                goto cleanup;
            
            if ((error = recursive_discard(info.header, ctx)) != READSTAT_OK)
                goto cleanup;
            break;
        case RDATA_SEXPTYPE_BUILTIN_FUNCTION:
        case RDATA_SEXPTYPE_SPECIAL_FUNCTION:
            error = discard_character_string(0, ctx);
            break;
        case RDATA_SEXPTYPE_PAIRLIST:
            error = discard_pairlist(sexptype_header, ctx);
            break;
        case RDATA_SEXPTYPE_CHARACTER_STRING:
            error = discard_character_string(1, ctx);
            break;
        case RDATA_SEXPTYPE_RAW_VECTOR:
            error = discard_vector(sexptype_header, 1, ctx);
            break;
        case RDATA_SEXPTYPE_LOGICAL_VECTOR:
            error = discard_vector(sexptype_header, 4, ctx);
            break;
        case RDATA_SEXPTYPE_INTEGER_VECTOR:
            error = discard_vector(sexptype_header, 4, ctx);
            break;
        case RDATA_SEXPTYPE_REAL_VECTOR:
            error = discard_vector(sexptype_header, 8, ctx);
            break;
        case RDATA_SEXPTYPE_COMPLEX_VECTOR:
            error = discard_vector(sexptype_header, 16, ctx);
            break;
        case RDATA_SEXPTYPE_CHARACTER_VECTOR:
            if (read_st(ctx, &length, sizeof(length)) != sizeof(length)) {
                return READSTAT_ERROR_READ;
            }
            if (ctx->machine_needs_byteswap)
                length = byteswap4(length);
            
            for (i=0; i<length; i++) {
                error = read_sexptype_header(&info, ctx);
                if (error != READSTAT_OK)
                    goto cleanup;
                if (info.header.type != RDATA_SEXPTYPE_CHARACTER_STRING) {
                    error = READSTAT_ERROR_PARSE;
                    goto cleanup;
                }
                
                error = discard_character_string(0, ctx);
                if (error != READSTAT_OK)
                    goto cleanup;
            }
            break;
        case RDATA_SEXPTYPE_GENERIC_VECTOR:
        case RDATA_SEXPTYPE_EXPRESSION_VECTOR:
            if (read_st(ctx, &length, sizeof(length)) != sizeof(length)) {
                return READSTAT_ERROR_READ;
            }
            if (ctx->machine_needs_byteswap)
                length = byteswap4(length);
            
            for (i=0; i<length; i++) {
                if ((error = read_sexptype_header(&info, ctx)) != READSTAT_OK)
                    goto cleanup;
                if ((error = recursive_discard(info.header, ctx)) != READSTAT_OK)
                    goto cleanup;
            }
            if (sexptype_header.attributes) {
                if ((error = read_attributes(NULL, ctx)) != READSTAT_OK)
                    goto cleanup;
            }
            break;
        case RDATA_SEXPTYPE_DOT_DOT_DOT:
        case RDATA_SEXPTYPE_PROMISE:
        case RDATA_SEXPTYPE_LANGUAGE_OBJECT: 
        case RDATA_SEXPTYPE_CLOSURE:
            if (sexptype_header.attributes) {
                if ((error = read_sexptype_header(&info, ctx)) != READSTAT_OK)
                    goto cleanup;
                
                if ((error = recursive_discard(info.header, ctx)) != READSTAT_OK)
                    goto cleanup;
            }
            if (sexptype_header.tag) {
                if ((error = read_sexptype_header(&info, ctx)) != READSTAT_OK)
                    goto cleanup;
                
                if ((error = recursive_discard(info.header, ctx)) != READSTAT_OK)
                    goto cleanup;
            }
            /* CAR */
            if ((error = read_sexptype_header(&info, ctx)) != READSTAT_OK)
                goto cleanup;
            
            if ((error = recursive_discard(info.header, ctx)) != READSTAT_OK)
                goto cleanup;
            
            /* CDR */
            if ((error = read_sexptype_header(&info, ctx)) != READSTAT_OK)
                goto cleanup;
            
            if ((error = recursive_discard(info.header, ctx)) != READSTAT_OK)
                goto cleanup;
            break;
        case RDATA_SEXPTYPE_EXTERNAL_POINTER:
            read_sexptype_header(&prot, ctx);
            recursive_discard(prot.header, ctx);
            
            read_sexptype_header(&tag, ctx);
            recursive_discard(tag.header, ctx);
            break;
        case RDATA_SEXPTYPE_ENVIRONMENT:
            /* locked */
            if (lseek_st(ctx, sizeof(uint32_t)) == -1) {
                return READSTAT_ERROR_READ;
            }
            
            rdata_sexptype_info_t enclosure, frame, hash_table, attributes;
            read_sexptype_header(&enclosure, ctx);
            recursive_discard(enclosure.header, ctx);
            
            read_sexptype_header(&frame, ctx);
            recursive_discard(frame.header, ctx);
            
            read_sexptype_header(&hash_table, ctx);
            recursive_discard(hash_table.header, ctx);
            
            read_sexptype_header(&attributes, ctx);
            recursive_discard(attributes.header, ctx);
            /*
             if (sexptype_header.attributes) {
             if (lseek(ctx->fd, sizeof(uint32_t), SEEK_CUR) == -1) {
             return READSTAT_ERROR_READ;
             }
             } */
            break;
        case RDATA_PSEUDO_SXP_REF:
        case RDATA_PSEUDO_SXP_NIL:
        case RDATA_PSEUDO_SXP_GLOBAL_ENVIRONMENT:
        case RDATA_PSEUDO_SXP_UNBOUND_VALUE:
        case RDATA_PSEUDO_SXP_MISSING_ARGUMENT:
        case RDATA_PSEUDO_SXP_BASE_NAMESPACE:
        case RDATA_PSEUDO_SXP_EMPTY_ENVIRONMENT:
        case RDATA_PSEUDO_SXP_BASE_ENVIRONMENT:
            break;
        default:
            return READSTAT_ERROR_READ;
    }
cleanup:
    
    return error;
}
Example #3
0
int main (int argc, char **argv) 
{
	FILE *debugfile;
	int logopts = LOG_PID|LOG_NDELAY;
	int chars_read = 0;
#ifdef CHROOT_CHECKDIR
	struct stat	homedirstat;
#endif

	/*
	 * set debuglevel.  any nonzero number will result in debugging info to log
	 */
	if (NULL!=(debugfile=fopen(DEBUGFILE,"r")))
	{
		chars_read = fscanf(debugfile,"%d",&debuglevel);
		if (chars_read < 1)
			debuglevel = 0;
		fclose(debugfile);
	}
#ifndef UNIX_COMPAT
	if (debuglevel > 1) /* debuglevel 1 will still log to syslog */
		logopts |= LOG_PERROR;
#endif

#ifdef UNIX_COMPAT 
	openlog(PACKAGE_NAME, logopts, LOG_AUTH);
#elif IRIX_COMPAT
	openlog(PACKAGE_NAME, logopts, LOG_AUTH);
#else
	if (debuglevel > 1) /* debuglevel 1 will still log to syslog */
		logopts |= LOG_PERROR;
	openlog(PACKAGE_NAME, logopts, LOG_AUTHPRIV);
#endif

	if (debuglevel > 0)
		debug = syslog;
	else
		debug = noop_syslog;

#ifdef HAVE_GETOPT_H
	scponly_getopt_long = getopt_long;
#else
	debug(LOG_INFO, "using netbsd's bundled getopt_long");
	scponly_getopt_long = netbsd_getopt_long;
#endif

#ifdef CHROOTED_NAME
	/*
	 *	is this a chroot'ed scponly installation?
	 */
#ifdef WINSCP_COMPAT
	if ((argc==3 && (0==strncmp(argv[0],CHROOTED_NAME,FILENAME_MAX)) ) || 
		( argc==1 && (0==strncmp(&argv[0][1],CHROOTED_NAME,FILENAME_MAX ))))

#else
	if (0==strncmp(argv[0],CHROOTED_NAME,FILENAME_MAX))
#endif
	{
		debug(LOG_INFO, "chrooted binary in place, will chroot()");
		chrooted=1;
	}
#endif /* CHROOTED_NAME */

	if (debuglevel)
	{
		int i;
		syslog(LOG_DEBUG, "%d arguments in total.", argc);
		for (i=0;i<argc;i++)
			syslog(LOG_DEBUG, "\targ %u is %s", i, argv[i]);
	}

#ifdef UNIX_COMPAT
	debug(LOG_DEBUG, "opened log at LOG_AUTH, opts 0x%08x", logopts);
#else
	debug(LOG_DEBUG, "opened log at LOG_AUTHPRIV, opts 0x%08x", logopts);
#endif

	if (getuid()==0)
	{	
		syslog(LOG_ERR, "root login denied [%s]", logstamp());
		exit(EXIT_FAILURE);
	}

#ifdef WINSCP_COMPAT
	if ((argc!=3) && (argc!=1))
#else
	if (argc!=3)
#endif
	{
		debug(LOG_ERR, "incorrect number of args");
		exit(EXIT_FAILURE);
	}
	if (!get_uservar())
	{
		syslog(LOG_ERR, "%s is misconfigured. contact sysadmin.", argv[0]);
		exit (EXIT_FAILURE);
	}

#ifdef CHROOTED_NAME
	if (chrooted)
	{
		char **av = NULL;
		char *tmprequest = NULL;
		char *root_dir = chrootdir;
		char chdir_path[FILENAME_MAX];
		

		strcpy(chrootdir, homedir);
		strcpy(chdir_path, "/");
		while((root_dir = strchr(root_dir, '/')) != NULL) 
		{
			if (strncmp(root_dir, "//", 2) == 0) 
			{
				snprintf(chdir_path, FILENAME_MAX, "%s", root_dir + 1);
				/* make sure HOME will be set to something correct if used*/
				debug(LOG_DEBUG, "Setting homedir to %s", chdir_path);
				strcpy(homedir, chdir_path);
				*root_dir = '\0';
				break;
			}
			root_dir++;
		}
#ifdef CHROOT_CHECKDIR
		bzero(&homedirstat, sizeof(struct stat));
		if (-1 == stat(chrootdir, &homedirstat))
		{
			syslog (LOG_ERR, "couldnt stat chroot dir: %s with errno %u", chrootdir, errno);
			exit(EXIT_FAILURE);
		}
		if (0 == (homedirstat.st_mode | S_IFDIR))
		{
			syslog (LOG_ERR, "chroot dir is not a directory: %s", chrootdir);
			exit(EXIT_FAILURE);
		}
		if (homedirstat.st_uid != 0)
		{
			syslog (LOG_ERR, "chroot dir not owned by root: %s", chrootdir);
			exit(EXIT_FAILURE);
		}
		if (0 != (homedirstat.st_mode & S_IWOTH))
		{
			syslog (LOG_ERR, "chroot dir writable by other: %s", chrootdir);
			exit(EXIT_FAILURE);
		}
		if (0 != (homedirstat.st_mode & S_IWGRP))
		{
			syslog (LOG_ERR, "chroot dir writable by group: %s", chrootdir);
			exit(EXIT_FAILURE);
		}
#endif

/* already within CHROOTED_NAME block */
#if defined(PASSWD_COMPAT) || defined(QUOTA_COMPAT)
		
		/*
		 * perhaps we need to refactor so we don't have to exit right
		 * in the middle of the code, but we can't chroot and expect to be
		 * able to change the password and have it be of any use unless
		 * there is some additional process that scponly is unaware of
		 * happening on the back end.
		 */
		tmprequest = strdup(argv[2]);
		av = build_arg_vector(tmprequest);
		free(tmprequest);
		if (
#ifdef PASSWD_COMPAT
			(exact_match(av[0],"passwd"))
			|| (exact_match(av[0],PROG_PASSWD))
#else
			0
#endif
#ifdef QUOTA_COMPAT
			|| (exact_match(av[0],"quota"))
			|| (exact_match(av[0],PROG_QUOTA))
#endif
		) {
			int status = process_pre_chroot_request(av);
			discard_vector(av);
			
			if (status) {
				syslog(LOG_ERR, "process_pre_chroot_request(%s) failed with code %i [%s]",
					argv[2],WEXITSTATUS(status),logstamp());
				exit(EXIT_FAILURE);
			}
			debug(LOG_DEBUG, "scponly completed");
			exit(EXIT_SUCCESS);
		} else {
			discard_vector(av);
		}

#endif /* passwd or quota */

		debug(LOG_DEBUG, "chrooting to dir: \"%s\"", chrootdir);
		if (-1==(chroot(chrootdir)))
		{
			debug(LOG_ERR, "chroot: %m");
			syslog(LOG_ERR, "couldn't chroot to %s [%s]", chrootdir, logstamp());
			exit(EXIT_FAILURE);
		}
		
		debug(LOG_DEBUG, "chdiring to dir: \"%s\"", chdir_path);					     
		if (-1==(chdir(chdir_path)))										   
		{													      
			debug(LOG_ERR, "chdir: %m");								 
			syslog (LOG_ERR, "couldn't chdir to %s [%s]", chdir_path, logstamp());				      
			exit(EXIT_FAILURE);     
		}
	}
#endif /* CHROOTED_NAME */

	debug(LOG_DEBUG, "setting uid to %u", getuid());
	if (-1==(seteuid(getuid())))
	{
		syslog(LOG_ERR, "couldn't revert to my real uid. seteuid: %m");
		exit(EXIT_FAILURE);
	}

#ifdef WINSCP_COMPAT
	if (argc==1)
	{
		debug(LOG_DEBUG, "entering WinSCP compatibility mode [%s]",logstamp());
		if (-1==process_winscp_requests())
		{
			syslog(LOG_ERR, "failed WinSCP compatibility mode [%s]", logstamp());
			exit(EXIT_FAILURE);
		}
	}
#else
	if (0)	{}	/*  placeholder */
#endif
	else if (-1==process_ssh_request(argv[2]))
	{
		syslog(LOG_ERR, "bad request: %s [%s]", argv[2], logstamp());
		exit(EXIT_FAILURE);
	}
	debug(LOG_DEBUG, "scponly completed");
	exit(EXIT_SUCCESS);
}