static int do_referral(isieve_t *obj, char *refer_to) { int ret; struct servent *serv; isieve_t *obj_new; char *mechlist; int port; char *errstr = NULL; const char *mtried; const char *scheme = "sieve://"; char *host, *p; sasl_callback_t *callbacks; sasl_ssf_t ssf; /* check scheme */ if (strncasecmp(refer_to, scheme, strlen(scheme))) return STAT_NO; /* get host */ if ((host = strrchr(refer_to, '@'))) { char *authid, *userid; int n; *host++ = '\0'; /* get authid - make a copy so it persists for the callbacks */ authid = obj->refer_authinfo = xstrdup(refer_to + strlen(scheme)); /* get userid */ if ((userid = strrchr(authid, ';'))) *userid++ = '\0'; /* count the callbacks */ for (n = 0; obj->callbacks[n++].id != SASL_CB_LIST_END;); /* copy the callbacks, substituting some of our own */ callbacks = obj->refer_callbacks = xmalloc(n*sizeof(sasl_callback_t)); while (--n >= 0) { callbacks[n].id = obj->callbacks[n].id; switch (callbacks[n].id) { case SASL_CB_USER: callbacks[n].proc = (int (*)(void))&refer_simple_cb; callbacks[n].context = userid ? userid : authid; break; case SASL_CB_AUTHNAME: callbacks[n].proc = (int (*)(void))&refer_simple_cb; callbacks[n].context = authid; break; default: callbacks[n].proc = obj->callbacks[n].proc; callbacks[n].context = obj->callbacks[n].context; break; } } } else { host = refer_to + strlen(scheme); callbacks = obj->callbacks; } /* get port */ p = host; if (*host == '[') { if ((p = strrchr(host + 1, ']')) != NULL) { *p++ = '\0'; host++; /* skip first bracket */ } else p = host; } if ((p = strchr(p, ':'))) { *p++ = '\0'; port = atoi(p); } else { serv = getservbyname("sieve", "tcp"); if (serv == NULL) { port = 4190; } else { port = ntohs(serv->s_port); } } ret = init_net(host, port, &obj_new); if(ret) return STAT_NO; /* Start up SASL */ ret = init_sasl(obj_new, 128, callbacks); if(ret) return STAT_NO; /* Authenticate */ mechlist = read_capability(obj_new); do { mtried = NULL; ret = auth_sasl(mechlist, obj_new, &mtried, &ssf, &errstr); if (errstr) { free(errstr); errstr = NULL; } if(ret) init_sasl(obj_new, 128, callbacks); if(mtried) { char *newlist = (char*) xmalloc(strlen(mechlist)+1); char *mtr = xstrdup(mtried); char *tmp; ucase(mtr); tmp = strstr(mechlist,mtr); if (tmp) { strcpy(newlist, mechlist); tmp++; tmp = strchr(tmp, ' '); if (tmp) { strcat(newlist,tmp); } } free(mtr); free(mechlist); mechlist = newlist; } } while(ret && mtried); /* xxx leak? */ if(ret) return STAT_NO; if (ssf) { /* SASL security layer negotiated -- check if SASL mech list changed */ if (detect_mitm(obj_new, mechlist)) { free(mechlist); return STAT_NO; } } free(mechlist); /* free old isieve_t */ sieve_dispose(obj); /* Copy new isieve_t into memory used by old object */ memcpy(obj,obj_new,sizeof(isieve_t)); free(obj_new); /* Destroy the string that was allocated to save the destination server */ free(refer_to); return STAT_OK; }
int auth_sasl_ex(const char *method, const char *initresponse, const char *externalauth, char *(*callback_func)(const char *, void *), void *callback_arg, char **authtype_ptr, /* Returned - AUTHTYPE */ char **authdata_ptr) { char *uid; int n; if (strcmp(method, "EXTERNAL")) return auth_sasl(method, initresponse, callback_func, callback_arg, authtype_ptr, authdata_ptr); if (!externalauth || !*externalauth) return AUTHSASL_ERROR; if (initresponse && !*initresponse) initresponse=NULL; if (initresponse && strcmp(initresponse, externalauth)) return AUTHSASL_ERROR; if (!initresponse) { uid=callback_func("", callback_arg); if (*uid == '*') { free(uid); return (AUTHSASL_ABORTED); } n=authsasl_frombase64(uid); if (n < 0) { free(uid); return AUTHSASL_ABORTED; } uid[n]=0; if (uid[0]) { free(uid); return AUTHSASL_ABORTED; } free(uid); } if ((*authtype_ptr=strdup("EXTERNAL")) == NULL) return AUTHSASL_ABORTED; if ((*authdata_ptr=strdup(externalauth)) == NULL) { free(authtype_ptr); return AUTHSASL_ABORTED; } return 0; }