Revert "more autogenerated stuff being checked in"
[cyrus-sasl.git] / utils / saslpasswd.c
1 /* saslpasswd.c -- SASL password setting program
2  * Rob Earhart
3  */
4 /* 
5  * Copyright (c) 1998-2003 Carnegie Mellon University.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer. 
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * 3. The name "Carnegie Mellon University" must not be used to
20  *    endorse or promote products derived from this software without
21  *    prior written permission. For permission or any other legal
22  *    details, please contact  
23  *      Office of Technology Transfer
24  *      Carnegie Mellon University
25  *      5000 Forbes Avenue
26  *      Pittsburgh, PA  15213-3890
27  *      (412) 268-4387, fax: (412) 268-7395
28  *      tech-transfer@andrew.cmu.edu
29  *
30  * 4. Redistributions of any form whatsoever must retain the following
31  *    acknowledgment:
32  *    "This product includes software developed by Computing Services
33  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
34  *
35  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
36  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
37  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
38  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
39  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
40  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
41  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
42  */
43
44 #include <config.h>
45 #include <stdio.h>
46 #include <assert.h>
47
48 #ifndef WIN32
49 #include <termios.h>
50 #include <unistd.h>
51
52 /* perror can't be used on Windows system calls, so we define a new macro to underline this */
53 #define p_oserror(str)      perror(str)
54
55 #else /* WIN32 */
56
57 #include <stdio.h>
58 #include <io.h>
59
60 #include <saslutil.h>
61 __declspec(dllimport) char *optarg;
62 __declspec(dllimport) int optind;
63
64 /* perror can't be used on Windows system calls, so we define a new macro to underline this */
65 void p_oserror (const char *string);
66 #endif /*WIN32*/
67
68 #include <sasl.h>
69 #include <saslplug.h>
70
71 char myhostname[1025];
72
73 #define PW_BUF_SIZE 2048
74
75 static const char build_ident[] = "$Build: saslpasswd " PACKAGE "-" VERSION " $";
76
77 const char *progname = NULL;
78 char *sasldb_path = NULL;
79
80 #ifdef WIN32
81
82 /* This is almost like _plug_get_error_message(), but uses malloc */
83 char * _get_error_message (
84    DWORD error
85 )
86 {
87     char * return_value;
88     LPVOID lpMsgBuf;
89
90     FormatMessage( 
91         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
92         FORMAT_MESSAGE_FROM_SYSTEM | 
93         FORMAT_MESSAGE_IGNORE_INSERTS,
94         NULL,
95         error,
96         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
97         (LPTSTR) &lpMsgBuf,
98         0,
99         NULL 
100     );
101
102     return_value = strdup (lpMsgBuf);
103
104     LocalFree( lpMsgBuf );
105     return (return_value);
106 }
107
108 /* perror() like function that works on OS error codes returned by GetLastError() */
109 void p_oserror (
110     const char *message
111 )
112 {
113 /* Try to match perror() behaviour:
114     string is printed first, followed by a colon, then by the system error message
115     for the last library call that produced the error, and finally by a newline
116     character. If string is a null pointer or a pointer to a null string, perror
117     prints only the system error message.
118  */
119     if (message && *message) {
120         fprintf (stderr, "%s: %s\n", message, _get_error_message(GetLastError()));
121     } else {
122         fprintf (stderr, "%s\n", _get_error_message(GetLastError()));
123     }
124 }
125 #endif /* WIN32 */
126
127 void read_password(const char *prompt,
128                    int flag_pipe,
129                    char ** password,
130                    unsigned *passlen)
131 {
132   char buf[PW_BUF_SIZE];
133 #ifndef WIN32
134   struct termios ts, nts;
135   ssize_t n_read;
136 #else
137   HANDLE hStdin;
138   DWORD n_read, fdwMode, fdwOldMode;
139   hStdin = GetStdHandle(STD_INPUT_HANDLE);
140   if (hStdin == INVALID_HANDLE_VALUE) {
141           p_oserror(progname);
142           exit(-(SASL_FAIL));
143   }
144 #endif /*WIN32*/
145
146   if (! flag_pipe) {
147     fputs(prompt, stdout);
148     fflush(stdout);
149 #ifndef WIN32
150     tcgetattr(STDIN_FILENO, &ts);
151     nts = ts;
152     nts.c_lflag &= ~(ECHO | ECHOE | ECHOK
153 #ifdef ECHOCTL
154     | ECHOCTL
155 #endif
156 #ifdef ECHOPRT
157     | ECHOPRT
158 #endif
159 #ifdef ECHOKE
160     | ECHOKE
161 #endif
162     );
163     nts.c_lflag |= ICANON | ECHONL;
164     tcsetattr(STDIN_FILENO, TCSAFLUSH, &nts);
165 #else
166   if (! GetConsoleMode(hStdin, &fdwOldMode)) {
167           p_oserror(progname);
168           exit(-(SASL_FAIL));
169   }
170   fdwMode = fdwOldMode & ~ENABLE_ECHO_INPUT;
171   if (! SetConsoleMode(hStdin, fdwMode)) {
172           p_oserror(progname);
173           exit(-(SASL_FAIL));
174   }
175 #endif /*WIN32*/
176   }
177
178 #ifndef WIN32
179   n_read = read(STDIN_FILENO, buf, PW_BUF_SIZE);
180   if (n_read < 0) {
181 #else
182   if (! ReadFile(hStdin, buf, PW_BUF_SIZE, &n_read, NULL)) {
183 #endif /*WIN32*/
184
185     p_oserror(progname);
186     exit(-(SASL_FAIL));
187   }
188
189   if (! flag_pipe) {
190 #ifndef WIN32
191     tcsetattr(STDIN_FILENO, TCSANOW, &ts);
192     if (0 < n_read && buf[n_read - 1] != '\n') {
193       /* if we didn't end with a \n, echo one */
194       putchar('\n');
195       fflush(stdout);
196     }
197 #else
198         SetConsoleMode(hStdin, fdwOldMode);
199     putchar('\n');
200     fflush(stdout);
201 #endif /*WIN32*/
202   }
203
204   if (0 < n_read && buf[n_read - 1] == '\n') /* if we ended with a \n */
205     n_read--;                                /* remove it */
206
207 #ifdef WIN32
208   /*WIN32 will have a CR in the buffer also*/
209   if (0 < n_read && buf[n_read - 1] == '\r') /* if we ended with a \r */
210     n_read--;                                /* remove it */
211 #endif /*WIN32*/
212
213   *password = malloc(n_read + 1);
214   if (! *password) {
215 /* Can use perror() here even on Windows, as malloc is in std C library */
216     perror(progname);
217     exit(-(SASL_FAIL));
218   }
219
220   memcpy(*password, buf, n_read);
221   (*password)[n_read] = '\0';   /* be nice... */
222   *passlen = n_read;
223 }
224
225 void exit_sasl(int result, const char *errstr) __attribute__((noreturn));
226
227 void
228 exit_sasl(int result, const char *errstr)
229 {
230   (void)fprintf(stderr, errstr ? "%s: %s: %s\n" : "%s: %s\n",
231                 progname,
232                 sasl_errstring(result, NULL, NULL),
233                 errstr);
234   exit(result < 0 ? -result : result);
235 }
236
237 int good_getopt(void *context __attribute__((unused)), 
238                 const char *plugin_name __attribute__((unused)), 
239                 const char *option,
240                 const char **result,
241                 unsigned *len)
242 {
243     if (sasldb_path && !strcmp(option, "sasldb_path")) {
244         *result = sasldb_path;
245         if (len)
246             *len = strlen(sasldb_path);
247         return SASL_OK;
248     }
249
250     return SASL_FAIL;
251 }
252
253 static struct sasl_callback goodsasl_cb[] = {
254     { SASL_CB_GETOPT, &good_getopt, NULL },
255     { SASL_CB_LIST_END, NULL, NULL }
256 };
257
258 int
259 main(int argc, char *argv[])
260 {
261   int flag_pipe = 0, flag_create = 0, flag_disable = 0, flag_error = 0;
262   int flag_nouserpass = 0;
263   int c;
264   char *userid, *password, *verify;
265   unsigned passlen, verifylen;
266   const char *errstr = NULL;
267   int result;
268   sasl_conn_t *conn;
269   char *user_domain = NULL;
270   char *appname = "saslpasswd";
271   const char *sasl_implementation;
272   int libsasl_version;
273   int libsasl_major;
274   int libsasl_minor;
275   int libsasl_step;
276
277 #ifdef WIN32
278   /* initialize winsock */
279   WSADATA wsaData;
280
281   result = WSAStartup( MAKEWORD(2, 0), &wsaData );
282   if ( result != 0) {
283     exit_sasl(SASL_FAIL, "WSAStartup");
284   }
285 #endif
286
287   memset(myhostname, 0, sizeof(myhostname));
288   result = gethostname(myhostname, sizeof(myhostname)-1);
289   if (result == -1) exit_sasl(SASL_FAIL, "gethostname");
290
291   if (! argv[0])
292     progname = "saslpasswd";
293   else {
294     progname = strrchr(argv[0], HIER_DELIMITER);
295     if (progname)
296       progname++;
297     else
298       progname = argv[0];
299   }
300
301   while ((c = getopt(argc, argv, "vpcdnf:u:a:h?")) != EOF)
302     switch (c) {
303     case 'p':
304       flag_pipe = 1;
305       break;
306     case 'c':
307       if (flag_disable)
308         flag_error = 1;
309       else
310         flag_create = 1;
311       break;
312     case 'd':
313       if (flag_create)
314         flag_error = 1;
315       else
316         flag_disable = 1;
317       break;
318     case 'n':
319         flag_nouserpass = 1;
320         break;
321     case 'u':
322       user_domain = optarg;
323       break;
324     case 'f':
325       sasldb_path = optarg;
326       break;
327     case 'a':
328       appname = optarg;
329       if (strchr(optarg, '/') != NULL) {
330         (void)fprintf(stderr, "appname must not contain /\n");
331         exit(-(SASL_FAIL));
332       }
333       break;
334     case 'v':
335       sasl_version (&sasl_implementation, &libsasl_version);
336       libsasl_major = libsasl_version >> 24;
337       libsasl_minor = (libsasl_version >> 16) & 0xFF;
338       libsasl_step = libsasl_version & 0xFFFF;
339
340       (void)fprintf(stderr, "\nThis product includes software developed by Computing Services\n"
341          "at Carnegie Mellon University (http://www.cmu.edu/computing/).\n\n"
342          "Built against SASL API version %u.%u.%u\n"
343          "LibSasl version %u.%u.%u by \"%s\"\n",
344          SASL_VERSION_MAJOR, SASL_VERSION_MINOR, SASL_VERSION_STEP,
345          libsasl_major, libsasl_minor, libsasl_step, sasl_implementation);
346       exit(0);
347       break;
348     default:
349       flag_error = 1;
350       break;
351     }
352
353   if (optind != argc - 1)
354     flag_error = 1;
355
356   if (flag_error) {
357     (void)fprintf(stderr,
358         "\nThis product includes software developed by Computing Services\n"
359          "at Carnegie Mellon University (http://www.cmu.edu/computing/).\n\n"
360         "%s: usage: %s [-v] [-c [-p] [-n]] [-d] [-a appname] [-f sasldb] [-u DOM] userid\n"
361                   "\t-p\tpipe mode -- no prompt, password read on stdin\n"
362                   "\t-c\tcreate -- ask mechs to create the account\n"
363                   "\t-d\tdisable -- ask mechs to disable/delete the account\n"
364                   "\t-n\tno userPassword -- don't set plaintext userPassword property\n"
365                   "\t  \t                   (only set mechanism-specific secrets)\n"
366                   "\t-f sasldb\tuse given file as sasldb\n"
367                   "\t-a appname\tuse appname as application name\n"
368                   "\t-u DOM\tuse DOM for user domain\n"
369                   "\t-v\tprint version numbers and exit\n",
370                   progname, progname);
371     exit(-(SASL_FAIL));
372   }
373
374   userid = argv[optind];
375
376   result = sasl_server_init(goodsasl_cb, appname);
377   if (result != SASL_OK)
378     exit_sasl(result, NULL);
379
380   result = sasl_server_new("sasldb",
381                            myhostname,
382                            user_domain,
383                            NULL,
384                            NULL,
385                            NULL,
386                            0,
387                            &conn);
388   if (result != SASL_OK)
389     exit_sasl(result, NULL);
390  
391 #ifndef WIN32
392   if (! flag_pipe && ! isatty(STDIN_FILENO))
393     flag_pipe = 1;
394 #endif /*WIN32*/
395
396   if (!flag_disable) {
397       read_password("Password: ", flag_pipe, &password, &passlen);
398
399       if (! flag_pipe) {
400           read_password("Again (for verification): ", flag_pipe, &verify,
401                   &verifylen);
402           if (passlen != verifylen
403               || memcmp(password, verify, verifylen)) {
404               fprintf(stderr, "%s: passwords don't match; aborting\n", 
405                       progname);
406               exit(-(SASL_BADPARAM));
407           }
408       }
409   }
410
411   result = sasl_setpass(conn,
412                         userid,
413                         password,
414                         passlen,
415                         NULL, 0,
416                         (flag_create ? SASL_SET_CREATE : 0)
417                         | (flag_disable ? SASL_SET_DISABLE : 0)
418                         | (flag_nouserpass ? SASL_SET_NOPLAIN : 0));
419
420   if (result != SASL_OK && !flag_disable)
421       exit_sasl(result, NULL);
422   else {
423       struct propctx *propctx = NULL;
424       const char *delete_request[] = { "cmusaslsecretCRAM-MD5",
425                                        "cmusaslsecretDIGEST-MD5",
426                                        "cmusaslsecretPLAIN",
427                                        NULL };
428       int ret = SASL_OK;
429       /* Either we were setting and succeeded or we were disabling and
430          failed.  In either case, we want to wipe old entries */
431
432       /* Delete the possibly old entries */
433       /* We don't care if these fail */
434       propctx = prop_new(0);
435       if (!propctx) ret = SASL_FAIL;
436       if (!ret) ret = prop_request(propctx, delete_request);
437       if (!ret) {
438           ret = prop_set(propctx, "cmusaslsecretCRAM-MD5", NULL, 0);
439           ret = prop_set(propctx, "cmusaslsecretDIGEST-MD5", NULL, 0);
440           ret = prop_set(propctx, "cmusaslsecretPLAIN", NULL, 0);
441           ret = sasl_auxprop_store(conn, propctx, userid);
442       }
443       if (propctx) prop_dispose(&propctx);
444   }
445       
446   if (result != SASL_OK)
447 /* errstr is currently always NULL */
448     exit_sasl(result, errstr);
449
450   sasl_dispose(&conn);
451   sasl_done();
452
453   return 0;
454 }