📄 krb4-security.c
字号:
} else { security = NULL; body = pkt->body; } /* * Get a checksum of the non-security parts of the body */ cksum = krb4_cksum(body); /* * Now deal with the data we did (or didn't) parse above */ switch (pkt->type) { case P_REQ: /* * Request packets have a ticket after the security tag * Get the ticket, make sure the checksum agrees with the * checksum of the request we sent. * * Must look like: SECURITY TICKET [ticket str] */ /* there must be some security info */ if (security == NULL) return (-1); /* second word must be TICKET */ if ((tok = strtok(security, " ")) == NULL) return (-1); if (strcmp(tok, "TICKET") != 0) { security_seterror(&kh->sech, _("REQ SECURITY line parse error, expecting TICKET, got %s"), tok); return (-1); } /* the third word is the encoded ticket */ if ((tok = strtok(NULL, "")) == NULL) return (-1); if (check_ticket(kh, pkt, tok, cksum) < 0) return (-1); /* we're good to go */ break; case P_REP: /* * Reply packets check the mutual authenticator for this host. * * Must look like: SECURITY MUTUAL-AUTH [mutual auth str] */ if (security == NULL) return (-1); if ((tok = strtok(security, " ")) == NULL) return (-1); if (strcmp(tok, "MUTUAL-AUTH") != 0) { security_seterror(&kh->sech, "REP SECURITY line parse error, expecting MUTUAL-AUTH, got %s", tok); return (-1); } if ((tok = strtok(NULL, "")) == NULL) return (-1); if (check_mutual_auth(kh, tok) < 0) return (-1); case P_ACK: case P_NAK: default: /* * These packets have no security. They should, but such * is life. We can't change it without breaking compatibility. * * XXX Should we complain if argc > 0? (ie, some security info was * sent?) */ break; } /* * If there is security info at the front of the packet, we need * to shift the rest of the data up and nuke it. */ if (body != pkt->body) memmove(pkt->body, body, strlen(body) + 1); return (0);}/* * Check the ticket in a REQ packet for authenticity */static intcheck_ticket( struct krb4_handle *kh, const pkt_t * pkt, const char * ticket_str, unsigned long cksum){ char inst[INST_SZ]; KTEXT_ST ticket; AUTH_DAT auth; struct passwd *pwd; char *user; int rc; (void)pkt; /* Quiet unused parameter warning */ assert(kh != NULL); assert(pkt != NULL); assert(ticket_str != NULL); ticket.length = (int)sizeof(ticket.dat); astr2bin((unsigned char *)ticket_str, ticket.dat, &ticket.length); assert(ticket.length > 0); /* get a copy of the instance into writable memory */#if CLIENT_HOST_INSTANCE == HOSTNAME_INSTANCE strncpy(inst, krb_get_phost(hostname), SIZEOF(inst) - 1);#else strncpy(inst, CLIENT_HOST_INSTANCE, SIZEOF(inst) - 1);#endif inst[SIZEOF(inst) - 1] = '\0'; /* get the checksum out of the ticket */ rc = krb_rd_req(&ticket, CLIENT_HOST_PRINCIPAL, inst, kh->peer.sin6_addr.s_addr, &auth, CLIENT_HOST_KEY_FILE); if (rc != 0) { security_seterror(&kh->sech, _("krb_rd_req failed for %s: %s (%d)"), kh->hostname, error_message(rc), rc); return (-1); } /* verify and save the checksum and session key */ if (auth.checksum != cksum) { security_seterror(&kh->sech, _("krb4 checksum mismatch for %s (remote=%lu, local=%lu)"), kh->hostname, (long)auth.checksum, cksum); return (-1); } kh->cksum = (unsigned long)cksum; memcpy(kh->session_key, auth.session, SIZEOF(kh->session_key)); /* * If CHECK_USERID is set, then we need to specifically * check the userid we're forcing ourself to. Otherwise, * just check the login we're currently setuid to. */#ifdef CHECK_USERID if ((pwd = getpwnam(CLIENT_LOGIN)) == NULL) error(_("error [getpwnam(%s) fails]"), CLIENT_LOGIN);#else if ((pwd = getpwuid(getuid())) == NULL) error(_("error [getpwuid(%d) fails]"), getuid());#endif /* save the username in case it's overwritten */ user = stralloc(pwd->pw_name); /* check the klogin file */ if (kuserok(&auth, user)) { security_seterror(&kh->sech, _("access as %s not allowed from %s.%s@%s"), user, auth.pname, auth.pinst, auth.prealm); amfree(user); return (-1); } amfree(user); /* it's good */ return (0);}/* * Verify that the packet received is secure by verifying that it has * the same checksum as our request + 1. */static intcheck_mutual_auth( struct krb4_handle *kh, const char * mutual_auth_str){ union mutual mutual; int len; assert(kh != NULL); assert(mutual_auth_str != NULL); assert(kh->inst[0] != '\0'); /* we had better have a checksum from a request we sent */ assert(kh->cksum != 0); /* convert the encoded string into binary data */ len = (int)sizeof(mutual); astr2bin((unsigned char *)mutual_auth_str, (unsigned char *)&mutual, &len); /* unencrypt the string using the key in the ticket file */ host2key(kh->hostname, kh->inst, &kh->session_key); decrypt_data(&mutual, (size_t)len, &kh->session_key); mutual.cksum = (unsigned long)ntohl((guint32)mutual.cksum); /* the data must be the same as our request cksum + 1 */ if (mutual.cksum != (kh->cksum + 1)) { security_seterror(&kh->sech, _("krb4 checksum mismatch from %s (remote=%lu, local=%lu)"), kh->hostname, mutual.cksum, kh->cksum + 1); return (-1); } return (0);}/* * Convert a pkt_t into a header string for our packet */static const char *kpkthdr2str( const struct krb4_handle * kh, const pkt_t * pkt){ static char retbuf[256]; assert(kh != NULL); assert(pkt != NULL); g_snprintf(retbuf, SIZEOF(retbuf), "Amanda %d.%d %s HANDLE %s SEQ %d\n", VERSION_MAJOR, VERSION_MINOR, pkt_type2str(pkt->type), kh->proto_handle, kh->sequence); /* check for truncation. If only we had asprintf()... */ assert(retbuf[strlen(retbuf) - 1] == '\n'); return (retbuf);}/* * Parses out the header line in 'str' into the pkt and handle * Returns negative on parse error. */static intstr2kpkthdr( const char *origstr, pkt_t * pkt, char * handle, size_t handlesize, int * sequence){ char *str; const char *tok; assert(origstr != NULL); assert(pkt != NULL); str = stralloc(origstr); /* "Amanda %d.%d <ACK,NAK,...> HANDLE %s SEQ %d\n" */ /* Read in "Amanda" */ if ((tok = strtok(str, " ")) == NULL || strcmp(tok, "Amanda") != 0) goto parse_error; /* nothing is done with the major/minor numbers currently */ if ((tok = strtok(NULL, " ")) == NULL || strchr(tok, '.') == NULL) goto parse_error; /* Read in the packet type */ if ((tok = strtok(NULL, " ")) == NULL) goto parse_error; pkt_init(pkt, pkt_str2type(tok), ""); if (pkt->type == (pktype_t)-1) goto parse_error; /* Read in "HANDLE" */ if ((tok = strtok(NULL, " ")) == NULL || strcmp(tok, "HANDLE") != 0) goto parse_error; /* parse the handle */ if ((tok = strtok(NULL, " ")) == NULL) goto parse_error; strncpy(handle, tok, handlesize - 1); handle[handlesize - 1] = '\0'; /* Read in "SEQ" */ if ((tok = strtok(NULL, " ")) == NULL || strcmp(tok, "SEQ") != 0) goto parse_error; /* parse the sequence number */ if ((tok = strtok(NULL, "\n")) == NULL) goto parse_error; *sequence = atoi(tok); /* Save the body, if any */ if ((tok = strtok(NULL, "")) != NULL) pkt_cat(pkt, tok); amfree(str); return (0);parse_error:#if 0 /* XXX we have no way of passing this back up */ security_seterror(&kh->sech, _("parse error in packet header : '%s'"), origstr);#endif amfree(str); return (-1);}static voidhost2key( const char *hostname, const char *inst, des_cblock *key){ char realm[256]; CREDENTIALS cred; strncpy(realm, krb_realmofhost((char *)hostname), SIZEOF(realm) - 1); realm[SIZEOF(realm) - 1] = '\0';#if CLIENT_HOST_INSTANCE != HOSTNAME_INSTANCE inst = CLIENT_HOST_INSTANCE#endif krb_get_cred(CLIENT_HOST_PRINCIPAL, (char *)inst, realm, &cred); memcpy(key, cred.session, SIZEOF(des_cblock));}/* * Convert a chunk of data into a string. */static const char *bin2astr( const unsigned char *buf, int len){ static const char tohex[] = "0123456789ABCDEF"; static char *str = NULL; char *q; const unsigned char *p; size_t slen; int i; /* * calculate output string len * We quote everything, so each input byte == 3 output chars, plus * two more for quotes */ slen = ((size_t)len * 3) + 2; /* allocate string and fill it in */ if (str != NULL) amfree(str); str = alloc(slen + 1); p = buf; q = str; *q++ = '"'; for (i = 0; i < len; i++) { *q++ = '$'; *q++ = tohex[(*p >> 4) & 0xF]; *q++ = tohex[*p & 0xF]; p++; } *q++ = '"'; *q = '\0'; /* make sure we didn't overrun our allocated buffer */ assert((size_t)(q - str) == slen); return (str);}/* * Convert an encoded string into a block of data bytes */static voidastr2bin( const unsigned char *astr, unsigned char * buf, int * lenp){ const unsigned char *p; unsigned char *q;#define fromhex(h) (isdigit((int)h) ? (h) - '0' : (h) - 'A' + 10) /* * Skip leading quote, if any */ if (*astr == '"') astr++; /* * Run through the string. Anything starting with a $ is a three * char representation of this byte. Everything else is literal. */ for (p = astr, q = buf; *p != '"' && *p != '\0'; ) { if (*p != '$') { *q++ = *p++; } else { *q++ = (fromhex(p[1]) << 4) + fromhex(p[2]); p += 3; } if ((int)(q - buf) >= *lenp) break; } *lenp = q - buf;}static unsigned longkrb4_cksum( const char *str){ des_cblock seed; memset(seed, 0, SIZEOF(seed)); /* * The first arg is an unsigned char * in some krb4 implementations, * and in others, it's a des_cblock *. Just make it void here * to shut them all up. */ return (quad_cksum((void *)str, NULL, (long)strlen(str), 1, &seed));}static voidkrb4_getinst( const char *hname, char * inst, size_t size){ /* * Copy the first part of the hostname up to the first '.' into * the buffer provided. Leave room for a NULL. */ while (size > 1 && *hname != '\0' && *hname != '.') { *inst++ = isupper((int)*hname) ? tolower((int)*hname) : *hname; hname++; size--; } *inst = '\0';}static voidencrypt_data( void * data, size_t length, des_cblock *key){ des_key_schedule sched; /* * First arg is des_cblock * in some places, and just des_cblock in * others. Since des_cblock is a char array, they are equivalent. * Just cast it to void * to keep both compilers quiet. typedefing * arrays should be outlawed. */ des_key_sched((void *)key, sched); des_pcbc_encrypt(data, data, (long)length, sched, key, DES_ENCRYPT);}static voiddecrypt_data( void * data, size_t length, des_cblock *key){ des_key_schedule sched; des_key_sched((void *)key, sched); des_pcbc_encrypt(data, data, (long)length, sched, key, DES_DECRYPT);}/* * like write(), but always writes out the entire buffer. */static intknet_write( int fd, const void *vbuf, size_t size){ const char *buf = vbuf; /* so we can do ptr arith */ ssize_t n; while (size > 0) { n = write(fd, buf, size); if (n < 0) return (-1); buf += n; size -= n; } return (0);}/* * Like read(), but waits until the entire buffer has been filled. */static intknet_read( int fd, void * vbuf, size_t size, int timeout){ char *buf = vbuf; /* ptr arith */ ssize_t n; int neof = 0; SELECT_ARG_TYPE readfds; struct timeval tv; while (size > 0) { FD_ZERO(&readfds); FD_SET(fd, &readfds); tv.tv_sec = timeout; tv.tv_usec = 0; switch (select(fd + 1, &readfds, NULL, NULL, &tv)) { case 0: errno = ETIMEDOUT; /* FALLTHROUGH */ case -1: return (-1); case 1: assert(FD_ISSET(fd, &readfds)); break; default: assert(0); break; } n = read(fd, buf, size); if (n < 0) return (-1); /* we only tolerate so many eof's */ if (n == 0 && ++neof > 1024) { errno = EIO; return (-1); } buf += n; size -= n; } return (0);}#if 0/* -------------------------- *//* debug routines */static voidprint_hex( const char * str, const unsigned char * buf, size_t len){ int i; dbprintf("%s:", str); for(i=0;i<len;i++) { if(i%25 == 0) dbprintf("\n"); dbprintf(" %02X", buf[i]); } dbprintf("\n");}static voidprint_ticket( const char *str, KTEXT tktp){ dbprintf(_("%s: length %d chk %lX\n"), str, tktp->length, tktp->mbz); print_hex(_("ticket data"), tktp->dat, tktp->length); fflush(stdout);}static voidprint_auth( AUTH_DAT *authp){ g_printf("\nAuth Data:\n"); g_printf(" Principal \"%s\" Instance \"%s\" Realm \"%s\"\n", authp->pname, authp->pinst, authp->prealm); g_printf(" cksum %d life %d keylen %ld\n", authp->checksum, authp->life, SIZEOF(authp->session)); print_hex("session key", authp->session, SIZEOF(authp->session)); fflush(stdout);}static voidprint_credentials( CREDENTIALS *credp){ g_printf("\nCredentials:\n"); g_printf(" service \"%s\" instance \"%s\" realm \"%s\" life %d kvno %d\n", credp->service, credp->instance, credp->realm, credp->lifetime, credp->kvno); print_hex("session key", credp->session, SIZEOF(credp->session)); print_hex("ticket", credp->ticket_st.dat, credp->ticket_st.length); fflush(stdout);}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -