+2006-02-15 Michael Poole <mdpoole@troilus.org>
+
+ * doc/example.conf: Include new HIS_STATS_IAUTH feature.
+
+ * doc/readme.features: Document the feature.
+
+ * doc/readme.iauth: Rewrite to reflect the new progressive iauth
+ protocol, based on IRCnet's iauth.
+
+ * doc/snomask.html: Document SNO_AUTH server notice flag.
+
+ * include/client.h (FLAG_IAUTHED): Delete.
+ (con_cookie): Delete.
+ (con_unreg): Delete.
+ (con_auth): Make comment capitalization consistent.
+ (con_iauth): Delete.
+ (CLIREG_*): Delete.
+ (cli_unreg): Delete.
+ (cli_cookie): Delete.
+ (cli_iauth): Delete.
+ (con_unreg): Delete.
+ (con_iauth): Delete.
+ (IsIAuthed): Delete.
+ (SetIAuthed): Delete.
+ (SNO_AUTH): New server notice flag.
+ (SNO_ALL): Update to include SNO_AUTH.
+ (SNO_OPER): Update to include SNO_AUTH.
+
+ * include/ircd_auth.h: Delete file.
+
+ * include/ircd_features.h (HIS_STATS_IAUTH): New feature.
+
+ * include/s_auth.h: Rewrite almost everything for new auth system.
+
+ * include/s_user.h (COOKIE_VERIFIED): Delete.
+ (register_user): Remove redundant nick and username arguments.
+
+ * ircd/ircd_auth.c: Delete file.
+
+ * ircd/ircd_features.c (HIS_STATS_IAUTH): New feature.
+
+ * ircd/ircd_lexer.l (PROGRAM): New token in grammar.
+
+ * ircd/ircd_log.c (masks): Add SNO_AUTH flag.
+
+ * ircd/ircd_parser.y (stringlist): Simplify production.
+ (iauthblock): Revise to only include a PROGRAM production.
+
+ * ircd/list.c (make_client): Do not assign to deleted field.
+
+ * ircd/m_cap.c (cap_ls): Use auth_cap_start() instead of
+ cli_unreg().
+ (cap_req): Likewise.
+ (cap_end): Use auth_cap_done() instead of cli_unreg().
+
+ * ircd/m_pass.c (mr_pass): Merge arguments to PASS. Use
+ auth_set_password() to notify iauth of password.
+
+ * ircd/m_pong.c (mr_pong): Use auth_set_pong() instead of
+ cli_cookie() and cli_unreg().
+
+ * ircd/m_user.c (m_user): Use auth_set_user() instead of
+ cli_unreg(), etc.
+
+ * ircd/s_auth.c: Rewrite most of the infrastructure for the new
+ auth system.
+
+ * ircd/s_conf.c (rehash): Call auth_*() instead of iauth_*().
+
+ * ircd/s_misc.c (exit_one_client): Do not use iauth_exit_client().
+ (exit_client): Use auth_send_exit() instead.
+
+ * ircd/s_stats.c (statsinfo): Include iauth and iauthconf.
+
+ * ircd/s_user.c (clean_user_id): Delete (moved into s_auth.c).
+ (register_user): Remove nick and username parameters; move conf
+ interactions and username validation to s_auth.c.
+ (set_nick_name): Use auth_set_nick() instead of cli_cookie(),
+ cli_unreg(), etc.
+
+ * tools/iauth-test: Implementation of iauth for testing purposes.
+
2006-02-15 Michael Poole <mdpoole@troilus.org>
* ircd/ircd_snprintf.c (doprintf): Fix typecast for %hu.
# "HIS_STATS_x" = "TRUE";
# "HIS_STATS_y" = "TRUE";
# "HIS_STATS_z" = "TRUE";
+# "HIS_STATS_IAUTH" = "TRUE";
# "HIS_WHOIS_SERVERNAME" = "TRUE";
# "HIS_WHOIS_IDLETIME" = "TRUE";
# "HIS_WHOIS_LOCALCHAN" = "TRUE";
As per UnderNet CFV-165, this removes /STATS z from users.
+HIS_STATS_IAUTH
+ * Type: boolean
+ * Default: TRUE
+
+As per UnderNet CFV-165, this disables /STATS IAUTH and
+/STATS IAUTHCONF from users.
+
HIS_WHOIS_SERVERNAME
* Type: boolean
* Default: TRUE
OVERVIEW
========
-The IAUTH protocol used here is based on the one in ircd-hybrid 7.0,
-with minor changes to support login-on-connect and true IAUTH-side
-connection classes. (Several networks use central authorities to vary
-per-netblock connection limits; for example, users from one ISP may
-only be allowed one connection per IP, or one shell provider's
-netblock may be limited to 50 total connections.) IAUTH-side
-connection classes are controlled by a configuration option; if that
-is enabled, this document will say ICLASS is enabled.
-
-As in IRC, lines sent between the IRC and IAUTH servers are limited to
-512 characters, including the terminating <CR> <LF> sequence. As in
-IRC, the final argument on a line may be prefixed with :, and must be
-prefixed with : if it contains a space (decimal 32) character. Tokens
-are separated by single space characters, and each line is a separate
-command. The first token on a line is a case-insensitive command
-name; unrecognized commands must be ignored.
-
-GREETING
-========
+The iauth protocol used here is based on the one in irc2.11.1, with
+minor changes to support challenge-response protocols and
+login-on-connect. Reference to that version's iauth-internals.txt and
+source code may be useful. For clarity, this document uses "server"
+to refer to any IRC server implementing this protocol, "ircu" to refer
+to Undernet ircd, and "ircd" to refer to IRCnet ircd.
+
+Certain messages are relayed to interested operators. ircu implements
+this by using the 131072 (SNO_AUTH) server notice mask. ircd
+implements this by using the &AUTH local channel.
+
+STARTING IAUTH
+==============
+
+The path to the iauth program is specified in the server configuration
+file. The server spawns that program when reading the configuration
+file or when the previous iauth instance terminates. To protect
+against a series of crashes, the server will refuse to restart an
+iauth instance that it spawned in the last five seconds. A rehash
+operation will clear this behavior. The server and iauth instance
+communicate over the iauth instance's stdin and stdout.
+
+Every message from the server to the iauth instance is a single line.
+The line starts with an integer client identifier. This may be -1 to
+indicate no particular client or a non-negative number to indicate a
+client connected to the server.
+
+When the server starts the iauth instance, it sends a line formatted
+like "-1 M irc.example.org 20000" to indicate its name and an
+exclusive upper bound on valid client identifiers. In that example,
+possible client identifiers would be from 0 through 19999 inclusive.
+This upper bound is called MAXCONNECTIONS in the server code.
+
+When the iauth instance starts, it sends a V message to indicate its
+version.
+
+The server should provide /stats subcommands that report the iauth
+instance's version, configuration and statistics.
+
+Line formats in both direction are IRC-like in format: space
+characters separate arguments and a colon at the start of an argument
+indicates that the remainder of the line is one argument. To avoid
+problems, IPv6 address arguments with a leading colon may have to be
+prefixed with a 0 -- for example, ::1 sent as 0::1.
+
+When the iauth instance sends messages that relate to a particular
+client, that client is identified by three parameters from the
+server's Client Introduction message (<id>, <remoteip> and
+<remoteport>). If any of these disagree with the server's current
+user tables, it is an error.
+
+CLIENT STATES
+=============
+
+Each client is conceptually in one of four states: GONE, REGISTER,
+HURRY or NORMAL. Each client starts in the GONE state. Certain
+messages from the server signal a client's transition from one state
+to another, and certain messages from the iauth instance cause a state
+transition.
+
+To be pedantic, the REGISTER state is a collection of sub-states since
+certain commands must occur at most and/or at least one time during
+the REGISTER state. The distinctions between these sub-states are
+distracting and not important, so they are described as one state and
+the repetition limitations are described for each command.
+
+The rationale for the HURRY state is to give explicit input to the
+iauth instance as to when the server believes it has sent the complete
+set of data for the client. Rather than defining the complete set of
+information in this protocol document, that is left to the server.
+ircd does not indicate this state.
+
+POLICIES AND USE CASES
+======================
+
+The historical application of iauth has been to block users that
+appear to be drones early, before they have a chance to disrupt the
+network, and without affecting other users on the same host (which
+K-lines do). This protocol extends that application by adding the n
+server message and by allowing challenge-response exchanges with the
+client.
+
+Eventually it would be nice to move the DNS and ident lookups into
+iauth, and remove that code from the IRC server. ircd already does
+this; since ircu does not, it adds the u server message.
+
+For trusted proxies, this protocol gives the capability for clients
+connecting through those proxies to be displayed with their actual
+username, IP address and hostname. The same functions allow other
+clients to use iauth-assigned spoofs, for example to hide the IP
+addresses used by operators.
+
+This protocol allows login-on-connect, for example by clients that
+send their account name and password in PASS, through the R iauth
+message.
+
+This protocol allows iauth to assign a client to a particular class by
+specifying a class name in the D or R iauth message.
+
+SERVER MESSAGES
+===============
+
+X - Example Message Description
+Syntax: <id> X <several> <arguments>
+Example: 5 X arguments vary
+States: REGISTER(1), HURRY, NORMAL
+Next State: -
+Comments: This is an example message description. Each message is a
+ single character. The States field indicates which states the
+ message may occur in and any restrictions on how many times the
+ message may be sent during those states (restrictions only make
+ sense when Next State is -). The Next State field indicates which
+ new state is implied by the message; a hyphen indicates no state
+ change is implied. The X (Example) message is not a real message
+ type.
+Compatibility: If we believe ircu behavior is different than ircd's,
+ this describes ircd's behavior or expectations.
+
+C - Client Introduction
+Syntax: <id> C <remoteip> <remoteport> <localip> <localport>
+Example: 5 C 192.168.1.10 23367 192.168.0.1 6667
+States: GONE
+Next State: REGISTER
+Comments: Indicates that <localport> on <localip> accepted a client
+ connection from <remoteport> on <remoteip>.
+
+D - Client Disconnect
+Syntax: <id> D
+Example: 5 D
+States: REGISTER, HURRY, NORMAL
+Next State: GONE
+Comments: Indicates that a client is disconnecting from the server.
+
+N - Hostname Received
+Syntax: <id> N <hostname>
+Example: 5 N host-1-10.example.org
+States: REGISTER(1)
+Next State: -
+Comments: Indicates that the server received hostname information for
+ a client. Only one of 'H' and 'd' is sent.
-The IRC server connects and sends the Server greeting:
- Server <servername> [password]
-If ICLASS is enabled, it sends a list of currently connected users:
- MyUsers <uid>:<username>@<hostname>:<ip> ...
-The IRC server may send several MyUsers lines. When it has sent all
-MyUsers lines, it sends an EndUsers line:
- EndUsers
-If ICLASS is disabled, EndUsers is sent immediately after Server.
+d - Hostname Timeout
+Syntax: <id> d
+Example: 5 d
+States: REGISTER(1)
+Next State: -
+Comments: Indicates that the server did not receive hostname
+ information for a client in a timely fashion. Only one of 'H' and
+ 'd' is sent.
-LOGIN REQUESTS
+P - Client Password
+Syntax: <id> P :<password ...>
+Example: 5 P :buddha n1rvan4
+States: REGISTER
+Next State: -
+Comments: Indicates the client's password information. This may be a
+ traditional client password, an account and pass phrase pair, or the
+ response to a challenge (see the iauth C message). This message is
+ enabled by requesting the A policy.
+
+U - Client Username
+Syntax: <id> U <username> :<userinfo ...>
+Example: 5 U buddha :Gautama Siddhartha
+States: REGISTER(1+)
+Next State: -
+Comments: Indicates the client's claimed username and "GECOS"
+ information. This information is not reliable. This message is
+ enabled by requesting the A policy.
+Compatibility: ircd does not include the <userinfo> data.
+
+u - Client Username
+Syntax: <id> u <username>
+Example: 5 u notbuddha
+States: REGISTER(1)
+Next State: -
+Comments: Indicates a more reliable username for the client.
+Compatibility: This is an Undernet extension and ircd does not send
+ it. It is enabled by the iauth instance requesting the U policy.
+
+n - Client Nickname
+Syntax: <id> n <nickname>
+Example: 5 n Buddha
+States: REGISTER(1+), HURRY
+Next State: -
+Comments: Indicates the client's requested nickname.
+Compatibility: This is an Undernet extension and ircd does not send
+ it. It is enabled by the iauth instance requesting the U policy.
+
+H - Hurry Up
+Syntax: <id> H <class>
+Example: 5 H Others
+States: REGISTER
+Next State: HURRY
+Comments: Indicates that the server is ready to register the client
+ except for needing a response from the iauth server. <class> is
+ a tentative connection class for the user, which will be used unless
+ iauth overrides it in a D or R message.
+Compatibility: This is an Undernet extension and ircd does not send
+ it. It is enabled by the iauth instance requesting the U policy.
+
+T - Client Registered
+Syntax: <id> T
+Example: 5 T
+States: HURRY
+Next State: NORMAL
+Comments: Indicates the server got tired of waiting for iauth to
+ finish and the client is being accepted. This message should
+ never be sent when the R policy is in effect.
+Compatibility: ircd allows this message for clients in the REGISTER
+ state.
+
+E - Error
+Syntax: <id> E <type> :<additional text>
+Example: 5 E Gone
+States: N/A
+Next State: -
+Comments: Indicates that a message received from the iauth instance
+ could not be rationally interpreted. This may be because the client
+ could not be found, the client was in an inappropriate state for the
+ message, or for other reasons. The <type> argument specifies the
+ general type of error and <additional text> provides details. <id>
+ may be -1.
+
+M - Server Name and Capacity
+Syntax: <id> M <servername> <capacity>
+Example: -1 M irc.example.org 20000
+States: GONE(1)
+Next State: -
+Comments: Indicates the server's name and upper bound on client
+ identifiers.
+Compatibility: ircd does not include the <capacity> information.
+ The <id> should be ignored: ircd sends 0 and ircu sends -1.
+
+IAUTH MESSAGES
==============
-When users connect, the IRC server sends a DoAuth request:
- FullAuth <uid> <nickname> <username> <hostname> <ip> <account> <password> <realname>
-<uid> is a text string up to 20 characters long that identifies the
-client, and is unique a BadAuth response is received or until an
-ExitUser command is sent with the same uid (see below for details on
-those messages). <uid> may not contain a colon character. <nickname>
-is the client's initially requested nickname. <username> is the
-username returned by the ident server (RFC 1413), or a tilde-prefixed
-username supplied by the user. <hostname> is a text hostname,
-possibly in the form of a dotted quad or IPv6 address, or the
-character '?'. <ip> is a dotted quad IPv4 address or an IPv6 hex
-address. <account> and <password> are optional, and are used when the
-client attempts login-on-connect. <realname> is the realname
-specified by the client's USER message, and may contain spaces.
-
-If the client is accepted, the IAUTH server responds:
- DoneAuth <uid> <username> <hostname> <class> [account]
-<username> is a replacement username, and <hostname> is a replacement
-hostname. If the <hostname> from DoAuth was ?, <hostname> is the
-result of a DNS lookup for the client. <class> is the name of a
-connection class for the client. <account> is optional and is
-provided if the user's login was successful.
-
-If the client is rejected, the IAUTH server responds:
- BadAuth <uid> :<reason>
-<reason> may include spaces, and should have a leading ':' sentinel.
-
-DISCONNECTS
-===========
-
-If ICLASS is enabled, the IRC server sends ExitUser when a client
-disconnects:
- ExitUser <uid>
-
-DIFFERENCES FROM IRCD-HYBRID
-============================
-
-The ircd-hybrid IAUTH code is slightly bitrotted and disabled in 7.0
-(through at least 7.0.1). This code added the following items:
- MyUsers, EndUsers and ExitUser commands
- Server passwords may contain whitespace and be prefixed by :
- DoneAuth may include an account name
- FullAuth command replaces DoAuth command and adds account, password,
- realname parameters
-The Class command is present in ircd-hybrid's code but not used here.
-IP addresses in ircd-hybrid are "in unsigned int format," which is
-limited to IPv4, and so it is not used here.
+X - Example Message Description
+Syntax: X <arguments>
+Example: X something
+Notify: yes
+States: N/A
+Next State: N/A
+Comments: This is an example message description. Each message is a
+ single character. If the Notify field is present and says yes,
+ interested operators (with SNO_AUTH set) should be notified of the
+ message. The States field, where present, indicate which states
+ accept this message. Clients in other states should ignore the
+ message or treat it as an error. The Next State field, where
+ present, indicates what the next state should be for the client.
+Compatibility: If we believe ircu behavior is different than ircd's,
+ this describes ircd's behavior or expectations.
+
+> - Operator Notification
+Syntax: > :<message text>
+Example: > :Hello Operators!
+Notify: yes
+Comments: Contains a message that the iauth instance wants to send to
+ interested operators.
+
+G - Set Debug Level
+Syntax: G <level>
+Example: G 1
+Notify: yes
+Comments: Sets a debug level for the server's end of the iauth
+ conversation. When enabled, debug messages should be sent to the
+ same channel (group, mask, etc) as other iauth notifications.
+ Debug level 0 suppresses iauth-related debug output, and positive
+ integers enable iauth debugging messages.
+
+O - Set Policy Options
+Syntax: O <options>
+Example: O RTAWU
+Notify: yes
+Comments: Sets policy options for the iauth conversation. Old policy
+ options should be forgotten. Valid policy options are:
+ A - Send username and password information.
+ This causes the server to send the U and P messages.
+ R - Require clients to be approved before registering them.
+ When this policy is in effect, it affects the behavior
+ of a registration timeout; for details, see the documentation
+ for the T server message.
+ T - When the R policy is in effect and the iauth service does not
+ respond for a client, this causes the server to count the number
+ of clients refused, to send a warning message to interested
+ operators periodically, and to send the count of rejected users
+ to interested operators when the iauth instance responds again.
+ U - Send nickname, confirmed username and hurry information.
+ This causes the server to send the n, u and H messages.
+ W - Allow extra time for iauth to respond based on hostname.
+ When this policy is in effect and a DNS message (N or d) is
+ sent for a client, that client's registration timeout is
+ extended or reset.
+Compatibility: The U policy is an Undernet extension and is not
+ recognized by ircd.
+
+V - iauth Program Version
+Syntax: V :<version string>
+Example: V :Undernet-iauthu v1.0
+Notify: yes
+Comments: Indicates the iauth program version. This should only be
+ used in diagnostic messages, and must not change protocol behavior.
+
+a - Start of new configuration
+Syntax: a
+Example: a
+Notify: yes
+Comments: Indicates that a new configuration is being loaded by the
+ iauth instance. Any cached configuration records should be cleared.
+
+A - Configuration Information
+Syntax: A <hosts?> <module> :<options>
+Example: A * rfc931
+Notify: yes
+Comments: Indicates new configuration information.
+
+s - Start of new statistics
+Syntax: s
+Example: s
+Notify: yes
+Comments: Indicates a new set of statistics will be sent. Any cached
+ statistics records should be cleared.
+
+S - Statistics Information
+Syntax: S <module> :<module information>
+Example: S rfc931 connected 0 unix 0 other 0 bad 0 out of 0
+Notify: yes
+Comments: Indicates new or additional statistics information.
+
+o - Forced Username
+Syntax: o <id> <remoteip> <remoteport> <username>
+Example: o 5 192.168.1.10 23367 bubba
+States: REGISTER, HURRY
+Next State: -
+Comments: Indicates that the username should be used for the specified
+ client even if the normal sanity-checking would prohibit the
+ username.
+
+U - Trusted Username
+Syntax: U <id> <remoteip> <remoteport> <username>
+Example: U 5 192.168.1.10 23367 buddha
+States: REGISTER, HURRY
+Next State: -
+Comments: Indicates that the iauth instance believes <username> is
+ accurate for the specified client.
+
+u - Untrusted Username
+Syntax: u <id> <remoteip> <remoteport> <username>
+Example: u 5 192.168.1.10 23367 enlightened_one
+States: REGISTER, HURRY
+Next State: -
+Comments: Indicates that the iauth instance does not strongly trust
+ <username> to be accurate, but has no more trusted username.
+
+N - Client Hostname
+Syntax: N <id> <remoteip> <remoteport> <hostname>
+Example: N 5 192.168.1.10 23367 buddha.example.org
+States: REGISTER, HURRY
+Next State: -
+Comments: Indicates that the iauth instance believes the specified
+ client should use the hostname given.
+Compatibility: This is an Undernet extension and ircd does not support
+ this message.
+
+I - Client IP Address
+Syntax: N <id> <currentip> <remoteport> <newip>
+Example: N 5 192.168.1.10 23367 127.128.129.130
+States: REGISTER, HURRY
+Next State: -
+Comments: Indicates that the iauth instance wants the server to
+ present and treat the client as using <newip>. This means that
+ future iauth messages relating to the client must use <newip>
+ as the <remoteip> parameter.
+Compatibility: This is an Undernet extension and ircd does not support
+ this message.
+
+C - Challenge User
+Syntax: C <id> <remoteip> <remoteport> :<challenge string>
+Example: C 5 192.168.1.10 23367 :In which year did Columbus sail the ocean blue?
+States: REGISTER, HURRY
+Next State: -
+Comments: Indicates that the challenge string should be sent to the
+ specified user, for example via NOTICE AUTH :*** <challenge string>.
+ The client responds by sending PASS :<response>, which should be
+ relayed via the P server message. This requires that the A policy
+ be in effect.
+Compatibility: This is an Undernet extension and ircd does not support
+ this message.
+
+k - Quietly Kill Client
+Syntax: k <id> <remoteip> <remoteport> :<reason>
+Example: k 5 192.168.1.10 23367 :Open proxy found.
+States: REGISTER, HURRY, NORMAL
+Next State: GONE
+Comments: Indicates that the specified client should be disconnected
+ for the reason given without notifying operators.
+Compatibility: ircu does not use the same notification mechanism as
+ ircd, so operators are notified using SNO_CONNEXIT anyway.
+
+K - Kill Client
+Syntax: K <id> <remoteip> <remoteport> :<reason>
+Example: K 5 192.168.1.10 23367 :We don't like you.
+States: REGISTER, HURRY, NORMAL
+Next State: GONE
+Comments: Indicates that the specified client should be disconnected
+ for the reason given. Operators should be notified.
+
+D - Done Checking
+Syntax: D <id> <remoteip> <remoteport> [class]
+Example: D 5 192.168.1.10 23367
+States: REGISTER, HURRY
+Next State: NORMAL
+Comments: Indicates that the iauth instance believes the specified
+ client should be allowed onto the network. If a class parameter is
+ given, the client should be assigned to that class.
+Compatibility: Specifying the class is an Undernet extension and ircd
+ does not support that parameter.
+
+R - Registered User
+Syntax: R <id> <remoteip> <remoteport> <account> [class]
+Example: R 5 192.168.1.10 23367 Buddha
+States: REGISTER, HURRY
+Next State: NORMAL
+Comments: Indicates that the iauth instance believes the specified
+ client should be allowed onto the network, pre-authenticated to
+ the account listed. If a class parameter is given, the client
+ should be assigned to that class.
+Compatibility: This is an Undernet extension and ircd does not support
+ this message.
<td><font face="arial" size="2">0x10000</td>\r
<td><font face="arial" size="2">/* debugging messages (DEBUGMODE only) */</td>\r
</tr>\r
+<tr>\r
+<td><font face="arial" size="2">131072</td>\r
+<td><font face="arial" size="2">SNO_AUTH</td>\r
+<td><font face="arial" size="2">0x20000</td>\r
+<td><font face="arial" size="2">/* iauth status messages */</td>\r
+</tr>\r
</table>\r
</center>\r
\r
</tr>\r
<tr>\r
<td><font face="arial" size="2">only opers may set</td>\r
-<td><font face="arial" size="2">SNO_OPER (SNO_CONNEXIT | SNO_OLDREALOP)</td>\r
+<td><font face="arial" size="2">SNO_OPER (SNO_CONNEXIT | SNO_OLDREALOP | SNO_AUTH)</td>\r
</tr>\r
</table>\r
\r
FLAG_BURST, /**< Server is receiving a net.burst */
FLAG_BURST_ACK, /**< Server is waiting for eob ack */
FLAG_IPCHECK, /**< Added or updated IPregistry data */
- FLAG_IAUTHED, /**< Got IAUTH response for user */
FLAG_LOCOP, /**< Local operator -- SRB */
FLAG_SERVNOTICE, /**< server notices such as kill */
FLAG_OPER, /**< Operator */
time_t con_nexttarget;/**< Next time a target change is allowed */
time_t con_lasttime; /**< Last time data read from socket */
time_t con_since; /**< Last time we accepted a command */
- unsigned int con_cookie; /**< Random number the user must PONG */
struct MsgQ con_sendQ; /**< Outgoing message queue */
struct DBuf con_recvQ; /**< Incoming data yet to be parsed */
unsigned int con_sendM; /**< Stats: protocol messages sent */
HandlerType con_handler; /**< Message index into command table
for parsing. */
struct ListingArgs* con_listing; /**< Current LIST status. */
- unsigned long con_unreg; /**< Indicate what still needs to be done */
unsigned int con_max_sendq; /**< cached max send queue for client */
unsigned int con_ping_freq; /**< cached ping freq */
unsigned short con_lastsq; /**< # 2k blocks when sendqueued
struct Privs con_privs; /**< Oper privileges */
struct CapSet con_capab; /**< Client capabilities (from us) */
struct CapSet con_active; /**< Active capabilities (to us) */
- struct AuthRequest* con_auth; /**< auth request for client */
- struct IAuthRequest* con_iauth; /**< iauth request for client */
+ struct AuthRequest* con_auth; /**< Auth request for client */
};
/** Magic constant to identify valid Connection structures. */
char cli_info[REALLEN + 1]; /**< Free form additional client information */
};
-#define CLIREG_NICK 0x0001 /**< Client must set nickname */
-#define CLIREG_USER 0x0002 /**< Client must set username */
-#define CLIREG_COOKIE 0x0004 /**< Client must return cookie */
-#define CLIREG_CAP 0x0008 /**< Client in capability negotiation */
-
-#define CLIREG_INIT (CLIREG_NICK | CLIREG_USER | CLIREG_COOKIE)
-
/** Magic constant to identify valid Client structures. */
#define CLIENT_MAGIC 0x4ca08286
#define cli_capab(cli) con_capab(cli_connect(cli))
/** Get active client capabilities for client */
#define cli_active(cli) con_active(cli_connect(cli))
-/** Get flags for remaining registration tasks */
-#define cli_unreg(cli) con_unreg(cli_connect(cli))
/** Get client name. */
#define cli_name(cli) ((cli)->cli_name)
/** Get client username (ident). */
#define cli_nextnick(cli) con_nextnick(cli_connect(cli))
/** Get next time a target change is allowed for the client. */
#define cli_nexttarget(cli) con_nexttarget(cli_connect(cli))
-/** Get required PING/PONG cookie for client. */
-#define cli_cookie(cli) con_cookie(cli_connect(cli))
/** Get SendQ for client. */
#define cli_sendQ(cli) con_sendQ(cli_connect(cli))
/** Get RecvQ for client. */
#define cli_proc(cli) con_proc(cli_connect(cli))
/** Get auth request for client. */
#define cli_auth(cli) con_auth(cli_connect(cli))
-/** Get iauth request for client. */
-#define cli_iauth(cli) con_iauth(cli_connect(cli))
/** Get sentalong marker for client. */
#define cli_sentalong(cli) con_sentalong(cli_connect(cli))
#define con_lasttime(con) ((con)->con_lasttime)
/** Get last time we accepted a command from the connection. */
#define con_since(con) ((con)->con_since)
-/** Get PING/PONG confirmation cookie for connection. */
-#define con_cookie(con) ((con)->con_cookie)
/** Get SendQ for connection. */
#define con_sendQ(con) ((con)->con_sendQ)
/** Get RecvQ for connection. */
#define con_handler(con) ((con)->con_handler)
/** Get the LIST status for the connection. */
#define con_listing(con) ((con)->con_listing)
-/** Get remining steps before registration completes. */
-#define con_unreg(con) ((con)->con_unreg)
/** Get the maximum permitted SendQ size for the connection. */
#define con_max_sendq(con) ((con)->con_max_sendq)
/** Get the ping frequency for the connection. */
#define con_active(con) (&(con)->con_active)
/** Get the auth request for the connection. */
#define con_auth(con) ((con)->con_auth)
-/** Get the iauth request for the connection. */
-#define con_iauth(con) ((con)->con_iauth)
#define STAT_CONNECTING 0x001 /**< connecting to another server */
#define STAT_HANDSHAKE 0x002 /**< pass - server sent */
#define IsDeaf(x) HasFlag(x, FLAG_DEAF)
/** Return non-zero if the client has been IP-checked for clones. */
#define IsIPChecked(x) HasFlag(x, FLAG_IPCHECK)
-/** Return non-zero if the client has been okayed by iauth. */
-#define IsIAuthed(x) HasFlag(x, FLAG_IAUTHED)
/** Return non-zero if we have received an ident response for the client. */
#define IsIdented(x) HasFlag(x, FLAG_GOTID)
/** Return non-zero if the client has set mode +i (invisible). */
#define SetGotId(x) SetFlag(x, FLAG_GOTID)
/** Mark a client as being IP-checked. */
#define SetIPChecked(x) SetFlag(x, FLAG_IPCHECK)
-/** Mark a client as being iauth-checked. */
-#define SetIAuthed(x) SetFlag(x, FLAG_IAUTHED)
/** Mark a client as having mode +i (invisible). */
#define SetInvisible(x) SetFlag(x, FLAG_INVISIBLE)
/** Mark a client as causing a net.join. */
#define SNO_CONNEXIT 0x4000 /**< client connect/exit (ugh) */
#define SNO_AUTO 0x8000 /**< AUTO G-Lines */
#define SNO_DEBUG 0x10000 /**< debugging messages (DEBUGMODE only) */
+#define SNO_AUTH 0x20000 /**< IAuth notices */
+/** Bitmask of all valid server notice bits. */
#ifdef DEBUGMODE
-# define SNO_ALL 0x1ffff /**< Bitmask of all valid server
- * notice bits. */
+# define SNO_ALL 0x3ffff
#else
-# define SNO_ALL 0xffff
+# define SNO_ALL 0x2ffff
#endif
/** Server notice bits allowed to normal users. */
/** Server notice bits enabled by default for IRC operators. */
#define SNO_OPERDEFAULT (SNO_DEFAULT|SNO_HACK2|SNO_HACK4|SNO_THROTTLE|SNO_OLDSNO)
/** Server notice bits reserved to IRC operators. */
-#define SNO_OPER (SNO_CONNEXIT|SNO_OLDREALOP)
+#define SNO_OPER (SNO_CONNEXIT|SNO_OLDREALOP|SNO_AUTH)
/** Noisy server notice bits that cause other bits to be cleared during connect. */
#define SNO_NOISY (SNO_SERVKILL|SNO_UNAUTH)
+++ /dev/null
-#ifndef INCLUDED_ircd_auth_h
-#define INCLUDED_ircd_auth_h
-
-/*
- * IRC - Internet Relay Chat, ircd/ircd_auth.h
- * Copyright 2004 Michael Poole <mdpoole@troilus.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
- * 02111-1307, USA.
- */
-/** @file
- * @brief Interface to IAuth client implementation.
- * @version $Id$
- */
-
-#ifndef INCLUDED_config_h
-#include "config.h"
-#endif
-
-struct IAuth;
-extern struct IAuth *iauth_active;
-
-struct IAuth *iauth_connect(char *host, unsigned short port, char *passwd, time_t reconnect, time_t timeout);
-int iauth_start_client(struct IAuth *iauth, struct Client *cptr);
-void iauth_exit_client(struct Client *cptr);
-
-void iauth_mark_closing(void);
-void iauth_close_unused(void);
-
-#endif
FEAT_HIS_STATS_x,
FEAT_HIS_STATS_y,
FEAT_HIS_STATS_z,
+ FEAT_HIS_STATS_IAUTH,
FEAT_HIS_WHOIS_SERVERNAME,
FEAT_HIS_WHOIS_IDLETIME,
FEAT_HIS_WHOIS_LOCALCHAN,
#endif
struct Client;
-
-/** Stores state of the DNS and RFC 1413 ident lookups for a client. */
-struct AuthRequest {
- struct AuthRequest* next; /**< linked list node ptr */
- struct AuthRequest* prev; /**< linked list node ptr */
- struct Client* client; /**< pointer to client struct for request */
- unsigned int flags; /**< current state of request */
- int fd; /**< file descriptor for auth queries */
- struct Socket socket; /**< socket descriptor for auth queries */
- struct Timer timeout; /**< timeout timer for auth queries */
-};
-
-/*
- * flag values for AuthRequest
- * NAMESPACE: AM_xxx - Authentication Module
- */
-#define AM_AUTH_CONNECTING 0x01 /**< waiting for ident connect to complete */
-#define AM_AUTH_PENDING 0x02 /**< ident connected, waiting for response */
-#define AM_DNS_PENDING 0x04 /**< dns request sent, waiting for response */
-
-#define AM_SOCKET 0x40 /**< socket structure not destroyed */
-#define AM_TIMEOUT 0x80 /**< timer structure not destroyed */
-
-/** If any of AM_FREE_MASK bits are set, operations are still in progress. */
-#define AM_FREE_MASK (AM_SOCKET | AM_TIMEOUT)
-
-#define SetDNSPending(x) ((x)->flags |= AM_DNS_PENDING)
-#define ClearDNSPending(x) ((x)->flags &= ~AM_DNS_PENDING)
-#define IsDNSPending(x) ((x)->flags & AM_DNS_PENDING)
-
-#define SetAuthConnect(x) ((x)->flags |= AM_AUTH_CONNECTING)
-#define ClearAuthConnect(x) ((x)->flags &= ~AM_AUTH_CONNECTING)
-#define IsAuthConnect(x) ((x)->flags & AM_AUTH_CONNECTING)
-
-#define SetAuthPending(x) ((x)->flags |= AM_AUTH_PENDING)
-#define ClearAuthPending(x) ((x)->flags &= AM_AUTH_PENDING)
-#define IsAuthPending(x) ((x)->flags & AM_AUTH_PENDING)
-
-#define ClearAuth(x) ((x)->flags &= ~(AM_AUTH_PENDING | AM_AUTH_CONNECTING))
-#define IsDoingAuth(x) ((x)->flags & (AM_AUTH_PENDING | AM_AUTH_CONNECTING))
+struct AuthRequest;
+struct StatDesc;
extern void start_auth(struct Client *);
-extern void read_auth_reply(struct AuthRequest* req);
-extern void send_auth_query(struct AuthRequest* req);
+extern int auth_set_pong(struct AuthRequest *auth, unsigned int cookie);
+extern int auth_set_user(struct AuthRequest *auth, const char *username, const char *userinfo);
+extern int auth_set_nick(struct AuthRequest *auth, const char *nickname);
+extern int auth_set_password(struct AuthRequest *auth, const char *password);
+extern int auth_cap_start(struct AuthRequest *auth);
+extern int auth_cap_done(struct AuthRequest *auth);
extern void destroy_auth_request(struct AuthRequest *req, int send_reports);
+extern int auth_spawn(int argc, char *argv[]);
+extern void auth_send_exit(struct Client *cptr);
+extern void auth_mark_closing(void);
+extern void auth_close_unused(void);
+extern void report_iauth_conf(struct Client *cptr, const struct StatDesc *sd, char *param);
+extern void report_iauth_stats(struct Client *cptr, const struct StatDesc *sd, char *param);
+
#endif /* INCLUDED_s_auth_h */
#define MATCH_SERVER 1 /**< flag for relay_masked_message (etc) to indicate the mask matches a server name */
#define MATCH_HOST 2 /**< flag for relay_masked_message (etc) to indicate the mask matches host name */
-#define COOKIE_VERIFIED 0xffffffff /**< value for cli_cookie() to show completion */
-
/** Formatter function for send_user_info().
* @param who Client being displayed.
* @param sptr Client requesting information.
*/
extern struct User* make_user(struct Client *cptr);
extern void free_user(struct User *user);
-extern int register_user(struct Client* cptr, struct Client* sptr,
- const char* nick, char* username);
+extern int register_user(struct Client* cptr, struct Client *sptr);
extern void user_count_memory(size_t* count_out, size_t* bytes_out);
hash.c \
ircd.c \
ircd_alloc.c \
- ircd_auth.c \
ircd_crypt.c \
ircd_events.c \
ircd_features.c \
ircd_alloc.o: ircd_alloc.c ../config.h ../include/ircd_alloc.h \
../include/ircd_log.h ../include/ircd_string.h ../include/ircd_chattr.h \
../include/s_debug.h ../config.h ../include/ircd_defs.h
-ircd_auth.o: ircd_auth.c ../config.h ../include/client.h \
- ../include/ircd_defs.h ../include/dbuf.h ../include/msgq.h \
- ../include/ircd_events.h ../config.h ../include/ircd_handler.h \
- ../include/res.h ../include/capab.h ../include/ircd_alloc.h \
- ../include/ircd_auth.h ../include/ircd_events.h \
- ../include/ircd_features.h ../include/ircd_log.h \
- ../include/ircd_osdep.h ../include/ircd_snprintf.h \
- ../include/ircd_string.h ../include/ircd_chattr.h ../include/ircd.h \
- ../include/struct.h ../include/msg.h ../include/msgq.h ../include/res.h \
- ../include/s_bsd.h ../include/s_debug.h ../include/s_misc.h \
- ../include/s_user.h ../include/send.h
ircd_crypt.o: ircd_crypt.c ../config.h ../include/ircd_crypt.h \
../include/ircd_alloc.h ../include/ircd_features.h \
../include/ircd_log.h ../include/ircd_string.h ../include/ircd_chattr.h \
../include/ircd_reslib.h ../include/ircd_defs.h ../include/fileio.h \
../include/ircd_string.h ../include/ircd_chattr.h
ircd_signal.o: ircd_signal.c ../config.h ../include/ircd.h \
- ../include/struct.h ../include/ircd_defs.h ../include/ircd_events.h \
- ../config.h ../include/ircd_log.h ../include/ircd_signal.h \
- ../include/s_conf.h ../include/client.h ../include/dbuf.h \
- ../include/msgq.h ../include/ircd_handler.h ../include/res.h \
- ../include/capab.h
+ ../include/struct.h ../include/ircd_defs.h ../include/ircd_alloc.h \
+ ../include/ircd_events.h ../config.h ../include/ircd_log.h \
+ ../include/ircd_signal.h ../include/s_conf.h ../include/client.h \
+ ../include/dbuf.h ../include/msgq.h ../include/ircd_handler.h \
+ ../include/res.h ../include/capab.h
ircd_snprintf.o: ircd_snprintf.c ../config.h ../include/client.h \
../include/ircd_defs.h ../include/dbuf.h ../include/msgq.h \
../include/ircd_events.h ../config.h ../include/ircd_handler.h \
../include/capab.h ../include/ircd.h ../include/struct.h \
../include/ircd_chattr.h ../include/ircd_log.h ../include/ircd_reply.h \
../include/ircd_snprintf.h ../include/ircd_string.h ../include/msg.h \
- ../include/numeric.h ../include/send.h ../include/s_user.h
+ ../include/numeric.h ../include/send.h ../include/s_auth.h \
+ ../include/s_user.h
m_clearmode.o: m_clearmode.c ../config.h ../include/client.h \
../include/ircd_defs.h ../include/dbuf.h ../include/msgq.h \
../include/ircd_events.h ../config.h ../include/ircd_handler.h \
../include/ircd_chattr.h ../include/ircd_features.h \
../include/ircd_log.h ../include/ircd_reply.h ../include/ircd_string.h \
../include/msg.h ../include/numeric.h ../include/numnicks.h \
- ../include/s_debug.h ../include/s_user.h ../include/send.h
+ ../include/s_debug.h ../include/s_user.h ../include/send.h \
+ ../include/sys.h
m_jupe.o: m_jupe.c ../config.h ../include/client.h ../include/ircd_defs.h \
../include/dbuf.h ../include/msgq.h ../include/ircd_events.h \
../config.h ../include/ircd_handler.h ../include/res.h \
../include/dbuf.h ../include/msgq.h ../include/ircd_events.h \
../config.h ../include/ircd_handler.h ../include/res.h \
../include/capab.h ../include/ircd_log.h ../include/ircd_reply.h \
- ../include/ircd_string.h ../include/ircd_chattr.h ../include/send.h
+ ../include/ircd_string.h ../include/ircd_chattr.h ../include/s_auth.h \
+ ../include/send.h
m_ping.o: m_ping.c ../config.h ../include/client.h ../include/ircd_defs.h \
../include/dbuf.h ../include/msgq.h ../include/ircd_events.h \
../config.h ../include/ircd_handler.h ../include/res.h \
../include/struct.h ../include/ircd_log.h ../include/ircd_reply.h \
../include/ircd_string.h ../include/ircd_chattr.h ../include/msg.h \
../include/numeric.h ../include/numnicks.h ../include/opercmds.h \
- ../include/s_user.h ../include/send.h
+ ../include/s_auth.h ../include/s_user.h ../include/send.h
m_privmsg.o: m_privmsg.c ../config.h ../include/client.h \
../include/ircd_defs.h ../include/dbuf.h ../include/msgq.h \
../include/ircd_events.h ../config.h ../include/ircd_handler.h \
../include/res.h ../include/capab.h ../include/ircd.h \
../include/struct.h ../include/ircd_chattr.h ../include/ircd_log.h \
../include/ircd_reply.h ../include/ircd_string.h ../include/numeric.h \
- ../include/numnicks.h ../include/s_debug.h ../include/s_misc.h \
- ../include/s_user.h ../include/send.h
+ ../include/numnicks.h ../include/s_auth.h ../include/s_debug.h \
+ ../include/s_misc.h ../include/s_user.h ../include/send.h
m_userhost.o: m_userhost.c ../config.h ../include/client.h \
../include/ircd_defs.h ../include/dbuf.h ../include/msgq.h \
../include/ircd_events.h ../config.h ../include/ircd_handler.h \
../include/res.h ../include/capab.h ../include/ircd_log.h \
../include/ircd_md5.h ../include/ircd_reply.h ../include/send.h
s_auth.o: s_auth.c ../config.h ../include/s_auth.h \
- ../include/ircd_events.h ../config.h ../include/client.h \
- ../include/ircd_defs.h ../include/dbuf.h ../include/msgq.h \
- ../include/ircd_handler.h ../include/res.h ../include/capab.h \
- ../include/IPcheck.h ../include/ircd.h ../include/struct.h \
- ../include/ircd_alloc.h ../include/ircd_chattr.h \
- ../include/ircd_events.h ../include/ircd_features.h \
- ../include/ircd_log.h ../include/ircd_osdep.h \
+ ../include/ircd_events.h ../config.h ../include/class.h \
+ ../include/client.h ../include/ircd_defs.h ../include/dbuf.h \
+ ../include/msgq.h ../include/ircd_handler.h ../include/res.h \
+ ../include/capab.h ../include/client.h ../include/IPcheck.h \
+ ../include/ircd.h ../include/struct.h ../include/ircd_alloc.h \
+ ../include/ircd_chattr.h ../include/ircd_events.h \
+ ../include/ircd_features.h ../include/ircd_log.h \
+ ../include/ircd_osdep.h ../include/ircd_reply.h \
../include/ircd_snprintf.h ../include/ircd_string.h ../include/list.h \
../include/numeric.h ../include/querycmds.h ../include/ircd_features.h \
- ../include/res.h ../include/s_bsd.h ../include/s_debug.h \
- ../include/s_misc.h ../include/send.h ../include/struct.h \
- ../include/sys.h
+ ../include/random.h ../include/res.h ../include/s_bsd.h \
+ ../include/s_conf.h ../include/s_debug.h ../include/s_misc.h \
+ ../include/s_user.h ../include/send.h
s_bsd.o: s_bsd.c ../config.h ../include/s_bsd.h ../include/client.h \
../include/ircd_defs.h ../include/dbuf.h ../include/msgq.h \
../include/ircd_events.h ../config.h ../include/ircd_handler.h \
../include/class.h ../include/client.h ../include/crule.h \
../include/ircd_features.h ../include/fileio.h ../include/gline.h \
../include/hash.h ../include/ircd.h ../include/struct.h \
- ../include/ircd_alloc.h ../include/ircd_auth.h ../include/ircd_chattr.h \
- ../include/ircd_log.h ../include/ircd_reply.h \
- ../include/ircd_snprintf.h ../include/ircd_string.h ../include/list.h \
- ../include/listener.h ../include/match.h ../include/motd.h \
- ../include/numeric.h ../include/numnicks.h ../include/opercmds.h \
- ../include/parse.h ../include/res.h ../include/s_bsd.h \
+ ../include/ircd_alloc.h ../include/ircd_chattr.h ../include/ircd_log.h \
+ ../include/ircd_reply.h ../include/ircd_snprintf.h \
+ ../include/ircd_string.h ../include/list.h ../include/listener.h \
+ ../include/match.h ../include/motd.h ../include/numeric.h \
+ ../include/numnicks.h ../include/opercmds.h ../include/parse.h \
+ ../include/res.h ../include/s_auth.h ../include/s_bsd.h \
../include/s_debug.h ../include/s_misc.h ../include/send.h \
../include/struct.h ../include/sys.h
s_debug.o: s_debug.c ../config.h ../include/s_debug.h ../config.h \
../config.h ../include/client.h ../include/dbuf.h ../include/msgq.h \
../include/ircd_events.h ../include/ircd_handler.h ../include/capab.h \
../include/hash.h ../include/ircd.h ../include/struct.h \
- ../include/ircd_alloc.h ../include/ircd_auth.h \
- ../include/ircd_features.h ../include/ircd_log.h \
- ../include/ircd_reply.h ../include/ircd_snprintf.h \
- ../include/ircd_string.h ../include/ircd_chattr.h ../include/list.h \
- ../include/match.h ../include/msg.h ../include/numeric.h \
- ../include/numnicks.h ../include/parse.h ../include/querycmds.h \
- ../include/ircd_features.h ../include/res.h ../include/s_bsd.h \
+ ../include/ircd_alloc.h ../include/ircd_features.h \
+ ../include/ircd_log.h ../include/ircd_reply.h \
+ ../include/ircd_snprintf.h ../include/ircd_string.h \
+ ../include/ircd_chattr.h ../include/list.h ../include/match.h \
+ ../include/msg.h ../include/numeric.h ../include/numnicks.h \
+ ../include/parse.h ../include/querycmds.h ../include/ircd_features.h \
+ ../include/res.h ../include/s_auth.h ../include/s_bsd.h \
../include/s_conf.h ../include/client.h ../include/s_debug.h \
../include/s_stats.h ../include/s_user.h ../include/send.h \
../include/struct.h ../include/sys.h ../include/uping.h \
../include/listener.h ../include/list.h ../include/match.h \
../include/motd.h ../include/msg.h ../include/msgq.h \
../include/numeric.h ../include/numnicks.h ../include/querycmds.h \
- ../include/ircd_features.h ../include/res.h ../include/s_bsd.h \
- ../include/s_conf.h ../include/s_debug.h ../include/s_misc.h \
- ../include/s_serv.h ../include/s_stats.h ../include/s_user.h \
- ../include/send.h ../include/struct.h ../include/userload.h
+ ../include/ircd_features.h ../include/res.h ../include/s_auth.h \
+ ../include/s_bsd.h ../include/s_conf.h ../include/s_debug.h \
+ ../include/s_misc.h ../include/s_serv.h ../include/s_stats.h \
+ ../include/s_user.h ../include/send.h ../include/struct.h \
+ ../include/userload.h
s_user.o: s_user.c ../config.h ../include/s_user.h ../include/IPcheck.h \
../include/channel.h ../include/ircd_defs.h ../include/res.h \
../config.h ../include/class.h ../include/client.h ../include/dbuf.h \
../include/msgq.h ../include/ircd_events.h ../include/ircd_handler.h \
../include/capab.h ../include/client.h ../include/hash.h \
../include/ircd.h ../include/struct.h ../include/ircd_alloc.h \
- ../include/ircd_auth.h ../include/ircd_chattr.h \
- ../include/ircd_features.h ../include/ircd_log.h \
- ../include/ircd_reply.h ../include/ircd_snprintf.h \
- ../include/ircd_string.h ../include/list.h ../include/match.h \
- ../include/motd.h ../include/msg.h ../include/msgq.h \
+ ../include/ircd_chattr.h ../include/ircd_features.h \
+ ../include/ircd_log.h ../include/ircd_reply.h \
+ ../include/ircd_snprintf.h ../include/ircd_string.h ../include/list.h \
+ ../include/match.h ../include/motd.h ../include/msg.h ../include/msgq.h \
../include/numeric.h ../include/numnicks.h ../include/parse.h \
../include/querycmds.h ../include/ircd_features.h ../include/random.h \
- ../include/s_bsd.h ../include/s_conf.h ../include/s_debug.h \
- ../include/s_misc.h ../include/s_serv.h ../include/send.h \
- ../include/struct.h ../include/supported.h ../include/channel.h \
- ../include/sys.h ../include/userload.h ../include/version.h \
- ../include/whowas.h ../include/handlers.h
+ ../include/s_auth.h ../include/s_bsd.h ../include/s_conf.h \
+ ../include/s_debug.h ../include/s_misc.h ../include/s_serv.h \
+ ../include/send.h ../include/struct.h ../include/supported.h \
+ ../include/channel.h ../include/sys.h ../include/userload.h \
+ ../include/version.h ../include/whowas.h ../include/handlers.h
send.o: send.c ../config.h ../include/send.h ../include/channel.h \
../include/ircd_defs.h ../include/res.h ../config.h ../include/class.h \
../include/client.h ../include/dbuf.h ../include/msgq.h \
../include/client.h ../include/crule.h ../include/ircd_features.h \
../include/fileio.h ../include/gline.h ../include/hash.h \
../include/ircd.h ../include/struct.h ../include/ircd_alloc.h \
- ../include/ircd_auth.h ../include/ircd_chattr.h ../include/ircd_log.h \
- ../include/ircd_reply.h ../include/ircd_snprintf.h \
- ../include/ircd_string.h ../include/list.h ../include/listener.h \
- ../include/match.h ../include/motd.h ../include/numeric.h \
- ../include/numnicks.h ../include/opercmds.h ../include/parse.h \
- ../include/res.h ../include/s_bsd.h ../include/s_debug.h \
- ../include/s_misc.h ../include/send.h ../include/struct.h \
- ../include/sys.h
+ ../include/ircd_chattr.h ../include/ircd_log.h ../include/ircd_reply.h \
+ ../include/ircd_snprintf.h ../include/ircd_string.h ../include/list.h \
+ ../include/listener.h ../include/match.h ../include/motd.h \
+ ../include/numeric.h ../include/numnicks.h ../include/opercmds.h \
+ ../include/parse.h ../include/res.h ../include/s_auth.h \
+ ../include/s_bsd.h ../include/s_debug.h ../include/s_misc.h \
+ ../include/send.h ../include/struct.h ../include/sys.h
+engine_devpoll.o: engine_devpoll.c ../config.h ../include/ircd_events.h \
+ ../config.h ../include/ircd.h ../include/struct.h \
+ ../include/ircd_defs.h ../include/ircd_alloc.h \
+ ../include/ircd_features.h ../include/ircd_log.h ../include/s_debug.h
engine_poll.o: engine_poll.c ../config.h ../include/ircd_events.h \
../config.h ../include/ircd.h ../include/struct.h \
../include/ircd_defs.h ../include/ircd_alloc.h ../include/ircd_log.h \
../include/s_debug.h
+engine_kqueue.o: engine_kqueue.c ../config.h ../include/ircd_events.h \
+ ../config.h ../include/ircd.h ../include/struct.h \
+ ../include/ircd_defs.h ../include/ircd_alloc.h \
+ ../include/ircd_features.h ../include/ircd_log.h ../include/s_debug.h
engine_select.o: engine_select.c ../config.h ../include/ircd_events.h \
../config.h ../include/ircd.h ../include/struct.h \
../include/ircd_defs.h ../include/ircd_log.h ../include/s_debug.h
+++ /dev/null
-/*
- * IRC - Internet Relay Chat, ircd/ircd_auth.c
- * Copyright 2004 Michael Poole <mdpoole@troilus.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
- * 02111-1307, USA.
- */
-/** @file
- * @brief IAuth client implementation for an IRC server.
- * @version $Id$
- */
-
-#include "config.h"
-#include "client.h"
-#include "ircd_alloc.h"
-#include "ircd_auth.h"
-#include "ircd_events.h"
-#include "ircd_features.h"
-#include "ircd_log.h"
-#include "ircd_osdep.h"
-#include "ircd_snprintf.h"
-#include "ircd_string.h"
-#include "ircd.h"
-#include "msg.h"
-#include "msgq.h"
-#include "res.h"
-#include "s_bsd.h"
-#include "s_debug.h"
-#include "s_misc.h"
-#include "s_user.h"
-#include "send.h"
-
-/* #include <assert.h> -- Now using assert in ircd_log.h */
-#include <errno.h>
-#include <netdb.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#ifdef HAVE_STDINT_H
-#include <stdint.h>
-#endif
-
-/** Describes state of a single pending IAuth request. */
-struct IAuthRequest {
- struct IAuthRequest *iar_prev; /**< previous request struct */
- struct IAuthRequest *iar_next; /**< next request struct */
- struct Client *iar_client; /**< client being authenticated */
- char iar_timed; /**< if non-zero, using parent i_request_timer */
-};
-
-/** Enumeration of IAuth connection flags. */
-enum IAuthFlag
-{
- IAUTH_BLOCKED, /**< socket buffer full */
- IAUTH_CONNECTED, /**< server greeting/handshake done */
- IAUTH_ABORT, /**< abort connection asap */
- IAUTH_ICLASS, /**< tell iauth about all local users */
- IAUTH_CLOSING, /**< candidate to be disposed */
- IAUTH_LAST_FLAG /**< total number of flags */
-};
-/** Declare a bitset structure indexed by IAuthFlag. */
-DECLARE_FLAGSET(IAuthFlags, IAUTH_LAST_FLAG);
-
-/** Describes state of an IAuth connection. */
-struct IAuth {
- struct IAuthRequest i_list_head; /**< doubly linked list of requests */
- struct MsgQ i_sendQ; /**< messages queued to send */
- struct Socket i_socket; /**< connection to server */
- struct Timer i_reconn_timer; /**< when to reconnect the connection */
- struct Timer i_request_timer; /**< when the current request times out */
- struct IAuthFlags i_flags; /**< connection state/status/flags */
- unsigned int i_recvM; /**< messages received */
- unsigned int i_sendM; /**< messages sent */
- unsigned int i_recvK; /**< kilobytes received */
- unsigned int i_sendK; /**< kilobytes sent */
- unsigned short i_recvB; /**< bytes received modulo 1024 */
- unsigned short i_sendB; /**< bytes sent modulo 1024 */
- time_t i_reconnect; /**< seconds to wait before reconnecting */
- time_t i_timeout; /**< seconds to wait for a request */
- unsigned int i_count; /**< characters used in i_buffer */
- char i_buffer[BUFSIZE+1]; /**< partial unprocessed line from server */
- char i_passwd[PASSWDLEN+1]; /**< password for connection */
- char i_host[HOSTLEN+1]; /**< iauth server hostname */
- struct irc_sockaddr i_addr; /**< iauth server ip address and port */
- struct IAuth *i_next; /**< next connection in list */
-};
-
-/** Return flags element of \a iauth. */
-#define i_flags(iauth) ((iauth)->i_flags)
-/** Return whether flag \a flag is set on \a iauth. */
-#define IAuthGet(iauth, flag) FlagHas(&i_flags(iauth), flag)
-/** Set flag \a flag on \a iauth. */
-#define IAuthSet(iauth, flag) FlagSet(&i_flags(iauth), flag)
-/** Clear flag \a flag from \a iauth. */
-#define IAuthClr(iauth, flag) FlagClr(&i_flags(iauth), flag)
-/** Get blocked state for \a iauth. */
-#define i_GetBlocked(iauth) IAuthGet(iauth, IAUTH_BLOCKED)
-/** Set blocked state for \a iauth. */
-#define i_SetBlocked(iauth) IAuthSet(iauth, IAUTH_BLOCKED)
-/** Clear blocked state for \a iauth. */
-#define i_ClrBlocked(iauth) IAuthClr(iauth, IAUTH_BLOCKED)
-/** Get connected flag for \a iauth. */
-#define i_GetConnected(iauth) IAuthGet(iauth, IAUTH_CONNECTED)
-/** Set connected flag for \a iauth. */
-#define i_SetConnected(iauth) IAuthSet(iauth, IAUTH_CONNECTED)
-/** Clear connected flag for \a iauth. */
-#define i_ClrConnected(iauth) IAuthClr(iauth, IAUTH_CONNECTED)
-/** Get abort flag for \a iauth. */
-#define i_GetAbort(iauth) IAuthGet(iauth, IAUTH_ABORT)
-/** Set abort flag for \a iauth. */
-#define i_SetAbort(iauth) IAuthSet(iauth, IAUTH_ABORT)
-/** Clear abort flag for \a iauth. */
-#define i_ClrAbort(iauth) IAuthClr(iauth, IAUTH_ABORT)
-/** Get IClass flag for \a iauth. */
-#define i_GetIClass(iauth) IAuthGet(iauth, IAUTH_ICLASS)
-/** Set IClass flag for \a iauth. */
-#define i_SetIClass(iauth) IAuthSet(iauth, IAUTH_ICLASS)
-/** Clear IClass flag for \a iauth. */
-#define i_ClrIClass(iauth) IAuthClr(iauth, IAUTH_ICLASS)
-/** Get closing flag for \a iauth. */
-#define i_GetClosing(iauth) IAuthGet(iauth, IAUTH_CLOSING)
-/** Set closing flag for \a iauth. */
-#define i_SetClosing(iauth) IAuthSet(iauth, IAUTH_CLOSING)
-/** Clear closing flag for \a iauth. */
-#define i_ClrClosing(iauth) IAuthClr(iauth, IAUTH_CLOSING)
-
-/** Return head of request linked list for \a iauth. */
-#define i_list_head(iauth) ((iauth)->i_list_head)
-/** Return socket event generator for \a iauth. */
-#define i_socket(iauth) ((iauth)->i_socket)
-/** Return reconnect timer for \a iauth. */
-#define i_reconn_timer(iauth) ((iauth)->i_reconn_timer)
-/** Return request timeout timer for \a iauth. */
-#define i_request_timer(iauth) ((iauth)->i_request_timer)
-/** Return DNS query for \a iauth. */
-#define i_query(iauth) ((iauth)->i_query)
-/** Return received bytes (modulo 1024) for \a iauth. */
-#define i_recvB(iauth) ((iauth)->i_recvB)
-/** Return received kilobytes (modulo 1024) for \a iauth. */
-#define i_recvK(iauth) ((iauth)->i_recvK)
-/** Return received megabytes for \a iauth. */
-#define i_recvM(iauth) ((iauth)->i_recvM)
-/** Return sent bytes (modulo 1024) for \a iauth. */
-#define i_sendB(iauth) ((iauth)->i_sendB)
-/** Return sent kilobytes (modulo 1024) for \a iauth. */
-#define i_sendK(iauth) ((iauth)->i_sendK)
-/** Return sent megabytes for \a iauth. */
-#define i_sendM(iauth) ((iauth)->i_sendM)
-/** Return outbound message queue for \a iauth. */
-#define i_sendQ(iauth) ((iauth)->i_sendQ)
-/** Return reconnection interval for \a iauth. */
-#define i_reconnect(iauth) ((iauth)->i_reconnect)
-/** Return request timeout interval for \a iauth. */
-#define i_timeout(iauth) ((iauth)->i_timeout)
-/** Return length of unprocessed message data for \a iauth. */
-#define i_count(iauth) ((iauth)->i_count)
-/** Return start of unprocessed message data for \a iauth. */
-#define i_buffer(iauth) ((iauth)->i_buffer)
-/** Return password we send for \a iauth. */
-#define i_passwd(iauth) ((iauth)->i_passwd)
-/** Return server hostname for \a iauth. */
-#define i_host(iauth) ((iauth)->i_host)
-/** Return address of IAuth server for \a iauth. */
-#define i_addr(iauth) ((iauth)->i_addr)
-/** Return server port for \a iauth. */
-#define i_port(iauth) ((iauth)->i_addr.port)
-/** Return next IAuth connection after \a iauth. */
-#define i_next(iauth) ((iauth)->i_next)
-
-/** Command table entry. */
-struct IAuthCmd {
- const char *iac_name; /**< Name of command. */
- void (*iac_func)(struct IAuth *iauth, int, char *[]); /**< Handler function. */
-};
-
-/** Active %IAuth connection(s). */
-struct IAuth *iauth_active;
-
-static void iauth_write(struct IAuth *iauth);
-static void iauth_reconnect(struct IAuth *iauth);
-static void iauth_disconnect(struct IAuth *iauth);
-static void iauth_sock_callback(struct Event *ev);
-static void iauth_send_request(struct IAuth *iauth, struct IAuthRequest *iar);
-static void iauth_dispose_request(struct IAuth *iauth, struct IAuthRequest *iar);
-static void iauth_cmd_doneauth(struct IAuth *iauth, int argc, char *argv[]);
-static void iauth_cmd_badauth(struct IAuth *iauth, int argc, char *argv[]);
-
-/** Table of responses we might get from the IAuth server. */
-static const struct IAuthCmd iauth_cmdtab[] = {
- { "DoneAuth", iauth_cmd_doneauth },
- { "BadAuth", iauth_cmd_badauth },
- { NULL, NULL }
-};
-
-/** Start (or update) a connection to an %IAuth server.
- * If a connection already exists for the specified server name and
- * port, update it with the other parameters; otherwise allocate a new
- * IAuth record.
- * @param[in] host %IAuth server hostname.
- * @param[in] port %IAuth server port.
- * @param[in] passwd Password to send.
- * @param[in] reconnect Reconnect interval.
- * @param[in] timeout Request timeout interval.
- * @return IAuth structure for that connection.
- */
-struct IAuth *iauth_connect(char *host, unsigned short port, char *passwd, time_t reconnect, time_t timeout)
-{
- struct IAuth *iauth;
-
- for (iauth = iauth_active; iauth; iauth = i_next(iauth)) {
- if (!ircd_strncmp(i_host(iauth), host, HOSTLEN)
- && (i_port(iauth) == port)) {
- i_ClrClosing(iauth);
- i_reconnect(iauth) = reconnect;
- if (t_active(&i_reconn_timer(iauth)) && (t_expire(&i_reconn_timer(iauth)) > CurrentTime + i_reconnect(iauth)))
- timer_chg(&i_reconn_timer(iauth), TT_RELATIVE, i_reconnect(iauth));
- break;
- }
- }
- if (NULL == iauth) {
- if (iauth_active && !i_GetClosing(iauth_active)) {
- log_write(LS_CONFIG, L_WARNING, 0, "Creating extra active IAuth connection to %s:%d.", host, port);
- }
- iauth = MyCalloc(1, sizeof(*iauth));
- i_list_head(iauth).iar_prev = &i_list_head(iauth);
- i_list_head(iauth).iar_next = &i_list_head(iauth);
- msgq_init(&i_sendQ(iauth));
- ircd_strncpy(i_host(iauth), host, HOSTLEN);
- memset(&i_addr(iauth), 0, sizeof(i_addr(iauth)));
- i_port(iauth) = port;
- iauth_active = iauth;
- timer_init(&i_reconn_timer(iauth));
- i_reconnect(iauth) = reconnect;
- iauth_reconnect(iauth);
- }
- if (passwd)
- ircd_strncpy(i_passwd(iauth), passwd, PASSWDLEN);
- else
- i_passwd(iauth)[0] = '\0';
- i_timeout(iauth) = timeout;
- i_SetIClass(iauth);
- return iauth;
-}
-
-/** Mark all %IAuth connections as closing. */
-void iauth_mark_closing(void)
-{
- struct IAuth *iauth;
- for (iauth = iauth_active; iauth; iauth = i_next(iauth))
- i_SetClosing(iauth);
-}
-
-/** Close a particular %IAuth connection.
- * @param[in] iauth %Connection to close.
- */
-void iauth_close(struct IAuth *iauth)
-{
- /* Figure out what to do with the closing connection's requests. */
- if (i_list_head(iauth).iar_next != &i_list_head(iauth)) {
- struct IAuthRequest *iar;
- if (iauth_active || i_next(iauth)) {
- /* If iauth_active != NULL, send requests to it; otherwise if
- * i_next(iauth) != NULL, we can hope it or some later
- * connection will be active.
- */
- struct IAuth *target = iauth_active ? iauth_active : i_next(iauth);
-
- /* Append iauth->i_list_head to end of target->i_list_head. */
- iar = i_list_head(iauth).iar_next;
- iar->iar_prev = i_list_head(target).iar_prev;
- i_list_head(target).iar_prev->iar_next = iar;
- iar = i_list_head(iauth).iar_prev;
- iar->iar_next = &i_list_head(target);
- i_list_head(target).iar_prev = iar;
-
- /* If the target is not closing, send the requests. */
- for (iar = i_list_head(iauth).iar_next;
- iar != &i_list_head(target);
- iar = iar->iar_next) {
- if (!i_GetClosing(target))
- iauth_send_request(target, iar);
- }
- } else {
- /* No active connections - approve the requests and drop them. */
- while ((iar = i_list_head(iauth).iar_next) != &i_list_head(iauth)) {
- struct Client *client = iar->iar_client;
- iauth_dispose_request(iauth, iar);
- register_user(client, client, cli_name(client), cli_username(client));
- }
- }
- }
- /* Make sure the connection closes with an empty request list. */
- i_list_head(iauth).iar_prev = &i_list_head(iauth);
- i_list_head(iauth).iar_next = &i_list_head(iauth);
- /* Cancel the timer, if it is active. */
- if (t_active(&i_reconn_timer(iauth)))
- timer_del(&i_reconn_timer(iauth));
- if (t_active(&i_request_timer(iauth)))
- timer_del(&i_request_timer(iauth));
- /* Disconnect from the server. */
- if (i_GetConnected(iauth))
- iauth_disconnect(iauth);
- /* Free memory. */
- MyFree(iauth);
-}
-
-/** Close all %IAuth connections marked as closing. */
-void iauth_close_unused(void)
-{
- struct IAuth *prev, *iauth, *next;
-
- for (prev = NULL, iauth = iauth_active; iauth; iauth = next) {
- next = i_next(iauth);
- if (i_GetClosing(iauth)) {
- /* Update iauth_active linked list. */
- if (prev)
- i_next(prev) = next;
- else
- iauth_active = next;
- /* Close and destroy the connection. */
- iauth_close(iauth);
- } else {
- prev = iauth;
- }
- }
-}
-
-/** Send a line to an %IAuth server.
- * @param[in] iauth %Connection to send on.
- * @param[in] format Format string for message.
- */
-static void iauth_send(struct IAuth *iauth, const char *format, ...)
-{
- va_list vl;
- struct MsgBuf *mb;
-
- va_start(vl, format);
- mb = msgq_vmake(0, format, vl);
- va_end(vl);
- msgq_add(&i_sendQ(iauth), mb, 0);
- msgq_clean(mb);
-}
-
-/** Report a protocol violation from the %IAuth server.
- * @param[in] iauth %Connection that experienced the violation.
- * @param[in] format Format string for message to operators.
- */
-static void iauth_protocol_violation(struct IAuth *iauth, const char *format, ...)
-{
- struct VarData vd;
- assert(iauth != 0);
- assert(format != 0);
- vd.vd_format = format;
- va_start(vd.vd_args, format);
- sendto_opmask_butone(NULL, SNO_CONNEXIT, "IAuth protocol violation: %v", &vd);
- va_end(vd.vd_args);
-}
-
-/** Send on-connect burst to an %IAuth server.
- * @param[in] iauth %Connection that has completed.
- */
-static void iauth_on_connect(struct IAuth *iauth)
-{
- struct IAuthRequest *iar;
- if (EmptyString(i_passwd(iauth)))
- iauth_send(iauth, "Server %s", cli_name(&me));
- else
- iauth_send(iauth, "Server %s %s", cli_name(&me), i_passwd(iauth));
- if (i_GetIClass(iauth)) {
- /* TODO: report local users to iauth */
- iauth_send(iauth, "EndUsers");
- }
- i_SetConnected(iauth);
- for (iar = i_list_head(iauth).iar_next;
- iar != &i_list_head(iauth);
- iar = iar->iar_next)
- iauth_send_request(iauth, iar);
- iauth_write(iauth);
-}
-
-/** Complete disconnection of an %IAuth connection.
- * @param[in] iauth %Connection to fully close.
- */
-static void iauth_disconnect(struct IAuth *iauth)
-{
- close(s_fd(&i_socket(iauth)));
- socket_del(&i_socket(iauth));
- i_ClrConnected(iauth);
-}
-
-/** DNS completion callback for an %IAuth connection.
- * @param[in] vptr Pointer to the IAuth struct.
- * @param[in] he DNS reply parameters.
- */
-static void iauth_dns_callback(void *vptr, const struct irc_in_addr *addr, const char *h_name)
-{
- struct IAuth *iauth = vptr;
- if (!addr) {
- log_write(LS_IAUTH, L_NOTICE, 0, "IAuth connection to %s failed: host lookup failed", i_host(iauth));
- } else {
- memcpy(&i_addr(iauth).addr, addr, sizeof(i_addr(iauth).addr));
- if (!irc_in_addr_valid(&i_addr(iauth).addr)) {
- log_write(LS_IAUTH, L_NOTICE, 0, "IAuth connection to %s failed: host came back as unresolved", i_host(iauth));
- return;
- }
- iauth_reconnect(iauth);
- }
-}
-
-/** Timer callback for reconnecting to %IAuth.
- * @param[in] ev Timer event for reconnect.
- */
-static void iauth_reconnect_ev(struct Event *ev)
-{
- if (ev_type(ev) == ET_EXPIRE)
- iauth_reconnect(t_data(ev_timer(ev)));
-}
-
-/** Schedule a reconnection for \a iauth.
- * @param[in] iauth %Connection that needs to be reconnected.
- */
-static void iauth_schedule_reconnect(struct IAuth *iauth)
-{
- struct Timer *timer;
- timer = &i_reconn_timer(iauth);
- if (t_onqueue(timer))
- timer_chg(timer, TT_RELATIVE, i_reconnect(iauth));
- else
- timer_add(&i_reconn_timer(iauth), iauth_reconnect_ev,
- iauth, TT_RELATIVE, i_reconnect(iauth));
-}
-
-/** Initiate a (re-)connection to \a iauth.
- * @param[in] iauth %Connection that should be initiated.
- */
-static void iauth_reconnect(struct IAuth *iauth)
-{
- struct irc_sockaddr *local;
- IOResult result;
- int fd;
-
- if (i_GetConnected(iauth)) {
- iauth_disconnect(iauth);
- iauth_schedule_reconnect(iauth);
- return;
- }
- log_write(LS_IAUTH, L_DEBUG, 0, "IAuth attempt connection to %s port %p.", i_host(iauth), i_port(iauth));
- if (!irc_in_addr_valid(&i_addr(iauth).addr)
- && !ircd_aton(&i_addr(iauth).addr, i_host(iauth))) {
- gethost_byname(i_host(iauth), iauth_dns_callback, iauth);
- return;
- }
- local = irc_in_addr_is_ipv4(&i_addr(iauth).addr) ? &VirtualHost_v4 : &VirtualHost_v6;
- fd = os_socket(local, SOCK_STREAM, "IAuth");
- if (fd < 0) {
- iauth_schedule_reconnect(iauth);
- return;
- }
- if (!os_set_sockbufs(fd, SERVER_TCP_WINDOW, SERVER_TCP_WINDOW)) {
- log_write(LS_IAUTH, L_WARNING, 0, "IAuth reconnect unable to set socket buffers: %s", strerror(errno));
- goto failure;
- }
- s_fd(&i_socket(iauth)) = fd;
- result = os_connect_nonb(fd, &i_addr(iauth));
- if (result == IO_FAILURE) {
- log_write(LS_IAUTH, L_NOTICE, 0, "IAuth reconnect unable to initiate connection: %s", strerror(errno));
- goto failure;
- }
- if (!socket_add(&i_socket(iauth), iauth_sock_callback, iauth,
- (result == IO_SUCCESS) ? SS_CONNECTED : SS_CONNECTING,
- SOCK_EVENT_READABLE | SOCK_EVENT_WRITABLE, fd)) {
- log_write(LS_IAUTH, L_WARNING, 0, "IAuth reconnect unable to add socket: %s", strerror(errno));
- goto failure;
- }
- return;
-failure:
- close(fd);
- i_ClrConnected(iauth);
- iauth_schedule_reconnect(iauth);
- return;
-}
-
-/** Read input from \a iauth.
- * Reads up to SERVER_TCP_WINDOW bytes per pass.
- * @param[in] iauth Readable connection.
- */
-static void iauth_read(struct IAuth *iauth)
-{
- char *src, *endp, *old_buffer, *argv[MAXPARA + 1];
- unsigned int length, argc, ii;
- char readbuf[SERVER_TCP_WINDOW];
-
- length = 0;
- if (IO_FAILURE == os_recv_nonb(s_fd(&i_socket(iauth)), readbuf, sizeof(readbuf), &length)
- || length == 0) {
- iauth_reconnect(iauth);
- return;
- }
- i_recvB(iauth) += length;
- if (i_recvB(iauth) > 1023) {
- i_recvK(iauth) += i_recvB(iauth) >> 10;
- i_recvB(iauth) &= 1023;
- }
- old_buffer = i_buffer(iauth);
- endp = old_buffer + i_count(iauth);
- for (src = readbuf; length > 0; --length) {
- *endp = *src++;
- if (IsEol(*endp)) {
- /* Skip blank lines. */
- if (endp == old_buffer)
- continue;
- /* NUL-terminate line and split parameters. */
- *endp = '\0';
- for (argc = 0, endp = old_buffer; *endp && (argc < MAXPARA); ) {
- while (*endp == ' ')
- *endp++ = '\0';
- if (*endp == '\0')
- break;
- if (*endp == ':')
- {
- argv[argc++] = endp + 1;
- break;
- }
- argv[argc++] = endp;
- for (; *endp && *endp != ' '; ++endp) ;
- }
- argv[argc] = NULL;
- /* Count line and reset endp to start of buffer. */
- i_recvM(iauth)++;
- endp = old_buffer;
- /* Look up command and try to dispatch. */
- if (argc > 0) {
- for (ii = 0; iauth_cmdtab[ii].iac_name; ++ii) {
- if (!ircd_strcmp(iauth_cmdtab[ii].iac_name, argv[0])) {
- iauth_cmdtab[ii].iac_func(iauth, argc, argv);
- if (i_GetAbort(iauth))
- iauth_disconnect(iauth);
- break;
- }
- }
- }
- }
- else if (endp < old_buffer + BUFSIZE)
- endp++;
- }
- i_count(iauth) = endp - old_buffer;
-}
-
-/** Send queued output to \a iauth.
- * @param[in] iauth Writable connection with queued data.
- */
-static void iauth_write(struct IAuth *iauth)
-{
- unsigned int bytes_tried, bytes_sent;
- IOResult iores;
-
- if (i_GetBlocked(iauth))
- return;
- while (MsgQLength(&i_sendQ(iauth)) > 0) {
- iores = os_sendv_nonb(s_fd(&i_socket(iauth)), &i_sendQ(iauth), &bytes_tried, &bytes_sent);
- switch (iores) {
- case IO_SUCCESS:
- msgq_delete(&i_sendQ(iauth), bytes_sent);
- i_sendB(iauth) += bytes_sent;
- if (i_sendB(iauth) > 1023) {
- i_sendK(iauth) += i_sendB(iauth) >> 10;
- i_sendB(iauth) &= 1023;
- }
- if (bytes_tried == bytes_sent)
- break;
- /* If bytes_sent < bytes_tried, fall through to IO_BLOCKED. */
- case IO_BLOCKED:
- i_SetBlocked(iauth);
- socket_events(&i_socket(iauth), SOCK_ACTION_ADD | SOCK_EVENT_WRITABLE);
- return;
- case IO_FAILURE:
- iauth_disconnect(iauth);
- return;
- }
- }
- /* We were able to flush all events, so remove notification. */
- socket_events(&i_socket(iauth), SOCK_ACTION_DEL | SOCK_EVENT_WRITABLE);
-}
-
-/** Handle socket activity for an %IAuth connection.
- * @param[in] ev &Socket event; the IAuth connection is the user data pointer for the socket.
- */
-static void iauth_sock_callback(struct Event *ev)
-{
- struct IAuth *iauth;
-
- assert(0 != ev_socket(ev));
- iauth = (struct IAuth*) s_data(ev_socket(ev));
- assert(0 != iauth);
-
- switch (ev_type(ev)) {
- case ET_CONNECT:
- socket_state(ev_socket(ev), SS_CONNECTED);
- iauth_on_connect(iauth);
- break;
- case ET_DESTROY:
- if (!i_GetClosing(iauth))
- iauth_schedule_reconnect(iauth);
- break;
- case ET_READ:
- iauth_read(iauth);
- break;
- case ET_WRITE:
- i_ClrBlocked(iauth);
- iauth_write(iauth);
- break;
- case ET_ERROR:
- log_write(LS_IAUTH, L_ERROR, 0, "IAuth socket error: %s", strerror(ev_data(ev)));
- /* and fall through to the ET_EOF case */
- case ET_EOF:
- iauth_disconnect(iauth);
- iauth_schedule_reconnect(iauth);
- break;
- default:
- assert(0 && "Unrecognized event type");
- break;
- }
-}
-
-/* Functions related to IAuthRequest structs */
-
-/** Handle timeout while waiting for a response.
- * @param[in] ev Timer event that expired.
- */
-static void iauth_request_ev(struct Event *ev)
-{
- /* TODO: this could probably be more intelligent */
- if (ev_type(ev) == ET_EXPIRE) {
- log_write(LS_IAUTH, L_NOTICE, 0, "IAuth request timed out; reconnecting");
- iauth_reconnect(t_data(ev_timer(ev)));
- }
-}
-
-/** Send a authorization request to an %IAuth server.
- * @param[in] iauth %Connection to send request on.
- * @param[in] iar Request to send.
- */
-static void iauth_send_request(struct IAuth *iauth, struct IAuthRequest *iar)
-{
- struct Client *client;
-
- /* If iauth is not connected, we must defer the request. */
- if (!i_GetConnected(iauth)) {
- Debug((DEBUG_SEND, "IAuth deferring request for %s because we are not connected.", cli_name(iar->iar_client)));
- return;
- }
-
- /* If no timed request, set up expiration timer. */
- if (!t_active(&i_request_timer(iauth))) {
- struct Timer *timer = timer_init(&i_request_timer(iauth));
- timer_add(timer, iauth_request_ev, iauth, TT_RELATIVE, i_timeout(iauth));
- iar->iar_timed = 1;
- } else
- iar->iar_timed = 0;
-
- /* Send the FullAuth request. */
- client = iar->iar_client;
- assert(iar->iar_client != NULL);
- iauth_send(iauth, "FullAuth %x %s %s %s %s %s :%s",
- client, cli_name(client), cli_username(client),
- cli_user(client)->host, cli_sock_ip(client),
- cli_passwd(client), cli_info(client));
-
- /* Write to the socket if we can. */
- iauth_write(iauth);
-}
-
-/** Start independent authorization check for a client.
- * @param[in] iauth %Connection to send request on.
- * @param[in] cptr Client to check.
- * @return Zero, or CPTR_KILLED in case of memory allocation failure.
- */
-int iauth_start_client(struct IAuth *iauth, struct Client *cptr)
-{
- struct IAuthRequest *iar;
-
- /* Allocate and initialize IAuthRequest struct. */
- if (!(iar = MyCalloc(1, sizeof(*iar))))
- return exit_client(cptr, cptr, &me, "IAuth memory allocation failed");
- cli_iauth(cptr) = iar;
- iar->iar_next = &i_list_head(iauth);
- iar->iar_prev = i_list_head(iauth).iar_prev;
- iar->iar_client = cptr;
- iar->iar_prev->iar_next = iar;
- iar->iar_next->iar_prev = iar;
-
- /* Send request. */
- iauth_send_request(iauth, iar);
-
- return 0;
-}
-
-/** Handle a client that is disconnecting.
- * If there is a pending %IAuth request for the client, close it.
- * @param[in] cptr Client that is disconnecting.
- */
-void iauth_exit_client(struct Client *cptr)
-{
- if (cli_iauth(cptr)) {
- iauth_dispose_request(iauth_active, cli_iauth(cptr));
- cli_iauth(cptr) = NULL;
- }
- if (iauth_active && i_GetConnected(iauth_active)) {
- iauth_send(iauth_active, "ExitUser %x", cptr);
- iauth_write(iauth_active);
- }
-}
-
-/** Find pending request with a particular ID.
- * @param[in] iauth %Connection context for the ID.
- * @param[in] id Identifier to look up.
- * @return IAuthRequest with that ID, or NULL.
- */
-static struct IAuthRequest *iauth_find_request(struct IAuth *iauth, char *id)
-{
- struct IAuthRequest *curr;
- struct Client *target;
- target = (struct Client*)strtoul(id, NULL, 16);
- for (curr = i_list_head(iauth).iar_next;
- curr != &i_list_head(iauth);
- curr = curr->iar_next) {
- assert(curr->iar_client != NULL);
- if (target == curr->iar_client)
- return curr;
- }
- return NULL;
-}
-
-/** Unlink and free a request.
- * @param[in] iauth Connection that owns the request.
- * @param[in] iar Request to free.
- */
-static void iauth_dispose_request(struct IAuth *iauth, struct IAuthRequest *iar)
-{
- assert(iar->iar_client != NULL);
- if (iar->iar_timed && t_active(&i_request_timer(iauth)))
- timer_del(&i_request_timer(iauth));
- cli_iauth(iar->iar_client) = NULL;
- iar->iar_prev->iar_next = iar->iar_next;
- iar->iar_next->iar_prev = iar->iar_prev;
- MyFree(iar);
-}
-
-/** Handle a DoneAuth response from %IAuth.
- * This means the client is authorized, so let them in.
- * @param[in] iauth Connection that sent the message.
- * @param[in] argc Argument count.
- * @param[in] argv Argument list.
- */
-static void iauth_cmd_doneauth(struct IAuth *iauth, int argc, char *argv[])
-{
- struct IAuthRequest *iar;
- struct Client *client;
- char *id;
- char *username;
- char *hostname;
- char *c_class;
- char *account;
-
- if (argc < 5) {
- iauth_protocol_violation(iauth, "Only %d parameters for DoneAuth (expected >=5)", argc);
- return;
- }
- id = argv[1];
- username = argv[2];
- hostname = argv[3];
- c_class = argv[4];
- account = (argc > 5) ? argv[5] : 0;
- iar = iauth_find_request(iauth, id);
- if (!iar) {
- iauth_protocol_violation(iauth, "Got unexpected DoneAuth for id %s", id);
- return;
- }
- client = iar->iar_client;
- ircd_strncpy(cli_username(client), username, USERLEN);
- ircd_strncpy(cli_user(client)->host, hostname, HOSTLEN);
- if (account) {
- ircd_strncpy(cli_user(client)->account, account, ACCOUNTLEN);
- SetAccount(client);
- }
- SetIAuthed(client);
- iauth_dispose_request(iauth, iar);
- register_user(client, client, cli_name(client), username);
-}
-
-/** Handle a BadAuth response from %IAuth.
- * This means the client is not authorized, so dump them.
- * @param[in] iauth Connection that sent the message.
- * @param[in] argc Argument count.
- * @param[in] argv Argument list.
- */
-static void iauth_cmd_badauth(struct IAuth *iauth, int argc, char *argv[])
-{
- struct IAuthRequest *iar;
- struct Client *client;
- char *id;
- char *reason;
-
- if (argc < 3) {
- iauth_protocol_violation(iauth, "Only %d parameters for BadAuth (expected >=3)", argc);
- return;
- }
- id = argv[1];
- reason = argv[2];
- if (EmptyString(reason)) {
- iauth_protocol_violation(iauth, "Empty BadAuth reason for id %s", id);
- return;
- }
- iar = iauth_find_request(iauth, id);
- if (!iar) {
- iauth_protocol_violation(iauth, "Got unexpected BadAuth for id %s", id);
- return;
- }
- client = iar->iar_client;
- iauth_dispose_request(iauth, iar);
- exit_client(client, client, &me, reason);
-}
F_B(HIS_STATS_x, 0, 1, 0),
F_B(HIS_STATS_y, 0, 1, 0),
F_B(HIS_STATS_z, 0, 1, 0),
+ F_B(HIS_STATS_IAUTH, 0, 1, 0),
F_B(HIS_WHOIS_SERVERNAME, 0, 1, 0),
F_B(HIS_WHOIS_IDLETIME, 0, 1, 0),
F_B(HIS_WHOIS_LOCALCHAN, 0, 1, 0),
TOKEN(USERMODE),
TOKEN(FAST),
TOKEN(AUTOCONNECT),
+ TOKEN(PROGRAM),
#undef TOKEN
{ "administrator", ADMIN },
{ "apass_opmode", TPRIV_APASS_OPMODE },
M(NONE), M(OLDSNO), M(SERVKILL), M(OPERKILL), M(HACK2),
M(HACK3), M(UNAUTH), M(TCPCOMMON), M(TOOMANY), M(HACK4),
M(GLINE), M(NETWORK), M(IPMISMATCH), M(THROTTLE), M(OLDREALOP),
- M(CONNEXIT), M(DEBUG),
+ M(CONNEXIT), M(DEBUG), M(AUTH),
#undef M
{ 0, 0 }
};
#include "hash.h"
#include "ircd.h"
#include "ircd_alloc.h"
-#include "ircd_auth.h"
#include "ircd_chattr.h"
#include "ircd_log.h"
#include "ircd_reply.h"
#include "opercmds.h"
#include "parse.h"
#include "res.h"
+#include "s_auth.h"
#include "s_bsd.h"
#include "s_conf.h"
#include "s_debug.h"
%token TIMEOUT
%token FAST
%token AUTOCONNECT
+%token PROGRAM
/* and now a lot of privileges... */
%token TPRIV_CHAN_LIMIT TPRIV_MODE_LCHAN TPRIV_DEOP_LCHAN TPRIV_WALK_LCHAN
%token TPRIV_LOCAL_KILL TPRIV_REHASH TPRIV_RESTART TPRIV_DIE
{
stringlist[0] = $1;
stringno = 1;
-} '=' stringlist ';';
-
-stringlist: QSTRING
-{
- stringlist[1] = $1;
- stringno = 2;
-} posextrastrings
-{
+} '=' stringlist ';' {
unsigned int ii;
feature_set(NULL, (const char * const *)stringlist, stringno);
for (ii = 0; ii < stringno; ++ii)
MyFree(stringlist[ii]);
};
-posextrastrings: /* empty */ | extrastrings;
-extrastrings: extrastrings extrastring | extrastring;
+
+stringlist: stringlist extrastring | extrastring;
extrastring: QSTRING
{
if (stringno < MAX_STRINGS)
smap->flags |= SMAP_FAST;
};
-iauthblock: IAUTH '{'
+iauthblock: IAUTH '{' iauthitems '}' ';'
{
- tconn = 60;
- tping = 60;
-} iauthitems '}' ';'
-{
- if (!host)
- parse_error("Missing host in iauth block");
- else if (!port)
- parse_error("Missing port in iauth block");
- else
- iauth_connect(host, port, pass, tconn, tping);
- MyFree(pass);
- MyFree(host);
- pass = host = NULL;
- port = tconn = tping = 0;
+ auth_spawn(stringno, stringlist);
+ while (stringno > 0)
+ MyFree(stringlist[stringno--]);
};
iauthitems: iauthitem iauthitems | iauthitem;
-iauthitem: iauthpass | iauthhost | iauthport | iauthconnfreq | iauthtimeout;
-iauthpass: PASS '=' QSTRING ';'
-{
- MyFree(pass);
- pass = $3;
-};
-iauthhost: HOST '=' QSTRING ';'
+iauthitem: iauthprogram;
+iauthprogram: PROGRAM '='
{
- MyFree(host);
- host = $3;
-};
-iauthport: PORT '=' NUMBER ';'
-{
- port = $3;
-};
-iauthconnfreq: CONNECTFREQ '=' timespec ';'
-{
- tconn = $3;
-};
-iauthtimeout: TIMEOUT '=' timespec ';'
-{
- tping = $3;
-};
+ while (stringno > 0)
+ MyFree(stringlist[stringno--]);
+} stringlist ';';
cli_connect(cptr) = con; /* set the connection and other fields */
cli_since(cptr) = cli_lasttime(cptr) = cli_firsttime(cptr) = CurrentTime;
cli_lastnick(cptr) = TStime();
- cli_unreg(cptr) = CLIREG_INIT;
} else
cli_connect(cptr) = cli_connect(from); /* use 'from's connection */
#include "msg.h"
#include "numeric.h"
#include "send.h"
+#include "s_auth.h"
#include "s_user.h"
#include <stdlib.h>
cap_ls(struct Client *sptr, const char *caplist)
{
if (IsUnknown(sptr)) /* registration hasn't completed; suspend it... */
- cli_unreg(sptr) |= CLIREG_CAP;
-
+ auth_cap_start(cli_auth(sptr));
return send_caplist(sptr, 0, 0, "LS"); /* send list of capabilities */
}
int neg;
if (IsUnknown(sptr)) /* registration hasn't completed; suspend it... */
- cli_unreg(sptr) |= CLIREG_CAP;
+ auth_cap_start(cli_auth(sptr));
memset(&set, 0, sizeof(set));
memset(&rem, 0, sizeof(rem));
if (!IsUnknown(sptr)) /* registration has completed... */
return 0; /* so just ignore the message... */
- cli_unreg(sptr) &= ~CLIREG_CAP; /* capability negotiation is now done... */
-
- if (!cli_unreg(sptr)) /* if client is now done... */
- return register_user(sptr, sptr, cli_name(sptr), cli_user(sptr)->username);
-
- return 0; /* Can't do registration yet... */
+ return auth_cap_done(cli_auth(sptr));
}
static int
#include "ircd_log.h"
#include "ircd_reply.h"
#include "ircd_string.h"
+#include "s_auth.h"
#include "send.h"
/* #include <assert.h> -- Now using assert in ircd_log.h */
*/
int mr_pass(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
{
- const char* password = parc > 1 ? parv[1] : 0;
+ char password[BUFSIZE];
+ int arg, len;
assert(0 != cptr);
assert(cptr == sptr);
assert(!IsRegistered(sptr));
+ /* Some clients (brokenly) send "PASS x y" rather than "PASS :x y"
+ * when the user enters "x y" as the password. Unsplit arguments to
+ * work around this.
+ */
+ for (arg = 1, len = 0; arg < parc; ++arg)
+ {
+ ircd_strncpy(password + len, parv[arg], sizeof(password) - len);
+ len += strlen(parv[arg]);
+ password[len++] = ' ';
+ }
+ password[--len] = '\0';
+
if (EmptyString(password))
return need_more_params(cptr, "PASS");
- /* TODO: For protocol negotiation */
-#if 0
- if (ircd_strcmp(password,"PROT")==0) {
- /* Do something here */
- }
-#endif
ircd_strncpy(cli_passwd(cptr), password, PASSWDLEN);
- return 0;
+ return cli_auth(cptr) ? auth_set_password(cli_auth(cptr), password) : 0;
}
#include "numeric.h"
#include "numnicks.h"
#include "opercmds.h"
+#include "s_auth.h"
#include "s_user.h"
#include "send.h"
ClrFlag(cptr, FLAG_PINGSENT);
cli_lasttime(cptr) = CurrentTime;
- /*
- * Check to see if this is a PONG :cookie reply from an
- * unregistered user. If so, process it. -record
- */
- if (0 != cli_cookie(sptr) && COOKIE_VERIFIED != cli_cookie(sptr)) {
- if (parc > 1 && cli_cookie(sptr) == atol(parv[parc - 1])) {
- cli_cookie(sptr) = COOKIE_VERIFIED;
- cli_unreg(sptr) &= ~CLIREG_COOKIE; /* cookie has been returned... */
- if (!cli_unreg(sptr)) /* no more registration tasks... */
- /*
- * NICK and USER OK
- */
- return register_user(cptr, sptr, cli_name(sptr), cli_user(sptr)->username);
- }
- else
- send_reply(sptr, SND_EXPLICIT | ERR_BADPING,
- ":To connect, type /QUOTE PONG %u", cli_cookie(sptr));
- }
- return 0;
+ return (parc > 1) ? auth_set_pong(cli_auth(sptr), atol(parv[parc - 1])) : 0;
}
/*
#include "ircd_string.h"
#include "numeric.h"
#include "numnicks.h"
+#include "s_auth.h"
#include "s_debug.h"
#include "s_misc.h"
#include "s_user.h"
{
char* username;
const char* info;
- struct User* user;
assert(0 != cptr);
assert(cptr == sptr);
info = (EmptyString(parv[4])) ? "No Info" : parv[4];
- user = make_user(cptr);
-
- user->server = &me;
- ircd_strncpy(cli_info(cptr), info, REALLEN);
-
- cli_unreg(sptr) &= ~CLIREG_USER; /* username now set */
-
- if (!cli_unreg(sptr)) {
- /*
- * NICK and PONG already received, now we have USER...
- */
- return register_user(cptr, sptr, cli_name(sptr), username);
- }
- else {
- ircd_strncpy(user->username, username, USERLEN);
- ircd_strncpy(user->host, cli_sockhost(cptr), HOSTLEN);
- }
- return 0;
+ return auth_set_user(cli_auth(cptr), username, info);
}
* released, the server does not know it exists and does not process
* any messages from it.
* --Bleep Thomas Helvey <tomh@inxpress.net>
+ *
+ * December 26, 2005 - Rewrite the flag handling and integrate that with
+ * an IRCnet-style IAuth protocol.
+ * -- Michael Poole
*/
/** @file
* @brief Implementation of DNS and ident lookups.
#include "config.h"
#include "s_auth.h"
+#include "class.h"
#include "client.h"
#include "IPcheck.h"
#include "ircd.h"
#include "ircd_features.h"
#include "ircd_log.h"
#include "ircd_osdep.h"
+#include "ircd_reply.h"
#include "ircd_snprintf.h"
#include "ircd_string.h"
#include "list.h"
#include "numeric.h"
#include "querycmds.h"
+#include "random.h"
#include "res.h"
#include "s_bsd.h"
+#include "s_conf.h"
#include "s_debug.h"
#include "s_misc.h"
+#include "s_user.h"
#include "send.h"
-#include "struct.h"
-#include "sys.h" /* TRUE bleah */
-#include <netdb.h> /* struct hostent */
+#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
-#include <errno.h>
#include <fcntl.h>
-/* #include <assert.h> -- Now using assert in ircd_log.h */
#include <sys/socket.h>
#include <sys/ioctl.h>
+/** Pending operations during registration. */
+enum AuthRequestFlag {
+ AR_AUTH_PENDING, /**< ident connecting or waiting for response */
+ AR_DNS_PENDING, /**< dns request sent, waiting for response */
+ AR_CAP_PENDING, /**< in middle of CAP negotiations */
+ AR_NEEDS_PONG, /**< user has not PONGed */
+ AR_NEEDS_USER, /**< user must send USER command */
+ AR_NEEDS_NICK, /**< user must send NICK command */
+ AR_LAST_SCAN = AR_NEEDS_NICK, /**< maximum flag to scan through */
+ AR_IAUTH_PENDING, /**< iauth request sent, waiting for response */
+ 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_NUM_FLAGS
+};
+
+DECLARE_FLAGSET(AuthRequestFlags, AR_NUM_FLAGS);
+
+/** Stores registration state of a client. */
+struct AuthRequest {
+ struct AuthRequest* next; /**< linked list node ptr */
+ struct AuthRequest* prev; /**< linked list node ptr */
+ struct Client* client; /**< pointer to client struct for request */
+ struct irc_sockaddr local; /**< local endpoint address */
+ struct irc_in_addr original; /**< original client IP address */
+ struct Socket socket; /**< socket descriptor for auth queries */
+ struct Timer timeout; /**< timeout timer for auth queries */
+ struct AuthRequestFlags flags; /**< current state of request */
+ unsigned int cookie; /**< cookie the user must PONG */
+ unsigned short port; /**< client's remote port number */
+};
+
/** Array of message text (with length) pairs for AUTH status
- * messages. Indexed using #ReportType. */
+ * messages. Indexed using #ReportType.
+ */
static struct {
const char* message;
unsigned int length;
#define MSG(STR) { STR, sizeof(STR) - 1 }
MSG("NOTICE AUTH :*** Looking up your hostname\r\n"),
MSG("NOTICE AUTH :*** Found your hostname\r\n"),
- MSG("NOTICE AUTH :*** Found your hostname, cached\r\n"),
MSG("NOTICE AUTH :*** Couldn't look up your hostname\r\n"),
MSG("NOTICE AUTH :*** Checking Ident\r\n"),
MSG("NOTICE AUTH :*** Got ident response\r\n"),
typedef enum {
REPORT_DO_DNS,
REPORT_FIN_DNS,
- REPORT_FIN_DNSC,
REPORT_FAIL_DNS,
REPORT_DO_ID,
REPORT_FIN_ID,
#define sendheader(c, r) \
send(cli_fd(c), HeaderMessages[(r)].message, HeaderMessages[(r)].length, 0)
-static void release_auth_client(struct Client* client);
-void free_auth_request(struct AuthRequest* auth);
-
-/** Verify that a hostname is valid, i.e., only contains characters
- * valid for a hostname and that a hostname is not too long.
- * @param host Hostname to check.
- * @param maxlen Maximum length of hostname, not including NUL terminator.
- * @return Non-zero if the hostname is valid.
- */
-static int
-auth_verify_hostname(const char *host, int maxlen)
+/** Enumeration of IAuth connection flags. */
+enum IAuthFlag
{
- int i;
-
- /* Walk through the host name */
- for (i = 0; host[i]; i++)
- /* If it's not a hostname character or if it's too long, return false */
- if (!IsHostChar(host[i]) || i >= maxlen)
- return 0;
-
- return 1; /* it's a valid hostname */
-}
+ IAUTH_BLOCKED, /**< socket buffer full */
+ IAUTH_CLOSING, /**< candidate to be disposed */
+ /* The following flags are controlled by iauth's "O" options command. */
+ IAUTH_ADDLINFO, /**< Send additional info
+ * (password and username). */
+ IAUTH_FIRST_OPTION = IAUTH_ADDLINFO, /**< First flag that is a policy option. */
+ IAUTH_REQUIRED, /**< IAuth completion required for registration. */
+ IAUTH_TIMEOUT, /**< Refuse new connections if IAuth is behind. */
+ IAUTH_EXTRAWAIT, /**< Give IAuth extra time to answer. */
+ IAUTH_UNDERNET, /**< Enable Undernet extensions. */
+ IAUTH_LAST_FLAG /**< total number of flags */
+};
+/** Declare a bitset structure indexed by IAuthFlag. */
+DECLARE_FLAGSET(IAuthFlags, IAUTH_LAST_FLAG);
+
+/** Describes state of an IAuth connection. */
+struct IAuth {
+ struct MsgQ i_sendQ; /**< messages queued to send */
+ struct Socket i_socket; /**< main socket to iauth */
+ struct Socket i_stderr; /**< error socket for iauth */
+ struct IAuthFlags i_flags; /**< connection state/status/flags */
+ uint64_t i_recvB; /**< bytes received */
+ uint64_t i_sendB; /**< bytes sent */
+ time_t started; /**< time that this instance was started */
+ unsigned int i_recvM; /**< messages received */
+ unsigned int i_sendM; /**< messages sent */
+ unsigned int i_count; /**< characters used in i_buffer */
+ unsigned int i_errcount; /**< characters used in i_errbuf */
+ int i_debug; /**< debug level */
+ char i_buffer[BUFSIZE+1]; /**< partial unprocessed line from server */
+ char i_errbuf[BUFSIZE+1]; /**< partial unprocessed error line */
+ char *i_version; /**< iauth version string */
+ struct SLink *i_config; /**< configuration string list */
+ struct SLink *i_stats; /**< statistics string list */
+ char **i_argv; /**< argument list */
+};
-/** Timeout a given auth request.
- * @param ev A timer event whose associated data is the expired struct
- * AuthRequest.
+/** Return whether flag \a flag is set on \a iauth. */
+#define IAuthHas(iauth, flag) FlagHas(&iauth->i_flags, flag)
+/** Set flag \a flag on \a iauth. */
+#define IAuthSet(iauth, flag) FlagSet(&iauth->i_flags, flag)
+/** Clear flag \a flag from \a iauth. */
+#define IAuthClr(iauth, flag) FlagClr(&iauth->i_flags, flag)
+/** Get connected flag for \a iauth. */
+#define i_GetConnected(iauth) (s_fd(i_socket(iauth)) > -1)
+
+/** Return socket event generator for \a iauth. */
+#define i_socket(iauth) (&(iauth)->i_socket)
+/** Return stderr socket for \a iauth. */
+#define i_stderr(iauth) (&(iauth)->i_stderr)
+/** Return outbound message queue for \a iauth. */
+#define i_sendQ(iauth) (&(iauth)->i_sendQ)
+/** Return debug level for \a iauth. */
+#define i_debug(iauth) (iauth->i_debug)
+
+/** Active instance of IAuth. */
+struct IAuth *iauth;
+
+static void iauth_sock_callback(struct Event *ev);
+static void iauth_stderr_callback(struct Event *ev);
+static int sendto_iauth(struct Client *cptr, const char *format, ...);
+static int preregister_user(struct Client *cptr);
+typedef int (*iauth_cmd_handler)(struct IAuth *iauth, struct Client *cli, char *params);
+
+/** 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.
*/
-static void auth_timeout_callback(struct Event* ev)
+static int auth_set_username(struct AuthRequest *auth)
{
- struct AuthRequest* auth;
-
- assert(0 != ev_timer(ev));
- assert(0 != t_data(ev_timer(ev)));
-
- auth = (struct AuthRequest*) t_data(ev_timer(ev));
-
- if (ev_type(ev) == ET_DESTROY) { /* being destroyed */
- auth->flags &= ~AM_TIMEOUT;
-
- if (!(auth->flags & AM_FREE_MASK)) {
- Debug((DEBUG_LIST, "Freeing auth from timeout callback; %p [%p]", auth,
- ev_timer(ev)));
- MyFree(auth); /* done with it, finally */
- }
- } else {
- assert(ev_type(ev) == ET_EXPIRE);
-
- destroy_auth_request(auth, 1);
+ struct Client *sptr = auth->client;
+ 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;
+ short digitgroups = 0;
+ char ch;
+ char last;
+
+ if (FlagHas(&auth->flags, AR_IAUTH_USERNAME))
+ {
+ ircd_strncpy(cli_user(sptr)->username, cli_username(sptr), USERLEN);
}
-}
-
-/** Handle socket I/O activity.
- * @param ev A socket event whos associated data is the active struct
- * AuthRequest.
- */
-static void auth_sock_callback(struct Event* ev)
-{
- struct AuthRequest* auth;
-
- assert(0 != ev_socket(ev));
- assert(0 != s_data(ev_socket(ev)));
-
- auth = (struct AuthRequest*) s_data(ev_socket(ev));
-
- switch (ev_type(ev)) {
- case ET_DESTROY: /* being destroyed */
- auth->flags &= ~AM_SOCKET;
-
- if (!(auth->flags & AM_FREE_MASK)) {
- Debug((DEBUG_LIST, "Freeing auth from sock callback; %p [%p]", auth,
- ev_socket(ev)));
- MyFree(auth); /* done with it finally */
+ else
+ {
+ /* 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;
}
- break;
-
- case ET_CONNECT: /* socket connection completed */
- Debug((DEBUG_LIST, "Connection completed for auth %p [%p]; sending query",
- auth, ev_socket(ev)));
- socket_state(&auth->socket, SS_CONNECTED);
- send_auth_query(auth);
- break;
-
- case ET_READ: /* socket is readable */
- case ET_EOF: /* end of file on socket */
- case ET_ERROR: /* error on socket */
- Debug((DEBUG_LIST, "Auth socket %p [%p] readable", auth, ev_socket(ev)));
- read_auth_reply(auth);
- break;
+ while (last && !IsCntrl(last) && rlen--)
+ {
+ ch = *s++;
+ *d++ = IsUserChar(last) ? last : '_';
+ last = (ch != '~') ? ch : '_';
+ }
+ *d = 0;
+ }
- default:
- assert(0 && "Unrecognized event in auth_socket_callback().");
- break;
+ /* If username is empty or just ~, reject. */
+ if ((user->username[0] == '\0')
+ || ((user->username[0] == '~') && (user->username[1] == '\0')))
+ return exit_client(sptr, sptr, &me, "USER: Bogus userid.");
+
+ /* Check for K- or G-line. */
+ killreason = find_kill(sptr);
+ if (killreason) {
+ ServerStats->is_ref++;
+ return exit_client(sptr, sptr, &me,
+ (killreason == -1 ? "K-lined" : "G-lined"));
}
-}
-/** Stop an auth request completely.
- * @param auth The struct AuthRequest to cancel.
- * @param send_reports If non-zero, report the failure to the user and
- * resolver log.
- */
-void destroy_auth_request(struct AuthRequest* auth, int send_reports)
-{
- if (IsDoingAuth(auth)) {
- if (-1 < auth->fd) {
- close(auth->fd);
- auth->fd = -1;
- socket_del(&auth->socket);
+ if (!FlagHas(&auth->flags, AR_IAUTH_FUSERNAME))
+ {
+ /* Check for mixed case usernames, meaning probably hacked. Jon2 3-94
+ * Explanations of rules moved to where it is checked Entrope 2-06
+ */
+ s = d = user->username + (user->username[0] == '~');
+ for (last = '\0';
+ (ch = *d++) != '\0';
+ pos++, last = ch)
+ {
+ if (IsLower(ch))
+ {
+ lower++;
+ }
+ else if (IsUpper(ch))
+ {
+ upper++;
+ /* Accept caps as leading if we haven't seen lower case or digits yet. */
+ if ((leadcaps || pos == 0) && !lower && !digits)
+ leadcaps++;
+ }
+ else if (IsDigit(ch))
+ {
+ digits++;
+ if (pos == 0 || !IsDigit(last))
+ {
+ digitgroups++;
+ /* If more than two groups of digits, reject. */
+ if (digitgroups > 2)
+ goto badid;
+ }
+ }
+ else if (ch == '-' || ch == '_' || ch == '.')
+ {
+ other++;
+ /* If -_. exist at start, consecutively, or more than twice, reject. */
+ if (pos == 0 || last == '-' || last == '_' || last == '.' || other > 2)
+ goto badid;
+ }
+ else /* All other punctuation is rejected. */
+ goto badid;
}
- if (send_reports && IsUserPort(auth->client))
- sendheader(auth->client, REPORT_FAIL_ID);
+ /* If mixed case, first must be capital, but no more than three;
+ * but if three capitals, they must all be leading. */
+ if (lower && upper && (!leadcaps || leadcaps > 3 ||
+ (upper > 2 && upper > leadcaps)))
+ goto badid;
+ /* If two different groups of digits, one must be either at the
+ * start or end. */
+ if (digitgroups == 2 && !(IsDigit(s[0]) || IsDigit(ch)))
+ goto badid;
+ /* Must have at least one letter. */
+ if (!lower && !upper)
+ goto badid;
+ /* Final character must not be punctuation. */
+ if (!IsAlnum(ch))
+ goto badid;
}
- if (IsDNSPending(auth)) {
- delete_resolver_queries(auth);
- if (send_reports && IsUserPort(auth->client))
- sendheader(auth->client, REPORT_FAIL_DNS);
- }
+ return 0;
- if (send_reports) {
- log_write(LS_RESOLVER, L_INFO, 0, "DNS/AUTH timeout %s",
- get_client_name(auth->client, HIDE_IP));
- release_auth_client(auth->client);
- }
+badid:
+ /* If we confirmed their username, and it is what they claimed,
+ * accept it. */
+ if (IsIdented(sptr) && !strcmp(cli_username(sptr), user->username))
+ return 0;
- free_auth_request(auth);
+ ServerStats->is_ref++;
+ send_reply(sptr, SND_EXPLICIT | ERR_INVALIDUSERNAME,
+ ":Your username is invalid.");
+ send_reply(sptr, SND_EXPLICIT | ERR_INVALIDUSERNAME,
+ ":Connect with your real username, in lowercase.");
+ send_reply(sptr, SND_EXPLICIT | ERR_INVALIDUSERNAME,
+ ":If your mail address were foo@bar.com, your username "
+ "would be foo.");
+ return exit_client(sptr, sptr, &me, "USER: Bad username");
}
-/** Allocate a new auth request.
- * @param client The client being looked up.
- * @return The newly allocated auth request.
+/** Check whether an authorization request is complete.
+ * This means that no flags from 0 to #AR_LAST_SCAN are set on \a auth.
+ * If #AR_IAUTH_PENDING is set, optionally go into "hurry" state. If
+ * 0 through #AR_LAST_SCAN and #AR_IAUTH_PENDING are all clear,
+ * destroy \a auth, clear the password, set the username, and register
+ * the client.
+ * @param[in] auth Authorization request to check.
+ * @param[in] send_reports Passed to destroy_auth_request() if \a auth
+ * is complete.
+ * @return Zero if client is kept, CPTR_KILLED if client rejected.
*/
-static struct AuthRequest* make_auth_request(struct Client* client)
+static int check_auth_finished(struct AuthRequest *auth, int send_reports)
{
- struct AuthRequest* auth =
- (struct AuthRequest*) MyMalloc(sizeof(struct AuthRequest));
- assert(0 != auth);
- memset(auth, 0, sizeof(struct AuthRequest));
- auth->flags = AM_TIMEOUT;
- auth->fd = -1;
- auth->client = client;
- cli_auth(client) = auth;
- timer_add(timer_init(&auth->timeout), auth_timeout_callback, (void*) auth,
- TT_RELATIVE, feature_int(FEAT_AUTH_TIMEOUT));
- return auth;
-}
+ enum AuthRequestFlag flag;
+ int res;
-/** Clean up auth request allocations (event loop objects, etc).
- * @param auth The request to clean up.
- */
-void free_auth_request(struct AuthRequest* auth)
-{
- if (-1 < auth->fd) {
- close(auth->fd);
- Debug((DEBUG_LIST, "Deleting auth socket for %p", auth->client));
- socket_del(&auth->socket);
- }
- Debug((DEBUG_LIST, "Deleting auth timeout timer for %p", auth->client));
- timer_del(&auth->timeout);
-}
+ /* Check non-iauth registration blocking flags. */
+ for (flag = 0; flag <= AR_LAST_SCAN; ++flag)
+ if (FlagHas(&auth->flags, flag))
+ {
+ Debug((DEBUG_INFO, "Auth %p [%d] still has flag %d", auth,
+ cli_fd(auth->client), flag));
+ return 0;
+ }
-/** Release auth client from auth system. This adds the client into
- * the local client lists so it can be read by the main io processing
- * loop.
- * @param client The client to release.
- */
-static void release_auth_client(struct Client* client)
-{
- assert(0 != client);
- cli_auth(client) = 0;
- cli_lasttime(client) = cli_since(client) = CurrentTime;
- if (cli_fd(client) > HighestFd)
- HighestFd = cli_fd(client);
- LocalClientArray[cli_fd(client)] = client;
+ /* Check if iauth is done. */
+ if (FlagHas(&auth->flags, AR_IAUTH_PENDING))
+ {
+ /* Switch auth request to hurry-up state. */
+ if (!FlagHas(&auth->flags, AR_IAUTH_HURRY))
+ {
+ struct ConfItem* aconf;
+
+ /* Set "hurry" flag in auth request. */
+ FlagSet(&auth->flags, AR_IAUTH_HURRY);
+
+ /* Do preliminary assignment to connection class. */
+ if (preregister_user(auth->client))
+ return CPTR_KILLED;
+
+ /* Check password now (to avoid challenge/response conflicts). */
+ aconf = cli_confs(auth->client)->value.aconf;
+ if (!EmptyString(aconf->passwd)
+ && strcmp(cli_passwd(auth->client), aconf->passwd))
+ {
+ ServerStats->is_ref++;
+ send_reply(auth->client, ERR_PASSWDMISMATCH);
+ return exit_client(auth->client, auth->client, &me, "Bad Password");
+ }
- add_client_to_list(client);
- socket_events(&(cli_socket(client)), SOCK_ACTION_SET | SOCK_EVENT_READABLE);
- Debug((DEBUG_INFO, "Auth: release_auth_client %s@%s[%s]",
- cli_username(client), cli_sockhost(client), cli_sock_ip(client)));
-}
+ /* If iauth wants it, send notification. */
+ if (IAuthHas(iauth, IAUTH_UNDERNET))
+ sendto_iauth(auth->client, "H %s", ConfClass(aconf));
-/** Terminate a client's connection due to auth failure.
- * @param auth The client to terminate.
- */
-static void auth_kill_client(struct AuthRequest* auth)
-{
- assert(0 != auth);
+ /* If iauth wants it, give client more time. */
+ if (IAuthHas(iauth, IAUTH_EXTRAWAIT))
+ timer_chg(&auth->timeout, TT_RELATIVE, feature_int(FEAT_AUTH_TIMEOUT));
+ }
- if (IsDNSPending(auth))
- delete_resolver_queries(auth);
- IPcheck_disconnect(auth->client);
- Count_unknowndisconnects(UserStats);
- cli_auth(auth->client) = 0;
- free_client(auth->client);
- free_auth_request(auth);
+ Debug((DEBUG_INFO, "Auth %p [%d] still has flag %d", auth,
+ cli_fd(auth->client), AR_IAUTH_PENDING));
+ return 0;
+ }
+
+ destroy_auth_request(auth, send_reports);
+ if (!IsUserPort(auth->client))
+ return 0;
+ memset(cli_passwd(auth->client), 0, sizeof(cli_passwd(auth->client)));
+ res = auth_set_username(auth);
+ if (res == 0)
+ res = register_user(auth->client, auth->client);
+ return res;
}
-/** Handle a complete DNS lookup. Send the client on it's way to a
- * connection completion, regardless of success or failure -- unless
- * there was a mismatch and KILL_IPMISMATCH is set.
- * @param vptr The pending struct AuthRequest.
- * @param hp Pointer to the DNS reply (or NULL, if lookup failed).
+/** Verify that a hostname is valid, i.e., only contains characters
+ * valid for a hostname and that a hostname is not too long.
+ * @param host Hostname to check.
+ * @param maxlen Maximum length of hostname, not including NUL terminator.
+ * @return Non-zero if the hostname is valid.
*/
-static void auth_dns_callback(void* vptr, const struct irc_in_addr *addr, const char *h_name)
+static int
+auth_verify_hostname(const char *host, int maxlen)
{
- struct AuthRequest* auth = (struct AuthRequest*) vptr;
- assert(auth);
- /*
- * need to do this here so auth_kill_client doesn't
- * try have the resolver delete the query it's about
- * to delete anyways. --Bleep
- */
- ClearDNSPending(auth);
+ int i;
- if (addr) {
- /*
- * Verify that the host to ip mapping is correct both ways and that
- * the ip#(s) for the socket is listed for the host.
- */
- if (irc_in_addr_cmp(addr, &cli_ip(auth->client))) {
- if (IsUserPort(auth->client))
- sendheader(auth->client, REPORT_IP_MISMATCH);
- sendto_opmask_butone(0, SNO_IPMISMATCH, "IP# Mismatch: %s != %s[%s]",
- cli_sock_ip(auth->client), h_name,
- ircd_ntoa(addr));
- if (feature_bool(FEAT_KILL_IPMISMATCH)) {
- auth_kill_client(auth);
- return;
- }
- }
- else if (!auth_verify_hostname(h_name, HOSTLEN))
- {
- if (IsUserPort(auth->client))
- sendheader(auth->client, REPORT_INVAL_DNS);
- }
- else
- {
- ircd_strncpy(cli_sockhost(auth->client), h_name, HOSTLEN);
- if (IsUserPort(auth->client))
- sendheader(auth->client, REPORT_FIN_DNS);
- }
- }
- else {
- /*
- * this should have already been done by s_bsd.c in add_connection
- *
- * strcpy(auth->client->sockhost, auth->client->sock_ip);
- */
- if (IsUserPort(auth->client))
- sendheader(auth->client, REPORT_FAIL_DNS);
- }
- if (!IsDoingAuth(auth)) {
- release_auth_client(auth->client);
- free_auth_request(auth);
- }
+ /* Walk through the host name */
+ for (i = 0; host[i]; i++)
+ /* If it's not a hostname character or if it's too long, return false */
+ if (!IsHostChar(host[i]) || i >= maxlen)
+ return 0;
+
+ return 1; /* it's a valid hostname */
}
-/** Handle auth send errors.
- * @param auth The request that saw the failure.
- * @param kill If non-zero, a critical error; close the client's connection.
+/** Assign a client to a connection class.
+ * @param[in] cptr Client to assign to a class.
+ * @return Zero if client is kept, CPTR_KILLED if rejected.
*/
-static void auth_error(struct AuthRequest* auth, int kill)
+static int preregister_user(struct Client *cptr)
{
- ++ServerStats->is_abad;
-
- assert(0 != auth);
- close(auth->fd);
- auth->fd = -1;
- socket_del(&auth->socket);
-
- if (IsUserPort(auth->client))
- sendheader(auth->client, REPORT_FAIL_ID);
-
- if (kill) {
- /*
- * we can't read the client info from the client socket,
- * close the client connection and free the client
- * Need to do this before we ClearAuth(auth) so we know
- * which list to remove the query from. --Bleep
- */
- auth_kill_client(auth);
- return;
- }
+ static time_t last_too_many1;
+ static time_t last_too_many2;
- ClearAuth(auth);
+ ircd_strncpy(cli_user(cptr)->host, cli_sockhost(cptr), HOSTLEN);
+ ircd_strncpy(cli_user(cptr)->realhost, cli_sockhost(cptr), HOSTLEN);
- if (!IsDNSPending(auth)) {
- release_auth_client(auth->client);
- free_auth_request(auth);
+ switch (conf_check_client(cptr))
+ {
+ case ACR_OK:
+ break;
+ case ACR_NO_AUTHORIZATION:
+ sendto_opmask_butone(0, SNO_UNAUTH, "Unauthorized connection from %s.",
+ get_client_name(cptr, HIDE_IP));
+ ++ServerStats->is_ref;
+ return exit_client(cptr, cptr, &me,
+ "No Authorization - use another server");
+ case ACR_TOO_MANY_IN_CLASS:
+ sendto_opmask_butone_ratelimited(0, SNO_TOOMANY, &last_too_many1,
+ "Too many connections in class %s for %s.",
+ get_client_class(cptr),
+ get_client_name(cptr, SHOW_IP));
+ ++ServerStats->is_ref;
+ return exit_client(cptr, cptr, &me,
+ "Sorry, your connection class is full - try "
+ "again later or try another server");
+ case ACR_TOO_MANY_FROM_IP:
+ sendto_opmask_butone_ratelimited(0, SNO_TOOMANY, &last_too_many2,
+ "Too many connections from same IP for %s.",
+ get_client_name(cptr, SHOW_IP));
+ ++ServerStats->is_ref;
+ return exit_client(cptr, cptr, &me,
+ "Too many connections from your host");
+ case ACR_ALREADY_AUTHORIZED:
+ /* Can this ever happen? */
+ case ACR_BAD_SOCKET:
+ ++ServerStats->is_ref;
+ IPcheck_connect_fail(cptr);
+ return exit_client(cptr, cptr, &me, "Unknown error -- Try again");
}
+ return 0;
}
-/** Flag the client to show an attempt to contact the ident server on
- * the client's host. Should the connect or any later phase of the
- * identifying process fail, it is aborted and the user is given a
- * username of "unknown".
- * @param auth The request for which to start the ident lookup.
- * @return Non-zero on success; zero if unable to start the lookup.
+/** Send the ident server a query giving "theirport , ourport". The
+ * write is only attempted *once* so it is deemed to be a fail if the
+ * entire write doesn't write all the data given. This shouldn't be a
+ * problem since the socket should have a write buffer far greater
+ * than this message to store it in should problems arise. -avalon
+ * @param[in] auth The request to send.
*/
-static int start_auth_query(struct AuthRequest* auth)
+static void send_auth_query(struct AuthRequest* auth)
{
- struct irc_sockaddr remote_addr;
- struct irc_sockaddr local_addr;
- int fd;
- IOResult result;
+ char authbuf[32];
+ unsigned int count;
assert(0 != auth);
- assert(0 != auth->client);
- /*
- * get the local address of the client and bind to that to
- * make the auth request. This used to be done only for
- * ifdef VIRTUAL_HOST, but needs to be done for all clients
- * since the ident request must originate from that same address--
- * and machines with multiple IP addresses are common now
- */
- memset(&local_addr, 0, sizeof(local_addr));
- os_get_sockname(cli_fd(auth->client), &local_addr);
- local_addr.port = 0;
- fd = os_socket(&local_addr, SOCK_STREAM, "auth query");
- if (fd < 0) {
- ++ServerStats->is_abad;
- return 0;
- }
- if (IsUserPort(auth->client))
- sendheader(auth->client, REPORT_DO_ID);
- memcpy(&remote_addr.addr, &cli_ip(auth->client), sizeof(remote_addr.addr));
- remote_addr.port = 113;
+ ircd_snprintf(0, authbuf, sizeof(authbuf), "%hu , %hu\r\n",
+ auth->port, auth->local.port);
- if ((result = os_connect_nonb(fd, &remote_addr)) == IO_FAILURE ||
- !socket_add(&auth->socket, auth_sock_callback, (void*) auth,
- result == IO_SUCCESS ? SS_CONNECTED : SS_CONNECTING,
- SOCK_EVENT_READABLE, fd)) {
- ServerStats->is_abad++;
- /*
- * No error report from this...
- */
- close(fd);
+ if (IO_SUCCESS != os_send_nonb(s_fd(&auth->socket), authbuf, strlen(authbuf), &count)) {
+ close(s_fd(&auth->socket));
+ socket_del(&auth->socket);
+ s_fd(&auth->socket) = -1;
+ ++ServerStats->is_abad;
if (IsUserPort(auth->client))
sendheader(auth->client, REPORT_FAIL_ID);
- return 0;
+ FlagClr(&auth->flags, AR_AUTH_PENDING);
+ check_auth_finished(auth, 0);
}
-
- auth->flags |= AM_SOCKET;
- auth->fd = fd;
-
- SetAuthConnect(auth);
- if (result == IO_SUCCESS)
- send_auth_query(auth); /* this does a SetAuthPending(auth) for us */
-
- return 1;
}
/** Enum used to index ident reply fields in a human-readable way. */
};
/** Parse an ident reply line and extract the userid from it.
- * @param reply The ident reply line.
+ * @param[in] reply The ident reply line.
* @return The userid, or NULL on parse failure.
*/
static char* check_ident_reply(char* reply)
if (IsSpace(*end) || '@' == *end || ':' == *end)
break;
}
- *end = '\0';
+ *end = '\0';
return token;
}
-/** Starts auth (identd) and dns queries for a client.
- * @param client The client for which to start queries.
+/** Read the reply (if any) from the ident server we connected to. We
+ * only give it one shot, if the reply isn't good the first time fail
+ * the authentication entirely. --Bleep
+ * @param[in] auth The request to read.
*/
-void start_auth(struct Client* client)
+static void read_auth_reply(struct AuthRequest* auth)
{
- struct AuthRequest* auth = 0;
-
- assert(0 != client);
+ char* username = 0;
+ unsigned int len;
+ /*
+ * rfc1453 sez we MUST accept 512 bytes
+ */
+ char buf[BUFSIZE + 1];
- auth = make_auth_request(client);
assert(0 != auth);
+ assert(0 != auth->client);
+ assert(auth == cli_auth(auth->client));
- Debug((DEBUG_INFO, "Beginning auth request on client %p", client));
-
- if (!feature_bool(FEAT_NODNS)) {
- if (irc_in_addr_is_loopback(&cli_ip(client)))
- strcpy(cli_sockhost(client), cli_name(&me));
- else {
- if (IsUserPort(auth->client))
- sendheader(client, REPORT_DO_DNS);
- gethost_byaddr(&cli_ip(client), auth_dns_callback, auth);
- SetDNSPending(auth);
- }
+ if (IO_SUCCESS == os_recv_nonb(s_fd(&auth->socket), buf, BUFSIZE, &len)) {
+ buf[len] = '\0';
+ Debug((DEBUG_INFO, "Auth %p [%d] reply: %s", auth, cli_fd(auth->client), buf));
+ username = check_ident_reply(buf);
+ Debug((DEBUG_INFO, "Username: %s", username));
}
- if (start_auth_query(auth)) {
- Debug((DEBUG_LIST, "identd query for %p initiated successfully",
- auth->client));
- } else if (IsDNSPending(auth)) {
- Debug((DEBUG_LIST, "identd query for %p not initiated successfully; "
- "waiting on DNS", auth->client));
+ Debug((DEBUG_INFO, "Deleting auth [%d] socket %p", auth, cli_fd(auth->client)));
+ close(s_fd(&auth->socket));
+ socket_del(&auth->socket);
+ s_fd(&auth->socket) = -1;
+
+ if (EmptyString(username)) {
+ if (IsUserPort(auth->client))
+ sendheader(auth->client, REPORT_FAIL_ID);
+ ++ServerStats->is_abad;
} else {
- Debug((DEBUG_LIST, "identd query for %p not initiated successfully; "
- "no DNS pending; releasing immediately", auth->client));
- free_auth_request(auth);
- release_auth_client(client);
+ if (IsUserPort(auth->client))
+ sendheader(auth->client, REPORT_FIN_ID);
+ ++ServerStats->is_asuc;
+ if (!FlagHas(&auth->flags, AR_IAUTH_USERNAME)) {
+ ircd_strncpy(cli_username(auth->client), username, USERLEN);
+ SetGotId(auth->client);
+ }
+ if (IAuthHas(iauth, IAUTH_UNDERNET))
+ sendto_iauth(auth->client, "u %s", username);
}
+
+ FlagClr(&auth->flags, AR_AUTH_PENDING);
+ check_auth_finished(auth, 0);
}
-/** Send the ident server a query giving "theirport , ourport". The
- * write is only attempted *once* so it is deemed to be a fail if the
- * entire write doesn't write all the data given. This shouldn't be a
- * problem since the socket should have a write buffer far greater
- * than this message to store it in should problems arise. -avalon
- * @param auth The request to send.
+/** Handle socket I/O activity.
+ * @param[in] ev A socket event whos associated data is the active
+ * struct AuthRequest.
*/
-void send_auth_query(struct AuthRequest* auth)
+static void auth_sock_callback(struct Event* ev)
{
- struct irc_sockaddr us;
- struct irc_sockaddr them;
- char authbuf[32];
- unsigned int count;
+ struct AuthRequest* auth;
+
+ assert(0 != ev_socket(ev));
+ assert(0 != s_data(ev_socket(ev)));
+
+ auth = (struct AuthRequest*) s_data(ev_socket(ev));
+
+ switch (ev_type(ev)) {
+ case ET_DESTROY: /* being destroyed */
+ break;
+
+ case ET_CONNECT: /* socket connection completed */
+ Debug((DEBUG_INFO, "Connection completed for auth %p [%d]; sending query",
+ auth, cli_fd(auth->client)));
+ socket_state(&auth->socket, SS_CONNECTED);
+ send_auth_query(auth);
+ break;
+
+ case ET_READ: /* socket is readable */
+ case ET_EOF: /* end of file on socket */
+ case ET_ERROR: /* error on socket */
+ Debug((DEBUG_INFO, "Auth socket %p [%p] readable", auth, ev_socket(ev)));
+ read_auth_reply(auth);
+ break;
+
+ default:
+ assert(0 && "Unrecognized event in auth_socket_callback().");
+ break;
+ }
+}
+
+/** Stop an auth request completely.
+ * @param[in] auth The struct AuthRequest to cancel.
+ * @param[in] send_reports If non-zero, report the failure to the user.
+ */
+void destroy_auth_request(struct AuthRequest* auth, int send_reports)
+{
+ Debug((DEBUG_INFO, "Deleting auth request for %p", auth->client));
+
+ if (FlagHas(&auth->flags, AR_AUTH_PENDING)) {
+ if (send_reports && IsUserPort(auth->client))
+ sendheader(auth->client, REPORT_FAIL_ID);
+ }
+
+ if (FlagHas(&auth->flags, AR_DNS_PENDING)) {
+ delete_resolver_queries(auth);
+ if (send_reports && IsUserPort(auth->client))
+ sendheader(auth->client, REPORT_FAIL_DNS);
+ }
+
+ if (-1 < s_fd(&auth->socket)) {
+ close(s_fd(&auth->socket));
+ socket_del(&auth->socket);
+ s_fd(&auth->socket) = -1;
+ }
+
+ timer_del(&auth->timeout);
+ cli_auth(auth->client) = NULL;
+}
+
+/** Timeout a given auth request.
+ * @param[in] ev A timer event whose associated data is the expired
+ * struct AuthRequest.
+ */
+static void auth_timeout_callback(struct Event* ev)
+{
+ struct AuthRequest* auth;
+
+ assert(0 != ev_timer(ev));
+ assert(0 != t_data(ev_timer(ev)));
+
+ auth = (struct AuthRequest*) t_data(ev_timer(ev));
+
+ if (ev_type(ev) == ET_EXPIRE) {
+ /* Report the timeout in the log. */
+ log_write(LS_RESOLVER, L_INFO, 0, "Registration timeout %s",
+ get_client_name(auth->client, HIDE_IP));
+ /* Tell iauth if we will let the client on. */
+ if (FlagHas(&auth->flags, AR_IAUTH_PENDING)
+ && !IAuthHas(iauth, IAUTH_REQUIRED))
+ {
+ sendto_iauth(auth->client, "T");
+ FlagClr(&auth->flags , AR_IAUTH_PENDING);
+ }
+ /* Try to register the client. */
+ check_auth_finished(auth, 1);
+ /* If that failed, kick them off. */
+ if (!IsUser(auth->client))
+ exit_client(auth->client, auth->client, &me, "Authorization timed out");
+ }
+}
+
+/** Handle a complete DNS lookup. Send the client on it's way to a
+ * connection completion, regardless of success or failure -- unless
+ * there was a mismatch and KILL_IPMISMATCH is set.
+ * @param[in] vptr The pending struct AuthRequest.
+ * @param[in] addr IP address being resolved.
+ * @param[in] h_name Resolved name, or NULL if lookup failed.
+ */
+static void auth_dns_callback(void* vptr, const struct irc_in_addr *addr, const char *h_name)
+{
+ struct AuthRequest* auth = (struct AuthRequest*) vptr;
+ assert(0 != auth);
+
+ FlagClr(&auth->flags, AR_DNS_PENDING);
+ if (!addr) {
+ /* DNS entry was missing for the IP. */
+ if (IsUserPort(auth->client))
+ sendheader(auth->client, REPORT_FAIL_DNS);
+ sendto_iauth(auth->client, "d");
+ } else if (irc_in_addr_cmp(addr, &cli_ip(auth->client))
+ && irc_in_addr_cmp(addr, &auth->original)) {
+ /* IP for hostname did not match client's IP. */
+ sendto_opmask_butone(0, SNO_IPMISMATCH, "IP# Mismatch: %s != %s[%s]",
+ cli_sock_ip(auth->client), h_name,
+ ircd_ntoa(addr));
+ if (IsUserPort(auth->client))
+ sendheader(auth->client, REPORT_IP_MISMATCH);
+ /* Clear DNS pending flag so free_client doesn't ask the resolver
+ * to delete the query that just finished.
+ */
+ if (feature_bool(FEAT_KILL_IPMISMATCH)) {
+ IPcheck_disconnect(auth->client);
+ Count_unknowndisconnects(UserStats);
+ free_client(auth->client);
+ }
+ } else if (!auth_verify_hostname(h_name, HOSTLEN)) {
+ /* Hostname did not look valid. */
+ if (IsUserPort(auth->client))
+ sendheader(auth->client, REPORT_INVAL_DNS);
+ sendto_iauth(auth->client, "d");
+ } else {
+ /* Hostname and mappings checked out. */
+ if (IsUserPort(auth->client))
+ sendheader(auth->client, REPORT_FIN_DNS);
+ ircd_strncpy(cli_sockhost(auth->client), h_name, HOSTLEN);
+ sendto_iauth(auth->client, "N %s", h_name);
+ }
+ check_auth_finished(auth, 0);
+}
+
+/** Flag the client to show an attempt to contact the ident server on
+ * the client's host. Should the connect or any later phase of the
+ * identifying process fail, it is aborted and the user is given a
+ * username of "unknown".
+ * @param[in] auth The request for which to start the ident lookup.
+ */
+static void start_auth_query(struct AuthRequest* auth)
+{
+ struct irc_sockaddr remote_addr;
+ struct irc_sockaddr local_addr;
+ int fd;
+ IOResult result;
assert(0 != auth);
assert(0 != auth->client);
- if (!os_get_sockname(cli_fd(auth->client), &us) ||
- !os_get_peername(cli_fd(auth->client), &them)) {
- auth_error(auth, 1);
+ /*
+ * get the local address of the client and bind to that to
+ * make the auth request. This used to be done only for
+ * ifdef VIRTUAL_HOST, but needs to be done for all clients
+ * since the ident request must originate from that same address--
+ * and machines with multiple IP addresses are common now
+ */
+ memcpy(&local_addr, &auth->local, sizeof(local_addr));
+ local_addr.port = 0;
+ memcpy(&remote_addr.addr, &cli_ip(auth->client), sizeof(remote_addr.addr));
+ remote_addr.port = 113;
+ fd = os_socket(&local_addr, SOCK_STREAM, "auth query");
+ if (fd < 0) {
+ ++ServerStats->is_abad;
+ if (IsUserPort(auth->client))
+ sendheader(auth->client, REPORT_FAIL_ID);
+ return;
+ }
+ if (IsUserPort(auth->client))
+ sendheader(auth->client, REPORT_DO_ID);
+
+ if ((result = os_connect_nonb(fd, &remote_addr)) == IO_FAILURE ||
+ !socket_add(&auth->socket, auth_sock_callback, (void*) auth,
+ result == IO_SUCCESS ? SS_CONNECTED : SS_CONNECTING,
+ SOCK_EVENT_READABLE, fd)) {
+ ++ServerStats->is_abad;
+ if (IsUserPort(auth->client))
+ sendheader(auth->client, REPORT_FAIL_ID);
+ close(fd);
+ return;
+ }
+
+ FlagSet(&auth->flags, AR_AUTH_PENDING);
+ if (result == IO_SUCCESS)
+ send_auth_query(auth);
+}
+
+/** Initiate DNS lookup for a client.
+ * @param[in] auth The auth request for which to start the DNS lookup.
+ */
+static void start_dns_query(struct AuthRequest *auth)
+{
+ if (feature_bool(FEAT_NODNS)) {
+ sendto_iauth(auth->client, "d");
return;
}
- ircd_snprintf(0, authbuf, sizeof(authbuf), "%u , %u\r\n",
- (unsigned int) them.port,
- (unsigned int) us.port);
- if (IO_SUCCESS == os_send_nonb(auth->fd, authbuf, strlen(authbuf), &count)) {
- ClearAuthConnect(auth);
- SetAuthPending(auth);
+ if (irc_in_addr_is_loopback(&cli_ip(auth->client))) {
+ strcpy(cli_sockhost(auth->client), cli_name(&me));
+ sendto_iauth(auth->client, "N %s", cli_sockhost(auth->client));
+ return;
}
- else
- auth_error(auth, 0);
+
+ if (IsUserPort(auth->client))
+ sendheader(auth->client, REPORT_DO_DNS);
+
+ FlagSet(&auth->flags, AR_DNS_PENDING);
+ gethost_byaddr(&cli_ip(auth->client), auth_dns_callback, auth);
}
+/** Initiate IAuth check for a client.
+ * @param[in] auth The auth request for which to star the IAuth check.
+ */
+static void start_iauth_query(struct AuthRequest *auth)
+{
+ FlagSet(&auth->flags, AR_IAUTH_PENDING);
+ if (!sendto_iauth(auth->client, "C %s %hu %s %hu",
+ cli_sock_ip(auth->client), auth->port,
+ ircd_ntoa(&auth->local.addr), auth->local.port))
+ FlagClr(&auth->flags, AR_IAUTH_PENDING);
+}
-/** Read the reply (if any) from the ident server we connected to. We
- * only give it one shot, if the reply isn't good the first time fail
- * the authentication entirely. --Bleep
- * @param auth The request to read.
+/** Starts auth (identd) and dns queries for a client.
+ * @param[in] client The client for which to start queries.
*/
-void read_auth_reply(struct AuthRequest* auth)
+void start_auth(struct Client* client)
{
- char* username = 0;
- unsigned int len;
+ struct irc_sockaddr remote;
+ struct AuthRequest* auth;
+
+ assert(0 != client);
+ Debug((DEBUG_INFO, "Beginning auth request on client %p", client));
+
+ /* Register with event handlers. */
+ cli_lasttime(client) = CurrentTime;
+ cli_since(client) = CurrentTime;
+ if (cli_fd(client) > HighestFd)
+ HighestFd = cli_fd(client);
+ LocalClientArray[cli_fd(client)] = client;
+ add_client_to_list(client);
+ socket_events(&(cli_socket(client)), SOCK_ACTION_SET | SOCK_EVENT_READABLE);
+
+ /* Allocate the AuthRequest. */
+ auth = MyCalloc(1, sizeof(*auth));
+ assert(0 != auth);
+ auth->client = client;
+ cli_auth(client) = auth;
+ s_fd(&auth->socket) = -1;
+ timer_add(timer_init(&auth->timeout), auth_timeout_callback, (void*) auth,
+ TT_RELATIVE, feature_int(FEAT_AUTH_TIMEOUT));
+
+ /* Try to get socket endpoint addresses. */
+ if (!os_get_sockname(cli_fd(client), &auth->local)
+ || !os_get_peername(cli_fd(client), &remote)) {
+ ++ServerStats->is_abad;
+ if (IsUserPort(auth->client))
+ sendheader(auth->client, REPORT_FAIL_ID);
+ IPcheck_disconnect(auth->client);
+ Count_unknowndisconnects(UserStats);
+ free_client(auth->client);
+ return;
+ }
+ auth->port = remote.port;
+
+ /* Try to start DNS lookup. */
+ start_dns_query(auth);
+
+ /* Try to start ident lookup. */
+ start_auth_query(auth);
+
+ /* Set required client inputs for users. */
+ if (IsUserPort(client)) {
+ cli_user(client) = make_user(client);
+ cli_user(client)->server = &me;
+ FlagSet(&auth->flags, AR_NEEDS_USER);
+ FlagSet(&auth->flags, AR_NEEDS_NICK);
+
+ /* Try to start iauth lookup. */
+ start_iauth_query(auth);
+ }
+
+ /* Check which auth events remain pending. */
+ check_auth_finished(auth, 0);
+}
+
+/** Mark that a user has PONGed while unregistered.
+ * @param[in] auth Authorization request for client.
+ * @param[in] cookie PONG cookie value sent by client.
+ * @return Zero if client should be kept, CPTR_KILLED if rejected.
+ */
+int auth_set_pong(struct AuthRequest *auth, unsigned int cookie)
+{
+ assert(auth != NULL);
+ if (!FlagHas(&auth->flags, AR_NEEDS_PONG))
+ return 0;
+ if (cookie != auth->cookie)
+ {
+ send_reply(auth->client, SND_EXPLICIT | ERR_BADPING,
+ ":To connect, type /QUOTE PONG %u", auth->cookie);
+ return 0;
+ }
+ FlagClr(&auth->flags, AR_NEEDS_PONG);
+ return check_auth_finished(auth, 0);
+}
+
+/** Record a user's claimed username and userinfo.
+ * @param[in] auth Authorization request for client.
+ * @param[in] username Client's asserted username.
+ * @param[in] userinfo Client's asserted self-description.
+ * @return Zero if client should be kept, CPTR_KILLED if rejected.
+ */
+int auth_set_user(struct AuthRequest *auth, const char *username, const char *userinfo)
+{
+ struct Client *cptr;
+
+ assert(auth != NULL);
+ if (FlagHas(&auth->flags, AR_IAUTH_HURRY))
+ return 0;
+ FlagClr(&auth->flags, AR_NEEDS_USER);
+ cptr = auth->client;
+ ircd_strncpy(cli_info(cptr), userinfo, REALLEN);
+ ircd_strncpy(cli_user(cptr)->username, username, USERLEN);
+ ircd_strncpy(cli_user(cptr)->host, cli_sockhost(cptr), HOSTLEN);
+ if (IAuthHas(iauth, IAUTH_UNDERNET))
+ sendto_iauth(cptr, "U %s %s", username, userinfo);
+ else if (IAuthHas(iauth, IAUTH_ADDLINFO))
+ sendto_iauth(cptr, "U %s", username);
+ return check_auth_finished(auth, 0);
+}
+
+/** Handle authorization-related aspects of initial nickname selection.
+ * This is called after verifying that the nickname is available.
+ * @param[in] auth Authorization request for client.
+ * @param[in] nickname Client's requested nickname.
+ * @return Zero if client should be kept, CPTR_KILLED if rejected.
+ */
+int auth_set_nick(struct AuthRequest *auth, const char *nickname)
+{
+ assert(auth != NULL);
+ FlagClr(&auth->flags, AR_NEEDS_NICK);
/*
- * rfc1453 sez we MUST accept 512 bytes
+ * If the client hasn't gotten a cookie-ping yet,
+ * choose a cookie and send it. -record!jegelhof@cloud9.net
*/
- char buf[BUFSIZE + 1];
+ if (!auth->cookie) {
+ do {
+ auth->cookie = ircrandom();
+ } while (!auth->cookie);
+ sendrawto_one(auth->client, "PING :%u", auth->cookie);
+ FlagSet(&auth->flags, AR_NEEDS_PONG);
+ }
+ if (IAuthHas(iauth, IAUTH_UNDERNET))
+ sendto_iauth(auth->client, "n %s", nickname);
+ return check_auth_finished(auth, 0);
+}
- assert(0 != auth);
- assert(0 != auth->client);
- assert(auth == cli_auth(auth->client));
+/** Record a user's password.
+ * @param[in] auth Authorization request for client.
+ * @param[in] password Client's password.
+ * @return Zero if client should be kept, CPTR_KILLED if rejected.
+ */
+int auth_set_password(struct AuthRequest *auth, const char *password)
+{
+ assert(auth != NULL);
+ if (IAuthHas(iauth, IAUTH_ADDLINFO))
+ sendto_iauth(auth->client, "P %s", password);
+ return 0;
+}
- if (IO_SUCCESS == os_recv_nonb(auth->fd, buf, BUFSIZE, &len)) {
- buf[len] = '\0';
- Debug((DEBUG_LIST, "Auth %p [%p] reply: %s", auth, &auth->socket, buf));
- username = check_ident_reply(buf);
- Debug((DEBUG_LIST, "Username: %s", username));
+/** Send exit notification for \a cptr to iauth.
+ * @param[in] cptr Client who is exiting.
+ */
+void auth_send_exit(struct Client *cptr)
+{
+ sendto_iauth(cptr, "D");
+}
+
+/** Mark that a user has started capabilities negotiation.
+ * This blocks authorization until auth_cap_done() is called.
+ * @param[in] auth Authorization request for client.
+ * @return Zero if client should be kept, CPTR_KILLED if rejected.
+ */
+int auth_cap_start(struct AuthRequest *auth)
+{
+ assert(auth != NULL);
+ FlagSet(&auth->flags, AR_CAP_PENDING);
+ return 0;
+}
+
+/** Mark that a user has completed capabilities negotiation.
+ * This unblocks authorization if auth_cap_start() was called.
+ * @param[in] auth Authorization request for client.
+ * @return Zero if client should be kept, CPTR_KILLED if rejected.
+ */
+int auth_cap_done(struct AuthRequest *auth)
+{
+ assert(auth != NULL);
+ FlagClr(&auth->flags, AR_CAP_PENDING);
+ return check_auth_finished(auth, 0);
+}
+
+/** Attempt to spawn the process for an IAuth instance.
+ * @param[in] iauth IAuth descriptor.
+ * @param[in] automatic If non-zero, apply sanity checks against
+ * excessive automatic restarts.
+ * @return 0 on success, non-zero on failure.
+ */
+int iauth_do_spawn(struct IAuth *iauth, int automatic)
+{
+ pid_t cpid;
+ int s_io[2];
+ int s_err[2];
+ int res;
+
+ if (automatic && CurrentTime - iauth->started < 5)
+ {
+ sendto_opmask_butone(NULL, SNO_AUTH, "IAuth crashed fast, leaving it dead.");
+ return -1;
}
- close(auth->fd);
- auth->fd = -1;
- Debug((DEBUG_LIST, "Deleting auth [%p] socket %p", auth, &auth->socket));
- socket_del(&auth->socket);
- ClearAuth(auth);
+ /* Record time we tried to spawn the iauth process. */
+ iauth->started = CurrentTime;
+
+ /* Attempt to allocate a pair of sockets. */
+ res = os_socketpair(s_io);
+ if (res)
+ return errno;
+
+ /* Mark the parent's side of the pair (element 0) as non-blocking. */
+ res = os_set_nonblocking(s_io[0]);
+ if (!res) {
+ res = errno;
+ close(s_io[1]);
+ close(s_io[0]);
+ return res;
+ }
- if (!EmptyString(username)) {
- ircd_strncpy(cli_username(auth->client), username, USERLEN);
- /*
- * Not needed, struct is zeroed by memset
- * auth->client->username[USERLEN] = '\0';
+ /* Initialize the socket structure to talk to the child. */
+ res = socket_add(i_socket(iauth), iauth_sock_callback, iauth,
+ SS_CONNECTED, SOCK_EVENT_READABLE, s_io[0]);
+ if (!res) {
+ res = errno;
+ close(s_io[1]);
+ close(s_io[0]);
+ return res;
+ }
+
+ /* Allocate another pair for stderr. */
+ res = os_socketpair(s_err);
+ if (res) {
+ res = errno;
+ socket_del(i_socket(iauth));
+ close(s_io[1]);
+ close(s_io[0]);
+ return res;
+ }
+
+ /* Mark parent side of this pair non-blocking, too. */
+ res = os_set_nonblocking(s_err[0]);
+ if (!res) {
+ res = errno;
+ close(s_err[1]);
+ close(s_err[0]);
+ socket_del(i_socket(iauth));
+ close(s_io[1]);
+ close(s_io[0]);
+ return res;
+ }
+
+ /* And set up i_stderr(iauth). */
+ res = socket_add(i_stderr(iauth), iauth_stderr_callback, iauth,
+ SS_CONNECTED, SOCK_EVENT_READABLE, s_err[0]);
+ if (!res) {
+ res = errno;
+ close(s_err[1]);
+ close(s_err[0]);
+ socket_del(i_socket(iauth));
+ close(s_io[1]);
+ close(s_io[0]);
+ return res;
+ }
+
+ /* Attempt to fork a child process. */
+ cpid = fork();
+ if (cpid < 0) {
+ /* Error forking the child, still in parent. */
+ res = errno;
+ socket_del(i_stderr(iauth));
+ close(s_err[1]);
+ close(s_err[0]);
+ socket_del(i_socket(iauth));
+ close(s_io[1]);
+ close(s_io[0]);
+ return res;
+ }
+
+ if (cpid > 0) {
+ /* We are the parent process. Close the child's sockets. */
+ close(s_io[1]);
+ close(s_err[1]);
+ /* Send our server name (supposedly for proxy checking purposes)
+ * and maximum number of connections (for allocation hints).
+ * Need to use conf_get_local() since &me may not be fully
+ * initialized the first time we run.
*/
- SetGotId(auth->client);
- ++ServerStats->is_asuc;
- if (IsUserPort(auth->client))
- sendheader(auth->client, REPORT_FIN_ID);
+ sendto_iauth(NULL, "M %s %d", conf_get_local()->name, MAXCONNECTIONS);
+ /* Indicate success (until the child dies). */
+ return 0;
}
- else {
- ++ServerStats->is_abad;
+
+ /* We are the child process.
+ * Duplicate our end of the socket to stdin, stdout and stderr.
+ * Then close all the higher-numbered FDs and exec the process.
+ */
+ if (dup2(s_io[1], 0) == 0
+ && dup2(s_io[1], 1) == 1
+ && dup2(s_err[1], 2) == 2) {
+ close_connections(0);
+ execvp(iauth->i_argv[0], iauth->i_argv);
}
- if (!IsDNSPending(auth)) {
- release_auth_client(auth->client);
- free_auth_request(auth);
+ /* If we got here, something was seriously wrong. */
+ exit(EXIT_FAILURE);
+}
+
+/** See if an %IAuth program must be spawned.
+ * If a process is already running with the specified options, keep it.
+ * Otherwise spawn a new child process to perform the %IAuth function.
+ * @param[in] argc Number of parameters to use when starting process.
+ * @param[in] argv Array of parameters to start process.
+ * @return 0 on failure, 1 on new process, 2 on reuse of existing process.
+ */
+int auth_spawn(int argc, char *argv[])
+{
+ int ii;
+
+ if (iauth) {
+ int same = 1;
+
+ /* Check that incoming arguments all match pre-existing arguments. */
+ for (ii = 0; same && (ii < argc); ++ii) {
+ if (NULL == iauth->i_argv[ii]
+ || 0 != strcmp(iauth->i_argv[ii], argv[ii]))
+ same = 0;
+ }
+ /* Check that we have no more pre-existing arguments. */
+ if (iauth->i_argv[ii])
+ same = 0;
+ /* If they are the same and still connected, clear the "closing" flag and exit.*/
+ if (same && i_GetConnected(iauth)) {
+ IAuthClr(iauth, IAUTH_CLOSING);
+ return 2;
+ }
+ /* Deallocate old argv elements. */
+ for (ii = 0; iauth->i_argv[ii]; ++ii)
+ MyFree(iauth->i_argv[ii]);
+ MyFree(iauth->i_argv);
+ }
+
+ /* Need to initialize a new connection. */
+ iauth = MyCalloc(1, sizeof(*iauth));
+ msgq_init(i_sendQ(iauth));
+ /* Populate iauth's argv array. */
+ iauth->i_argv = MyCalloc(argc + 1, sizeof(iauth->i_argv[0]));
+ for (ii = 0; ii < argc; ++ii)
+ DupString(iauth->i_argv[ii], argv[ii]);
+ iauth->i_argv[ii] = NULL;
+ /* Try to spawn it, and handle the results. */
+ if (iauth_do_spawn(iauth, 0))
+ return 0;
+ IAuthClr(iauth, IAUTH_CLOSING);
+ return 1;
+}
+
+/** Mark all %IAuth connections as closing. */
+void auth_mark_closing(void)
+{
+ if (iauth)
+ IAuthSet(iauth, IAUTH_CLOSING);
+}
+
+/** Complete disconnection of an %IAuth connection.
+ * @param[in] iauth %Connection to fully close.
+ */
+static void iauth_disconnect(struct IAuth *iauth)
+{
+ if (!i_GetConnected(iauth))
+ return;
+
+ /* Close main socket. */
+ close(s_fd(i_socket(iauth)));
+ socket_del(i_socket(iauth));
+ s_fd(i_socket(iauth)) = -1;
+
+ /* Close error socket. */
+ close(s_fd(i_stderr(iauth)));
+ socket_del(i_stderr(iauth));
+ s_fd(i_stderr(iauth)) = -1;
+}
+
+/** Close all %IAuth connections marked as closing. */
+void auth_close_unused(void)
+{
+ if (iauth && IAuthHas(iauth, IAUTH_CLOSING)) {
+ int ii;
+ iauth_disconnect(iauth);
+ if (iauth->i_argv) {
+ for (ii = 0; iauth->i_argv[ii]; ++ii)
+ MyFree(iauth->i_argv[ii]);
+ MyFree(iauth->i_argv);
+ }
+ MyFree(iauth);
+ }
+}
+
+/** Send queued output to \a iauth.
+ * @param[in] iauth Writable connection with queued data.
+ */
+static void iauth_write(struct IAuth *iauth)
+{
+ unsigned int bytes_tried, bytes_sent;
+ IOResult iores;
+
+ if (IAuthHas(iauth, IAUTH_BLOCKED))
+ return;
+ while (MsgQLength(i_sendQ(iauth)) > 0) {
+ iores = os_sendv_nonb(s_fd(i_socket(iauth)), i_sendQ(iauth), &bytes_tried, &bytes_sent);
+ switch (iores) {
+ case IO_SUCCESS:
+ msgq_delete(i_sendQ(iauth), bytes_sent);
+ iauth->i_sendB += bytes_sent;
+ if (bytes_tried == bytes_sent)
+ break;
+ /* If bytes_sent < bytes_tried, fall through to IO_BLOCKED. */
+ case IO_BLOCKED:
+ IAuthSet(iauth, IAUTH_BLOCKED);
+ socket_events(i_socket(iauth), SOCK_ACTION_ADD | SOCK_EVENT_WRITABLE);
+ return;
+ case IO_FAILURE:
+ iauth_disconnect(iauth);
+ return;
+ }
+ }
+ /* We were able to flush all events, so remove notification. */
+ socket_events(i_socket(iauth), SOCK_ACTION_DEL | SOCK_EVENT_WRITABLE);
+}
+
+/** Send a message to iauth.
+ * @param[in] cptr Optional client context for message.
+ * @param[in] format Format string for message.
+ * @return Non-zero on successful send or buffering, zero on failure.
+ */
+static int sendto_iauth(struct Client *cptr, const char *format, ...)
+{
+ struct VarData vd;
+ struct MsgBuf *mb;
+
+ /* Do not send requests when we have no iauth. */
+ if (!i_GetConnected(iauth))
+ return 0;
+ /* Do not send for clients in the NORMAL state. */
+ if (cptr
+ && (format[0] != 'D')
+ && (!cli_auth(cptr) || !FlagHas(&cli_auth(cptr)->flags, AR_IAUTH_PENDING)))
+ return 0;
+
+ /* Build the message buffer. */
+ vd.vd_format = format;
+ va_start(vd.vd_args, format);
+ if (0 == cptr)
+ mb = msgq_make(NULL, "-1 %v", &vd);
+ else
+ mb = msgq_make(NULL, "%d %v", cli_fd(cptr), &vd);
+ va_end(vd.vd_args);
+
+ /* Tack it onto the iauth sendq and try to write it. */
+ ++iauth->i_sendM;
+ msgq_add(i_sendQ(iauth), mb, 0);
+ iauth_write(iauth);
+ return 1;
+}
+
+/** Send text to interested operators (SNO_AUTH server notice).
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params Text to send.
+ * @return Zero.
+ */
+static int iauth_cmd_snotice(struct IAuth *iauth, struct Client *cli, char *params)
+{
+ sendto_opmask_butone(NULL, SNO_AUTH, "%s", params);
+ return 0;
+}
+
+/** Set the debug level for the session.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params String starting with an integer.
+ * @return Zero.
+ */
+static int iauth_cmd_debuglevel(struct IAuth *iauth, struct Client *cli, char *params)
+{
+ int new_level;
+
+ new_level = atoi(params);
+ if (i_debug(iauth) > 0 || new_level > 0) {
+ /* The "ia_dbg" name is borrowed from (IRCnet) ircd. */
+ sendto_opmask_butone(NULL, SNO_AUTH, "ia_dbg = %d", new_level);
+ }
+ i_debug(iauth) = new_level;
+ return 0;
+}
+
+/** Set policy options for the session.
+ * Old policy is forgotten, and any of the following characters in \a
+ * params enable the corresponding policy:
+ * \li A IAUTH_ADDLINFO
+ * \li R IAUTH_REQUIRED
+ * \li T IAUTH_TIMEOUT
+ * \li W IAUTH_EXTRAWAIT
+ * \li U IAUTH_UNDERNET
+ *
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params Zero or more policy options.
+ * @return Zero.
+ */
+static int iauth_cmd_policy(struct IAuth *iauth, struct Client *cli, char *params)
+{
+ enum IAuthFlag flag;
+ char *p;
+
+ /* Erase old policy first. */
+ for (flag = IAUTH_FIRST_OPTION; flag < IAUTH_LAST_FLAG; ++flag)
+ IAuthClr(iauth, flag);
+
+ /* Parse new policy set. */
+ for (p = params; *p; p++) switch (*p) {
+ case 'A': IAuthSet(iauth, IAUTH_ADDLINFO); break;
+ case 'R': IAuthSet(iauth, IAUTH_REQUIRED); break;
+ case 'T': IAuthSet(iauth, IAUTH_TIMEOUT); break;
+ case 'W': IAuthSet(iauth, IAUTH_EXTRAWAIT); break;
+ case 'U': IAuthSet(iauth, IAUTH_UNDERNET); break;
+ }
+
+ /* Optionally notify operators. */
+ if (i_debug(iauth) > 0)
+ sendto_opmask_butone(NULL, SNO_AUTH, "iauth options: %s", params);
+ return 0;
+}
+
+/** Set the iauth program version number.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params Version number or name.
+ * @return Zero.
+ */
+static int iauth_cmd_version(struct IAuth *iauth, struct Client *cli, char *params)
+{
+ MyFree(iauth->i_version);
+ while (IsSpace(*params))
+ ++params;
+ DupString(iauth->i_version, params);
+ sendto_opmask_butone(NULL, SNO_AUTH, "iauth version %s running.", iauth->i_version);
+ return 0;
+}
+
+/** Clear cached iauth configuration information.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params Parameter list (ignored).
+ * @return Zero.
+ */
+static int iauth_cmd_newconfig(struct IAuth *iauth, struct Client *cli, char *params)
+{
+ struct SLink *head;
+ struct SLink *next;
+
+ head = iauth->i_config;
+ iauth->i_config = NULL;
+ for (; head; head = next) {
+ next = head->next;
+ MyFree(head->value.cp);
+ free_link(head);
+ }
+ sendto_opmask_butone(NULL, SNO_AUTH, "New iauth configuration.");
+ return 0;
+}
+
+/** Append iauth configuration information.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params Description of configuration element.
+ * @return Zero.
+ */
+static int iauth_cmd_config(struct IAuth *iauth, struct Client *cli, char *params)
+{
+ struct SLink *node;
+
+ if (iauth->i_config) {
+ for (node = iauth->i_config; node->next; node = node->next) ;
+ node = node->next = make_link();
+ } else {
+ node = iauth->i_config = make_link();
+ }
+ while (IsSpace(*params))
+ ++params;
+ DupString(node->value.cp, params);
+ return 0;
+}
+
+/** Clear cached iauth configuration information.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params Parameter list (ignored).
+ * @return Zero.
+ */
+static int iauth_cmd_newstats(struct IAuth *iauth, struct Client *cli, char *params)
+{
+ struct SLink *head;
+ struct SLink *next;
+
+ head = iauth->i_stats;
+ iauth->i_stats = NULL;
+ for (; head; head = next) {
+ next = head->next;
+ MyFree(head->value.cp);
+ free_link(head);
+ }
+ sendto_opmask_butone(NULL, SNO_AUTH, "New iauth statistics.");
+ return 0;
+}
+
+/** Append iauth statistics information.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params Statistics element.
+ * @return Zero.
+ */
+static int iauth_cmd_stats(struct IAuth *iauth, struct Client *cli, char *params)
+{
+ struct SLink *node;
+ if (iauth->i_stats) {
+ for (node = iauth->i_stats; node->next; node = node->next) ;
+ node = node->next = make_link();
+ } else {
+ node = iauth->i_stats = make_link();
+ }
+ while (IsSpace(*params))
+ ++params;
+ DupString(node->value.cp, params);
+ return 0;
+}
+
+/** Set client's username to a trusted string even if it breaks the rules.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params Forced username.
+ * @return One.
+ */
+static int iauth_cmd_username_forced(struct IAuth *iauth, struct Client *cli, char *params)
+{
+ assert(cli_auth(cli) != NULL);
+ FlagClr(&cli_auth(cli)->flags, AR_AUTH_PENDING);
+ if (!EmptyString(params)) {
+ ircd_strncpy(cli_username(cli), params, USERLEN);
+ SetGotId(cli);
+ FlagSet(&cli_auth(cli)->flags, AR_IAUTH_USERNAME);
+ FlagSet(&cli_auth(cli)->flags, AR_IAUTH_FUSERNAME);
}
+ return 1;
+}
+
+/** Set client's username to a trusted string.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params Trusted username.
+ * @return One.
+ */
+static int iauth_cmd_username_good(struct IAuth *iauth, struct Client *cli, char *params)
+{
+ assert(cli_auth(cli) != NULL);
+ FlagClr(&cli_auth(cli)->flags, AR_AUTH_PENDING);
+ if (!EmptyString(params)) {
+ ircd_strncpy(cli_username(cli), params, USERLEN);
+ SetGotId(cli);
+ FlagSet(&cli_auth(cli)->flags, AR_IAUTH_USERNAME);
+ }
+ return 1;
+}
+
+/** Set client's username to an untrusted string.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params Untrusted username.
+ * @return One.
+ */
+static int iauth_cmd_username_bad(struct IAuth *iauth, struct Client *cli, char *params)
+{
+ assert(cli_auth(cli) != NULL);
+ FlagClr(&cli_auth(cli)->flags, AR_AUTH_PENDING);
+ if (!EmptyString(params))
+ ircd_strncpy(cli_user(cli)->username, params, USERLEN);
+ return 1;
+}
+
+/** Set client's hostname.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params New hostname for client.
+ * @return Non-zero if \a cli authorization should be checked for completion.
+ */
+static int iauth_cmd_hostname(struct IAuth *iauth, struct Client *cli, char *params)
+{
+ struct AuthRequest *auth;
+
+ if (EmptyString(params)) {
+ sendto_iauth(cli, "E Missing :Missing hostname parameter");
+ return 0;
+ }
+
+ auth = cli_auth(cli);
+ assert(auth != NULL);
+
+ /* If a DNS request is pending, abort it. */
+ if (FlagHas(&auth->flags, AR_DNS_PENDING)) {
+ FlagClr(&auth->flags, AR_DNS_PENDING);
+ delete_resolver_queries(auth);
+ if (IsUserPort(cli))
+ sendheader(cli, REPORT_FIN_DNS);
+ }
+ /* Set hostname from params. */
+ ircd_strncpy(cli_sockhost(cli), params, HOSTLEN);
+ return 1;
+}
+
+/** Set client's IP address.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params New IP address for client in dotted quad or
+ * standard IPv6 format.
+ * @return Zero.
+ */
+static int iauth_cmd_ip_address(struct IAuth *iauth, struct Client *cli, char *params)
+{
+ struct irc_in_addr addr;
+ struct AuthRequest *auth;
+
+ /* Get AuthRequest for client. */
+ auth = cli_auth(cli);
+ assert(auth != NULL);
+
+ /* Parse the client's new IP address. */
+ if (!ircd_aton(&addr, params)) {
+ sendto_iauth(cli, "E Invalid :Unable to parse IP address [%s]", params);
+ return 0;
+ }
+
+ /* If this is the first IP override, save the client's original
+ * address in case we get a DNS response later.
+ */
+ if (!irc_in_addr_valid(&auth->original))
+ memcpy(&auth->original, &cli_ip(cli), sizeof(auth->original));
+
+ /* Undo original IP connection in IPcheck. */
+ IPcheck_connect_fail(cli);
+ ClearIPChecked(cli);
+
+ /* Update the IP and charge them as a remote connect. */
+ memcpy(&cli_ip(cli), &addr, sizeof(cli_ip(cli)));
+ IPcheck_remote_connect(cli, 0);
+
+ return 0;
+}
+
+/** Find a ConfItem structure for a named connection class.
+ * @param[in] class_name Name of configuration class to find.
+ * @return A ConfItem of type CONF_CLIENT for the class, or NULL on failure.
+ */
+static struct ConfItem *auth_find_class_conf(const char *class_name)
+{
+ static struct ConfItem *aconf_list;
+ struct ConnectionClass *class;
+ struct ConfItem *aconf;
+
+ /* Make sure the configuration class is valid. */
+ class = find_class(class_name);
+ if (!class)
+ return NULL;
+
+ /* Look for an existing ConfItem for the class. */
+ for (aconf = aconf_list; aconf; aconf = aconf->next)
+ if (aconf->conn_class == class)
+ break;
+
+ /* If no ConfItem, create one. */
+ if (!aconf) {
+ aconf = make_conf(CONF_CLIENT);
+ if (!aconf) {
+ sendto_opmask_butone(NULL, SNO_AUTH,
+ "Unable to allocate ConfItem for class %s!",
+ ConClass(class));
+ return NULL;
+ }
+ aconf->conn_class = class;
+ aconf->next = aconf_list;
+ aconf_list = aconf;
+ }
+
+ return aconf;
+}
+
+/** Accept a client in IAuth.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params Optional class name for client.
+ * @return One.
+ */
+static int iauth_cmd_done_client(struct IAuth *iauth, struct Client *cli, char *params)
+{
+ static time_t warn_time;
+
+ /* Clear iauth pending flag. */
+ assert(cli_auth(cli) != NULL);
+ FlagClr(&cli_auth(cli)->flags, AR_IAUTH_PENDING);
+
+ /* If a connection class was specified (and usable), assign the client to it. */
+ if (!EmptyString(params)) {
+ struct ConfItem *aconf;
+
+ aconf = auth_find_class_conf(params);
+ if (aconf)
+ attach_conf(cli, aconf);
+ else
+ sendto_opmask_butone_ratelimited(NULL, SNO_AUTH, &warn_time,
+ "iauth tried to use undefined class [%s]",
+ params);
+ }
+
+ return 1;
+}
+
+/** Accept a client in IAuth and assign them to an account.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params Account name and optional class name for client.
+ * @return Non-zero if \a cli authorization should be checked for completion.
+ */
+static int iauth_cmd_done_account(struct IAuth *iauth, struct Client *cli, char *params)
+{
+ char *end;
+ size_t len;
+
+ /* Sanity check. */
+ if (EmptyString(params)) {
+ sendto_iauth(cli, "E Missing :Missing account parameter");
+ return 0;
+ }
+ /* Check length of account name. */
+ len = strcspn(params, ": ");
+ if (len > ACCOUNTLEN) {
+ sendto_iauth(cli, "E Invalid :Account parameter too long");
+ return 0;
+ }
+ /* If account has a creation timestamp, use it. */
+ assert(cli_user(cli) != NULL);
+ if (params[len] == ':')
+ cli_user(cli)->acc_create = strtoul(params + len + 1, &end, 10);
+ else
+ end = params + len;
+ /* Copy account name to User structure. */
+ ircd_strncpy(cli_user(cli)->account, params, ACCOUNTLEN);
+ SetAccount(cli);
+ /* Skip whitespace before next argument. */
+ while (IsSpace(*end))
+ ++end;
+ /* Fall through to the normal "done" handler. */
+ return iauth_cmd_done_client(iauth, cli, end);
+}
+
+/** Reject a client's connection.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params Optional kill message.
+ * @return Zero.
+ */
+static int iauth_cmd_kill(struct IAuth *iauth, struct Client *cli, char *params)
+{
+ if (cli_auth(cli))
+ FlagClr(&cli_auth(cli)->flags, AR_IAUTH_PENDING);
+ if (EmptyString(params))
+ params = "Access denied";
+ exit_client(cli, cli, &me, params);
+ return 0;
+}
+
+/** Send a challenge string to the client.
+ * @param[in] iauth Active IAuth session.
+ * @param[in] cli Client referenced by command.
+ * @param[in] params Challenge message for client.
+ * @return Zero.
+ */
+static int iauth_cmd_challenge(struct IAuth *iauth, struct Client *cli, char *params)
+{
+ sendrawto_one(cli, "NOTICE AUTH :*** %s", params);
+ return 0;
+}
+
+/** Read input from \a iauth.
+ * Reads up to SERVER_TCP_WINDOW bytes per pass.
+ * @param[in] iauth Readable connection.
+ */
+static void iauth_read(struct IAuth *iauth)
+{
+ static char readbuf[SERVER_TCP_WINDOW];
+ iauth_cmd_handler handler;
+ struct AuthRequest *auth;
+ struct Client *cli;
+ char *old_buffer;
+ char *params;
+ char *endp;
+ char *src;
+ unsigned int length;
+ int has_cli;
+ int res;
+ int id;
+
+ switch (os_recv_nonb(s_fd(i_socket(iauth)), readbuf, sizeof(readbuf), &length))
+ {
+ case IO_SUCCESS: break;
+ case IO_FAILURE: iauth_disconnect(iauth);
+ case IO_BLOCKED: return;
+ }
+ iauth->i_recvB += length;
+ old_buffer = iauth->i_buffer;
+ endp = old_buffer + iauth->i_count;
+ for (src = readbuf; length > 0; --length) {
+ *endp = *src++;
+ if (IsEol(*endp)) {
+ /* Terminate line, reset buffer and update statistics. */
+ *endp = '\0';
+ endp = old_buffer;
+ ++iauth->i_recvM;
+
+ /* If spammy debug, send the message to opers. */
+ if (i_debug(iauth) > 1)
+ sendto_opmask_butone(NULL, SNO_AUTH, "%s", endp);
+
+ /* Find command handler. A lot of the handlers would be simpler
+ * with an argument splitter like in parse.c, but some commands
+ * (notably '>') do not use delimiters that way.
+ */
+ switch (*(endp = old_buffer)) {
+ case '>': handler = iauth_cmd_snotice; has_cli = 0; break;
+ case 'G': handler = iauth_cmd_debuglevel; has_cli = 0; break;
+ case 'O': handler = iauth_cmd_policy; has_cli = 0; break;
+ case 'V': handler = iauth_cmd_version; has_cli = 0; break;
+ case 'a': handler = iauth_cmd_newconfig; has_cli = 0; break;
+ case 'A': handler = iauth_cmd_config; has_cli = 0; break;
+ case 's': handler = iauth_cmd_newstats; has_cli = 0; break;
+ case 'S': handler = iauth_cmd_stats; has_cli = 0; break;
+ case 'o': handler = iauth_cmd_username_forced; has_cli = 1; break;
+ case 'U': handler = iauth_cmd_username_good; has_cli = 1; break;
+ case 'u': handler = iauth_cmd_username_bad; has_cli = 1; break;
+ case 'N': handler = iauth_cmd_hostname; has_cli = 1; break;
+ case 'I': handler = iauth_cmd_ip_address; has_cli = 1; break;
+ case 'C': handler = iauth_cmd_challenge; 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
+ * off without telling opers. There is no way to
+ * signal that to exit_client(), so we fall through to
+ * the case that we do implement.
+ */
+ case 'K': handler = iauth_cmd_kill; has_cli = 2; break;
+ case 'r': /* we handle termination directly */ continue;
+ default: sendto_iauth(NULL, "E Garbage :[%s]", endp); continue;
+ }
+
+ /* Skip whitespace at start of arguments. */
+ while (IsSpace(*++endp)) ;
+
+ /* At this point, has_cli has one of three values:
+ * 0 - no client is identified
+ * 1 - a client is identified and must still be registering
+ * 2 - a client is identified but may be fully registered
+ */
+
+ /* Figure out how to handle the command. */
+ if (!has_cli) {
+ /* Handler does not need a client. */
+ handler(iauth, NULL, endp);
+ } else {
+ /* Try to find the client associated with the request. */
+ id = strtol(endp, ¶ms, 10);
+ while (IsSpace(*params))
+ ++params;
+ if (id < 0 || id > HighestFd || !(cli = LocalClientArray[id])) {
+ /* Client no longer exists (or never existed). */
+ sendto_iauth(NULL, "E Gone :[%s]", params);
+ } else if ((!(auth = cli_auth(cli))
+ || !FlagHas(&auth->flags, AR_IAUTH_PENDING))
+ && (has_cli == 1)) {
+ /* Client is done with IAuth checks. */
+ sendto_iauth(cli, "E Done :[%s]", params);
+ } else {
+ struct irc_sockaddr addr;
+ char *orig_ip;
+
+ /* Skip whitespace before IP address. */
+ while (IsSpace(*params))
+ ++params;
+ /* Record start of IP address, then null terminate it. */
+ orig_ip = params;
+ while (!IsSpace(*params) && *params != '\0')
+ ++params;
+ if (IsSpace(*params))
+ *params++ = '\0';
+ /* Parse out client IP address and port number. */
+ res = ipmask_parse(orig_ip, &addr.addr, NULL);
+ addr.port = strtol(params, ¶ms, 10);
+ /* Skip whitespace and optional sentinel after port number. */
+ while (IsSpace(*params))
+ ++params;
+ if (*params == ':')
+ ++params;
+ /* Check IP address and port number against expected. */
+ if (0 == res
+ || irc_in_addr_cmp(&addr.addr, &cli_ip(cli))
+ || (auth && addr.port != auth->port)) {
+ /* Report mismatch to iauth. */
+ sendto_iauth(cli, "E Mismatch :[%s] != [%s]",
+ orig_ip, ircd_ntoa(&cli_ip(cli)));
+ } else if (handler(iauth, cli, params)) {
+ /* Handler indicated a possible state change. */
+ check_auth_finished(auth, 0);
+ }
+ }
+ }
+
+ /* Reset buffer pointer to read next line. */
+ endp = old_buffer;
+ }
+ else if (endp < old_buffer + BUFSIZE)
+ ++endp;
+ }
+ iauth->i_count = endp - old_buffer;
+}
+
+/** Handle socket activity for an %IAuth connection.
+ * @param[in] ev &Socket event; the IAuth connection is the user data
+ * pointer for the socket.
+ */
+static void iauth_sock_callback(struct Event *ev)
+{
+ struct IAuth *iauth;
+
+ assert(0 != ev_socket(ev));
+ iauth = (struct IAuth*) s_data(ev_socket(ev));
+ assert(0 != iauth);
+
+ switch (ev_type(ev)) {
+ case ET_DESTROY:
+ /* Hm, what happened here? */
+ if (!IAuthHas(iauth, IAUTH_CLOSING))
+ iauth_do_spawn(iauth, 1);
+ break;
+ case ET_READ:
+ iauth_read(iauth);
+ break;
+ case ET_WRITE:
+ IAuthClr(iauth, IAUTH_BLOCKED);
+ iauth_write(iauth);
+ break;
+ case ET_ERROR:
+ log_write(LS_IAUTH, L_ERROR, 0, "IAuth socket error: %s", strerror(ev_data(ev)));
+ /* and fall through to the ET_EOF case */
+ case ET_EOF:
+ iauth_disconnect(iauth);
+ break;
+ default:
+ assert(0 && "Unrecognized event type");
+ break;
+ }
+}
+
+/** Read error input from \a iauth.
+ * @param[in] iauth Readable connection.
+ */
+static void iauth_read_stderr(struct IAuth *iauth)
+{
+ static char readbuf[SERVER_TCP_WINDOW];
+ unsigned int length;
+ char *sol;
+ char *eol;
+
+ /* Copy partial data to readbuf, append new data. */
+ length = iauth->i_errcount;
+ memcpy(readbuf, iauth->i_errbuf, length);
+ if (IO_SUCCESS != os_recv_nonb(s_fd(i_stderr(iauth)),
+ readbuf + length,
+ sizeof(readbuf) - length - 1,
+ &length))
+ return;
+ readbuf[length] = '\0';
+
+ /* Send each complete line to SNO_AUTH. */
+ for (sol = readbuf; (eol = strchr(sol, '\n')) != NULL; sol = eol + 1) {
+ *eol = '\0';
+ Debug((DEBUG_ERROR, "IAuth error: %s", sol));
+ log_write(LS_IAUTH, L_ERROR, 0, "IAuth error: %s", sol);
+ sendto_opmask_butone(NULL, SNO_AUTH, "%s", sol);
+ }
+
+ /* Put unused data back into connection's buffer. */
+ iauth->i_errcount = strlen(sol);
+ if (iauth->i_errcount > BUFSIZE)
+ iauth->i_errcount = BUFSIZE;
+ memcpy(iauth->i_errbuf, sol, iauth->i_errcount);
+}
+
+/** Handle error socket activity for an %IAuth connection.
+ * @param[in] ev &Socket event; the IAuth connection is the user data
+ * pointer for the socket.
+ */
+static void iauth_stderr_callback(struct Event *ev)
+{
+ struct IAuth *iauth;
+
+ assert(0 != ev_socket(ev));
+ iauth = (struct IAuth*) s_data(ev_socket(ev));
+ assert(0 != iauth);
+
+ switch (ev_type(ev)) {
+ case ET_READ:
+ iauth_read_stderr(iauth);
+ break;
+ case ET_ERROR:
+ log_write(LS_IAUTH, L_ERROR, 0, "IAuth stderr error: %s", strerror(ev_data(ev)));
+ /* and fall through to the ET_EOF/ET_DESTROY case */
+ case ET_DESTROY:
+ case ET_EOF:
+ break;
+ default:
+ assert(0 && "Unrecognized event type");
+ break;
+ }
+}
+
+/** Report active iauth's configuration to \a cptr.
+ * @param[in] cptr Client requesting statistics.
+ * @param[in] sd Stats descriptor for request.
+ * @param[in] param Extra parameter from user (may be NULL).
+ */
+void report_iauth_conf(struct Client *cptr, const struct StatDesc *sd, char *param)
+{
+ struct SLink *link;
+
+ for (link = iauth->i_config; link; link = link->next)
+ {
+ send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":%s",
+ link->value.cp);
+ }
+ send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":End of IAuth configuration.");
+}
+
+/** Report active iauth's statistics to \a cptr.
+ * @param[in] cptr Client requesting statistics.
+ * @param[in] sd Stats descriptor for request.
+ * @param[in] param Extra parameter from user (may be NULL).
+ */
+ void report_iauth_stats(struct Client *cptr, const struct StatDesc *sd, char *param)
+{
+ struct SLink *link;
+
+ for (link = iauth->i_stats; link; link = link->next)
+ {
+ send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":%s",
+ link->value.cp);
+ }
+ send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":End of IAuth statistics.");
}
#include "hash.h"
#include "ircd.h"
#include "ircd_alloc.h"
-#include "ircd_auth.h"
#include "ircd_chattr.h"
#include "ircd_log.h"
#include "ircd_reply.h"
#include "opercmds.h"
#include "parse.h"
#include "res.h"
+#include "s_auth.h"
#include "s_bsd.h"
#include "s_debug.h"
#include "s_misc.h"
class_mark_delete();
mark_listeners_closing();
- iauth_mark_closing();
+ auth_mark_closing();
close_mappings();
read_configuration_file();
log_reopen(); /* reopen log files */
- iauth_close_unused();
+ auth_close_unused();
close_listeners();
class_delete_marked(); /* unless it fails */
#include "hash.h"
#include "ircd.h"
#include "ircd_alloc.h"
-#include "ircd_auth.h"
#include "ircd_features.h"
#include "ircd_log.h"
#include "ircd_reply.h"
#include "parse.h"
#include "querycmds.h"
#include "res.h"
+#include "s_auth.h"
#include "s_bsd.h"
#include "s_conf.h"
#include "s_debug.h"
assert(!IsServer(bcptr));
/* bcptr->user->server->serv->client_list[IndexYXX(bcptr)] = NULL; */
RemoveYXXClient(cli_user(bcptr)->server, cli_yxx(bcptr));
- if (IsIAuthed(bcptr) || cli_iauth(bcptr))
- iauth_exit_client(bcptr);
}
/* Remove bcptr from the client list */
on_for = CurrentTime - cli_firsttime(victim);
+ if (IsUser(victim) || IsUserPort(victim))
+ auth_send_exit(victim);
+
if (IsUser(victim))
log_write(LS_USER, L_TRACE, 0, "%Tu %i %s@%s %s %s %s%s %s :%s",
cli_firsttime(victim), on_for,
#include "numnicks.h"
#include "querycmds.h"
#include "res.h"
+#include "s_auth.h"
#include "s_bsd.h"
#include "s_conf.h"
#include "s_debug.h"
{ 'z', "memory", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_z,
count_memory, 0,
"Memory/Structure allocation information." },
+ { ' ', "iauth", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_IAUTH,
+ report_iauth_stats, 0,
+ "IAuth statistics." },
+ { ' ', "iauthconf", STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_IAUTH,
+ report_iauth_conf, 0,
+ "IAuth configuration." },
{ '*', "help", STAT_FLAG_CASESENS, FEAT_LAST_F,
stats_help, 0,
"Send help for stats." },
#include "hash.h"
#include "ircd.h"
#include "ircd_alloc.h"
-#include "ircd_auth.h"
#include "ircd_chattr.h"
#include "ircd_features.h"
#include "ircd_log.h"
#include "parse.h"
#include "querycmds.h"
#include "random.h"
+#include "s_auth.h"
#include "s_bsd.h"
#include "s_conf.h"
#include "s_debug.h"
}
-/** Copy a cleaned-up version of a username.
- * Replace all instances of '~' and "invalid" username characters
- * (!isIrcUi()) with underscores, truncating at USERLEN or the first
- * control character. \a dest and \a source may be the same buffer.
- * @param[out] dest Destination buffer.
- * @param[in] source Source buffer.
- * @param[in] tilde If non-zero, prepend a '~' to \a dest.
- */
-static char *clean_user_id(char *dest, char *source, int tilde)
-{
- char ch;
- char *d = dest;
- char *s = source;
- int rlen = USERLEN;
-
- ch = *s++; /* Store first character to copy: */
- if (tilde)
- {
- *d++ = '~'; /* If `dest' == `source', then this overwrites `ch' */
- --rlen;
- }
- while (ch && !IsCntrl(ch) && rlen--)
- {
- char nch = *s++; /* Store next character to copy */
- *d++ = IsUserChar(ch) ? ch : '_'; /* This possibly overwrites it */
- if (nch == '~')
- ch = '_';
- else
- ch = nch;
- }
- *d = 0;
- return dest;
-}
-
/*
* register_user
*
*
* @param[in] cptr Client who introduced the user.
* @param[in,out] sptr Client who has been fully introduced.
- * @param[in] nick Client's new nickname.
- * @param[in] username Client's username.
* @return Zero or CPTR_KILLED.
*/
-int register_user(struct Client *cptr, struct Client *sptr,
- const char *nick, char *username)
+int register_user(struct Client *cptr, struct Client *sptr)
{
- struct ConfItem* aconf;
char* parv[4];
char* tmpstr;
- char* tmpstr2;
- char c = 0; /* not alphanum */
- char d = 'a'; /* not a digit */
- short upper = 0;
- short lower = 0;
- short pos = 0;
- short leadcaps = 0;
- short other = 0;
- short digits = 0;
- short badid = 0;
- short digitgroups = 0;
struct User* user = cli_user(sptr);
- int killreason;
char ip_base64[25];
user->last = CurrentTime;
if (MyConnect(sptr))
{
- static time_t last_too_many1;
- static time_t last_too_many2;
-
assert(cptr == sptr);
- assert(cli_unreg(sptr) == 0);
- if (!IsIAuthed(sptr)) {
- if (iauth_active)
- return iauth_start_client(iauth_active, sptr);
- else
- SetIAuthed(sptr);
- }
- switch (conf_check_client(sptr))
- {
- case ACR_OK:
- break;
- case ACR_NO_AUTHORIZATION:
- sendto_opmask_butone(0, SNO_UNAUTH, "Unauthorized connection from %s.",
- get_client_name(sptr, HIDE_IP));
- ++ServerStats->is_ref;
- return exit_client(cptr, sptr, &me,
- "No Authorization - use another server");
- case ACR_TOO_MANY_IN_CLASS:
- if (CurrentTime - last_too_many1 >= (time_t) 60)
- {
- last_too_many1 = CurrentTime;
- sendto_opmask_butone(0, SNO_TOOMANY, "Too many connections in "
- "class %s for %s.", get_client_class(sptr),
- get_client_name(sptr, SHOW_IP));
- }
- ++ServerStats->is_ref;
- IPcheck_connect_fail(sptr);
- return exit_client(cptr, sptr, &me,
- "Sorry, your connection class is full - try "
- "again later or try another server");
- case ACR_TOO_MANY_FROM_IP:
- if (CurrentTime - last_too_many2 >= (time_t) 60)
- {
- last_too_many2 = CurrentTime;
- sendto_opmask_butone(0, SNO_TOOMANY, "Too many connections from "
- "same IP for %s.",
- get_client_name(sptr, SHOW_IP));
- }
- ++ServerStats->is_ref;
- return exit_client(cptr, sptr, &me,
- "Too many connections from your host");
- case ACR_ALREADY_AUTHORIZED:
- /* Can this ever happen? */
- case ACR_BAD_SOCKET:
- ++ServerStats->is_ref;
- IPcheck_connect_fail(sptr);
- return exit_client(cptr, sptr, &me, "Unknown error -- Try again");
- }
- ircd_strncpy(user->host, cli_sockhost(sptr), HOSTLEN);
- ircd_strncpy(user->realhost, cli_sockhost(sptr), HOSTLEN);
- aconf = cli_confs(sptr)->value.aconf;
-
- clean_user_id(user->username,
- HasFlag(sptr, FLAG_GOTID) ? cli_username(sptr) : username,
- HasFlag(sptr, FLAG_DOID) && !HasFlag(sptr, FLAG_GOTID));
- if ((user->username[0] == '\0')
- || ((user->username[0] == '~') && (user->username[1] == '\000')))
- return exit_client(cptr, sptr, &me, "USER: Bogus userid.");
-
- if (!EmptyString(aconf->passwd)
- && strcmp(cli_passwd(sptr), aconf->passwd))
- {
- ServerStats->is_ref++;
- send_reply(sptr, ERR_PASSWDMISMATCH);
- return exit_client(cptr, sptr, &me, "Bad Password");
- }
- memset(cli_passwd(sptr), 0, sizeof(cli_passwd(sptr)));
- /*
- * following block for the benefit of time-dependent K:-lines
- */
- killreason = find_kill(sptr);
- if (killreason) {
- ServerStats->is_ref++;
- return exit_client(cptr, sptr, &me,
- (killreason == -1 ? "K-lined" : "G-lined"));
- }
- /*
- * Check for mixed case usernames, meaning probably hacked. Jon2 3-94
- * Summary of rules now implemented in this patch: Ensor 11-94
- * In a mixed-case name, if first char is upper, one more upper may
- * appear anywhere. (A mixed-case name *must* have an upper first
- * char, and may have one other upper.)
- * A third upper may appear if all 3 appear at the beginning of the
- * name, separated only by "others" (-/_/.).
- * A single group of digits is allowed anywhere.
- * Two groups of digits are allowed if at least one of the groups is
- * at the beginning or the end.
- * Only one '-', '_', or '.' is allowed (or two, if not consecutive).
- * But not as the first or last char.
- * No other special characters are allowed.
- * Name must contain at least one letter.
- */
- tmpstr2 = tmpstr = (username[0] == '~' ? &username[1] : username);
- while (*tmpstr && !badid)
- {
- pos++;
- c = *tmpstr;
- tmpstr++;
- if (IsLower(c))
- {
- lower++;
- }
- else if (IsUpper(c))
- {
- upper++;
- if ((leadcaps || pos == 1) && !lower && !digits)
- leadcaps++;
- }
- else if (IsDigit(c))
- {
- digits++;
- if (pos == 1 || !IsDigit(d))
- {
- digitgroups++;
- if (digitgroups > 2)
- badid = 1;
- }
- }
- else if (c == '-' || c == '_' || c == '.')
- {
- other++;
- if (pos == 1)
- badid = 1;
- else if (d == '-' || d == '_' || d == '.' || other > 2)
- badid = 1;
- }
- else
- badid = 1;
- d = c;
- }
- if (!badid)
- {
- if (lower && upper && (!leadcaps || leadcaps > 3 ||
- (upper > 2 && upper > leadcaps)))
- badid = 1;
- else if (digitgroups == 2 && !(IsDigit(tmpstr2[0]) || IsDigit(c)))
- badid = 1;
- else if ((!lower && !upper) || !IsAlnum(c))
- badid = 1;
- }
- if (badid && (!HasFlag(sptr, FLAG_GOTID) ||
- strcmp(cli_username(sptr), username) != 0))
- {
- ServerStats->is_ref++;
-
- send_reply(cptr, SND_EXPLICIT | ERR_INVALIDUSERNAME,
- ":Your username is invalid.");
- send_reply(cptr, SND_EXPLICIT | ERR_INVALIDUSERNAME,
- ":Connect with your real username, in lowercase.");
- send_reply(cptr, SND_EXPLICIT | ERR_INVALIDUSERNAME,
- ":If your mail address were foo@bar.com, your username "
- "would be foo.");
- return exit_client(cptr, sptr, &me, "USER: Bad username");
- }
Count_unknownbecomesclient(sptr, UserStats);
SetUser(sptr);
feature_str(FEAT_NETWORK),
feature_str(FEAT_PROVIDER) ? " via " : "",
feature_str(FEAT_PROVIDER) ? feature_str(FEAT_PROVIDER) : "",
- nick);
+ cli_name(sptr));
/*
* This is a duplicate of the NOTICE but see below...
*/
}
}
else {
- struct Client *acptr;
+ struct Client *acptr = user->server;
- ircd_strncpy(user->username, username, USERLEN);
- Count_newremoteclient(UserStats, user->server);
+ Count_newremoteclient(UserStats, acptr);
- acptr = user->server;
if (cli_from(acptr) != cli_from(sptr))
{
sendcmdto_one(&me, CMD_KILL, cptr, "%C :%s (%s != %s[%s])",
sendcmdto_flag_serv_butone(user->server, CMD_NICK, cptr,
FLAG_IPV6, FLAG_LAST_FLAG,
"%s %d %Tu %s %s %s%s%s%s %s%s :%s",
- nick, cli_hopcount(sptr) + 1, cli_lastnick(sptr),
+ cli_name(sptr), cli_hopcount(sptr) + 1,
+ cli_lastnick(sptr),
user->username, user->realhost,
*tmpstr ? "+" : "", tmpstr, *tmpstr ? " " : "",
iptobase64(ip_base64, &cli_ip(sptr), sizeof(ip_base64), 1),
sendcmdto_flag_serv_butone(user->server, CMD_NICK, cptr,
FLAG_LAST_FLAG, FLAG_IPV6,
"%s %d %Tu %s %s %s%s%s%s %s%s :%s",
- nick, cli_hopcount(sptr) + 1, cli_lastnick(sptr),
+ cli_name(sptr), cli_hopcount(sptr) + 1,
+ cli_lastnick(sptr),
user->username, user->realhost,
*tmpstr ? "+" : "", tmpstr, *tmpstr ? " " : "",
iptobase64(ip_base64, &cli_ip(sptr), sizeof(ip_base64), 0),
cli_serv(sptr)->ghost = 0; /* :server NICK means end of net.burst */
ircd_strncpy(cli_username(new_client), parv[4], USERLEN);
+ ircd_strncpy(cli_user(new_client)->username, parv[4], USERLEN);
ircd_strncpy(cli_user(new_client)->host, parv[5], HOSTLEN);
ircd_strncpy(cli_user(new_client)->realhost, parv[5], HOSTLEN);
ircd_strncpy(cli_info(new_client), parv[parc - 1], REALLEN);
ircd_snprintf(0, cli_user(new_client)->host, HOSTLEN, "%s.%s",
account, feature_str(FEAT_HIDDEN_HOST));
- return register_user(cptr, new_client, cli_name(new_client), parv[4]);
+ return register_user(cptr, new_client);
}
else if ((cli_name(sptr))[0]) {
/*
}
else {
/* Local client setting NICK the first time */
-
strcpy(cli_name(sptr), nick);
- if (!cli_user(sptr)) {
- cli_user(sptr) = make_user(sptr);
- cli_user(sptr)->server = &me;
- }
hAddClient(sptr);
-
- cli_unreg(sptr) &= ~CLIREG_NICK; /* nickname now set */
-
- /*
- * If the client hasn't gotten a cookie-ping yet,
- * choose a cookie and send it. -record!jegelhof@cloud9.net
- */
- if (!cli_cookie(sptr)) {
- do {
- cli_cookie(sptr) = (ircrandom() & 0x7fffffff);
- } while (!cli_cookie(sptr));
- sendrawto_one(cptr, MSG_PING " :%u", cli_cookie(sptr));
- }
- else if (!cli_unreg(sptr)) {
- /*
- * USER and PONG already received, now we have NICK.
- * register_user may reject the client and call exit_client
- * for it - must test this and exit m_nick too !
- */
- cli_lastnick(sptr) = TStime(); /* Always local client */
- if (register_user(cptr, sptr, nick, cli_user(sptr)->username) == CPTR_KILLED)
- return CPTR_KILLED;
- }
+ return auth_set_nick(cli_auth(sptr), nick);
}
return 0;
}
--- /dev/null
+#! /usr/bin/perl
+# iauth-test: test script for IRC authorization (iauth) protocol
+# Copyright 2006 Michael Poole
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+
+require 5.008; # We assume deferred signal handlers, new in 5.008.
+use strict;
+use warnings;
+use vars qw(%pending);
+
+use Carp; # for carp
+use Config; # for $Config{sig_name} and $Config{sig_num}
+use FileHandle; # for autoflush method on file handles
+
+# This script is intended to help test an implementation of the iauth
+# protocol by exercising every command in the protocol and by
+# exercising most distinct combinations of commands. It assumes IPv4
+# support in the server and POSIX real-time signal support in the OS
+# (recognized and supported by Perl).
+
+# Certain behavior is triggered by receipt of real-time signals.
+# SIGRTMIN + 0 -> Send server notice ('>').
+# SIGRTMIN + 1 -> Toggle debug level ('G').
+# SIGRTMIN + 2 -> Set policy options ('O').
+# SIGRTMIN + 3 -> Simulate config change ('a', 'A').
+# SIGRTMIN + 4 -> Simulate statistics change ('s', 'S').
+
+# In the following discussion, sX means message X from the server, and
+# iX means message X from iauth. The hard part is the ordering of
+# various events during client registration. This includes sC, sP,
+# sU, su, sn, sN/d, sH and sT; and o/U/u, iN, iI, iC and iD/R/k/K.
+
+# sC is first, sD/sT/iD/R/k/K is last. If sH is sent, no more sU, su,
+# sn, sN, sd or sH messages may be sent. If iI is sent, iN should
+# also be sent (either before or after iI). Multiple sP, sU and iC
+# messages may be sent. Otherwse, the ordering of unrelated messages
+# from either source are not constrained, but only one message from
+# each set of alternatives may be sent.
+
+# This means the sets of commands with interesting orderings are:
+# sU, su, io/U/u
+# sN/d, iN, iI
+# sH, sT or iD/R/k/K
+
+# 127.x.y.z IP addresses are used to exercise these orderings; see the
+# %handlers variable below.
+
+sub dolog ($) {
+ print LOG "$_[0]\n";
+}
+
+sub reply ($;$$) {
+ my ($msg, $client, $extra) = @_;
+
+ if (not defined $msg) {
+ # Accept this for easier handling of client reply messages.
+ return;
+ } elsif (ref $msg eq '') {
+ $msg =~ s/^(.) ?/$1 $client->{id} $client->{ip} $client->{port} / if $client;
+ dolog "< $msg";
+ print "$msg\n";
+ } elsif (ref $msg eq 'ARRAY') {
+ grep { reply($_, $client, $extra); } @$msg;
+ } elsif (ref $msg eq 'CODE') {
+ &$msg($client, $extra);
+ } else {
+ die "Unknown reply message type.";
+ }
+}
+
+# Find the names of signals with values SIGRTMIN+1, +2, etc.
+BEGIN {
+ my @sig_name;
+ my %sig_num;
+
+ sub populate_signals () {
+ die "No sigs?"
+ unless $Config{sig_name} and $Config{sig_num};
+ my @names = split ' ', $Config{sig_name};
+ @sig_num{@names} = split ' ', $Config{sig_num};
+ foreach (@names) { $sig_name[$sig_num{$_}] ||= $_; }
+ }
+
+ sub assign_signal_handlers() {
+ my $sigrtmin = $sig_num{RTMIN};
+ die "No realtime signals?"
+ unless $sigrtmin;
+ $SIG{$sig_name[$sigrtmin+0]} = \&send_server_notice;
+ $SIG{$sig_name[$sigrtmin+1]} = \&toggle_debug_level;
+ $SIG{$sig_name[$sigrtmin+2]} = \&set_policy_options;
+ $SIG{$sig_name[$sigrtmin+3]} = \&sim_config_changed;
+ $SIG{$sig_name[$sigrtmin+4]} = \&sim_stats_change;
+ }
+}
+
+BEGIN {
+ my $debug_level = 0;
+ my $max_debug_level = 2;
+
+ sub toggle_debug_level () {
+ if (++$debug_level > $max_debug_level) {
+ $debug_level = 0;
+ }
+ reply "G $debug_level";
+ }
+}
+
+BEGIN {
+ my %rotation = (
+ '' => 'AU',
+ 'AU' => 'AURTW',
+ 'AURTW' => '',
+ );
+ my $policy = '';
+
+ sub set_policy_options () {
+ $policy = $rotation{$policy};
+ reply "O $policy";
+ }
+}
+
+BEGIN {
+ my $generation = 0;
+
+ sub sim_config_changed () {
+ reply "a";
+ reply "A config $generation";
+ $generation++;
+ }
+}
+
+BEGIN {
+ my $generation = 0;
+
+ sub sim_stats_change () {
+ reply "s";
+ reply "S stats $generation";
+ $generation++;
+ }
+}
+
+sub send_server_notice () {
+ reply "> Hello the server!";
+}
+
+my %handlers = (
+ # Default handliner: immediately report done.
+ 'default' => { C_reply => 'D' },
+ # 127.0.0.x: various timings for iD/iR/ik/iK.
+ '127.0.0.1' => { C_reply => 'D' },
+ '127.0.0.2' => { C_reply => 'R account-1' },
+ '127.0.0.3' => { C_reply => 'k' },
+ '127.0.0.4' => { C_reply => 'K' },
+ '127.0.0.15' => { },
+ '127.0.0.16' => { H_reply => 'D' },
+ '127.0.0.17' => { H_reply => 'R account-2' },
+ '127.0.0.18' => { H_reply => 'k' },
+ '127.0.0.19' => { H_reply => 'K' },
+ '127.0.0.32' => { T_reply => 'D' },
+ '127.0.0.33' => { T_reply => 'R account-3' },
+ '127.0.0.34' => { T_reply => 'k' },
+ '127.0.0.35' => { T_reply => 'K' },
+ # 127.0.1.x: io/iU/iu functionality.
+ '127.0.1.0' => { C_reply => 'o forced',
+ H_reply => 'D' },
+ '127.0.1.1' => { C_reply => 'U trusted',
+ H_reply => 'D' },
+ '127.0.1.2' => { C_reply => 'u untrusted',
+ H_reply => 'D' },
+ # 127.0.2.x: iI/iN functionality.
+ '127.0.2.0' => { C_reply => 'N iauth.assigned.host',
+ H_reply => 'D' },
+ '127.0.2.1' => { C_reply => \&ip_change },
+ # 127.0.3.x: iC/sP functionality.
+ '127.0.3.0' => { C_reply => 'C Please enter the password.',
+ P_reply => \&passwd_check },
+);
+
+sub handle_new_client ($$$$) {
+ my ($id, $ip, $port, $extra) = @_;
+ my $handler = $handlers{$ip} || $handlers{default};
+ my $client = { id => $id, ip => $ip, port => $port, handler => $handler };
+
+ # If we have any deferred reply handlers, we must save the client.
+ $pending{$id} = $client if grep /^[^C]_reply$/, keys %$handler;
+ reply $client->{handler}->{C_reply}, $client, $extra;
+}
+
+sub ip_change ($$) {
+ my ($client, $extra) = @_;
+ reply 'I 127.255.255.254', $client;
+ $client->{ip} = '127.255.255.254';
+ reply 'N other.assigned.host', $client;
+ reply 'D', $client;
+}
+
+sub passwd_check ($$) {
+ my ($client, $extra) = @_;
+ if ($extra eq 'secret') {
+ reply 'D', $client;
+ } else {
+ reply 'C :Bad password', $client;
+ }
+}
+
+open LOG, ">> iauth.log";
+populate_signals();
+assign_signal_handlers();
+autoflush LOG 1;
+autoflush STDOUT 1;
+autoflush STDERR 1;
+dolog "IAuth starting " . scalar(localtime(time));
+
+while (<>) {
+ my ($id, $client);
+
+ # Chomp newline and log incoming message.
+ s/\r?\n?\r?$//;
+ dolog "> $_";
+
+ # If there's an ID at the start of the line, parse it out.
+ if (s/^(\d+) //) { $id = $1; $client = $pending{$id}; }
+
+ # Figure out how to handle the command.
+ if (/^C (\S+) (\S+) (.+)$/) {
+ handle_new_client($id, $1, $2, $3);
+ } elsif (/^([DT])/ and $client) {
+ reply $client->{handler}->{"${1}_reply"}, $client;
+ delete $pending{$id};
+ } elsif (/^([d])/ and $client) {
+ reply $client->{handler}->{"${1}_reply"}, $client;
+ } elsif (/^([HNPUu]) (.+)/ and $client) {
+ reply $client->{handler}->{"${1}_reply"}, $client, $2;
+ }
+}