GSS_S_PROMPTING_NEEDED is a bit
[cyrus-sasl.git] / saslauthd / ipc_doors.c
1 /*******************************************************************************
2  *
3  * ipc_doors.c
4  *
5  * Description:  Implements the Sun doors IPC method.
6  *
7  * Copyright (c) 1997-2000 Messaging Direct Ltd.
8  * All rights reserved.
9  *
10  * Portions Copyright (c) 2003 Jeremy Rumpf
11  * jrumpf@heavyload.net
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  *
22  * THIS SOFTWARE IS PROVIDED BY MESSAGING DIRECT LTD. ``AS IS'' AND ANY
23  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL MESSAGING DIRECT LTD. OR
26  * ITS EMPLOYEES OR AGENTS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
29  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
30  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
31  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
32  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
33  * DAMAGE.
34  *
35  *
36  * HISTORY
37  *
38  * 
39  * This source file created using 8 space tabs.
40  *
41  ********************************************************************************/
42
43
44 /****************************************
45  * enable/disable ifdef
46 *****************************************/
47 #include "saslauthd-main.h"
48
49 #ifdef USE_DOORS_IPC
50 /****************************************/
51
52
53
54 /****************************************
55  * includes
56 *****************************************/
57 #include <door.h>
58 #include <pthread.h>
59 #include <sys/types.h>
60 #include <sys/stat.h>
61 #include <netinet/in.h>
62 #include <fcntl.h>
63 #include <stdlib.h>
64 #include <stdio.h>
65 #include <errno.h>
66 #include <string.h>
67 #include <unistd.h>
68 #include <stropts.h>
69
70 #include "globals.h"
71 #include "utils.h"
72  
73
74 /****************************************
75  * declarations/protos
76  *****************************************/
77 static void     do_request(void *, char *, size_t, door_desc_t *, uint_t);
78 static void     send_no(char *);
79 static void     need_thread(door_info_t*);
80 static void     *server_thread(void *);
81
82 /****************************************
83  * module globals
84  *****************************************/
85 static char                     *door_file;  /* Path to the door file        */
86 static int                      door_fd;     /* Door file descriptor         */
87 static pthread_attr_t thread_attr;           /* Thread attributes            */
88 static int                      num_thr;     /* Number of threads            */
89 static pthread_mutex_t          num_lock;    /* Lock for update              */
90
91 /****************************************
92  * flags        global from saslauthd-main.c
93  * run_path     global from saslauthd-main.c
94  * num_procs    global from saslauthd-main.c
95  * detach_tty() function from saslauthd-main.c
96  * logger()             function from utils.c
97  *****************************************/
98
99 /*************************************************************
100  * IPC init. Initialize the environment specific to the 
101  * Sun doors IPC method.
102  *
103  * __Required Function__
104  **************************************************************/
105 void ipc_init() {
106         int     rc;
107         size_t  door_file_len;
108
109         /**************************************************************
110          * Doors detach immediately, otherwise the process gets confused.
111          * (they don't follow fork() properly)
112          **************************************************************/
113         detach_tty();
114
115         /**************************************************************
116          * Setup the door file and the door.
117          **************************************************************/
118         door_file_len = strlen(run_path) + sizeof(DOOR_FILE) + 1;
119         if (!(door_file = malloc(door_file_len))) {
120                 logger(L_ERR, L_FUNC, "could not allocate memory");
121                 exit(1);
122         }
123
124         strlcpy(door_file, run_path, door_file_len);
125         strlcat(door_file, DOOR_FILE, door_file_len);
126         unlink(door_file);
127
128         if ((door_fd = open(door_file, O_CREAT|O_RDWR|O_TRUNC, 0666)) == -1) {
129                 rc = errno;
130                 logger(L_ERR, L_FUNC, "could not open door file: %s",
131                        door_file);
132                 logger(L_ERR, L_FUNC, "open: %s", strerror(rc));
133                 exit(1);
134         }
135
136         close(door_fd);
137
138         if ((door_fd = door_create(&do_request, NULL, 0)) < 0) {
139                 logger(L_ERR, L_FUNC, "failed to create door");
140                 exit(1);
141         }
142
143         door_server_create(&need_thread);
144
145         if (fattach(door_fd, door_file) < 0) {
146                 logger(L_ERR, L_FUNC, "failed to attach door to file: %s",
147                        door_file);
148                 exit(1);
149         }
150
151         if (chmod(door_file, 0644) < 0) {
152                 rc = errno;
153                 logger(L_ERR, L_FUNC, "failed to chmod door file: %s",
154                        door_file);
155                 logger(L_ERR, L_FUNC, "chmod: %s", strerror(rc));
156                 exit(1);
157         }
158
159         logger(L_INFO, L_FUNC, "door on: %s", door_file);
160
161         /**************************************************************
162          * The doors api will handle threads for us, clear the process 
163          * model global flag.
164          **************************************************************/
165         flags &= ~USE_PROCESS_MODEL;
166
167         /* Initialize mutex */
168         pthread_mutex_init(&num_lock, NULL);
169
170         /* Initialize thread attributes */
171         pthread_attr_init(&thread_attr);
172         pthread_attr_setscope(&thread_attr, PTHREAD_SCOPE_SYSTEM);
173         pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
174
175         return;
176 }
177
178
179 /*************************************************************
180  * Main IPC loop. Sit idle waiting for a door request. All
181  * request get routed to do_request() via the doors api.
182  *
183  * __Required Function__
184  **************************************************************/
185 void ipc_loop() {
186         while(1) {
187                 pause();
188         }
189
190         return;
191 }
192
193
194 /*************************************************************
195  * General cleanup. Unlink our files.
196  *
197  * __Required Function__
198  **************************************************************/
199 void ipc_cleanup() {
200         unlink(door_file);
201
202         if (flags & VERBOSE)
203                 logger(L_DEBUG, L_FUNC, "door file removed: %s", door_file);
204 }
205
206
207 /*************************************************************
208  * Handle the door data, pass the request off to
209  * do_auth() back in saslauthd-main.c, then send the 
210  * result back through the door.
211  **************************************************************/
212 void do_request(void *cookie, char *data, size_t datasize, door_desc_t *dp, size_t ndesc) {
213         unsigned short          count = 0;                 /* input/output data byte count           */
214         char                    *response = NULL;          /* response to send to the client         */
215         char                    response_buff[1024];       /* temporary response buffer              */
216         char                    *dataend;                  /* EOD marker for the door data           */
217         char                    login[MAX_REQ_LEN + 1];    /* account name to authenticate           */
218         char                    password[MAX_REQ_LEN + 1]; /* password for authentication            */
219         char                    service[MAX_REQ_LEN + 1];  /* service name for authentication        */
220         char                    realm[MAX_REQ_LEN + 1];    /* user realm for authentication          */
221
222
223         /**************************************************************
224          * The input data string consists of the login id, password,
225          * service name and user realm. We'll break them up and then
226          * authenticate them.
227          **************************************************************/
228         dataend = data + datasize;
229
230         /* login id */
231         memcpy(&count, data, sizeof(unsigned short));
232
233         count = ntohs(count);
234         data += sizeof(unsigned short);
235
236         if (count > MAX_REQ_LEN || data + count > dataend) {
237                 logger(L_ERR, L_FUNC, "login exceeds MAX_REQ_LEN: %d",
238                        MAX_REQ_LEN);
239                 send_no("");
240                 return;
241         }       
242
243         memcpy(login, data, count);
244         login[count] = '\0';
245         data += count;
246
247         /* password */
248         memcpy(&count, data, sizeof(unsigned short));
249
250         count = ntohs(count);
251         data += sizeof(unsigned short);
252
253         if (count > MAX_REQ_LEN || data + count > dataend) {
254                 logger(L_ERR, L_FUNC, "password exceeds MAX_REQ_LEN: %d",
255                        MAX_REQ_LEN);
256                 send_no("");
257                 return;
258         }       
259
260         memcpy(password, data, count);
261         password[count] = '\0';
262         data += count;
263
264         /* service */
265         memcpy(&count, data, sizeof(unsigned short));
266
267         count = ntohs(count);
268         data += sizeof(unsigned short);
269
270         if (count > MAX_REQ_LEN || data + count > dataend) {
271                 logger(L_ERR, L_FUNC, "service exceeds MAX_REQ_LEN: %d",
272                        MAX_REQ_LEN);
273                 send_no("");
274                 return;
275         }       
276
277         memcpy(service, data, count);
278         service[count] = '\0';
279         data += count;
280
281         /* realm */
282         memcpy(&count, data, sizeof(unsigned short));
283
284         count = ntohs(count);
285         data += sizeof(unsigned short);
286
287         if (count > MAX_REQ_LEN || data + count > dataend) {
288                 logger(L_ERR, L_FUNC, "realm exceeds MAX_REQ_LEN: %d",
289                        MAX_REQ_LEN);
290                 send_no("");
291                 return;
292         }       
293
294         memcpy(realm, data, count);
295         realm[count] = '\0';
296
297         /**************************************************************
298          * We don't allow NULL passwords or login names
299          **************************************************************/
300         if (*login == '\0') {
301                 logger(L_ERR, L_FUNC, "NULL login received");
302                 send_no("NULL login received");
303                 return;
304         }       
305         
306         if (*password == '\0') {
307                 logger(L_ERR, L_FUNC, "NULL password received");
308                 send_no("NULL password received");
309                 return;
310         }       
311
312         /**************************************************************
313          * Get the mechanism response from do_auth() and send it back.
314          **************************************************************/
315         response = do_auth(login, password, service, realm);
316
317         memset(password, 0, strlen(password));
318
319         if (response == NULL) {
320             send_no("NULL response from mechanism");
321             return;
322         }       
323
324         strncpy(response_buff, response, 1023);
325         response_buff[1023] = '\0';
326         free(response);
327
328         if (flags & VERBOSE)
329             logger(L_DEBUG, L_FUNC, "response: %s", response_buff);
330
331         if(door_return(response_buff, strlen(response_buff), NULL, 0) < 0)
332             logger(L_ERR, L_FUNC, "door_return: %s", strerror(errno));
333
334         return;
335 }
336
337 /*************************************************************
338  * The available server  thread  pool  is  depleted.
339  * Create a new thread with suitable attributes.
340  * Client door_call() will block until server thread is available.
341  **************************************************************/
342 void need_thread(door_info_t *di) {
343     pthread_t newt;
344     int more;
345     
346     if (num_procs > 0) {
347         pthread_mutex_lock(&num_lock);
348         more = (num_thr < num_procs);
349         if (more) num_thr++;
350         pthread_mutex_unlock(&num_lock);
351         if (!more) return;
352     }
353
354     pthread_create(&newt, &thread_attr, &server_thread, NULL);
355 }
356  
357 /*************************************************************
358  * Start a new server thread.
359  * Make it available for door invocations.
360  **************************************************************/
361 void *server_thread(void *arg) {
362     pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
363     door_return(NULL, 0, NULL, 0);
364 }
365
366 /*************************************************************
367  * In case something went out to lunch while parsing the
368  * request data, we may want to attempt to send back a
369  * "NO" response through the door. The mesg is optional.
370  **************************************************************/
371 void send_no(char *mesg) {
372         char            buff[1024];
373
374         buff[0] = 'N';
375         buff[1] = 'O';
376         buff[2] = ' ';
377
378         /* buff, except for the trailing NUL and 'NO ' */
379         strncpy(buff + 3, mesg, sizeof(buff) - 1 - 3);
380         buff[1023] = '\0';
381
382         if (flags & VERBOSE)
383             logger(L_DEBUG, L_FUNC, "response: %s", buff);
384
385         if(door_return(buff, strlen(buff), NULL, 0) < 0)
386             logger(L_ERR, L_FUNC, "door_return: %s", strerror(errno));
387
388         return; 
389 }
390
391 #endif /* USE_DOORS_IPC */