/** Remove all conf entries from the client except those which match * the status field mask. * @param cptr Client to operate on. * @param mask ConfItem types to keep. */ void det_confs_butmask(struct Client* cptr, int mask) { struct SLink* link; struct SLink* next; assert(0 != cptr); for (link = cli_confs(cptr); link; link = next) { next = link->next; if ((link->value.aconf->status & mask) == 0) detach_conf(cptr, link->value.aconf); } }
/* * m_challenge - generate RSA challenge for wouldbe oper * parv[0] = sender prefix * parv[1] = operator to challenge for, or +response * */ static void m_challenge(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { char *challenge; dlink_node *ptr; struct ConfItem *aconf, *oconf; if(!(source_p->user) || !source_p->localClient) return; /* if theyre an oper, reprint oper motd and ignore */ if(IsOper(source_p)) { sendto_one(source_p, form_str(RPL_YOUREOPER), me.name, parv[0]); SendMessageFile(source_p, &ConfigFileEntry.opermotd); return; } if(*parv[1] == '+') { /* Ignore it if we aren't expecting this... -A1kmm */ if(!source_p->user->response) return; if(irccmp(source_p->user->response, ++parv[1])) { sendto_one(source_p, form_str(ERR_PASSWDMISMATCH), me.name, source_p->name); log_foper(source_p, source_p->user->auth_oper); if(ConfigFileEntry.failed_oper_notice) sendto_realops_flags(UMODE_ALL, L_ALL, "Failed OPER attempt by %s (%s@%s)", source_p->name, source_p->username, source_p->host); return; } if((aconf = find_conf_by_name(source_p->user->auth_oper, CONF_OPERATOR)) == NULL) { sendto_one(source_p, form_str(ERR_NOOPERHOST), me.name, parv[0]); log_foper(source_p, source_p->user->auth_oper); if(ConfigFileEntry.failed_oper_notice) sendto_realops_flags(UMODE_ALL, L_ALL, "Failed CHALLENGE attempt - host mismatch by %s (%s@%s)", source_p->name, source_p->username, source_p->host); return; } ptr = source_p->localClient->confs.head; oconf = ptr->data; detach_conf(source_p, oconf); if(attach_conf(source_p, aconf) != 0) { sendto_one(source_p, ":%s NOTICE %s :Can't attach conf!", me.name, source_p->name); sendto_realops_flags(UMODE_ALL, L_ALL, "Failed CHALLENGE attempt by %s (%s@%s) can't attach conf!", source_p->name, source_p->username, source_p->host); log_foper(source_p, source_p->user->auth_oper); attach_conf(source_p, oconf); return; } oper_up(source_p, aconf); ilog(L_TRACE, "OPER %s by %s!%s@%s", source_p->user->auth_oper, source_p->name, source_p->username, source_p->host); log_oper(source_p, source_p->user->auth_oper); MyFree(source_p->user->response); MyFree(source_p->user->auth_oper); source_p->user->response = NULL; source_p->user->auth_oper = NULL; return; } MyFree(source_p->user->response); MyFree(source_p->user->auth_oper); source_p->user->response = NULL; source_p->user->auth_oper = NULL; if(!(aconf = find_conf_exact(parv[1], source_p->username, source_p->host, CONF_OPERATOR)) && !(aconf = find_conf_exact(parv[1], source_p->username, source_p->localClient->sockhost, CONF_OPERATOR))) { sendto_one(source_p, form_str(ERR_NOOPERHOST), me.name, parv[0]); log_foper(source_p, parv[1]); if(ConfigFileEntry.failed_oper_notice) sendto_realops_flags(UMODE_ALL, L_ALL, "Failed CHALLENGE attempt - host mismatch by %s (%s@%s)", source_p->name, source_p->username, source_p->host); return; } if(!aconf->rsa_public_key) { sendto_one(source_p, ":%s NOTICE %s :I'm sorry, PK authentication " "is not enabled for your oper{} block.", me.name, parv[0]); return; } if(!generate_challenge(&challenge, &(source_p->user->response), aconf->rsa_public_key)) { sendto_one(source_p, form_str(RPL_RSACHALLENGE), me.name, parv[0], challenge); } DupString(source_p->user->auth_oper, aconf->name); MyFree(challenge); return; }
/* ** m_oper ** parv[0] = sender prefix ** parv[1] = oper name ** parv[2] = oper password */ static void m_oper(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { struct ConfItem *aconf; struct ConfItem *oconf = NULL; char *name; char *password; dlink_node *ptr; name = parv[1]; password = parv[2]; if(EmptyString(password)) { sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS), me.name, source_p->name, "OPER"); return; } /* end the grace period */ if(!IsFloodDone(source_p)) flood_endgrace(source_p); if((aconf = find_password_aconf(name, source_p)) == NULL) { sendto_one(source_p, form_str(ERR_NOOPERHOST), me.name, source_p->name); log_foper(source_p, name); if(ConfigFileEntry.failed_oper_notice) { sendto_realops_flags(UMODE_ALL, L_ALL, "Failed OPER attempt - host mismatch by %s (%s@%s)", source_p->name, source_p->username, source_p->host); } return; } if(match_oper_password(password, aconf)) { /* 20001216: detach old iline -einride */ ptr = source_p->localClient->confs.head; if(ptr) { oconf = ptr->data; detach_conf(source_p, oconf); } if(attach_conf(source_p, aconf) != 0) { sendto_one(source_p, ":%s NOTICE %s :Can't attach conf!", me.name, source_p->name); sendto_realops_flags(UMODE_ALL, L_ALL, "Failed OPER attempt by %s (%s@%s) can't attach conf!", source_p->name, source_p->username, source_p->host); log_foper(source_p, name); /* 20001216: Reattach old iline -einride */ attach_conf(source_p, oconf); return; } oper_up(source_p, aconf); ilog(L_TRACE, "OPER %s by %s!%s@%s", name, source_p->name, source_p->username, source_p->host); log_oper(source_p, name); return; } else { sendto_one(source_p, form_str(ERR_PASSWDMISMATCH), me.name, parv[0]); log_foper(source_p, name); if(ConfigFileEntry.failed_oper_notice) { sendto_realops_flags(UMODE_ALL, L_ALL, "Failed OPER attempt by %s (%s@%s)", source_p->name, source_p->username, source_p->host); } } }
/* * close_connection * Close the physical connection. This function must make * MyConnect(client_p) == FALSE, and set client_p->from == NULL. */ void close_connection(struct Client *client_p) { struct ConfItem *conf; struct AccessItem *aconf; struct ClassItem *aclass; assert(NULL != client_p); if (!IsDead(client_p)) { /* attempt to flush any pending dbufs. Evil, but .. -- adrian */ /* there is still a chance that we might send data to this socket * even if it is marked as blocked (COMM_SELECT_READ handler is called * before COMM_SELECT_WRITE). Let's try, nothing to lose.. -adx */ ClearSendqBlocked(client_p); send_queued_write(client_p); } if (IsServer(client_p)) { ++ServerStats.is_sv; ServerStats.is_sbs += client_p->localClient->send.bytes; ServerStats.is_sbr += client_p->localClient->recv.bytes; ServerStats.is_sti += CurrentTime - client_p->firsttime; /* XXX Does this even make any sense at all anymore? * scheduling a 'quick' reconnect could cause a pile of * nick collides under TSora protocol... -db */ /* * If the connection has been up for a long amount of time, schedule * a 'quick' reconnect, else reset the next-connect cycle. */ if ((conf = find_conf_exact(SERVER_TYPE, client_p->name, client_p->username, client_p->host))) { /* * Reschedule a faster reconnect, if this was a automatically * connected configuration entry. (Note that if we have had * a rehash in between, the status has been changed to * CONF_ILLEGAL). But only do this if it was a "good" link. */ aconf = (struct AccessItem *)map_to_conf(conf); aclass = (struct ClassItem *)map_to_conf(aconf->class_ptr); aconf->hold = time(NULL); aconf->hold += (aconf->hold - client_p->since > HANGONGOODLINK) ? HANGONRETRYDELAY : ConFreq(aclass); if (nextconnect > aconf->hold) nextconnect = aconf->hold; } } else if (IsClient(client_p)) { ++ServerStats.is_cl; ServerStats.is_cbs += client_p->localClient->send.bytes; ServerStats.is_cbr += client_p->localClient->recv.bytes; ServerStats.is_cti += CurrentTime - client_p->firsttime; } else ++ServerStats.is_ni; #ifdef HAVE_LIBCRYPTO if (client_p->localClient->fd.ssl) { SSL_set_shutdown(client_p->localClient->fd.ssl, SSL_RECEIVED_SHUTDOWN); if (!SSL_shutdown(client_p->localClient->fd.ssl)) SSL_shutdown(client_p->localClient->fd.ssl); } #endif if (client_p->localClient->fd.flags.open) fd_close(&client_p->localClient->fd); if (HasServlink(client_p)) { if (client_p->localClient->ctrlfd.flags.open) fd_close(&client_p->localClient->ctrlfd); } dbuf_clear(&client_p->localClient->buf_sendq); dbuf_clear(&client_p->localClient->buf_recvq); MyFree(client_p->localClient->passwd); detach_conf(client_p, CONF_TYPE); client_p->from = NULL; /* ...this should catch them! >:) --msa */ }
/*! \brief SVSMODE command handler * * \param source_p Pointer to allocated Client struct from which the message * originally comes from. This can be a local or remote client. * \param parc Integer holding the number of supplied arguments. * \param parv Argument vector where parv[0] .. parv[parc-1] are non-NULL * pointers. * \note Valid arguments for this command are: * - parv[0] = command * - parv[1] = nickname * - parv[2] = TS * - parv[3] = mode * - parv[4] = optional argument (services account, vhost) */ static int ms_svsmode(struct Client *source_p, int parc, char *parv[]) { const struct user_modes *tab = NULL; struct Client *target_p = NULL; int what = MODE_ADD; unsigned int setmodes = 0; const char *modes = NULL, *extarg = NULL; time_t ts = 0; if (!HasFlag(source_p, FLAGS_SERVICE)) return 0; ts = atol(parv[2]); modes = parv[3]; extarg = (parc > 4) ? parv[4] : NULL; if ((target_p = find_person(source_p, parv[1])) == NULL) return 0; if (ts && (ts != target_p->tsinfo)) return 0; setmodes = target_p->umodes; for (const char *m = modes; *m; ++m) { switch (*m) { case '+': what = MODE_ADD; break; case '-': what = MODE_DEL; break; case 'd': if (!EmptyString(extarg)) strlcpy(target_p->account, extarg, sizeof(target_p->account)); break; case 'x': if (!EmptyString(extarg) && valid_hostname(extarg)) user_set_hostmask(target_p, extarg, what); break; case 'o': if (what == MODE_DEL && HasUMode(target_p, UMODE_OPER)) { ClearOper(target_p); --Count.oper; if (MyConnect(target_p)) { dlink_node *node = NULL; detach_conf(target_p, CONF_OPER); ClrOFlag(target_p); DelUMode(target_p, ConfigGeneral.oper_only_umodes); if ((node = dlinkFindDelete(&oper_list, target_p))) free_dlink_node(node); } } break; case 'i': if (what == MODE_ADD && !HasUMode(target_p, UMODE_INVISIBLE)) { AddUMode(target_p, UMODE_INVISIBLE); ++Count.invisi; } if (what == MODE_DEL && HasUMode(target_p, UMODE_INVISIBLE)) { DelUMode(target_p, UMODE_INVISIBLE); --Count.invisi; } break; case 'S': /* Only servers may set +S in a burst */ case 'W': /* Only servers may set +W in a burst */ break; default: if ((tab = umode_map[(unsigned char)*m])) { if (what == MODE_ADD) AddUMode(target_p, tab->flag); else DelUMode(target_p, tab->flag); } break; } } if (extarg) sendto_server(source_p, 0, 0, ":%s SVSMODE %s %lu %s %s", source_p->id, target_p->id, (unsigned long)target_p->tsinfo, modes, extarg); else sendto_server(source_p, 0, 0, ":%s SVSMODE %s %lu %s", source_p->id, target_p->id, (unsigned long)target_p->tsinfo, modes); if (MyConnect(target_p) && (setmodes != target_p->umodes)) { char modebuf[IRCD_BUFSIZE] = ""; send_umode(target_p, target_p, setmodes, modebuf); } return 0; }
/*! \brief SVSMODE command handler (called by services) * * \param client_p Pointer to allocated Client struct with physical connection * to this server, i.e. with an open socket connected. * \param source_p Pointer to allocated Client struct from which the message * originally comes from. This can be a local or remote client. * \param parc Integer holding the number of supplied arguments. * \param parv Argument vector where parv[0] .. parv[parc-1] are non-NULL * pointers. * \note Valid arguments for this command are: * - parv[0] = sender prefix * - parv[1] = nickname * - parv[2] = TS (or mode, depending on svs version) * - parv[3] = mode (or services id if old svs version) * - parv[4] = optional argument (services id) */ static void ms_svsmode(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { struct Client *target_p = NULL; int what = MODE_ADD; unsigned int flag = 0, setflags = 0; char *m = NULL, *modes = NULL, *extarg = NULL; time_t ts = 0; if (!HasFlag(source_p, FLAGS_SERVICE)) return; if ((parc >= 4) && ((*parv[3] == '+') || (*parv[3] == '-'))) { ts = atol(parv[2]); modes = parv[3]; extarg = (parc > 4) ? parv[4] : NULL; } else { modes = parv[2]; extarg = (parc > 3) ? parv[3] : NULL; } if ((target_p = find_person(client_p, parv[1])) == NULL) return; if (ts && (ts != target_p->tsinfo)) return; setflags = target_p->umodes; for (m = modes; *m; ++m) { switch (*m) { case '+': what = MODE_ADD; break; case '-': what = MODE_DEL; break; case 'x': if (what == MODE_ADD && extarg) user_set_hostmask(target_p, extarg); break; case 'd': if (!EmptyString(extarg)) strlcpy(target_p->svid, extarg, sizeof(target_p->svid)); break; case 'o': if (what == MODE_DEL && HasUMode(target_p, UMODE_OPER)) { ClearOper(target_p); Count.oper--; if (MyConnect(target_p)) { dlink_node *dm = NULL; detach_conf(target_p, OPER_TYPE); ClrOFlag(target_p); DelUMode(target_p, ConfigFileEntry.oper_only_umodes); if ((dm = dlinkFindDelete(&oper_list, target_p)) != NULL) free_dlink_node(dm); } } break; case 'i': if (what == MODE_ADD && !HasUMode(target_p, UMODE_INVISIBLE)) { AddUMode(target_p, UMODE_INVISIBLE); ++Count.invisi; } if (what == MODE_DEL && HasUMode(target_p, UMODE_INVISIBLE)) { DelUMode(target_p, UMODE_INVISIBLE); --Count.invisi; } break; case ' ': case '\n': case '\r': case '\t': break; default: if ((flag = user_modes[(unsigned char) * m])) execute_callback(umode_cb, client_p, target_p, what, flag); break; } } if (extarg) { sendto_server(client_p, CAP_TS6, NOCAPS, ":%s SVSMODE %s %lu %s %s", ID(source_p), ID(target_p), (unsigned long)target_p->tsinfo, modes, extarg); sendto_server(client_p, NOCAPS, CAP_TS6, ":%s SVSMODE %s %lu %s %s", source_p->name, target_p->name, (unsigned long)target_p->tsinfo, modes, extarg); } else { sendto_server(client_p, CAP_TS6, NOCAPS, ":%s SVSMODE %s %lu %s", ID(source_p), ID(target_p), (unsigned long)target_p->tsinfo, modes); sendto_server(client_p, NOCAPS, CAP_TS6, ":%s SVSMODE %s %lu %s", source_p->name, target_p->name, (unsigned long)target_p->tsinfo, modes); } if (MyConnect(target_p) && (setflags != target_p->umodes)) { char modebuf[IRCD_BUFSIZE]; send_umode(target_p, target_p, setflags, 0xffffffff, modebuf); } }
/* set_user_mode() * * added 15/10/91 By Darren Reed. * parv[0] - command * parv[1] - username to change mode for * parv[2] - modes to change */ static void set_user_mode(struct Client *source_p, const int parc, char *parv[]) { const struct user_modes *tab = NULL; const unsigned int setmodes = source_p->umodes; const struct Client *target_p = NULL; int what = MODE_ADD, badmode = 0; if ((target_p = find_person(source_p, parv[1])) == NULL) { if (MyConnect(source_p)) sendto_one_numeric(source_p, &me, ERR_NOSUCHCHANNEL, parv[1]); return; } if (source_p != target_p) { sendto_one_numeric(source_p, &me, ERR_USERSDONTMATCH); return; } if (parc < 3) { char buf[IRCD_BUFSIZE] = ""; char *m = buf; *m++ = '+'; for (tab = umode_tab; tab->c; ++tab) if (HasUMode(source_p, tab->flag)) *m++ = tab->c; *m = '\0'; sendto_one_numeric(source_p, &me, RPL_UMODEIS, buf); return; } /* Parse user mode change string */ for (const char *m = parv[2]; *m; ++m) { switch (*m) { case '+': what = MODE_ADD; break; case '-': what = MODE_DEL; break; case 'o': if (what == MODE_ADD) { if (!MyConnect(source_p) && !HasUMode(source_p, UMODE_OPER)) { ++Count.oper; SetOper(source_p); } } else { if (!HasUMode(source_p, UMODE_OPER)) break; ClearOper(source_p); --Count.oper; if (MyConnect(source_p)) { dlink_node *node = NULL; detach_conf(source_p, CONF_OPER); ClrOFlag(source_p); DelUMode(source_p, ConfigGeneral.oper_only_umodes); if ((node = dlinkFindDelete(&oper_list, source_p))) free_dlink_node(node); } } break; case 'S': /* Only servers may set +S in a burst */ case 'W': /* Only servers may set +W in a burst */ case 'r': /* Only services may set +r */ case 'x': /* Only services may set +x */ break; default: if ((tab = umode_map[(unsigned char)*m])) { if (MyConnect(source_p) && !HasUMode(source_p, UMODE_OPER) && (ConfigGeneral.oper_only_umodes & tab->flag)) badmode = 1; else { if (what == MODE_ADD) AddUMode(source_p, tab->flag); else DelUMode(source_p, tab->flag); } } else if (MyConnect(source_p)) badmode = 1; break; } } if (badmode) sendto_one_numeric(source_p, &me, ERR_UMODEUNKNOWNFLAG); if (MyConnect(source_p) && HasUMode(source_p, UMODE_ADMIN) && !HasOFlag(source_p, OPER_FLAG_ADMIN)) { sendto_one_notice(source_p, &me, ":*** You have no admin flag;"); DelUMode(source_p, UMODE_ADMIN); } if (!(setmodes & UMODE_INVISIBLE) && HasUMode(source_p, UMODE_INVISIBLE)) ++Count.invisi; if ((setmodes & UMODE_INVISIBLE) && !HasUMode(source_p, UMODE_INVISIBLE)) --Count.invisi; /* * Compare new modes with old modes and send string which will cause * servers to update correctly. */ send_umode_out(source_p, setmodes); }