Пример #1
0
/** auth get handler */
static void _authreg_auth_get(c2s_t c2s, sess_t sess, nad_t nad) {
    int ns, elem, attr, err;
    char username[1024], id[128];
    int ar_mechs;

    /* can't auth if they're active */
    if(sess->active) {
        sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_NOT_ALLOWED), 0));
        return;
    }

    /* sort out the username */
    ns = nad_find_scoped_namespace(nad, uri_AUTH, NULL);
    elem = nad_find_elem(nad, 1, ns, "username", 1);
    if(elem < 0)
    {
        log_debug(ZONE, "auth get with no username, bouncing it");

        sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_BAD_REQUEST), 0));

        return;
    }

    snprintf(username, 1024, "%.*s", NAD_CDATA_L(nad, elem), NAD_CDATA(nad, elem));
    if(stringprep_xmpp_nodeprep(username, 1024) != 0) {
        log_debug(ZONE, "auth get username failed nodeprep, bouncing it");
        sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_JID_MALFORMED), 0));
        return;
    }

    ar_mechs = c2s->ar_mechanisms;
    if (sess->s->ssf>0) 
        ar_mechs = ar_mechs | c2s->ar_ssl_mechanisms;
        
    /* no point going on if we have no mechanisms */
    if(!(ar_mechs & (AR_MECH_TRAD_PLAIN | AR_MECH_TRAD_DIGEST | AR_MECH_TRAD_CRAMMD5))) {
        sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_FORBIDDEN), 0));
        return;
    }
    
    /* do we have the user? */
    if((sess->host->ar->user_exists)(sess->host->ar, sess, username, sess->host->realm) == 0) {
        sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_OLD_UNAUTH), 0));
        return;
    }

    /* extract the id */
    attr = nad_find_attr(nad, 0, -1, "id", NULL);
    if(attr >= 0)
        snprintf(id, 128, "%.*s", NAD_AVAL_L(nad, attr), NAD_AVAL(nad, attr));

    nad_free(nad);

    /* build a result packet */
    nad = nad_new();

    ns = nad_add_namespace(nad, uri_CLIENT, NULL);

    nad_append_elem(nad, ns, "iq", 0);
    nad_append_attr(nad, -1, "type", "result");

    if(attr >= 0)
        nad_append_attr(nad, -1, "id", id);

    ns = nad_add_namespace(nad, uri_AUTH, NULL);
    nad_append_elem(nad, ns, "query", 1);
    
    nad_append_elem(nad, ns, "username", 2);
    nad_append_cdata(nad, username, strlen(username), 3);

    nad_append_elem(nad, ns, "resource", 2);

    /* fill out the packet with available auth mechanisms */
    if(ar_mechs & AR_MECH_TRAD_PLAIN && (sess->host->ar->get_password != NULL || sess->host->ar->check_password != NULL))
        nad_append_elem(nad, ns, "password", 2);

    if(ar_mechs & AR_MECH_TRAD_DIGEST && sess->host->ar->get_password != NULL)
        nad_append_elem(nad, ns, "digest", 2);

    if (ar_mechs & AR_MECH_TRAD_CRAMMD5 && sess->host->ar->create_challenge != NULL) {
        err = (sess->host->ar->create_challenge)(sess->host->ar, sess, (char *) username, sess->host->realm,
                (char *) sess->auth_challenge, sizeof(sess->auth_challenge));
        if (0 == err) { /* operation failed */
            sx_nad_write(sess->s, stanza_tofrom(stanza_error(nad, 0, stanza_err_INTERNAL_SERVER_ERROR), 0));
            return;
        }
        else if (1 == err) { /* operation succeeded */
            nad_append_elem(nad, ns, "crammd5", 2);
            nad_append_attr(nad, -1, "challenge", sess->auth_challenge);
        }
        else ; /* auth method unsupported for user */
    }

    /* give it back to the client */
    sx_nad_write(sess->s, nad);

    return;
}
Пример #2
0
int user_table_load(router_t r) {
    const char *userfile;
    FILE *f;
    long size;
    char *buf;
    nad_t nad;
    int nusers, user, name, secret;

    log_debug(ZONE, "loading user table");

    if(r->users != NULL)
        xhash_free(r->users);

    r->users = xhash_new(51);

    userfile = config_get_one(r->config, "local.users", 0);
    if(userfile == NULL)
        userfile = CONFIG_DIR "/router-users.xml";

    f = fopen(userfile, "rb");
    if(f == NULL) {
        log_write(r->log, LOG_ERR, "couldn't open user table file %s: %s", userfile, strerror(errno));
        return 1;
    }

    fseek(f, 0, SEEK_END);
    size = ftell(f);
    if(size < 0) {
        log_write(r->log, LOG_ERR, "couldn't seek user table file %s: %s", userfile, strerror(errno));
        fclose(f);
        return 1;
    }
    if(size == 0) {
        log_write(r->log, LOG_ERR, "empty user table file %s", userfile);
        fclose(f);
        return 1;
    }
    fseek(f, 0, SEEK_SET);

    buf = (char *) malloc(sizeof(char) * size);

    if (fread(buf, 1, size, f) != size || ferror(f)) {
        log_write(r->log, LOG_ERR, "couldn't read from user table file: %s", strerror(errno));
        free(buf);
        fclose(f);
        return 1;
    }

    fclose(f);

    nad = nad_parse(buf, size);
    if(nad == NULL) {
        log_write(r->log, LOG_ERR, "couldn't parse user table");
        free(buf);
        return 1;
    }

    free(buf);

    nusers = 0;
    user = nad_find_elem(nad, 0, -1, "user", 1);
    while(user >= 0) {
        name = nad_find_elem(nad, user, -1, "name", 1);
        secret = nad_find_elem(nad, user, -1, "secret", 1);

        if(name < 0 || secret < 0 || NAD_CDATA_L(nad, name) <= 0 || NAD_CDATA_L(nad, secret) <= 0) {
            log_write(r->log, LOG_ERR, "malformed user entry in user table file, skipping");
            continue;
        }

        log_debug(ZONE, "remembering user '%.*s'", NAD_CDATA_L(nad, name), NAD_CDATA(nad, name));

        xhash_put(r->users, pstrdupx(xhash_pool(r->users), NAD_CDATA(nad, name), NAD_CDATA_L(nad, name)), pstrdupx(xhash_pool(r->users), NAD_CDATA(nad, secret), NAD_CDATA_L(nad, secret)));

        nusers++;

        user = nad_find_elem(nad, user, -1, "user", 0);
    }

    nad_free(nad);

    log_write(r->log, LOG_NOTICE, "loaded user table (%d users)", nusers);

    r->users_load = time(NULL);

    return 0;
}
Пример #3
0
/** handler for read data */
void _sx_process_read(sx_t s, sx_buf_t buf) {
    sx_error_t sxe;
    nad_t nad;
    char *errstring;
    int i;
    int ns, elem;

    /* Note that buf->len can validly be 0 here, if we got data from
       the socket but the plugin didn't return anything to us (e.g. a
       SSL packet was split across a tcp segment boundary) */

    /* count bytes parsed */
    s->pbytes += buf->len;

    /* parse it */
    if(XML_Parse(s->expat, buf->data, buf->len, 0) == 0) {
        /* only report error we haven't already */
        if(!s->fail) {
            /* parse error */
            errstring = (char *) XML_ErrorString(XML_GetErrorCode(s->expat));

            _sx_debug(ZONE, "XML parse error: %s, character %d: %.*s",
                      errstring, XML_GetCurrentByteIndex(s->expat) - s->tbytes, buf->len, buf->data);
            _sx_gen_error(sxe, SX_ERR_XML_PARSE, "XML parse error", errstring);
            _sx_event(s, event_ERROR, (void *) &sxe);

            _sx_error(s, stream_err_XML_NOT_WELL_FORMED, errstring);
            _sx_close(s);

            _sx_buffer_free(buf);

            return;
        }

        /* !!! is this the right thing to do? we should probably set
         *     s->fail and let the code further down handle it. */
        _sx_buffer_free(buf);

        return;
    }

    /* check if the stanza size limit is exceeded (it wasn't reset by parser) */
    if(s->rbytesmax && s->pbytes > s->rbytesmax) {
        /* parse error */
        _sx_debug(ZONE, "maximum stanza size (%d) exceeded by reading %d bytes", s->rbytesmax, s->pbytes);

        errstring = (char *) XML_ErrorString(XML_GetErrorCode(s->expat));

        _sx_gen_error(sxe, SX_ERR_XML_PARSE, "stream read error", "Maximum stanza size exceeded");
        _sx_event(s, event_ERROR, (void *) &sxe);

        _sx_error(s, stream_err_POLICY_VIOLATION, errstring);
        _sx_close(s);

        _sx_buffer_free(buf);

        return;
    }

    /* count bytes processed */
    s->tbytes += buf->len;

    /* done with the buffer */
    _sx_buffer_free(buf);

    /* process completed nads */
    if(s->state >= state_STREAM)
        while((nad = jqueue_pull(s->rnadq)) != NULL) {
            int plugin_error;
#ifdef SX_DEBUG
            const char *out; int len;
            nad_print(nad, 0, &out, &len);
            _sx_debug(ZONE, "completed nad: %.*s", len, out);
#endif

            /* check for errors */
            if(NAD_ENS(nad, 0) >= 0 && NAD_NURI_L(nad, NAD_ENS(nad, 0)) == strlen(uri_STREAMS) && strncmp(NAD_NURI(nad, NAD_ENS(nad, 0)), uri_STREAMS, strlen(uri_STREAMS)) == 0 && NAD_ENAME_L(nad, 0) == 5 && strncmp(NAD_ENAME(nad, 0), "error", 5) == 0) {

                errstring = NULL;

                /* get text error description if available - XMPP 4.7.2 */
                if((ns = nad_find_scoped_namespace(nad, uri_STREAM_ERR, NULL)) >= 0)
                    if((elem = nad_find_elem(nad, 0, ns, "text", 1)) >= 0)
                        if(NAD_CDATA_L(nad, elem) > 0) {
                            errstring = (char *) malloc(sizeof(char) * (NAD_CDATA_L(nad, elem) + 1));
                            sprintf(errstring, "%.*s", NAD_CDATA_L(nad, elem), NAD_CDATA(nad, elem));
                        }

                /* if not available, look for legacy error text as in <stream:error>description</stream:error> */
                if (errstring == NULL && NAD_CDATA_L(nad, 0) > 0) {
                    errstring = (char *) malloc(sizeof(char) * (NAD_CDATA_L(nad, 0) + 1));
                    sprintf(errstring, "%.*s", NAD_CDATA_L(nad, 0), NAD_CDATA(nad, 0));
                }

                /* if not available, log the whole packet for debugging */
                if (errstring == NULL) {
                    const char *xml;
                    int xlen;

                    nad_print(nad, 0, &xml, &xlen);
                    errstring = (char *) malloc(sizeof(char) * (xlen + 1));
                    sprintf(errstring, "%.*s", xlen, xml);
                }

                if(s->state < state_CLOSING) {
                    _sx_gen_error(sxe, SX_ERR_STREAM, "Stream error", errstring);
                    _sx_event(s, event_ERROR, (void *) &sxe);
                    _sx_state(s, state_CLOSING);
                }

                free(errstring);

                nad_free(nad);

                break;
            }

            /* check for close */
            if ((s->flags & SX_WEBSOCKET_WRAPPER) && NAD_ENS(nad, 0) >= 0 && NAD_NURI_L(nad, NAD_ENS(nad, 0)) == strlen(uri_XFRAMING) && strncmp(NAD_NURI(nad, NAD_ENS(nad, 0)), uri_XFRAMING, strlen(uri_XFRAMING)) == 0 && NAD_ENAME_L(nad, 0) == 5 && strncmp(NAD_ENAME(nad, 0), "close", 5) == 0) {
                _sx_debug(ZONE, "<close/> frame @ depth %d", s->depth);
                s->fail = 1;
                break;
            }

            /* run it by the plugins */
            if(_sx_chain_nad_read(s, nad) == 0)
                return;

            /* now let the plugins process the completed nad */
            plugin_error = 0;
            if(s->env != NULL)
                for(i = 0; i < s->env->nplugins; i++)
                    if(s->env->plugins[i]->process != NULL) {
                        int plugin_ret;
                        plugin_ret = (s->env->plugins[i]->process)(s, s->env->plugins[i], nad);
                        if(plugin_ret == 0) {
                            plugin_error ++;
                            break;
                        }
                    }

            /* hand it to the app */
            if ((plugin_error == 0) && (s->state < state_CLOSING))
                _sx_event(s, event_PACKET, (void *) nad);
        }

    /* something went wrong, bail */
    if(s->fail) {
        _sx_close(s);

        return;
    }

    /* stream was closed */
    if(s->depth < 0 && s->state < state_CLOSING) {
        /* close the stream if necessary */

        if(s->state >= state_STREAM_SENT) {
            if (s->flags & SX_WEBSOCKET_WRAPPER)
                jqueue_push(s->wbufq, _sx_buffer_new("<close xmlns='" uri_XFRAMING "' />", sizeof(uri_XFRAMING) + 17, NULL, NULL), 0);
            else
                jqueue_push(s->wbufq, _sx_buffer_new("</stream:stream>", 16, NULL, NULL), 0);
            s->want_write = 1;
        }

        _sx_state(s, state_CLOSING);

        return;
    }
}
Пример #4
0
int filter_load(router_t r) {
    const char *filterfile;
    FILE *f;
    long size;
    char *buf;
    nad_t nad;
    int i, nfilters, filter, from, to, what, redirect, error, log;
    acl_t list_tail, acl;

    log_debug(ZONE, "loading filter");
    
    if(r->filter != NULL)
        filter_unload(r);

    filterfile = config_get_one(r->config, "aci.filter", 0);
    if(filterfile == NULL)
        filterfile = CONFIG_DIR "/router-filter.xml";

    f = fopen(filterfile, "rb");
    if(f == NULL) {
        log_write(r->log, LOG_NOTICE, "couldn't open filter file %s: %s", filterfile, strerror(errno));
        r->filter_load = time(NULL);
        return 0;
    }

    fseek(f, 0, SEEK_END);
    size = ftell(f);
    fseek(f, 0, SEEK_SET);

    buf = (char *) malloc(sizeof(char) * size);

    if (fread(buf, 1, size, f) != size || ferror(f)) {
        log_write(r->log, LOG_ERR, "couldn't read from filter file: %s", strerror(errno));
        free(buf);
        fclose(f);
        return 1;
    }

    fclose(f);

    nad = nad_parse(buf, size);
    if(nad == NULL) {
        log_write(r->log, LOG_ERR, "couldn't parse filter file");
        free(buf);
        return 1;
    }

    free(buf);

    list_tail = NULL;

    log_debug(ZONE, "building filter list");

    nfilters = 0;
    filter = nad_find_elem(nad, 0, -1, "rule", 1);
    while(filter >= 0) {
        from = nad_find_attr(nad, filter, -1, "from", NULL);
        to = nad_find_attr(nad, filter, -1, "to", NULL);
        what = nad_find_attr(nad, filter, -1, "what", NULL);
        redirect = nad_find_attr(nad, filter, -1, "redirect", NULL);
        error = nad_find_attr(nad, filter, -1, "error", NULL);
        log = nad_find_attr(nad, filter, -1, "log", NULL);

        acl = (acl_t) calloc(1, sizeof(struct acl_s));

        if(from >= 0) {
            if (NAD_AVAL_L(nad, from) == 0 )
                acl->from = NULL;
            else {
                acl->from = (char *) malloc(sizeof(char) * (NAD_AVAL_L(nad, from) + 1));
                sprintf(acl->from, "%.*s", NAD_AVAL_L(nad, from), NAD_AVAL(nad, from));
            }
        }
        if(to >= 0) {
            if (NAD_AVAL_L(nad, to) == 0 )
                acl->to = NULL;
            else {
                acl->to = (char *) malloc(sizeof(char) * (NAD_AVAL_L(nad, to) + 1));
                sprintf(acl->to, "%.*s", NAD_AVAL_L(nad, to), NAD_AVAL(nad, to));
            }
        }
        if(what >= 0) {
            if (NAD_AVAL_L(nad, what) == 0 || strncmp(NAD_AVAL(nad, what), "*", NAD_AVAL_L(nad, what)) == 0)
                acl->what = NULL;
            else {
                acl->what = (char *) malloc(sizeof(char) * (NAD_AVAL_L(nad, what) + 1));
                sprintf(acl->what, "%.*s", NAD_AVAL_L(nad, what), NAD_AVAL(nad, what));
            }
        }
        if(redirect >= 0) {
            if (NAD_AVAL_L(nad, redirect) == 0)
                acl->redirect = NULL;
            else {
                acl->redirect_len = NAD_AVAL_L(nad, redirect);
                acl->redirect = (char *) malloc(sizeof(char) * (acl->redirect_len + 1));
                sprintf(acl->redirect, "%.*s", acl->redirect_len, NAD_AVAL(nad, redirect));
                acl->error = stanza_err_REDIRECT;
            }
        }
        if(error >= 0) {
            acl->error = stanza_err_NOT_ALLOWED;
            for(i=0; _stanza_errors[i].code != NULL; i++) {
                if(_stanza_errors[i].name != NULL && strncmp(_stanza_errors[i].name, NAD_AVAL(nad, error), NAD_AVAL_L(nad, error)) == 0) {
                    acl->error = stanza_err_BAD_REQUEST + i;
                    break;
                }
            }
        }
        if(log >= 0) {
            acl->log = ! strncasecmp(NAD_AVAL(nad, log), "YES", NAD_AVAL_L(nad, log));
            acl->log |= ! strncasecmp(NAD_AVAL(nad, log), "ON", NAD_AVAL_L(nad, log));
        }

        if(list_tail != NULL) {
           list_tail->next = acl;
           list_tail = acl;
        }

        /* record the head of the list */
        if(r->filter == NULL) {
           r->filter = acl;
           list_tail = acl;
        }
        
        log_debug(ZONE, "added %s rule: from=%s, to=%s, what=%s, redirect=%s, error=%d, log=%s", (acl->error?"deny":"allow"), acl->from, acl->to, acl->what, acl->redirect, acl->error, (acl->log?"yes":"no"));

        nfilters++;

        filter = nad_find_elem(nad, filter, -1, "rule", 0);
    }

    nad_free(nad);

    log_write(r->log, LOG_NOTICE, "loaded filters (%d rules)", nfilters);

    r->filter_load = time(NULL);

    return 0;
}
Пример #5
0
static int _sx_compress_process(sx_t s, sx_plugin_t p, nad_t nad) {
    int flags;
    char *ns = NULL, *to = NULL, *from = NULL, *version = NULL;
    sx_error_t sxe;

    /* not interested if we're a server and we never offered it */
    if(s->type == type_SERVER && !(s->flags & SX_COMPRESS_OFFER))
        return 1;

    /* only want compress packets */
    if(NAD_ENS(nad, 0) < 0 || NAD_NURI_L(nad, NAD_ENS(nad, 0)) != sizeof(uri_COMPRESS)-1 || strncmp(NAD_NURI(nad, NAD_ENS(nad, 0)), uri_COMPRESS, sizeof(uri_COMPRESS)-1) != 0)
        return 1;

    /* compress from client */
    if(s->type == type_SERVER) {
        if(NAD_ENAME_L(nad, 0) == 8 && strncmp(NAD_ENAME(nad, 0), "compress", 8) == 0) {
            nad_free(nad);

            /* can't go on if we've been here before */
            if(s->compressed) {
                _sx_debug(ZONE, "compress requested on already compressed channel, dropping packet");
                return 0;
            }

            _sx_debug(ZONE, "compress requested, setting up");

            /* go ahead */
            jqueue_push(s->wbufq, _sx_buffer_new("<compressed xmlns='" uri_COMPRESS "'/>", sizeof(uri_COMPRESS)-1 + 22, _sx_compress_notify_compress, NULL), 0);
            s->want_write = 1;

            /* handled the packet */
            return 0;
        }
    }

    else if(s->type == type_CLIENT) {
        /* kick off the handshake */
        if(NAD_ENAME_L(nad, 0) == 7 && strncmp(NAD_ENAME(nad, 0), "compressed", 7) == 0) {
            nad_free(nad);

            /* save interesting bits */
            flags = s->flags;

            if(s->ns != NULL) ns = strdup(s->ns);

            if(s->req_to != NULL) to = strdup(s->req_to);
            if(s->req_from != NULL) from = strdup(s->req_from);
            if(s->req_version != NULL) version = strdup(s->req_version);

            /* reset state */
            _sx_reset(s);

            _sx_debug(ZONE, "server ready for compression, starting");

            /* second time round */
            sx_client_init(s, flags | SX_COMPRESS_WRAPPER, ns, to, from, version);

            /* free bits */
            if(ns != NULL) free(ns);
            if(to != NULL) free(to);
            if(from != NULL) free(from);
            if(version != NULL) free(version);

            return 0;
        }

        /* busted server */
        if(NAD_ENAME_L(nad, 0) == 7 && strncmp(NAD_ENAME(nad, 0), "failure", 7) == 0) {
            nad_free(nad);

            _sx_debug(ZONE, "server can't handle compression, business as usual");

            _sx_gen_error(sxe, SX_ERR_COMPRESS_FAILURE, "compress failure", "Server was unable to establish compression");
            _sx_event(s, event_ERROR, (void *) &sxe);

            return 0;
        }
    }

    _sx_debug(ZONE, "unknown compress namespace element '%.*s', dropping packet", NAD_ENAME_L(nad, 0), NAD_ENAME(nad, 0));
    nad_free(nad);
    return 0;
}