Fix denial of service - memory corruption.
[samba.git] / source3 / lib / readline.c
index 7c127817be445443f891fa86f66e0f25ff05e725..70a82f27ab7b5abf325af0051de2480c5ddd9d23 100644 (file)
 #  define RL_COMPLETION_CAST
 #endif /* HAVE_NEW_LIBREADLINE */
 
+static bool smb_rl_done;
+
+#if HAVE_LIBREADLINE
+/*
+ * MacOS/X does not have rl_done in readline.h, but
+ * readline.so has it
+ */
+extern int rl_done;
+#endif
+
+void smb_readline_done(void)
+{
+       smb_rl_done = true;
+#if HAVE_LIBREADLINE
+       rl_done = 1;
+#endif
+}
+
 /****************************************************************************
  Display the prompt and wait for input. Call callback() regularly
 ****************************************************************************/
@@ -53,7 +71,7 @@ static char *smb_readline_replacement(const char *prompt, void (*callback)(void)
                                char **(completion_fn)(const char *text, int start, int end))
 {
        fd_set fds;
-       static char *line;
+       char *line = NULL;
        struct timeval timeout;
        int fd = x_fileno(x_stdin);
        char *ret;
@@ -64,34 +82,43 @@ static char *smb_readline_replacement(const char *prompt, void (*callback)(void)
                x_fflush(x_stdout);
        }
 
-       if (line == NULL) {
-               line = (char *)SMB_MALLOC(BUFSIZ);
-               if (!line) {
-                       return NULL;
-               }
+       line = (char *)SMB_MALLOC(BUFSIZ);
+       if (!line) {
+               return NULL;
        }
 
-       while (1) {
+       while (!smb_rl_done) {
                timeout.tv_sec = 5;
                timeout.tv_usec = 0;
 
+               if (fd < 0 || fd >= FD_SETSIZE) {
+                       errno = EBADF;
+                       break;
+               }
+
                FD_ZERO(&fds);
                FD_SET(fd,&fds);
 
                if (sys_select_intr(fd+1,&fds,NULL,NULL,&timeout) == 1) {
                        ret = x_fgets(line, BUFSIZ, x_stdin);
+                       if (ret == 0) {
+                               SAFE_FREE(line);
+                       }
                        return ret;
                }
-               if (callback)
+               if (callback) {
                        callback();
+               }
        }
+       SAFE_FREE(line);
+       return NULL;
 }
 
 /****************************************************************************
  Display the prompt and wait for input. Call callback() regularly.
 ****************************************************************************/
 
-char *smb_readline(const char *prompt, void (*callback)(void), 
+char *smb_readline(const char *prompt, void (*callback)(void),
                   char **(completion_fn)(const char *text, int start, int end))
 {
        char *ret;
@@ -99,7 +126,7 @@ char *smb_readline(const char *prompt, void (*callback)(void),
 
        interactive = isatty(x_fileno(x_stdin)) || getenv("CLI_FORCE_INTERACTIVE");
        if (!interactive) {
-           return smb_readline_replacement(NULL, callback, completion_fn);
+               return smb_readline_replacement(NULL, callback, completion_fn);
        }
 
 #if HAVE_LIBREADLINE
@@ -167,7 +194,7 @@ int cmd_history(void)
        int i;
 
        hlist = history_list();
-       
+
        for (i = 0; hlist && hlist[i]; i++) {
                DEBUG(0, ("%d: %s\n", i, hlist[i]->line));
        }
@@ -177,4 +204,3 @@ int cmd_history(void)
 
        return 0;
 }
-