Example #1
0
int knet_vty_set_iacs(struct knet_vty *vty)
{
	unsigned char cmdreply[VTY_MAX_BUFFER_SIZE];
	unsigned char cmdsga[] = { IAC, WILL, TELOPT_SGA, '\0' };
	unsigned char cmdsgareply[] = { IAC, DO, TELOPT_SGA, '\0' };
	unsigned char cmdlm[] = { IAC, DONT, TELOPT_LINEMODE, '\0' };
	ssize_t readlen;

	if (check_vty(vty))
		return -1;

	if (knet_vty_set_echo(vty, 0) < 0)
		return -1;

	if (knet_vty_write(vty, "%s", cmdsga) < 0)
		return -1;

	readlen = knet_vty_read_real(vty, cmdreply, VTY_MAX_BUFFER_SIZE, 0);
	if (readlen < 0)
		return readlen;

	if (memcmp(&cmdreply, &cmdsgareply, readlen))
		return -1;

	if (knet_vty_write(vty, "%s", cmdlm) < 0)
		return -1;

	return 0;
}
Example #2
0
static void *vty_accept_thread(void *arg)
{
    struct knet_vty *vty = (struct knet_vty *)&knet_vtys[*(int *)arg];
    char *src_ip[2];
    int err;

    knet_vty_print_banner(vty);
    if (vty->got_epipe)
        goto out_clean;

    src_ip[0] = NULL;
    err = addrtostr((struct sockaddr *)&vty->src_sa,
                    vty->src_sa_len,
                    src_ip);

    if (!err) {
        strncpy(vty->ip, src_ip[0], sizeof(vty->ip));
    } else {
        strcpy(vty->ip, "unknown");
    }

    if (src_ip[0])
        addrtostr_free(src_ip);

    if ((knet_vty_auth_user(vty, NULL) < 0) && (!vty->got_epipe)) {
        log_info("User failed to authenticate (ip: %s)", vty->ip);
        goto out_clean;
    }
    if (vty->got_epipe)
        goto out_clean;

    log_info("User %s connected from %s", vty->username, vty->ip);
    knet_vty_write(vty, "Welcome %s (%s) on vty(%d)\n\n", vty->username, vty->ip, vty->conn_num);
    if (vty->got_epipe)
        goto out_clean;

    if (knet_vty_set_iacs(vty) < 0) {
        knet_vty_write(vty, "Unable to set telnet session preferences");
        goto out_clean;
    }
    if (vty->got_epipe)
        goto out_clean;

    knet_vty_cli_bind(vty);

out_clean:
    pthread_mutex_lock(&knet_vty_mutex);
    knet_vty_close(vty);
    pthread_mutex_unlock(&knet_vty_mutex);

    return NULL;
}
Example #3
0
void knet_vty_exit_node(struct knet_vty *vty)
{
	switch(vty->node) {
		case NODE_LINK:
			vty->node = NODE_PEER;
			break;
		case NODE_PEER:
			vty->node = NODE_INTERFACE;
			break;
		case NODE_INTERFACE:
			vty->node = NODE_CONFIG;
			break;
		case NODE_CONFIG:
			pthread_mutex_lock(&knet_vty_mutex);
			knet_vty_config = -1;
			pthread_mutex_unlock(&knet_vty_mutex);
			vty->node = NODE_ROOT;
			break;
		case NODE_ROOT:
			vty->got_epipe = 1;
			break;
		default:
			knet_vty_write(vty, "No idea where to go..%s", telnet_newline);
			break;
	}
}
Example #4
0
void knet_vty_print_banner(struct knet_vty *vty)
{
	if (check_vty(vty))
		return;

	knet_vty_write(vty,
		"Welcome to " PACKAGE " " PACKAGE_VERSION " (built " __DATE__
		" " __TIME__ ")\n");
}
Example #5
0
void knet_vty_prompt(struct knet_vty *vty)
{
	char buf[3];

	if (vty->user_can_enable) {
		buf[0] = '#';
	} else {
		buf[0] = '>';
	}
	buf[1] = ' ';
	buf[2] = 0;
	knet_vty_write(vty, "%s%s", knet_vty_nodes[vty->node].prompt, buf);
}
Example #6
0
static int knet_vty_set_echoon(struct knet_vty *vty)
{
	unsigned char cmdreply[VTY_MAX_BUFFER_SIZE];
	unsigned char cmdechoon[] = { IAC, WONT, TELOPT_ECHO, '\0' };
	unsigned char cmdechoonreply[] = { IAC, DONT, TELOPT_ECHO, '\0' };
	ssize_t readlen;

	if (knet_vty_write(vty, "%s", cmdechoon) < 0)
		return -1;

	readlen = knet_vty_read_real(vty, cmdreply, VTY_MAX_BUFFER_SIZE, 0);
	if (readlen < 0)
		return readlen;

	if (memcmp(&cmdreply, &cmdechoonreply, readlen))
		return -1;

	return 0;
}
Example #7
0
static int knet_vty_group_check(struct knet_vty *vty)
{
	struct group grp;
	char *buf;
	size_t buflen;
	long int initlen;
	struct group *result;
	char *gr_mem;
	int err, i;

	errno = 0;
	initlen = sysconf(_SC_GETGR_R_SIZE_MAX);
	if ((initlen < 0) && (errno == EINVAL))
		return -1;

	if (initlen < 0)
		initlen = 1024;

	buflen = (size_t) initlen;

	buf = malloc(buflen);
	if (!buf)
		return -1;

	while ((err = getgrnam_r(DEFAULTADMGROUP, &grp, buf, buflen, &result)) == ERANGE) {
		size_t newlen = 2 * buflen;
		char *newbuf;

		newbuf = realloc(buf, newlen);
		if (!newbuf) {
			err = -1;
			goto out_clean;
		}
		buf = newbuf;
	}
	if (err)
		goto out_clean;

	if (result == NULL) {
		errno = EACCES;
		log_error("No " DEFAULTADMGROUP " group found on the system");
		knet_vty_write(vty, "No " DEFAULTADMGROUP " group found on the system\n");
		err = -1;
		goto out_clean;
	}

	gr_mem = *grp.gr_mem;

	i = 0;
	while(gr_mem != NULL) {
		if (!strcmp(vty->username, gr_mem)) {
			vty->user_can_enable = 1;
			break;
		}
		gr_mem = *(grp.gr_mem + i);
		i++;
	}

out_clean:
	free(buf);

	return err;
}
Example #8
0
static int knet_pam_misc_conv(int num_msg, const struct pam_message **msgm,
			      struct pam_response **response, void *appdata_ptr)
{
	int count = 0;
	struct pam_response *reply;
	struct knet_vty *vty = (struct knet_vty *)appdata_ptr;

