#include extern const char *__progname; #include "pp.h" #include "bpp.h" #include "algs.h" #include "msgs.h" #include "cmdline.h" #include "pkt-util.h" typedef struct uac_state UAC_STATE; struct uac_state { int stage; #define UAS_RQSERV 1 #define UAS_RUNNING 2 #define UAS_DONE 3 PKTQ oq; UASTATE uastate; } ; static void send_service_request(LAYER *l) { unsigned char *opp; l->b->opkt[0] = SSH_MSG_SERVICE_REQUEST; opp = put_string(&l->b->opkt[1],"ssh-userauth",-1); l->b->oplen = opp - &l->b->opkt[0]; below_opkt(l); } static unsigned char *pkthead(UASTATE *s) { unsigned char *opp; opp = &s->layer->b->opkt[0]; *opp++ = SSH_MSG_USERAUTH_REQUEST; opp = put_string(opp,remuser,-1); opp = put_string(opp,"ssh-connection",-1); opp = put_string(opp,uaalg_name(s->curalg),-1); return(opp); } static void start_method(LAYER *l, UASTATE *s, UAALG *m) { s->curalg = m; s->algpriv = 0; l->b->oplen = (*s->curalg->start)(s) - &l->b->opkt[0]; below_opkt(l); } static void handle_banner(BPP *b) { STR msg; STR lang; parse_packet(b,pp_fail, PP_IGNORE(1), PP_STRING(&msg), PP_STRING(&lang), PP_ENDSHERE ); printf("Banner"); if (lang.len) printf(" (language %.*s)",lang.len,lang.data); printf("\n%.*s\n",msg.len,msg.data); } static void next_alg(UASTATE *s, ALGLIST *contin) { ALGLIST *l; (*s->curalg->done)(s); do <"gotalg"> { for (l=s->cando;l;l=l->link) { ALGLIST *l2; for (l2=contin;l2;l2=l2->link) { if (l2->alg == l->alg) break <"gotalg">; } } fprintf(stderr,"%s: authentication failed\n",__progname); exit(1); } while (0); start_method(s->layer,s,l->alg); } static void userauth_failure(UASTATE *s) { BPP *b; STR methods; int partial; ALGLIST *contin; int curlisted; ALGLIST *l; b = s->layer->b; parse_packet(b,pp_fail, PP_IGNORE(1), PP_STRING(&methods), PP_BOOL(&partial), PP_ENDSHERE ); /* Four cases: (1) partial success, current algorithm listed in methods current alg running -> continue current alg (2) partial success, current algorithm not listed in methods current alg succeeded, more auth needed -> end current alg -> pick next alg -> start it (3) no partial success, current algorithm listed in methods not sure what this can mean; maybe public key failed, but another public key might succeed? -> retry current alg (4) no partial success, current algorithm not listed in methods current alg failed, try another -> end current alg -> pick next alg -> start it */ { ALGLIST **lp; static int savemethod(const void *name, int len) { ALGLIST *l; void *a; a = uaalg_vfind_rostr((ROSTR){.data=name,.len=len}); if (a) { l = malloc(sizeof(ALGLIST)); l->alg = a; *lp = l; lp = &l->link; } return(0); } lp = &contin; comma_list(methods.data,methods.len,savemethod); *lp = 0; } do <"ret"> { curlisted = 0; for (l=contin;l;l=l->link) { if (l->alg == s->curalg) { curlisted = 1; break; } } if (curlisted) { switch (partial ? (*s->curalg->partial)(s) : (*s->curalg->retry)(s)) { case UA_READ: break <"ret">; break; case UA_SEND: below_opkt(s->layer); break <"ret">; break; case UA_NEXT: break; default: abort(); break; } } /* Case 2 or 4, or a UA_NEXT from a (*partial) call */ /* End current alg, pick new one, start it */ next_alg(s,contin); } while (0); alglist_free(contin); } static void *uac_init(LAYER *l) { UAC_STATE *s; ALGLIST **alp; ALGLIST *a; UAALG *alg; int i; s = malloc(sizeof(UAC_STATE)); s->stage = UAS_RQSERV; send_service_request(l); pktq_init(&s->oq); s->uastate.layer = l; s->uastate.curalg = 0; s->uastate.algpriv = 0; s->uastate.pkthead = &pkthead; alp = &s->uastate.cando; for (i=0;(alg=uaalg_enum(i));i++) { if ((*alg->canuse)(l->b)) { a = malloc(sizeof(ALGLIST)); a->alg = alg; *alp = a; alp = &a->link; } } *alp = 0; return(s); } static void uac_r(LAYER *l, void *arg) { UAC_STATE *s; BPP *b; s = arg; b = l->b; switch (s->stage) { case UAS_RQSERV: switch (b->ipkt[0]) { case SSH_MSG_SERVICE_ACCEPT: start_method(l,&s->uastate,uaalg_vfind_c("none")); s->stage = UAS_RUNNING; break; case SSH_MSG_USERAUTH_BANNER: handle_banner(b); break; default: badmsg("RQSERV","type %d",b->ipkt[0]); break; } break; case UAS_RUNNING: switch (b->ipkt[0]) { case SSH_MSG_USERAUTH_SUCCESS: (*s->uastate.curalg->done)(&s->uastate); s->stage = UAS_DONE; while (pktq_get(&s->oq,&b->opkt[0],&b->oplen,sizeof(b->opkt))) { below_opkt(l); } break; case SSH_MSG_USERAUTH_FAILURE: userauth_failure(&s->uastate); break; case SSH_MSG_USERAUTH_BANNER: handle_banner(b); break; case 60 ... 79: switch ((*s->uastate.curalg->run)(&s->uastate)) { case UA_READ: break; case UA_SEND: below_opkt(s->uastate.layer); break; case UA_NEXT: /* We don't have a contin list at this point, which is annoying. Get one with "none". */ (*s->uastate.curalg->done)(&s->uastate); start_method(l,&s->uastate,uaalg_vfind_c("none")); break; default: abort(); break; } break; default: badmsg("PICKMETHOD","type %d",b->ipkt[0]); break; } break; case UAS_DONE: above_ipkt(l); break; } } static void uac_w(LAYER *l, void *arg) { UAC_STATE *s; s = arg; switch (s->stage) { default: pktq_put(&s->oq,&l->b->opkt[0],l->b->oplen); break; case UAS_DONE: below_opkt(l); break; } } LAYERDESC layer_userauth_conn = { &uac_init, &uac_r, &uac_w };