summaryrefslogtreecommitdiffstats
path: root/auth/socket/socket-auth.c
blob: 41396fb667a7871095d30fc0c0ad954bb9d197ca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
/*  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 <dazo@users.sourceforge.net>
 *
 *  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 <http://www.gnu.org/licenses/>.
 *
 */

/**
 * @file   socket-auth.c
 * @author David Sommerseth <dazo@users.sourceforge.net>
 * @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 <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <eurephia_nullsafe.h>
#include <eurephia_context.h>
#include <eurephia_log.h>
#include <eurephia_authplugin_driver.h>

/*
 *  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 <dazo@users.sourceforge.net>",
                                .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;
}