diff --git a/core/Makefile b/core/Makefile index 2127f9f6d..b88961a06 100644 --- a/core/Makefile +++ b/core/Makefile @@ -56,13 +56,15 @@ LDFLAGS=-rdynamic -L$(LOGPATH) \ SRCS= atomic.c buffer.c spinlock.c gateway.c gateway_mysql_protocol.c \ gw_utils.c utils.c dcb.c load_utils.c session.c service.c server.c \ poll.c config.c users.c hashtable.c dbusers.c thread.c gwbitmask.c \ - monitor.c + monitor.c adminusers.c HDRS= ../include/atomic.h ../include/buffer.h ../include/dcb.h \ ../include/gateway_mysql.h ../include/gw.h ../include/mysql_protocol.h \ ../include/session.h ../include/spinlock.h ../include/thread.h \ ../include/modules.h ../include/poll.h ../include/config.h \ - ../include/users.h ../include/hashtable.h ../include/gwbitmask.h + ../include/users.h ../include/hashtable.h ../include/gwbitmask.h \ + ../include/adminusers.h + OBJ=$(SRCS:.c=.o) LIBS=-L../inih/extra -linih -lssl -lstdc++ \ diff --git a/core/adminusers.c b/core/adminusers.c new file mode 100644 index 000000000..0766dfa90 --- /dev/null +++ b/core/adminusers.c @@ -0,0 +1,169 @@ +/* + * This file is distributed as part of the SkySQL Gateway. It is free + * software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, + * version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright SkySQL Ab 2013 + */ +#include +#include +#include +#define _XOPEN_SOURCE +#include +#include +#include +#include + +/** + * @file adminusers.c - Administration user account management + * + * @verbatim + * Revision History + * + * Date Who Description + * 18/07/13 Mark Riddoch Initial implementation + * + * @endverbatim + */ + +static USERS *loadUsers(); +static void initialise(); + +static USERS *users = NULL; +static int admin_init = 0; + +/** + * Admin Users initialisation + */ +static void +initialise() +{ + if (admin_init) + return; + admin_init = 1; + users = loadUsers(); +} + +/** + * Verify a username and password + * + * @param username Username to verify + * @param password Password to verify + * @return Non-zero if the username/password combination is valid + */ +int +admin_verify(char *username, char *password) +{ +char *pw; + + initialise(); + if (users == NULL) + { + if (strcmp(username, "admin") == 0 && strcmp(password, "skysql") == 0) + return 1; + } + else + { + if ((pw = users_fetch(users, username)) == NULL) + return 0; + if (strcmp(pw, crypt(password, ADMIN_SALT)) == 0) + return 1; + } + return 0; +} + + +/** + * Load the admin users + * + * @return Table of users + */ +static USERS * +loadUsers() +{ +USERS *rval; +FILE *fp; +char fname[1024], *home; +char uname[80], passwd[80]; + + initialise(); + if ((home = getenv("MAXSCALE_HOME")) != NULL) + sprintf(fname, "%s/etc/passwd", home); + else + sprintf(fname, "/usr/local/skysql/MaxScale/etc/passwd"); + if ((fp = fopen(fname, "r")) == NULL) + return NULL; + if ((rval = users_alloc()) == NULL) + return NULL; + while (fscanf(fp, "%[^:]:%s\n", uname, passwd) == 2) + { + users_add(rval, uname, passwd); + } + fclose(fp); + + return rval; +} + +/** + * Add user + * + * @param username Name of the new user + * @param password Password for the new user + * @return The number of users added + */ +int +admin_add_user(char *uname, char *passwd) +{ +FILE *fp; +char fname[1024], *home, *cpasswd; + + initialise(); + if ((home = getenv("MAXSCALE_HOME")) != NULL) + sprintf(fname, "%s/etc/passwd", home); + else + sprintf(fname, "/usr/local/skysql/MaxScale/etc/passwd"); + if (users == NULL) + { + if ((users = users_alloc()) == NULL) + return 0; + if ((fp = fopen(fname, "w")) == NULL) + return 0; + fclose(fp); + } + if (users_fetch(users, uname) != NULL) + { + return 0; + } + cpasswd = crypt(passwd, ADMIN_SALT); + users_add(users, uname, cpasswd); + if ((fp = fopen(fname, "a")) == NULL) + return 0; + fprintf(fp, "%s:%s\n", uname, cpasswd); + fclose(fp); + return 1; +} + +/** + * Check for existance of the user + * + * @param user The user name to test + * @return Non-zero if the user exists + */ +int +admin_test_user(char *user) +{ + initialise(); + if (users == NULL) + return 0; + return users_fetch(users, user) != NULL; +} diff --git a/include/adminusers.h b/include/adminusers.h new file mode 100644 index 000000000..ef1cf0814 --- /dev/null +++ b/include/adminusers.h @@ -0,0 +1,38 @@ +#ifndef _ADMINUSERS_H +#define _ADMINUSERS_H +/* + * This file is distributed as part of the SkySQL Gateway. It is free + * software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, + * version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright SkySQL Ab 2013 + */ + +/** + * @file adminusers.h - Administration users support routines + * + * @verbatim + * Revision History + * + * Date Who Description + * 18/07/13 Mark Riddoch Initial implementation + * + * @endverbatim + */ +#define ADMIN_SALT "MS" + +extern int admin_verify(char *, char *); +extern int admin_add_user(char *, char *); +extern int admin_test_user(char *); + +#endif diff --git a/modules/include/telnetd.h b/modules/include/telnetd.h index f9122b876..6e13ba0b2 100644 --- a/modules/include/telnetd.h +++ b/modules/include/telnetd.h @@ -29,7 +29,11 @@ * * @endverbatim */ +#include +/** + * The telnetd specific protocol structure to put in the DCB. + */ typedef struct telnetd { int state; /**< The connection state */ char *username; /**< The login name of the user */ @@ -57,5 +61,4 @@ typedef struct telnetd { #define TELNET_IAC 255 #define TELNET_ECHO 1 #define TELNET_SUPPRESS_GO_AHEAD 3 - #endif diff --git a/modules/protocol/Makefile b/modules/protocol/Makefile index eba1186e3..3eba66efb 100644 --- a/modules/protocol/Makefile +++ b/modules/protocol/Makefile @@ -57,7 +57,7 @@ libMySQLBackend.so: $(MYSQLBACKENDOBJ) $(CC) $(LDFLAGS) $(MYSQLBACKENDOBJ) $(LIBS) -o $@ libtelnetd.so: $(TELNETDOBJ) - $(CC) $(LDFLAGS) $(TELNETDOBJ) $(LIBS) -o $@ + $(CC) $(LDFLAGS) $(TELNETDOBJ) $(LIBS) -lcrypt -o $@ libHTTPD.so: $(HTTPDOBJ) $(CC) $(LDFLAGS) $(HTTPDOBJ) $(LIBS) -o $@ diff --git a/modules/protocol/telnetd.c b/modules/protocol/telnetd.c index 6a1911508..3ac9748fa 100644 --- a/modules/protocol/telnetd.c +++ b/modules/protocol/telnetd.c @@ -17,6 +17,7 @@ */ #include #include +#include #include #include #include @@ -32,6 +33,7 @@ #include #include #include +#include /** * @file telnetd.c - telnet daemon protocol module @@ -82,9 +84,8 @@ static GWPROTOCOL MyObject = { NULL /**< Session */ }; -static void telnetd_command(DCB *, unsigned char *cmd); -static void telnetd_echo(DCB *dcb, int enable); -static int password_verify(char *, char *); +static void telnetd_command(DCB *, unsigned char *cmd); +static void telnetd_echo(DCB *dcb, int enable); /** * Implementation of the mandatory version entry point @@ -137,7 +138,7 @@ ROUTER_OBJECT *router = session->service->router; ROUTER *router_instance = session->service->router_instance; void *rsession = session->router_session; TELNETD *telnetd = (TELNETD *)dcb->protocol; -char *password; +char *password, *t; if ((n = dcb_read(dcb, &head)) != -1) { @@ -158,13 +159,22 @@ char *password; { case TELNETD_STATE_LOGIN: telnetd->username = strndup(GWBUF_DATA(head), GWBUF_LENGTH(head)); + /* Strip the cr/lf from the username */ + t = strstr(telnetd->username, "\r\n"); + if (t) + *t = 0; telnetd->state = TELNETD_STATE_PASSWD; dcb_printf(dcb, "Password: "); telnetd_echo(dcb, 0); + GWBUF_CONSUME(head, GWBUF_LENGTH(head)); break; case TELNETD_STATE_PASSWD: password = strndup(GWBUF_DATA(head), GWBUF_LENGTH(head)); - if (password_verify(telnetd->username, password)) + /* Strip the cr/lf from the username */ + t = strstr(password, "\r\n"); + if (t) + *t = 0; + if (admin_verify(telnetd->username, password)) { telnetd_echo(dcb, 1); telnetd->state = TELNETD_STATE_DATA; @@ -176,6 +186,8 @@ char *password; telnetd_echo(dcb, 1); telnetd->state = TELNETD_STATE_LOGIN; } + GWBUF_CONSUME(head, GWBUF_LENGTH(head)); + free(password); break; case TELNETD_STATE_DATA: router->routeQuery(router_instance, rsession, head); @@ -385,27 +397,3 @@ char *buf; buf[2] = TELNET_ECHO; dcb_write(dcb, gwbuf); } - -/** - * Verify a username and password - * - * @param username Username to verify - * @param password Password to verify - * @return Non-zero if the username/password combination is valid - */ -static int -password_verify(char *username, char *password) -{ -char *t; - - /* Strip the cr/lf from the username and password */ - t = strstr(username, "\r\n"); - if (t) - *t = 0; - t = strstr(password, "\r\n"); - if (t) - *t = 0; - if (strcmp(username, "admin") == 0 && strcmp(password, "skysql") == 0) - return 1; - return 0; -} diff --git a/modules/routing/debugcmd.c b/modules/routing/debugcmd.c index 8ef43661c..0910442ee 100644 --- a/modules/routing/debugcmd.c +++ b/modules/routing/debugcmd.c @@ -54,6 +54,8 @@ #include #include #include +#include +#include #include #define MAXARGS 5 @@ -166,6 +168,17 @@ struct subcommand reloadoptions[] = { {0, 0, 0} } }; +static void telnetdAddUser(DCB *, char *, char *); +/** + * The subcommands of the add command + */ +struct subcommand addoptions[] = { + { "user", 2, telnetdAddUser, "Add a new user for the debug interface. E.g. add user john today", + {ARG_TYPE_STRING, ARG_TYPE_STRING, 0} }, + { NULL, 0, NULL, NULL, + {0, 0, 0} } +}; + /** * The debug command table */ @@ -179,6 +192,7 @@ static struct { { "set", setoptions }, { "clear", clearoptions }, { "reload", reloadoptions }, + { "add", addoptions }, { NULL, NULL } }; @@ -446,3 +460,24 @@ reload_config(DCB *dcb) dcb_printf(dcb, "Reloading configuration from file.\n"); config_reload(); } + +/** + * Add a new admin user + * + * @param dcb The DCB for messages + * @param user The user name + * @param passwd The Password of the user + */ +static void +telnetdAddUser(DCB *dcb, char *user, char *passwd) +{ + if (admin_test_user(user)) + { + dcb_printf(dcb, "User %s already exists.\n", user); + return; + } + if (admin_add_user(user, passwd)) + dcb_printf(dcb, "User %s has been succesfully added.\n", user); + else + dcb_printf(dcb, "Failed to add new user.\n"); +}