	if (num_msg <= 0)
		return PAM_CONV_ERR;

	reply = (struct pam_response *) calloc(num_msg, sizeof(struct pam_response));

	if (reply == NULL)
		return PAM_CONV_ERR;

	for (count=0; count < num_msg; ++count) {
		unsigned char readbuf[VTY_MAX_BUFFER_SIZE];
		char *string=NULL;
		int nc;

		memset(readbuf, 0, sizeof(readbuf));

		switch (msgm[count]->msg_style) {
		case PAM_PROMPT_ECHO_OFF:
			if (knet_vty_set_echo(vty, 0) < 0) {
				knet_vty_write(vty, "Unable to turn off terminal/telnet echo");
				goto failed_conversation;
			}
			knet_vty_write(vty, "%s", msgm[count]->msg);
			nc = knet_vty_read(vty, readbuf, sizeof(readbuf));
			if (nc < 0)
				goto failed_conversation;
			if (knet_vty_set_echo(vty, 1) < 0) {
				/* doesn't really make a lot of sense tho.... */
				knet_vty_write(vty, "Unable to turn on terminal/telnet echo");
				goto failed_conversation;
			}
			knet_vty_write(vty, "\n");
			readbuf[nc-2] = 0;
			string = strdup((const char*)readbuf);
			if (!string)
				goto failed_conversation;
			break;
		case PAM_PROMPT_ECHO_ON:
			knet_vty_write(vty, "\n%s", msgm[count]->msg);
			nc = knet_vty_read(vty, readbuf, sizeof(readbuf));
			if (nc < 0)
				goto failed_conversation;
			readbuf[nc-2] = 0;
			string = strdup((const char*)readbuf);
			if (!string)
				goto failed_conversation;
			break;
		case PAM_ERROR_MSG:
			log_error("Received PAM error message %s", msgm[count]->msg);
			knet_vty_write(vty, "%s", msgm[count]->msg);
			break;
		case PAM_TEXT_INFO:
			log_error("Received PAM text info: %s", msgm[count]->msg);
			knet_vty_write(vty, "%s", msgm[count]->msg);
			break;
		default:
			if (!vty->got_epipe) {
				log_error("Unknown PAM conversation message");
				knet_vty_write(vty, "Unknown PAM conversation message");
			}
			goto failed_conversation;
		}

		if (string) {
			reply[count].resp_retcode = 0;
			reply[count].resp = string;
			string = NULL;
		}
	}

