1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 #include "memcached.h"
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <sasl/saslplug.h>
7 
8 char my_sasl_hostname[1025];
9 
10 #if defined(HAVE_SASL_CB_GETCONF) || defined(HAVE_SASL_CB_GETCONFPATH)
11 /* The locations we may search for a SASL config file if the user didn't
12  * specify one in the environment variable SASL_CONF_PATH
13  */
14 const char * const locations[] = {
15     "/etc/sasl/memcached.conf",
16     "/etc/sasl2/memcached.conf",
17     NULL
18 };
19 
20 /* If the element of locations is file, locations_dir_path stores the
21  * directory path of these elements */
22 const char *const locations_dir_path[] = {
23     "/etc/sasl",
24     "/etc/sasl2",
25     NULL
26 };
27 
28 /* If the element of locations is directory, locations_file_path stores
29  * the actual configure file which used by sasl, when GETCONFPATH is
30  * enabled */
31 const char *const locations_file_path[] = {
32     "/etc/sasl/memcached.conf/memcached.conf",
33     "/etc/sasl2/memcached.conf/memcached.conf",
34     NULL
35 };
36 #endif
37 
38 #ifndef HAVE_SASL_CALLBACK_FT
39 typedef int (*sasl_callback_ft)(void);
40 #endif
41 
42 #ifdef ENABLE_SASL_PWDB
43 #define MAX_ENTRY_LEN 256
44 
45 static const char *memcached_sasl_pwdb;
46 
sasl_server_userdb_checkpass(sasl_conn_t * conn,void * context,const char * user,const char * pass,unsigned passlen,struct propctx * propctx)47 static int sasl_server_userdb_checkpass(sasl_conn_t *conn,
48                                         void *context,
49                                         const char *user,
50                                         const char *pass,
51                                         unsigned passlen,
52                                         struct propctx *propctx)
53 {
54     size_t unmlen = strlen(user);
55     if ((passlen + unmlen) > (MAX_ENTRY_LEN - 4)) {
56         fprintf(stderr,
57                 "WARNING: Failed to authenticate <%s> due to too long password (%d)\n",
58                 user, passlen);
59         return SASL_NOAUTHZ;
60     }
61 
62     FILE *pwfile = fopen(memcached_sasl_pwdb, "r");
63     if (pwfile == NULL) {
64         if (settings.verbose) {
65             vperror("WARNING: Failed to open sasl database <%s>",
66                     memcached_sasl_pwdb);
67         }
68         return SASL_NOAUTHZ;
69     }
70 
71     char buffer[MAX_ENTRY_LEN];
72     bool ok = false;
73 
74     while ((fgets(buffer, sizeof(buffer), pwfile)) != NULL) {
75         if (memcmp(user, buffer, unmlen) == 0 && buffer[unmlen] == ':') {
76             /* This is the correct user */
77             ++unmlen;
78             if (memcmp(pass, buffer + unmlen, passlen) == 0 &&
79                 (buffer[unmlen + passlen] == ':' || /* Additional tokens */
80                  buffer[unmlen + passlen] == '\n' || /* end of line */
81                  buffer[unmlen + passlen] == '\r'|| /* dos format? */
82                  buffer[unmlen + passlen] == '\0')) { /* line truncated */
83                 ok = true;
84             }
85 
86             break;
87         }
88     }
89     (void)fclose(pwfile);
90     if (ok) {
91         return SASL_OK;
92     }
93 
94     if (settings.verbose) {
95         fprintf(stderr, "INFO: User <%s> failed to authenticate\n", user);
96     }
97 
98     return SASL_NOAUTHZ;
99 }
100 #endif
101 
102 #if defined(HAVE_SASL_CB_GETCONF) || defined(HAVE_SASL_CB_GETCONFPATH)
sasl_getconf(void * context,const char ** path)103 static int sasl_getconf(void *context, const char **path)
104 {
105     *path = getenv("SASL_CONF_PATH");
106 
107     if (*path == NULL) {
108 #if defined(HAVE_SASL_CB_GETCONF)
109         for (int i = 0; locations[i] != NULL; ++i) {
110             if (access(locations[i], F_OK) == 0) {
111                 *path = locations[i];
112                 break;
113             }
114         }
115 #elif defined(HAVE_SASL_CB_GETCONFPATH)
116         for (int i = 0; locations[i] != NULL; ++i) {
117             if (access(locations_file_path[i], F_OK) == 0) {
118                 *path = locations[i];
119                 break;
120             } else if (access(locations[i], F_OK) == 0) {
121                 *path = locations_dir_path[i];
122                 break;
123             }
124         }
125 #endif
126     }
127 
128     if (settings.verbose) {
129         if (*path != NULL) {
130             fprintf(stderr, "Reading configuration from: <%s>\n", *path);
131         } else {
132             fprintf(stderr, "Failed to locate a config path\n");
133         }
134 
135     }
136 
137     return (*path != NULL) ? SASL_OK : SASL_FAIL;
138 }
139 #endif
140 
sasl_log(void * context,int level,const char * message)141 static int sasl_log(void *context, int level, const char *message)
142 {
143     bool log = true;
144 
145     switch (level) {
146     case SASL_LOG_NONE:
147         log = false;
148         break;
149     case SASL_LOG_PASS:
150     case SASL_LOG_TRACE:
151     case SASL_LOG_DEBUG:
152     case SASL_LOG_NOTE:
153         if (settings.verbose < 2) {
154             log = false;
155         }
156         break;
157     case SASL_LOG_WARN:
158     case SASL_LOG_FAIL:
159         if (settings.verbose < 1) {
160             log = false;
161         }
162         break;
163     default:
164         /* This is an error */
165         ;
166     }
167 
168     if (log) {
169         fprintf(stderr, "SASL (severity %d): %s\n", level, message);
170     }
171 
172     return SASL_OK;
173 }
174 
175 static sasl_callback_t sasl_callbacks[] = {
176 #ifdef ENABLE_SASL_PWDB
177    { SASL_CB_SERVER_USERDB_CHECKPASS, (sasl_callback_ft)sasl_server_userdb_checkpass, NULL },
178 #endif
179 
180    { SASL_CB_LOG, (sasl_callback_ft)sasl_log, NULL },
181 
182 #ifdef HAVE_SASL_CB_GETCONF
183    { SASL_CB_GETCONF, sasl_getconf, NULL },
184 #else
185 #ifdef HAVE_SASL_CB_GETCONFPATH
186    { SASL_CB_GETCONFPATH, (sasl_callback_ft)sasl_getconf, NULL },
187 #endif
188 #endif
189 
190    { SASL_CB_LIST_END, NULL, NULL }
191 };
192 
init_sasl(void)193 void init_sasl(void) {
194 #ifdef ENABLE_SASL_PWDB
195     memcached_sasl_pwdb = getenv("MEMCACHED_SASL_PWDB");
196     if (memcached_sasl_pwdb == NULL) {
197        if (settings.verbose) {
198           fprintf(stderr,
199                   "INFO: MEMCACHED_SASL_PWDB not specified. "
200                   "Internal passwd database disabled\n");
201        }
202        sasl_callbacks[0].id = SASL_CB_LIST_END;
203        sasl_callbacks[0].proc = NULL;
204     }
205 #endif
206 
207     memset(my_sasl_hostname, 0, sizeof(my_sasl_hostname));
208     if (gethostname(my_sasl_hostname, sizeof(my_sasl_hostname)-1) == -1) {
209         if (settings.verbose) {
210             fprintf(stderr, "Error discovering hostname for SASL\n");
211         }
212         my_sasl_hostname[0] = '\0';
213     }
214 
215     if (sasl_server_init(sasl_callbacks, "memcached") != SASL_OK) {
216         fprintf(stderr, "Error initializing sasl.\n");
217         exit(EXIT_FAILURE);
218     } else {
219         if (settings.verbose) {
220             fprintf(stderr, "Initialized SASL.\n");
221         }
222     }
223 }
224