int iks_start_sasl(iksparser *prs, enum ikssasltype type, char *username, char *pass) { iks *x; x = iks_new("auth"); iks_insert_attrib(x, "xmlns", IKS_NS_XMPP_SASL); switch(type) { case IKS_SASL_PLAIN: { int len = iks_strlen(username) + iks_strlen(pass) + 2; char *s = iks_malloc(80 + len); char *base64; iks_insert_attrib(x, "mechanism", "PLAIN"); sprintf(s, "%c%s%c%s", 0, username, 0, pass); base64 = iks_base64_encode(s, len); iks_insert_cdata(x, base64, 0); iks_free(base64); iks_free(s); break; } case IKS_SASL_DIGEST_MD5: { struct stream_data *data = iks_user_data(prs); iks_insert_attrib(x, "mechanism", "DIGEST-MD5"); data->auth_username = username; data->auth_pass = pass; break; } case IKS_SASL_OAUTH_2: { int len = iks_strlen(username) + iks_strlen(pass) + 2; char *s = iks_malloc(80 + len); char *base64; iks_insert_attrib(x, "mechanism", "X-OAUTH2"); iks_insert_attrib(x, "auth:service", "oauth2"); iks_insert_attrib(x, "xmlns:auth", "http://www.google.com/talk/protocol/auth"); sprintf(s, "%c%s%c%s", 0, username, 0, pass); base64 = iks_base64_encode(s, len); iks_insert_cdata(x, base64, 0); iks_free(base64); iks_free(s); break; } default: iks_delete(x); return IKS_NET_NOTSUPP; } iks_send(prs, x); iks_delete(x); return IKS_OK; }
char *iks_base64_decode(const char *buf) { char *res, *save; char val; const char *foo; const char *end; int index; size_t len; if (!buf) return NULL; len = iks_strlen(buf) * 6 / 8 + 1; save = res = iks_malloc(len); if (!save) return NULL; memset(res, 0, len); index = 0; end = buf + iks_strlen(buf); while (*buf && buf < end) { if (!(foo = strchr(base64_charset, *buf))) foo = base64_charset; val = (int)(foo - base64_charset); buf++; switch (index) { case 0: *res |= val << 2; break; case 1: *res++ |= val >> 4; *res |= val << 4; break; case 2: *res++ |= val >> 2; *res |= val << 6; break; case 3: *res++ |= val; break; } index++; index %= 4; } *res = 0; return save; }
// if returns IKS_OK, do not send more before ikss_SENT callback int ikss_Stream_start_sasl (ikss_Stream *self, enum ikssasltype type, const char *username, const char *pass) { iks *x; x = iks_new ("auth"); // xxx check return? iks_insert_attrib (x, "xmlns", IKS_NS_XMPP_SASL); // xxx check return? switch (type) { case IKS_SASL_PLAIN: { int len = iks_strlen (username) + iks_strlen (pass) + 2; char *s = iks_malloc (80+len); // xxx check for oom, on error free x and return error code char *base64; iks_insert_attrib (x, "mechanism", "PLAIN"); // xxx check return? sprintf (s, "%c%s%c%s", 0, username, 0, pass); base64 = iks_base64_encode (s, len); // xxx check return? iks_insert_cdata (x, base64, 0); iks_free (base64); iks_free (s); break; } case IKS_SASL_DIGEST_MD5: { iks_insert_attrib (x, "mechanism", "DIGEST-MD5"); // xxx check return? self->auth_username = username; self->auth_pass = pass; break; } default: iks_delete (x); return IKS_NET_NOTSUPP; } int ret = ikss_Stream_send_node (self, x); iks_delete (x); // delete ok since above makes a copy if (ret) return ret; return IKS_OK; }
char *iks_base64_encode(const char *buf, int len) { char *res, *save; int k, t; if (len == 0) len = (iks_strlen(buf)); save = res = iks_malloc((len*8) / 6 + 4); if (!save) return NULL; for (k = 0; k < len/3; ++k) { *res++ = base64_charset[*buf >> 2]; t = ((*buf & 0x03) << 4); buf++; *res++ = base64_charset[t | (*buf >> 4)]; t = ((*buf & 0x0F) << 2); buf++; *res++ = base64_charset[t | (*buf >> 6)]; *res++ = base64_charset[*buf++ & 0x3F]; } switch (len % 3) { case 2: *res++ = base64_charset[*buf >> 2]; t = ((*buf & 0x03) << 4); buf++; *res++ = base64_charset[t | (*buf >> 4)]; *res++ = base64_charset[((*buf++ & 0x0F) << 2)]; *res++ = '='; break; case 1: *res++ = base64_charset[*buf >> 2]; *res++ = base64_charset[(*buf++ & 0x03) << 4]; *res++ = '='; *res++ = '='; break; } *res = 0; return save; }
//! Imports model file static int model_import(const char *model_file) { /*! * Imports model file to node table. * * @model_file File to import * @return 0 on success, -1 on error */ iks *obj, *met; size_t size = 0; size_t obj_size, met_size; int obj_no; int count = 0; int e; e = iks_load(model_file, &model_xml); if (e != 0) { log_error("XML load error.\n"); return -1; } if (iks_strcmp(iks_name(model_xml), "comarModel") != 0) { log_error("Bad XML: not a Comar model.\n"); return -1; } // scan the model for (obj = iks_first_tag(model_xml); obj; obj = iks_next_tag(obj)) { if (iks_strcmp(iks_name(obj), "interface") == 0) { obj_size = iks_strlen(iks_find_attrib(obj, "name")); if (!obj_size) { log_error("Bad XML: interface has no name.\n"); return -1; } size += obj_size + 1; ++count; for (met = iks_first_tag(obj); met; met = iks_next_tag(met)) { if (iks_strcmp(iks_name(met), "method") == 0 || iks_strcmp(iks_name(met), "signal") == 0) { met_size = iks_strlen(iks_find_attrib(met, "name")); if (!met_size) { log_error("Bad XML: method/signal has no name.\n"); return -1; } size += obj_size + 1 + met_size + 1; ++count; iks *arg; for (arg = iks_first_tag(met); arg; arg = iks_next_tag(arg)) { if (iks_strcmp(iks_name(arg), "arg") != 0 && iks_strcmp(iks_name(arg), "annotation") != 0) { log_error("Bad XML: method/signal may contain <arg> or <annotation> only\n"); return -1; } } } else { log_error("Bad XML: interface may contain <method> or <signal> only\n"); return -1; } } } else { log_error("Bad XML: root node may contain <interface> only\n"); return -1; } } // size is counted to alloc mem for paths // prepare data structures if (prepare_tables(count, size) != 0) return -1; // load model for (obj = iks_first_tag(model_xml); obj; obj = iks_next_tag(obj)) { if (iks_strcmp(iks_find_attrib(obj, "name"), "Comar") == 0) { continue; } obj_no = add_node(-1, build_path(obj, NULL), "", N_INTERFACE); for (met = iks_first_tag(obj); met; met = iks_next_tag(met)) { if (iks_strcmp(iks_name(met), "method") == 0) { char *label = iks_find_attrib(met, "access_label"); if (label) { iks_insert_attrib(met, "access_label", NULL); } else { label = iks_find_attrib(met, "name"); } add_node(obj_no, build_path(obj, met), label, N_METHOD); } else if (iks_strcmp(iks_name(met), "signal") == 0) { add_node(obj_no, build_path(obj, met), "", N_SIGNAL); } } } return 0; }
static iks *make_sasl_response(struct stream_data *data, char *message) { iks *x = NULL; char *realm, *realm_end; char *nonce, *nonce_end; char cnonce[CNONCE_LEN * 8 + 1]; iksmd5 *md5; unsigned char a1_h[16], a1[33], a2[33], response_value[33]; char *response, *response_coded; int i; parse_digest(message, "realm=\"", &realm, &realm_end); parse_digest(message, "nonce=\"", &nonce, &nonce_end); /* nonce is necessary for auth */ if(!nonce || !nonce_end) return NULL; *nonce_end = '\0'; /* if no realm is given use the server hostname */ if(realm) { if(!realm_end) return NULL; *realm_end = '\0'; } else { realm = (char *) data->server; } /* generate random client challenge */ for(i = 0; i < CNONCE_LEN; ++i) sprintf(cnonce + i * 8, "%08x", rand()); md5 = iks_md5_new(); if(!md5) return NULL; iks_md5_hash(md5, (const unsigned char*)data->auth_username, iks_strlen(data->auth_username), 0); iks_md5_hash(md5, (const unsigned char*)":", 1, 0); iks_md5_hash(md5, (const unsigned char*)realm, iks_strlen(realm), 0); iks_md5_hash(md5, (const unsigned char*)":", 1, 0); iks_md5_hash(md5, (const unsigned char*)data->auth_pass, iks_strlen(data->auth_pass), 1); iks_md5_digest(md5, a1_h); iks_md5_reset(md5); iks_md5_hash(md5, (const unsigned char*)a1_h, 16, 0); iks_md5_hash(md5, (const unsigned char*)":", 1, 0); iks_md5_hash(md5, (const unsigned char*)nonce, iks_strlen(nonce), 0); iks_md5_hash(md5, (const unsigned char*)":", 1, 0); iks_md5_hash(md5, (const unsigned char*)cnonce, iks_strlen(cnonce), 1); iks_md5_print(md5, (char*)a1); iks_md5_reset(md5); iks_md5_hash(md5, (const unsigned char*)"AUTHENTICATE:xmpp/", 18, 0); iks_md5_hash(md5, (const unsigned char*)data->server, iks_strlen(data->server), 1); iks_md5_print(md5, (char*)a2); iks_md5_reset(md5); iks_md5_hash(md5, (const unsigned char*)a1, 32, 0); iks_md5_hash(md5, (const unsigned char*)":", 1, 0); iks_md5_hash(md5, (const unsigned char*)nonce, iks_strlen(nonce), 0); iks_md5_hash(md5, (const unsigned char*)":00000001:", 10, 0); iks_md5_hash(md5, (const unsigned char*)cnonce, iks_strlen(cnonce), 0); iks_md5_hash(md5, (const unsigned char*)":auth:", 6, 0); iks_md5_hash(md5, (const unsigned char*)a2, 32, 1); iks_md5_print(md5, (char*)response_value); iks_md5_delete(md5); i = iks_strlen(data->auth_username) + iks_strlen(realm) + iks_strlen(nonce) + iks_strlen(data->server) + CNONCE_LEN * 8 + 136; response = iks_malloc(i); if(!response) return NULL; sprintf(response, "username=\"%s\",realm=\"%s\",nonce=\"%s\"" ",cnonce=\"%s\",nc=00000001,qop=auth,digest-uri=\"" "xmpp/%s\",response=%s,charset=utf-8", data->auth_username, realm, nonce, cnonce, data->server, response_value); response_coded = iks_base64_encode(response, 0); if(response_coded) { x = iks_new("response"); iks_insert_cdata(x, response_coded, 0); iks_free(response_coded); } iks_free(response); return x; }
int model_init(void) { iks *model; iks *grp, *obj, *met; int count = 0; size_t size = 0; size_t grp_size, obj_size, met_size; int grp_no, obj_no; int e; // parse model file e = iks_load(cfg_model_file, &model); if (e) { log_error("Cannot process model file '%s'\n", cfg_model_file); return -1; } if (iks_strcmp(iks_name(model), "comarModel") != 0) { log_error("Not a COMAR model file '%s'\n", cfg_model_file); return -1; } // FIXME: ugly code ahead, split into functions and simplify // scan the model for (grp = iks_first_tag(model); grp; grp = iks_next_tag(grp)) { if (iks_strcmp(iks_name(grp), "group") == 0) { grp_size = iks_strlen(iks_find_attrib(grp, "name")); if (!grp_size) { log_error("Broken COMAR model file '%s'\n", cfg_model_file); return -1; } size += grp_size + 1; ++count; for (obj = iks_first_tag(grp); obj; obj = iks_next_tag(obj)) { if (iks_strcmp(iks_name(obj), "class") == 0) { obj_size = iks_strlen(iks_find_attrib(obj, "name")); if (!obj_size) { log_error("Broken COMAR model file '%s'\n", cfg_model_file); return -1; } size += grp_size + obj_size + 2; ++count; for (met = iks_first_tag(obj); met; met = iks_next_tag(met)) { if (iks_strcmp(iks_name(met), "method") == 0 || iks_strcmp(iks_name(met), "notify") == 0) { met_size = iks_strlen(iks_find_attrib(met, "name")); if (!met_size) { log_error("Broken COMAR model file '%s'\n", cfg_model_file); return -1; } size += grp_size + obj_size + met_size + 3; ++count; } if (iks_strcmp(iks_name(met), "method") == 0) { iks *arg; for (arg = iks_first_tag(met); arg; arg = iks_next_tag(arg)) { if (iks_strcmp(iks_name(arg), "argument") == 0 || iks_strcmp(iks_name(arg), "instance") == 0) { size += iks_cdata_size(iks_child(arg)) + 1; } } } } } } } } // prepare data structures if (prepare_tables(count, size)) return -1; // load the model for (grp = iks_first_tag(model); grp; grp = iks_next_tag(grp)) { if (iks_strcmp(iks_name(grp), "group") == 0) { grp_no = add_node(-1, build_path(grp, NULL, NULL), N_GROUP); for (obj = iks_first_tag(grp); obj; obj = iks_next_tag(obj)) { if (iks_strcmp(iks_name(obj), "class") == 0) { obj_no = add_node(grp_no, build_path(grp, obj, NULL), N_CLASS); for (met = iks_first_tag(obj); met; met = iks_next_tag(met)) { int no; if (iks_strcmp(iks_name(met), "method") == 0) { iks *arg; char *prof; no = add_node(obj_no, build_path(grp, obj, met), N_METHOD); prof = iks_find_attrib(met, "access"); if (prof) { if (strcmp(prof, "user") == 0) nodes[no].level = ACL_USER; if (strcmp(prof, "guest") == 0) nodes[no].level = ACL_GUEST; } prof = iks_find_attrib(met, "profile"); if (prof) { if (strcmp(prof, "global") == 0) nodes[no].flags |= P_GLOBAL; if (strcmp(prof, "package") == 0) nodes[no].flags |= P_PACKAGE; } prof = iks_find_attrib(met, "profileOp"); if (prof) { if (strcmp(prof, "delete") == 0) nodes[no].flags |= P_DELETE; if (strcmp(prof, "startup") == 0) nodes[no].flags |= P_STARTUP; } for (arg = iks_first_tag(met); arg; arg = iks_next_tag(arg)) { if (iks_strcmp(iks_name(arg), "instance") == 0) { build_arg(no, 1, iks_cdata(iks_child(arg))); } } for (arg = iks_first_tag(met); arg; arg = iks_next_tag(arg)) { if (iks_strcmp(iks_name(arg), "argument") == 0) { char *argname; argname = iks_cdata(iks_child(arg)); if (argname) { build_arg(no, 0, argname); } else { log_error("Argument name needed in <argument> tag of model.xml\n"); } } } } else if (iks_strcmp(iks_name(met), "notify") == 0) { no = add_node(obj_no, build_path(grp, obj, met), N_NOTIFY); if (no >= model_max_notifications) model_max_notifications = no + 1; } } } } } } // no need to keep dom tree in memory iks_delete(model); return 0; }
//! Validates model file int db_validate_model(iks *xml, char *filename) { /*! * Validates model file. * * @xml Iksemel document * @return 0 on success, -1 on error * */ iks *iface, *met, *arg; DBusError bus_error; dbus_error_init(&bus_error); // Check root tag if (iks_strcmp(iks_name(xml), "comarModel") != 0) { log_error("Not a valid model XML: %s\n", filename); return -1; } for (iface = iks_first_tag(xml); iface; iface = iks_next_tag(iface)) { // Only "interface" tag is allowed if (iks_strcmp(iks_name(iface), "interface") != 0) { log_error("Unknown tag '%s' in XML: %s\n", iks_name(iface), filename); return -1; } // Interfaces must have a "name" attribute if (!iks_strlen(iks_find_attrib(iface, "name"))) { log_error("Model with no name in XML: %s\n", filename); return -1; } for (met = iks_first_tag(iface); met; met = iks_next_tag(met)) { // Only "method" and "signal" tags are allowed if (iks_strcmp(iks_name(met), "method") == 0 || iks_strcmp(iks_name(met), "signal") == 0) { // Tags must have a "name" attribute if (!iks_strlen(iks_find_attrib(met, "name"))) { log_error("Method/Signal tag without name under '%s' in XML: %s\n", iks_find_attrib(iface, "name"), filename); return -1; } for (arg = iks_first_tag(met); arg; arg = iks_next_tag(arg)) { if (iks_strcmp(iks_name(arg), "arg") == 0) { // Arguments must have a "name" attribute if (!iks_strlen(iks_find_attrib(arg, "name"))) { log_error("Argument tag with no name under '%s/%s' in XML: %s\n", iks_find_attrib(iface, "name"), iks_find_attrib(met, "name"), filename); return -1; } // Arguments must have a "type" attribute if (!iks_strlen(iks_find_attrib(arg, "type"))) { log_error("Argument tag without type under '%s/%s' in XML: %s\n", iks_find_attrib(iface, "name"), iks_find_attrib(met, "name"), filename); return -1; } // Types must be a valid DBus signature if (!dbus_signature_validate(iks_find_attrib(arg, "type"), &bus_error)) { dbus_error_free(&bus_error); log_error("Argument tag with invalid type (%s/%s/%s) in XML: %s\n", iks_find_attrib(iface, "name"), iks_find_attrib(met, "name"), iks_find_attrib(arg, "name"), filename); return -1; } // Types must be single type object if (!dbus_signature_validate_single(iks_find_attrib(arg, "type"), &bus_error)) { dbus_error_free(&bus_error); log_error("Argument tag with a non-single element type (%s/%s/%s) in XML: %s\n", iks_find_attrib(iface, "name"), iks_find_attrib(met, "name"), iks_find_attrib(arg, "name"), filename); return -1; } } else if (iks_strcmp(iks_name(arg), "annotation") == 0) { // Attributes must have a "name" attribute if (!iks_strlen(iks_find_attrib(arg, "name"))) { log_error("Annotation tag without name under '%s' in XML: %s\n", iks_find_attrib(iface, "name"), iks_find_attrib(met, "name"), filename); return -1; } } else { log_error("Unknown tag '%s' under '%s/%s' in XML: %s\n", iks_name(arg), iks_find_attrib(iface, "name"), iks_find_attrib(met, "name"), filename); return -1; } } } else { log_error("Unknown tag '%s' under '%s' in XML: %s\n", iks_name(met), iks_find_attrib(iface, "name"), filename); return -1; } } } return 0; }