	*response = reply;
	reply = NULL;

	return PAM_SUCCESS;

failed_conversation:
	if (!vty->got_epipe) {
		log_error("PAM conversation error");
		knet_vty_write(vty, "PAM conversation error");
	}
	if (reply) {
		for (count=0; count < num_msg; ++count) {
			if (reply[count].resp == NULL)
				continue;
			switch (msgm[count]->msg_style) {
			case PAM_PROMPT_ECHO_ON:
			case PAM_PROMPT_ECHO_OFF:
				_pam_overwrite(reply[count].resp);
				free(reply[count].resp);
				break;
			case PAM_BINARY_PROMPT:
				{
					void *bt_ptr = reply[count].resp;
					pam_binary_handler_free(appdata_ptr, bt_ptr);
					break;
				}
			case PAM_ERROR_MSG:
			case PAM_TEXT_INFO:
				free(reply[count].resp);
			}
		}
		free(reply);
		reply = NULL;
	}

	return PAM_CONV_ERR;
}
Example #9
0
static int knet_vty_pam_auth_user(struct knet_vty *vty, const char *user)
{
	pam_handle_t *pamh=NULL;
	struct pam_conv conv;
	int err;
	int retry = 1;

	conv.conv = knet_pam_misc_conv;
	conv.appdata_ptr = (void *)vty;

retry_auth:
	err = pam_start("kronosnet", user, &conv, &pamh);
	if (err != PAM_SUCCESS) {
		errno = EINVAL;
		log_error("PAM fatal error: %s", pam_strerror(pamh, err));
		knet_vty_write(vty, "PAM fatal error: %s",
				pam_strerror(pamh, err));
		goto out_fatal;
	}

	err = pam_authenticate(pamh, 0);
	if (err != PAM_SUCCESS) {
		if (vty->got_epipe) {
			errno = EPIPE;
			goto out_fatal;
		} else {
			errno = EINVAL;
			goto out_clean;
		}
	}

	if (knet_vty_get_pam_user(vty, pamh) != PAM_SUCCESS) {
		log_error("PAM: unable to get PAM_USER: %s",
			  pam_strerror(pamh, err));
		knet_vty_write(vty, "PAM: unable to get PAM_USER: %s",
				pam_strerror(pamh, err));
		goto out_clean;
	}

	err = pam_acct_mgmt(pamh, 0);
	if (err != PAM_SUCCESS) {
		log_info("User: %s failed to authenticate on vty(%d) attempt %d",
			 vty->username, vty->conn_num, retry);
		goto out_clean;
	}

out_clean:
	if (pamh) {
		pam_end(pamh, err);
		pamh = NULL;
	}

	if ((err != PAM_SUCCESS) && (retry < AUTH_MAX_RETRY)) {
		retry++;
		goto retry_auth;
	}

out_fatal:
	if (pamh) {
		pam_end(pamh, err);
		pamh = NULL;
	}

	knet_vty_write(vty, "\n");

	return err;
}
Example #10
0
/*
 * mainloop is not thread safe as there should only be one
 */
