s_auth.c: Fix off-by-one errors in last commit.
[ircu2.10.12-pk.git] / ircd / s_auth.c
index ff245d6d65423bcb3f30e82767a803294d1b8dc1..b3e993af3137b147814043082d07d2626e9daec6 100644 (file)
@@ -84,6 +84,7 @@ enum AuthRequestFlag {
     AR_IAUTH_HURRY,     /**< we told iauth to hurry up */
     AR_IAUTH_USERNAME,  /**< iauth sent a username (preferred or forced) */
     AR_IAUTH_FUSERNAME, /**< iauth sent a forced username */
+    AR_IAUTH_SOFT_DONE, /**< iauth has no objection to client */
     AR_PASSWORD_CHECKED, /**< client password already checked */
     AR_NUM_FLAGS
 };
@@ -212,6 +213,31 @@ static int preregister_user(struct Client *cptr);
 typedef int (*iauth_cmd_handler)(struct IAuth *iauth, struct Client *cli,
                                 int parc, char **params);
 
+/** Copies a username, cleaning it in the process.
+ *
+ * @param[out] dest Destination buffer for user name.
+ * @param[in] src Source buffer for user name.  Must be distinct from
+ *   \a dest.
+ */
+void clean_username(char *dest, const char *src)
+{
+  int rlen = USERLEN;
+  char ch;
+
+  /* First character can be ~, later characters cannot. */
+  if (!IsCntrl(*src))
+  {
+    ch = *src++;
+    *dest++ = IsUserChar(ch) ? ch : '_';
+    rlen--;
+  }
+  while (rlen-- && !IsCntrl(ch = *src++))
+  {
+    *dest++ = (IsUserChar(ch) && (ch != '~')) ? ch : '_';
+  }
+  *dest = '\0';
+}
+
 /** Set username for user associated with \a auth.
  * @param[in] auth Client authorization request to work on.
  * @return Zero if client is kept, CPTR_KILLED if client rejected.
@@ -222,11 +248,9 @@ static int auth_set_username(struct AuthRequest *auth)
   struct User   *user = cli_user(sptr);
   char *d;
   char *s;
-  int   rlen = USERLEN;
   int   killreason;
   short upper = 0;
   short lower = 0;
-  short pos = 0;
   short leadcaps = 0;
   short other = 0;
   short digits = 0;
@@ -234,32 +258,27 @@ static int auth_set_username(struct AuthRequest *auth)
   char  ch;
   char  last;
 
-  if (FlagHas(&auth->flags, AR_IAUTH_USERNAME))
+  if (FlagHas(&auth->flags, AR_IAUTH_FUSERNAME))
   {
-      ircd_strncpy(cli_user(sptr)->username, cli_username(sptr), USERLEN);
+    ircd_strncpy(user->username, cli_username(sptr), USERLEN);
   }
-  else
+  else if (IsIdented(sptr))
   {
-    /* Copy username from source to destination.  Since they may be the
-     * same, and we may prefix with a '~', use a buffer character (ch)
-     * to hold the next character to copy.
-     */
-    s = IsIdented(sptr) ? cli_username(sptr) : user->username;
-    last = *s++;
-    d = user->username;
-    if (HasFlag(sptr, FLAG_DOID) && !IsIdented(sptr))
-    {
-      *d++ = '~';
-      --rlen;
-    }
-    while (last && !IsCntrl(last) && rlen--)
+    clean_username(user->username, cli_username(sptr));
+  }
+  else if (HasFlag(sptr, FLAG_DOID))
+  {
+    /* Prepend ~ to user->username. */
+    s = user->username;
+    s[USERLEN] = '\0';
+    for (last = '~'; (ch = *s) != '\0'; )
     {
-      ch = *s++;
-      *d++ = IsUserChar(last) ? last : '_';
-      last = (ch != '~') ? ch : '_';
+      *s++ = last;
+      last = ch;
     }
-    *d = 0;
-  }
+    *s++ = last;
+    *s = '\0';
+  } /* else cleaned version of client-provided name is in place */
 
   /* If username is empty or just ~, reject. */
   if ((user->username[0] == '\0')
@@ -282,7 +301,7 @@ static int auth_set_username(struct AuthRequest *auth)
     s = d = user->username + (user->username[0] == '~');
     for (last = '\0';
          (ch = *d++) != '\0';
-         pos++, last = ch)
+         last = ch)
     {
       if (IsLower(ch))
       {
@@ -292,13 +311,13 @@ static int auth_set_username(struct AuthRequest *auth)
       {
         upper++;
         /* Accept caps as leading if we haven't seen lower case or digits yet. */
-        if ((leadcaps || pos == 0) && !lower && !digits)
+        if ((leadcaps || last == '\0') && !lower && !digits)
           leadcaps++;
       }
       else if (IsDigit(ch))
       {
         digits++;
-        if (pos == 0 || !IsDigit(last))
+        if (!IsDigit(last))
         {
           digitgroups++;
           /* If more than two groups of digits, reject. */
@@ -310,7 +329,7 @@ static int auth_set_username(struct AuthRequest *auth)
       {
         other++;
         /* If -_. exist at start, consecutively, or more than twice, reject. */
-        if (pos == 0 || last == '-' || last == '_' || last == '.' || other > 2)
+        if (last == '\0' || last == '-' || last == '_' || last == '.' || other > 2)
           goto badid;
       }
       else /* All other punctuation is rejected. */
@@ -528,7 +547,7 @@ static int preregister_user(struct Client *cptr)
     /* Can this ever happen? */
   case ACR_BAD_SOCKET:
     ++ServerStats->is_ref;
-    IPcheck_connect_fail(cptr);
+    IPcheck_connect_fail(cptr, 0);
     return exit_client(cptr, cptr, &me, "Unknown error -- Try again");
   }
   return 0;
@@ -678,6 +697,8 @@ static void read_auth_reply(struct AuthRequest* auth)
     if (IsUserPort(auth->client))
       sendheader(auth->client, REPORT_FAIL_ID);
     ++ServerStats->is_abad;
+    if (IAuthHas(iauth, IAUTH_UNDERNET))
+      sendto_iauth(auth->client, "u");
   } else {
     if (IsUserPort(auth->client))
       sendheader(auth->client, REPORT_FIN_ID);
@@ -791,7 +812,8 @@ int auth_ping_timeout(struct Client *cptr)
 
   /* Check for iauth timeout. */
   if (FlagHas(&auth->flags, AR_IAUTH_PENDING)) {
-    if (IAuthHas(iauth, IAUTH_REQUIRED)) {
+    if (IAuthHas(iauth, IAUTH_REQUIRED)
+        && !FlagHas(&auth->flags, AR_IAUTH_SOFT_DONE)) {
       sendheader(cptr, REPORT_FAIL_IAUTH);
       return exit_client_msg(cptr, cptr, &me, "Authorization Timeout");
     }
@@ -1085,10 +1107,10 @@ int auth_set_user(struct AuthRequest *auth, const char *username, const char *ho
   FlagClr(&auth->flags, AR_NEEDS_USER);
   cptr = auth->client;
   ircd_strncpy(cli_info(cptr), userinfo, REALLEN);
-  ircd_strncpy(cli_user(cptr)->username, username, USERLEN);
+  clean_username(cli_user(cptr)->username, username);
   ircd_strncpy(cli_user(cptr)->host, cli_sockhost(cptr), HOSTLEN);
   if (IAuthHas(iauth, IAUTH_UNDERNET))
-    sendto_iauth(cptr, "U %s %s %s :%s", username, hostname, servername, userinfo);
+    sendto_iauth(cptr, "U %s %s %s :%s", cli_user(cptr)->username, hostname, servername, userinfo);
   else if (IAuthHas(iauth, IAUTH_ADDLINFO))
     sendto_iauth(cptr, "U %s", username);
   return check_auth_finished(auth);
@@ -1371,19 +1393,19 @@ static void iauth_disconnect(struct IAuth *iauth)
   if (iauth == NULL)
     return;
 
-  /* Close main socket. */
-  if (s_fd(i_socket(iauth)) != -1) {
-    close(s_fd(i_socket(iauth)));
-    socket_del(i_socket(iauth));
-    s_fd(i_socket(iauth)) = -1;
-  }
-
   /* Close error socket. */
   if (s_fd(i_stderr(iauth)) != -1) {
     close(s_fd(i_stderr(iauth)));
     socket_del(i_stderr(iauth));
     s_fd(i_stderr(iauth)) = -1;
   }
+
+  /* Close main socket. */
+  if (s_fd(i_socket(iauth)) != -1) {
+    close(s_fd(i_socket(iauth)));
+    socket_del(i_socket(iauth));
+    s_fd(i_socket(iauth)) = -1;
+  }
 }
 
 /** Close all %IAuth connections marked as closing. */
@@ -1826,7 +1848,7 @@ static int iauth_cmd_ip_address(struct IAuth *iauth, struct Client *cli,
     memcpy(&auth->original, &cli_ip(cli), sizeof(auth->original));
 
   /* Undo original IP connection in IPcheck. */
-  IPcheck_connect_fail(cli);
+  IPcheck_connect_fail(cli, 1);
   ClearIPChecked(cli);
 
   /* Update the IP and charge them as a remote connect. */
@@ -1880,6 +1902,22 @@ static struct ConfItem *auth_find_class_conf(const char *class_name)
   return aconf;
 }
 
+/** Tentatively accept a client in IAuth.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] parc Number of parameters.
+ * @param[in] params Optional class name for client.
+ * @return Negative (CPTR_KILLED) if the connection is refused, one otherwise.
+ */
+static int iauth_cmd_soft_done(struct IAuth *iauth, struct Client *cli,
+                              int parc, char **params)
+{
+  /* Clear iauth pending flag. */
+  assert(cli_auth(cli) != NULL);
+  FlagSet(&cli_auth(cli)->flags, AR_IAUTH_SOFT_DONE);
+  return 1;
+}
+
 /** Accept a client in IAuth.
  * @param[in] iauth Active IAuth session.
  * @param[in] cli Client referenced by command.
@@ -2099,6 +2137,7 @@ static void iauth_parse(struct IAuth *iauth, char *message)
   case 'I': handler = iauth_cmd_ip_address; has_cli = 1; break;
   case 'M': handler = iauth_cmd_usermode; has_cli = 1; break;
   case 'C': handler = iauth_cmd_challenge; has_cli = 1; break;
+  case 'd': handler = iauth_cmd_soft_done; has_cli = 1; break;
   case 'D': handler = iauth_cmd_done_client; has_cli = 1; break;
   case 'R': handler = iauth_cmd_done_account; has_cli = 1; break;
   case 'k': /* The 'k' command indicates the user should be booted
@@ -2229,8 +2268,7 @@ static void iauth_sock_callback(struct Event *ev)
 
   switch (ev_type(ev)) {
   case ET_DESTROY:
-    /* Hm, what happened here? */
-    if (!IAuthHas(iauth, IAUTH_CLOSING))
+    if (!IAuthHas(iauth, IAUTH_CLOSING) && !s_active(i_stderr(iauth)))
       iauth_do_spawn(iauth, 1);
     break;
   case ET_READ:
@@ -2303,7 +2341,8 @@ static void iauth_stderr_callback(struct Event *ev)
 
   switch (ev_type(ev)) {
   case ET_DESTROY:
-    /* We do not restart iauth here: the stdout handler does that for us. */
+    if (!IAuthHas(iauth, IAUTH_CLOSING) && !s_active(i_socket(iauth)))
+      iauth_do_spawn(iauth, 1);
     break;
   case ET_READ:
     iauth_read_stderr(iauth);