From: Michael Poole Date: Thu, 16 Feb 2006 03:49:55 +0000 (+0000) Subject: Implement a progressive iauth system. X-Git-Url: http://git.pk910.de/?p=ircu2.10.12-pk.git;a=commitdiff_plain;h=f1acbdf96cb8c8094df4aaf8011b20c7332ba2d6 Implement a progressive iauth system. git-svn-id: file:///home/klmitch/undernet-ircu/undernet-ircu-svn/ircu2/branches/u2_10_12_branch@1620 c9e4aea6-c8fd-4c43-8297-357d70d61c8c --- diff --git a/ChangeLog b/ChangeLog index 0067eb6..8e6a0fd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,85 @@ +2006-02-15 Michael Poole + + * 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 * ircd/ircd_snprintf.c (doprintf): Fix typecast for %hu. diff --git a/doc/example.conf b/doc/example.conf index 44eae6f..d90e0c0 100644 --- a/doc/example.conf +++ b/doc/example.conf @@ -845,6 +845,7 @@ features # "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"; diff --git a/doc/readme.features b/doc/readme.features index bef0e1c..6412b94 100644 --- a/doc/readme.features +++ b/doc/readme.features @@ -654,6 +654,13 @@ HIS_STATS_z 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 diff --git a/doc/readme.iauth b/doc/readme.iauth index 455d9da..6aaec31 100644 --- a/doc/readme.iauth +++ b/doc/readme.iauth @@ -1,82 +1,431 @@ 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 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 (, and +). 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: X +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: C +Example: 5 C 192.168.1.10 23367 192.168.0.1 6667 +States: GONE +Next State: REGISTER +Comments: Indicates that on accepted a client + connection from on . + +D - Client Disconnect +Syntax: 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: N +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 [password] -If ICLASS is enabled, it sends a list of currently connected users: - MyUsers :@: ... -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: 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: P : +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: U : +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 data. + +u - Client Username +Syntax: u +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: n +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: H +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. 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: 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: E : +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 argument specifies the + general type of error and provides details. + may be -1. + +M - Server Name and Capacity +Syntax: M +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 information. + The should be ignored: ircd sends 0 and ircu sends -1. + +IAUTH MESSAGES ============== -When users connect, the IRC server sends a DoAuth request: - FullAuth - 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). may not contain a colon character. -is the client's initially requested nickname. is the -username returned by the ident server (RFC 1413), or a tilde-prefixed -username supplied by the user. is a text hostname, -possibly in the form of a dotted quad or IPv6 address, or the -character '?'. is a dotted quad IPv4 address or an IPv6 hex -address. and are optional, and are used when the -client attempts login-on-connect. is the realname -specified by the client's USER message, and may contain spaces. - -If the client is accepted, the IAUTH server responds: - DoneAuth [account] - is a replacement username, and is a replacement -hostname. If the from DoAuth was ?, is the -result of a DNS lookup for the client. is the name of a -connection class for the client. is optional and is -provided if the user's login was successful. - -If the client is rejected, the IAUTH server responds: - BadAuth : - may include spaces, and should have a leading ':' sentinel. - -DISCONNECTS -=========== - -If ICLASS is enabled, the IRC server sends ExitUser when a client -disconnects: - ExitUser - -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 +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: > : +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 +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 +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 : +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 : +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 : +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 +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 +Example: U 5 192.168.1.10 23367 buddha +States: REGISTER, HURRY +Next State: - +Comments: Indicates that the iauth instance believes is + accurate for the specified client. + +u - Untrusted Username +Syntax: u +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 + to be accurate, but has no more trusted username. + +N - Client Hostname +Syntax: N +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 +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 . This means that + future iauth messages relating to the client must use + as the parameter. +Compatibility: This is an Undernet extension and ircd does not support + this message. + +C - Challenge User +Syntax: C : +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 :*** . + The client responds by sending PASS :, 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 : +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 : +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 [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 [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. diff --git a/doc/snomask.html b/doc/snomask.html index 8abc768..237a3be 100644 --- a/doc/snomask.html +++ b/doc/snomask.html @@ -137,6 +137,12 @@ Usage: 0x10000 /* debugging messages (DEBUGMODE only) */ + +131072 +SNO_AUTH +0x20000 +/* iauth status messages */ + @@ -153,7 +159,7 @@ Usage: only opers may set -SNO_OPER (SNO_CONNEXIT | SNO_OLDREALOP) +SNO_OPER (SNO_CONNEXIT | SNO_OLDREALOP | SNO_AUTH) diff --git a/include/client.h b/include/client.h index 6e23e42..aea930a 100644 --- a/include/client.h +++ b/include/client.h @@ -155,7 +155,6 @@ enum Flag 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 */ @@ -198,7 +197,6 @@ struct Connection 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 */ @@ -211,7 +209,6 @@ struct Connection 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 @@ -233,8 +230,7 @@ struct Connection 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. */ @@ -264,13 +260,6 @@ struct Client { 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 @@ -322,8 +311,6 @@ struct Client { #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). */ @@ -345,8 +332,6 @@ struct Client { #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. */ @@ -389,8 +374,6 @@ struct 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)) @@ -424,8 +407,6 @@ struct Client { #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. */ @@ -446,8 +427,6 @@ struct Client { #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. */ @@ -476,8 +455,6 @@ struct Client { #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 */ @@ -572,8 +549,6 @@ struct Client { #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). */ @@ -626,8 +601,6 @@ struct Client { #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. */ @@ -716,12 +689,13 @@ struct Client { #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. */ @@ -732,7 +706,7 @@ struct Client { /** 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) diff --git a/include/ircd_auth.h b/include/ircd_auth.h deleted file mode 100644 index 9d36764..0000000 --- a/include/ircd_auth.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef INCLUDED_ircd_auth_h -#define INCLUDED_ircd_auth_h - -/* - * IRC - Internet Relay Chat, ircd/ircd_auth.h - * Copyright 2004 Michael Poole - * - * 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 diff --git a/include/ircd_features.h b/include/ircd_features.h index 524f23a..737ffc9 100644 --- a/include/ircd_features.h +++ b/include/ircd_features.h @@ -135,6 +135,7 @@ enum Feature { 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, diff --git a/include/s_auth.h b/include/s_auth.h index 2925339..6e2a067 100644 --- a/include/s_auth.h +++ b/include/s_auth.h @@ -30,51 +30,24 @@ #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 */ diff --git a/include/s_user.h b/include/s_user.h index a395f35..205776c 100644 --- a/include/s_user.h +++ b/include/s_user.h @@ -50,8 +50,6 @@ struct Flags; #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. @@ -64,8 +62,7 @@ typedef void (*InfoFormatter)(struct Client* who, struct Client *sptr, struct Ms */ 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); diff --git a/ircd/Makefile.in b/ircd/Makefile.in index f0a96eb..22c5a06 100644 --- a/ircd/Makefile.in +++ b/ircd/Makefile.in @@ -98,7 +98,6 @@ IRCD_SRC = \ hash.c \ ircd.c \ ircd_alloc.c \ - ircd_auth.c \ ircd_crypt.c \ ircd_events.c \ ircd_features.c \ @@ -458,17 +457,6 @@ ircd.o: ircd.c ../config.h ../include/ircd.h ../include/struct.h \ 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 \ @@ -534,11 +522,11 @@ ircd_reslib.o: ircd_reslib.c ../include/ircd.h ../include/struct.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 \ @@ -633,7 +621,8 @@ m_cap.o: m_cap.c ../config.h ../include/client.h ../include/ircd_defs.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 \ @@ -780,7 +769,8 @@ m_join.o: m_join.c ../config.h ../include/channel.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 \ @@ -920,7 +910,8 @@ m_pass.o: m_pass.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_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 \ @@ -936,7 +927,7 @@ m_pong.o: m_pong.c ../config.h ../include/client.h ../include/ircd_defs.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 \ @@ -1116,8 +1107,8 @@ m_user.o: m_user.c ../config.h ../include/handlers.h ../include/client.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 \ @@ -1272,18 +1263,19 @@ random.o: random.c ../config.h ../include/random.h ../include/client.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 \ @@ -1308,12 +1300,12 @@ s_conf.o: s_conf.c ../config.h ../include/s_conf.h ../include/client.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 \ @@ -1336,13 +1328,13 @@ s_misc.o: s_misc.c ../config.h ../include/s_misc.h ../include/IPcheck.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 \ @@ -1380,28 +1372,28 @@ s_stats.o: s_stats.c ../config.h ../include/class.h ../include/client.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 \ @@ -1462,18 +1454,25 @@ y.tab.o: y.tab.c ../config.h ../include/s_conf.h ../include/client.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 diff --git a/ircd/ircd_auth.c b/ircd/ircd_auth.c deleted file mode 100644 index c38bf5c..0000000 --- a/ircd/ircd_auth.c +++ /dev/null @@ -1,835 +0,0 @@ -/* - * IRC - Internet Relay Chat, ircd/ircd_auth.c - * Copyright 2004 Michael Poole - * - * 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 -- Now using assert in ircd_log.h */ -#include -#include -#include -#include -#include -#include -#ifdef HAVE_STDINT_H -#include -#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); -} diff --git a/ircd/ircd_features.c b/ircd/ircd_features.c index 0ffad1a..fa33160 100644 --- a/ircd/ircd_features.c +++ b/ircd/ircd_features.c @@ -388,6 +388,7 @@ static struct FeatureDesc { 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), diff --git a/ircd/ircd_lexer.l b/ircd/ircd_lexer.l index 0df1f70..4ec8a67 100644 --- a/ircd/ircd_lexer.l +++ b/ircd/ircd_lexer.l @@ -102,6 +102,7 @@ static struct lexer_token { TOKEN(USERMODE), TOKEN(FAST), TOKEN(AUTOCONNECT), + TOKEN(PROGRAM), #undef TOKEN { "administrator", ADMIN }, { "apass_opmode", TPRIV_APASS_OPMODE }, diff --git a/ircd/ircd_log.c b/ircd/ircd_log.c index 02aeb13..dda7141 100644 --- a/ircd/ircd_log.c +++ b/ircd/ircd_log.c @@ -126,7 +126,7 @@ static struct { 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 } }; diff --git a/ircd/ircd_parser.y b/ircd/ircd_parser.y index bd86914..692616b 100644 --- a/ircd/ircd_parser.y +++ b/ircd/ircd_parser.y @@ -32,7 +32,6 @@ #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" @@ -47,6 +46,7 @@ #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" @@ -156,6 +156,7 @@ static void parse_error(char *pattern,...) { %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 @@ -910,21 +911,14 @@ featureitem: QSTRING { 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) @@ -1002,45 +996,17 @@ pseudoflags: FAST ';' 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 ';'; diff --git a/ircd/list.c b/ircd/list.c index dcbe466..cbf98c5 100644 --- a/ircd/list.c +++ b/ircd/list.c @@ -219,7 +219,6 @@ struct Client* make_client(struct Client *from, int status) 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 */ diff --git a/ircd/m_cap.c b/ircd/m_cap.c index a415933..2443c42 100644 --- a/ircd/m_cap.c +++ b/ircd/m_cap.c @@ -36,6 +36,7 @@ #include "msg.h" #include "numeric.h" #include "send.h" +#include "s_auth.h" #include "s_user.h" #include @@ -193,8 +194,7 @@ static int 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 */ } @@ -209,7 +209,7 @@ cap_req(struct Client *sptr, const char *caplist) 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)); @@ -300,12 +300,7 @@ cap_end(struct Client *sptr, const char *caplist) 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 diff --git a/ircd/m_pass.c b/ircd/m_pass.c index d25d8a9..b1aef62 100644 --- a/ircd/m_pass.c +++ b/ircd/m_pass.c @@ -85,6 +85,7 @@ #include "ircd_log.h" #include "ircd_reply.h" #include "ircd_string.h" +#include "s_auth.h" #include "send.h" /* #include -- Now using assert in ircd_log.h */ @@ -94,21 +95,28 @@ */ 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; } diff --git a/ircd/m_pong.c b/ircd/m_pong.c index f66668d..9e43395 100644 --- a/ircd/m_pong.c +++ b/ircd/m_pong.c @@ -91,6 +91,7 @@ #include "numeric.h" #include "numnicks.h" #include "opercmds.h" +#include "s_auth.h" #include "s_user.h" #include "send.h" @@ -163,25 +164,7 @@ int mr_pong(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) 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; } /* diff --git a/ircd/m_user.c b/ircd/m_user.c index efbe6eb..babbd34 100644 --- a/ircd/m_user.c +++ b/ircd/m_user.c @@ -90,6 +90,7 @@ #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" @@ -113,7 +114,6 @@ int m_user(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { char* username; const char* info; - struct User* user; assert(0 != cptr); assert(cptr == sptr); @@ -139,23 +139,6 @@ int m_user(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) 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); } diff --git a/ircd/s_auth.c b/ircd/s_auth.c index 3aa6da4..15ccb13 100644 --- a/ircd/s_auth.c +++ b/ircd/s_auth.c @@ -24,6 +24,10 @@ * released, the server does not know it exists and does not process * any messages from it. * --Bleep Thomas Helvey + * + * 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. @@ -32,6 +36,7 @@ #include "config.h" #include "s_auth.h" +#include "class.h" #include "client.h" #include "IPcheck.h" #include "ircd.h" @@ -41,31 +46,64 @@ #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 /* struct hostent */ +#include #include #include #include -#include #include -/* #include -- Now using assert in ircd_log.h */ #include #include +/** 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; @@ -73,7 +111,6 @@ static struct { #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"), @@ -88,7 +125,6 @@ static struct { typedef enum { REPORT_DO_DNS, REPORT_FIN_DNS, - REPORT_FIN_DNSC, REPORT_FAIL_DNS, REPORT_DO_ID, REPORT_FIN_ID, @@ -101,357 +137,383 @@ typedef enum { #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. */ @@ -464,7 +526,7 @@ enum IdentReplyFields { }; /** 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) @@ -531,132 +593,1452 @@ 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."); } diff --git a/ircd/s_conf.c b/ircd/s_conf.c index 0c1b230..f4dde53 100644 --- a/ircd/s_conf.c +++ b/ircd/s_conf.c @@ -34,7 +34,6 @@ #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" @@ -49,6 +48,7 @@ #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" @@ -949,14 +949,14 @@ int rehash(struct Client *cptr, int sig) 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 */ diff --git a/ircd/s_misc.c b/ircd/s_misc.c index 3463854..8eab44e 100644 --- a/ircd/s_misc.c +++ b/ircd/s_misc.c @@ -33,7 +33,6 @@ #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" @@ -47,6 +46,7 @@ #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" @@ -266,8 +266,6 @@ static void exit_one_client(struct Client* bcptr, const char* comment) 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 */ @@ -379,6 +377,9 @@ int exit_client(struct Client *cptr, 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, diff --git a/ircd/s_stats.c b/ircd/s_stats.c index c37630b..bfc2d58 100644 --- a/ircd/s_stats.c +++ b/ircd/s_stats.c @@ -43,6 +43,7 @@ #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" @@ -617,6 +618,12 @@ struct StatDesc statsinfo[] = { { '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." }, diff --git a/ircd/s_user.c b/ircd/s_user.c index d0a6217..8226a8e 100644 --- a/ircd/s_user.c +++ b/ircd/s_user.c @@ -34,7 +34,6 @@ #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" @@ -51,6 +50,7 @@ #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" @@ -304,40 +304,6 @@ int hunt_server_prio_cmd(struct Client *from, const char *cmd, const char *tok, } -/** 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 * @@ -369,29 +335,13 @@ static char *clean_user_id(char *dest, char *source, int tilde) * * @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; @@ -400,165 +350,8 @@ int register_user(struct Client *cptr, struct Client *sptr, 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); @@ -569,7 +362,7 @@ int register_user(struct Client *cptr, struct Client *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... */ @@ -621,12 +414,10 @@ int register_user(struct Client *cptr, struct Client *sptr, } } 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])", @@ -671,7 +462,8 @@ int register_user(struct Client *cptr, struct Client *sptr, 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), @@ -680,7 +472,8 @@ int register_user(struct Client *cptr, struct Client *sptr, 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), @@ -789,6 +582,7 @@ int set_nick_name(struct Client* cptr, struct Client* sptr, 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); @@ -807,7 +601,7 @@ int set_nick_name(struct Client* cptr, struct Client* sptr, 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]) { /* @@ -880,36 +674,9 @@ int set_nick_name(struct Client* cptr, struct Client* sptr, } 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; } diff --git a/tools/iauth-test b/tools/iauth-test new file mode 100755 index 0000000..2629b52 --- /dev/null +++ b/tools/iauth-test @@ -0,0 +1,238 @@ +#! /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; + } +}