Skip to content

Commit

Permalink
vty: add CLI threads and PAM authentication
Browse files Browse the repository at this point in the history
NOTE: /etc/pam.d/kronosnet is now required to telnet to CLI

Signed-off-by: Fabio M. Di Nitto <fdinitto@redhat.com>
  • Loading branch information
fabbione committed Oct 20, 2010
1 parent f9ed864 commit 38cb1bb
Show file tree
Hide file tree
Showing 9 changed files with 376 additions and 5 deletions.
13 changes: 11 additions & 2 deletions Makefile.am
Expand Up @@ -18,14 +18,23 @@ dist_doc_DATA = \
COPYRIGHT \
README.licence

noinst_HEADERS =
noinst_HEADERS = knet.h \
ring.h \
utils.h \
vty.h \
vty_auth.h \
vty_utils.h

sbin_PROGRAMS = kronosnetd

kronosnetd_SOURCES = \
main.c \
utils.c \
vty.c
vty.c \
vty_auth.c \
vty_utils.c

kronosnetd_LDFLAGS = -lpthread

maintainer-clean-local:
rm -rf m4
9 changes: 9 additions & 0 deletions configure.ac
Expand Up @@ -106,6 +106,15 @@ AC_CHECK_FUNCS([strchr])
AC_CHECK_FUNCS([atexit])
AC_CHECK_FUNCS([ftruncate])

# PAM check
AC_CHECK_HEADERS([security/pam_appl.h],
[AC_CHECK_LIB([pam], [pam_start])],
[AC_MSG_ERROR([Unable to find LinuxPAM devel files])])

AC_CHECK_HEADERS([security/pam_misc.h],
[AC_CHECK_LIB([pam_misc], [misc_conv])],
[AC_MSG_ERROR([Unable to find LinuxPAM MISC devel files])])

# local options
AC_ARG_ENABLE([debug],
[ --enable-debug enable debug build. ],
Expand Down
2 changes: 2 additions & 0 deletions tests/Makefile.am
Expand Up @@ -38,4 +38,6 @@ khandle_test_SOURCES = \
vty_test_SOURCES = \
vty_test.c \
../vty.c \
../vty_auth.c \
../vty_utils.c \
../utils.c
37 changes: 34 additions & 3 deletions vty.c
Expand Up @@ -11,12 +11,16 @@

#include "utils.h"
#include "vty.h"
#include "vty_auth.h"
#include "vty_utils.h"

STATIC pthread_mutex_t knet_vty_mutex = PTHREAD_MUTEX_INITIALIZER;

STATIC int vty_max_connections = KNET_VTY_DEFAULT_MAX_CONN;
STATIC int vty_current_connections = 0;

STATIC pthread_t vty_thread[KNET_VTY_TOTAL_MAX_CONN];

STATIC int daemon_quit = 0;

static void sigterm_handler(int sig)
Expand All @@ -29,6 +33,24 @@ static void sigpipe_handler(int sig)
return;
}

static void *vty_accept_thread(void *arg)
{
int vty_sock = *(int *)arg;

knet_vty_print_banner(vty_sock);

if (knet_vty_auth_user(vty_sock) < 0)
goto out_clean;

out_clean:
pthread_mutex_lock(&knet_vty_mutex);
close(vty_sock);
vty_current_connections--;
pthread_mutex_unlock(&knet_vty_mutex);

return NULL;
}

/*
* mainloop is not thread safe as there should only be one
*/
Expand Down Expand Up @@ -78,6 +100,9 @@ int knet_vty_main_loop(const char *configfile, const char *ip_addr,
if ((se_result == 0) || (!FD_ISSET(vty_listener_fd, &rfds)))
continue;

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

vty_accept_fd = accept(vty_listener_fd, &incoming_sa, &salen);
if (vty_accept_fd < 0) {
log_error("Unable to accept connection to vty");
Expand All @@ -87,6 +112,7 @@ int knet_vty_main_loop(const char *configfile, const char *ip_addr,
// check for ip address access list here against incoming_sa

pthread_mutex_lock(&knet_vty_mutex);

if (vty_current_connections == vty_max_connections) {
errno = ECONNREFUSED;
log_error("Too many connections to VTY");
Expand All @@ -95,9 +121,14 @@ int knet_vty_main_loop(const char *configfile, const char *ip_addr,
continue;
}

// start vty thread here
// vty_current_connections++; should be incremented once
// the thread is started and operating
vty_current_connections++;
err = pthread_create(&vty_thread[vty_current_connections],
NULL, vty_accept_thread,
(void *)&vty_accept_fd);
if (err < 0) {
log_error("Unable to spawn vty thread");
vty_current_connections--;
}

pthread_mutex_unlock(&knet_vty_mutex);
}
Expand Down
2 changes: 2 additions & 0 deletions vty.h
Expand Up @@ -2,7 +2,9 @@
#define __VTY_H__

#define KNET_VTY_DEFAULT_PORT 50000

#define KNET_VTY_DEFAULT_MAX_CONN 4
#define KNET_VTY_TOTAL_MAX_CONN 16

int knet_vty_main_loop(const char *configfile, const char *ip_addr,
const unsigned short port);
Expand Down
168 changes: 168 additions & 0 deletions vty_auth.c
@@ -0,0 +1,168 @@
#include "config.h"

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include <security/pam_appl.h>
#include <security/pam_misc.h>

#include "utils.h"
#include "vty_auth.h"
#include "vty_utils.h"

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;
int vty_sock = *(int *)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_sock, 0) < 0) {
knet_vty_write(vty_sock, "Unable to turn off terminal/telnet echo");
goto failed_conversation;
}
knet_vty_write(vty_sock, "%s", msgm[count]->msg);
nc = knet_vty_read(vty_sock, readbuf, sizeof(readbuf));
if (nc < 0)
goto failed_conversation;
if (knet_vty_set_echo(vty_sock, 1) < 0) {
/* doesn't really make a lot of sense tho.... */
knet_vty_write(vty_sock, "Unable to turn on terminal/telnet echo");
goto failed_conversation;
}
knet_vty_write(vty_sock, "\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_sock, "\n%s", msgm[count]->msg);
nc = knet_vty_read(vty_sock, 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_sock, "%s", msgm[count]->msg);
break;
case PAM_TEXT_INFO:
log_error("Received PAM text info: %s", msgm[count]->msg);
knet_vty_write(vty_sock, "%s", msgm[count]->msg);
break;
default:
log_error("Unknown PAM conversation message");
knet_vty_write(vty_sock, "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:
log_error("PAM conversation error");
knet_vty_write(vty_sock, "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;
}

#define AUTH_MAX_RETRY 3

int knet_vty_auth_user(int vty_sock)
{
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_sock;

retry_auth:
err = pam_start("kronosnet", NULL, &conv, &pamh);
if (err != PAM_SUCCESS)
goto out_clean;

err = pam_authenticate(pamh, 0);
if (err != PAM_SUCCESS)
goto out_clean;

err = pam_acct_mgmt(pamh, 0);
if (err != PAM_SUCCESS)
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;
}

if ((err != PAM_SUCCESS) && (retry = AUTH_MAX_RETRY))
knet_vty_write(vty_sock, "%s", pam_strerror(pamh, err));

knet_vty_write(vty_sock, "\n");

return err;
}
6 changes: 6 additions & 0 deletions vty_auth.h
@@ -0,0 +1,6 @@
#ifndef __VTY_AUTH_H__
#define __VTY_AUTH_H__

int knet_vty_auth_user(int vty_sock);

#endif

0 comments on commit 38cb1bb

Please sign in to comment.