/* socket-auth.c -- Authenticates user/password against a socket service * * The socket service retrieves the username and password and replies with * PASS or FAIL. See the demo-auth-server.py for more info * * Copyright (C) 2013 David Sommerseth * * This program 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, either version 3 of the License, or * (at your option) any later version. * * 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, see . * */ /** * @file socket-auth.c * @author David Sommerseth * @date 2013-06-08 * * @brief Authenticate users using a socket based auth server */ /* * The wire protocol for the socket service is: * * The client sends: * 1 byte - lenght of username * x bytes - the username * 1 byte - lenght of password * x bytes - the password * * And now the server is expected to reply with: * 4 bytes - containing the string FAIL or PASS * */ #include #include #include #include #include #include #include #include #include #include /* * INTERNAL VARIABLES AND FUNCTIONS */ static int authservfd = -1; static struct sockaddr_un sockaddr; /** * Send data to the remote end according to the wire protocol * where the first byte is the length of data to be sent. Maximum * 127 bytes can be transferred. * * @param fd Socket fd to send the data to * @param msg Pointer to the string to be sent * * @return Returns 1 on success, otherwise 0 */ static int send_data(int fd, const char *msg) { int msglen = strlen(msg); char buf[130]; memset(&buf, 0, 130); if( msglen > 127 ) { msglen = 127; } snprintf(buf, msglen+1, "%c", (int) strlen(msg)); if (write(fd, buf, strlen(buf)) != strlen(buf)) { return 0; } snprintf(buf, msglen+1, "%s", msg); if (write(fd, buf, strlen(buf)) != strlen(buf)) { return 0; } return 1; } /** * Reads the server response and parses it. Anything except of 'PASS' is * handled as a failure. But it expects the response to alwyas be 4 bytes. * * @param fd Socket fd where to read the response from * * @returns Returns 1 on successful authentication, otherwise 0. If less than 4 bytes * was read it will return -1. In this case, it might be wise to reconnect to * the server. */ static int parse_result(int fd) { char buf[6]; memset(&buf, 0, 6); int r = read(fd, &buf, 4); if( r == 4 ) { return strcmp(buf, "PASS") == 0; } return -1; } /** * Sends username and password via the socket to the authentication server and * parses the result back. * * @param fd Socket fd to the authentication server * @param username Username to be authenticated * @param passwd Matching password to the username * * @returns See parse_result() for valid result codes. */ static int authenticate_user(int fd, const char *username, const char *passwd) { if( !send_data(fd, username) ) { return 0; } if( !send_data(fd, passwd) ) { return 0; } return parse_result(fd); } /* * Required plug-in functions */ static ePluginInfo pluginfo = { .name = "socket-auth plug-in", .version = "1.0", .copyright = "2013 (C) David Sommerseth ", .pluginType = eptAUTH, .APIversion = 1 }; /** * @copydoc PluginInfo() */ ePluginInfo * PluginInfo() { return &pluginfo; } /** * @copydoc PluginInit() */ int PluginInit(eurephiaCTX *ctx, const char *args) { authservfd = socket(AF_UNIX, SOCK_STREAM, 0); if ( authservfd == -1 ) { eurephia_log(ctx, LOG_FATAL, 0, "Failed to initialise socket to the socket-auth server"); return 0; } memset(&sockaddr, 0, sizeof(struct sockaddr_un)); sockaddr.sun_family = AF_UNIX; strncpy(sockaddr.sun_path, args, sizeof(sockaddr.sun_path)-1); if( connect(authservfd, (struct sockaddr*)&sockaddr, sizeof(struct sockaddr_un)) == -1) { eurephia_log(ctx, LOG_FATAL, 0, "Failed to connect to the socket-auth server"); close(authservfd); authservfd = -1; return 0; } eurephia_log(ctx, LOG_INFO, 0, "socket-auth connected via %s", args); return 1; } /** * @copydoc PluginClose() */ void PluginClose(eurephiaCTX *ctx) { send_data(authservfd, "***SHUTDOWN***"); close(authservfd); eurephia_log(ctx, LOG_INFO, 0, "Disconnected from auth-socket"); } /** * @copydoc AuthenticateUser() */ eAuthResult * AuthenticateUser(eurephiaCTX *ctx, const char *username, const char *passwd) { eAuthResult *ret = NULL; int authres = -2; DEBUG(ctx, 20, "socket-auth:AuthenticateUser('%s', xxxxxx)", username); ret = malloc_nullsafe(ctx, sizeof(eAuthResult)+2); authres = authenticate_user(authservfd, username, passwd); if( authres < 0 ) { ret->status = eAUTH_PLGERROR; ret->msg = strdup("Failed communicating with auth-server"); } else if( authres == 0 ) { ret->status = eAUTH_FAILED; ret->msg = strdup("Wrong password"); } else if( authres == 1 ) { ret->status = eAUTH_SUCCESS; } else { // This should never ever happen ret->status = eAUTH_PLGERROR; ret->msg = strdup("Unknown result code"); } return ret; }