int knet_vty_main_loop(int debug)
{
    int logfd[2];
    int vty_listener6_fd;
    int vty_listener4_fd;
    int vty_listener_fd;
    int vty_accept_fd;
    struct sockaddr_storage incoming_sa;
    socklen_t salen;
    fd_set rfds;
    int se_result = 0;
    struct timeval tv;
    int err = 0;
    int conn_index, found;

    signal(SIGTERM, sigterm_handler);
    signal(SIGINT, sigterm_handler);
    signal(SIGPIPE, sigpipe_handler);

    if (pipe(logfd)) {
        log_error("Unable to create logging pipe");
        return -1;
    }

    if ((_fdset_cloexec(logfd[0])) ||
            (_fdset_cloexec(logfd[1]))) {
        log_error("Unable to set FD_CLOEXEC on logfd pipe");
        return -1;
    }

    memset(&knet_vtys, 0, sizeof(knet_vtys));

    for(conn_index = 0; conn_index < KNET_VTY_TOTAL_MAX_CONN; conn_index++) {
        knet_vtys[conn_index].logfd = logfd[1];
        if (debug) {
            knet_vtys[conn_index].loglevel = KNET_LOG_DEBUG;
        } else {
            knet_vtys[conn_index].loglevel = KNET_LOG_INFO;
        }
    }

    if (knet_read_conf() < 0) {
        log_error("Unable to read config file %s", knet_cfg_head.conffile);
        return -1;
    }

    vty_listener6_fd = knet_vty_init_listener(knet_cfg_head.vty_ipv6,
                       knet_cfg_head.vty_port);
    if (vty_listener6_fd < 0) {
        log_error("Unable to setup vty listener for ipv6");
        return -1;
    }

    vty_listener4_fd = knet_vty_init_listener(knet_cfg_head.vty_ipv4,
                       knet_cfg_head.vty_port);

    if (vty_listener4_fd < 0) {
        log_error("Unable to setup vty listener for ipv4");
        goto out;
    }

    while (se_result >= 0 && !daemon_quit) {
        FD_ZERO (&rfds);
        FD_SET (vty_listener6_fd, &rfds);
        FD_SET (vty_listener4_fd, &rfds);
        FD_SET (logfd[0], &rfds);

        tv.tv_sec = 1;
        tv.tv_usec = 0;

        se_result = select(FD_SETSIZE, &rfds, 0, 0, &tv);

        if ((se_result == -1) && (daemon_quit)) {
            log_info("Got a SIGTERM, requesting CLI threads to exit");
            for(conn_index = 0; conn_index < KNET_VTY_TOTAL_MAX_CONN; conn_index++) {
                if (knet_vtys[conn_index].active) {
                    knet_vty_write(&knet_vtys[conn_index], "%s%sServer is going down..%s%s",
                                   telnet_newline, telnet_newline, telnet_newline, telnet_newline);
                    knet_vty_close(&knet_vtys[conn_index]);
                    knet_vtys[conn_index].got_epipe = 1;
                }
            }
            sleep(2); /* give time to all vty to exit */
            knet_close_down();
            log_info("Have a nice day! Goodbye");
            goto out;
        }

        if (se_result == -1) {
            err = se_result;
            log_error("Unable to select on vty listener socket!");
            goto out;
        }

        if (se_result == 0) {
            pthread_mutex_lock(&knet_vty_mutex);
            for(conn_index = 0; conn_index < KNET_VTY_TOTAL_MAX_CONN; conn_index++) {
                if ((knet_vtys[conn_index].active) &&
                        (!knet_vtys[conn_index].disable_idle)) {
                    knet_vtys[conn_index].idle++;
                    if (knet_vtys[conn_index].idle > KNET_VTY_CLI_TIMEOUT) {
                        knet_vty_close(&knet_vtys[conn_index]);
                        knet_vtys[conn_index].got_epipe = 1;
                    }
                }
            }
            pthread_mutex_unlock(&knet_vty_mutex);
            continue;
        }

        if (FD_ISSET(vty_listener6_fd, &rfds)) {
            vty_listener_fd = vty_listener6_fd;
        } else if (FD_ISSET(vty_listener4_fd, &rfds)) {
            vty_listener_fd = vty_listener4_fd;
        } else if (FD_ISSET(logfd[0], &rfds))  {
            struct knet_log_msg msg;
            size_t bytes_read = 0;
            size_t len;

            while (bytes_read < sizeof(struct knet_log_msg)) {
                len = read(logfd[0], &msg + bytes_read,
                           sizeof(struct knet_log_msg) - bytes_read);
                if (len <= 0) {
                    break;
                }
                bytes_read += len;
            }

            if (bytes_read != sizeof(struct knet_log_msg))
                continue;

            switch(msg.msglevel) {
            case KNET_LOG_WARN:
                log_warn("(%s) %s", knet_get_subsystem_name(msg.subsystem), msg.msg);
                break;
            case KNET_LOG_INFO:
                log_info("(%s) %s", knet_get_subsystem_name(msg.subsystem), msg.msg);
                break;
            case KNET_LOG_DEBUG:
                log_kdebug("(%s) %s", knet_get_subsystem_name(msg.subsystem), msg.msg);
                break;
            case KNET_LOG_ERR:
            default:
                log_error("(%s) %s", knet_get_subsystem_name(msg.subsystem), msg.msg);
            }
            continue;
        } else {
            continue;
        }

        memset(&incoming_sa, 0, sizeof(struct sockaddr_storage));
        salen = sizeof(struct sockaddr_storage);

        vty_accept_fd = accept(vty_listener_fd, (struct sockaddr *)&incoming_sa, &salen);
        if (vty_accept_fd < 0) {
            log_error("Unable to accept connection to vty");
            continue;
        }

        // check for ip address access list here against incoming_sa

        pthread_mutex_lock(&knet_vty_mutex);

        found = 0;
        for(conn_index = 0; conn_index <= vty_max_connections; conn_index++) {
            if (knet_vtys[conn_index].active == 0) {
                found = 1;
                break;
            }
        }

        if ((vty_current_connections == vty_max_connections) || (!found)) {
            errno = ECONNREFUSED;
            log_error("Too many connections to VTY or no available slots");
            close(vty_accept_fd);
            pthread_mutex_unlock(&knet_vty_mutex);
            continue;
        }

        vty_current_connections++;

        memset(&knet_vtys[conn_index], 0,
               sizeof(struct knet_vty));

        knet_vtys[conn_index].vty_sock = vty_accept_fd;
        knet_vtys[conn_index].conn_num = conn_index;
        memcpy(&knet_vtys[conn_index].src_sa, &incoming_sa, salen);
        knet_vtys[conn_index].src_sa_len = salen;
        knet_vtys[conn_index].active = 1;
        knet_vtys[conn_index].logfd = logfd[1];
        if (debug) {
            knet_vtys[conn_index].loglevel = KNET_LOG_DEBUG;
        } else {
            knet_vtys[conn_index].loglevel = KNET_LOG_INFO;
        }

        err = pthread_create(&knet_vtys[conn_index].vty_thread,
                             NULL, vty_accept_thread,
                             (void *)&conn_index);
        if (err < 0) {
            log_error("Unable to spawn vty thread");
            memset(&knet_vtys[conn_index], 0,
                   sizeof(struct knet_vty));
            vty_current_connections--;
        }

        pthread_mutex_unlock(&knet_vty_mutex);
    }

out:
    knet_vty_close_listener(vty_listener6_fd);
    knet_vty_close_listener(vty_listener4_fd);
    close(logfd[0]);
    close(logfd[1]);

    return err;
}